README.TXT

The \Mssdk\Samples\NetDS directory contains samples for the following
Platform SDK components:

* Active Directory Service Interface (ADSI)
* Microsoft Message Queue
* Routing and Remote Access Services
* TAPI
* Windows Sockets
* SNA Server
* RPC
* SNMP
* NetAPI
* DLC

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\NetDS
node, you'll need to install the Network and Distributed Services
components from the Platform SDK compact disc or download and install
them from http://www.microsoft.com/msdn/sdk/platform.htm.


README.TXT

Active Directory Service Interfaces 1.0, Sample C++ Application

Overview:

This simple command line based program shows how to use Active Directory to browse and display objects of any Directory Service. Its usage is as follows:

    adscmd [list|dump] <ADsPath>

For example, for a Windows NT system

    adscmd dump WinNT://MYDOMAIN/SomeUser

will dump the properties of SomeUser in MYDOMAIN. Or, on an NDS system:

    adscmd list NDS://MYTREE

will list the top of the MYTREE tree. Note that the namespace ("WinNT", "NDS") is case sensitive.

List of files:

MAIN.CXXmain program
UTIL.HXXhelper functions
DUMP.CXXfunctions to dump properties of an object
ENUM.CXXfunctions to enumerate children of a container
MAIN.HXXmain include file
MAKEFILEmakefile


ENUM.CXX

//----------------------------------------------------------------------------
//
//  Microsoft Active Directory 1.0 Sample Code
//
//  Copyright (C) Microsoft Corporation, 1996
//
//  File:       enum.cxx
//
//  Contents:   Active Drectory container enumeration
//
//
//----------------------------------------------------------------------------

#include "main.hxx"

//
// Private defines
//

#define MAX_ADS_FILTERS   10
#define MAX_ADS_ENUM      100     // number of entries to read each time

//
// Local functions
//

HRESULT
PrintLongFormat(
    IADs * pObject
    );

//
//  List contents of a container identified by the ADsPath
//

int
DoList(char * AnsiADsPath)
{
    HRESULT hr;
    int i = 0 ;
    LPWSTR pszADsPath, apszTypes[MAX_ADS_FILTERS] ;

    if (!(pszADsPath = AllocateUnicodeString(AnsiADsPath))) {

        return(1) ;
    }

    apszTypes[0] = NULL ;

    //
    //  Filter may be set as follows. For example, to get users and group:
    //
    //  apszTypes[0] = L"User" ;
    //  apszTypes[1] = L"Group" ;
    //  apszTypes[2] = NULL ;
    //

    hr = EnumObject(
             pszADsPath,
             apszTypes,
             i
             );

    return (FAILED(hr) ? 1 : 0) ;
}

//
// Enumerates the contents of a container object.
//

HRESULT
EnumObject(
    LPWSTR pszADsPath,
    LPWSTR * lppClassNames,
    DWORD dwClassNames
    )
{
    ULONG cElementFetched = 0L;
    IEnumVARIANT * pEnumVariant = NULL;
    VARIANT VarFilter, VariantArray[MAX_ADS_ENUM];

    HRESULT hr;
    IADsContainer * pADsContainer =  NULL;
    DWORD dwObjects = 0, dwEnumCount = 0, i = 0;
    BOOL  fContinue = TRUE;

    VariantInit(&VarFilter);

    hr = ADsGetObject(
                pszADsPath,
                IID_IADsContainer,
                (void **)&pADsContainer
                );

    if (FAILED(hr)) {

        printf("\"%S\" is not a valid container object.\n", pszADsPath) ;
        goto exitpoint ;
    }

    hr = ADsBuildVarArrayStr(
                lppClassNames,
                dwClassNames,
                &VarFilter
                );
    BAIL_ON_FAILURE(hr);

    hr = pADsContainer->put_Filter(VarFilter);
    BAIL_ON_FAILURE(hr);

    hr = ADsBuildEnumerator(
            pADsContainer,
            &pEnumVariant
            );
    BAIL_ON_FAILURE(hr);

    while (fContinue) {

        IADs *pObject ;

        hr = ADsEnumerateNext(
                    pEnumVariant,
                    MAX_ADS_ENUM,
                    VariantArray,
                    &cElementFetched
                    );

        if (hr == S_FALSE) {
            fContinue = FALSE;
        }

        dwEnumCount++;

        for (i = 0; i < cElementFetched; i++ ) {

            IDispatch *pDispatch = NULL;

            pDispatch = VariantArray[i].pdispVal;

            hr = pDispatch->QueryInterface(IID_IADs,
                                           (VOID **) &pObject) ;
            BAIL_ON_FAILURE(hr);

            PrintLongFormat(pObject);

            pObject->Release();
            pDispatch->Release();
        }

        memset(VariantArray, 0, sizeof(VARIANT)*MAX_ADS_ENUM);

        dwObjects += cElementFetched;

    }

    printf("Total Number of Objects enumerated is %d\n", dwObjects);

    if (pEnumVariant) {
        pEnumVariant->Release();
    }

    if (pADsContainer) {
        pADsContainer->Release();
    }

    return(S_OK);

error:
    if (FAILED(hr)) {

        printf("Unable to list contents of: %S\n", pszADsPath) ;
    }

exitpoint:

    if (pEnumVariant) {
        pEnumVariant->Release();
    }

    VariantClear(&VarFilter);

    if (pADsContainer) {
        pADsContainer->Release();
    }

    return(hr);
}

HRESULT
PrintLongFormat(IADs * pObject)
{

    HRESULT hr = S_OK;
    BSTR bstrName = NULL;
    BSTR bstrClass = NULL;
    BSTR bstrSchema = NULL;

    hr = pObject->get_Name(&bstrName) ;
    BAIL_ON_FAILURE(hr);

    hr = pObject->get_Class(&bstrClass);
    BAIL_ON_FAILURE(hr);

    // hr = pObject->get_Schema(&bstrSchema);

    printf("  %S(%S)\n", bstrName, bstrClass) ;

error:
    if (bstrClass) {
        SysFreeString(bstrClass);
    }
    if (bstrName) {
        SysFreeString(bstrName);
    }
    if (bstrSchema) {
        SysFreeString(bstrSchema);
    }
    return(hr);
}


MAIN.CXX

//----------------------------------------------------------------------------
//
//  Microsoft Active Directory 1.0 Sample Code
//
//  Copyright (C) Microsoft Corporation, 1996
//
//  File:       main.cxx
//
//  Contents:   Main for adscmd
//
//
//----------------------------------------------------------------------------

#include "main.hxx"

//-------------------------------------------------------------------------
//
// main
//
//-------------------------------------------------------------------------

int __cdecl
main(int argc, char * argv[])
{
    HRESULT hr;
    int exitcode;

    if (argc != 3) {
        PrintUsage() ;
        exit(1);
    }

    hr = CoInitialize(NULL);

    if (FAILED(hr)) {
        printf("CoInitialize failed\n");
        exit(1);
    }

    if (_stricmp(argv[1],"list") == 0) {

        //
        // Call the List helper function
        //

        exitcode = DoList(argv[2]) ;
    }
    else if (_stricmp(argv[1],"dump") == 0) {

        //
        // Call the Dump helper function
        //

        exitcode = DoDump(argv[2]) ;
    }
    else {

        //
        // Unknown command
        //

        PrintUsage() ;
        exitcode = 1 ;
    }

    exit(exitcode) ;
    return 0;
}


DUMP.CXX

//----------------------------------------------------------------------
//
//  Microsoft Active Directory 1.0 Sample Code
//
//  Copyright (C) Microsoft Corporation, 1996
//
//  File:       dump.cxx
//
//  Contents:   Functions for dumping the properties for an object.
//
//
//----------------------------------------------------------------------

#include "main.hxx"

//
// Given an ADsPath, bind to the object and call the DumpObject routine.
//

int
DoDump(char *AnsiADsPath)
{
 HRESULT hr = E_OUTOFMEMORY ;
 LPWSTR pszADsPath = NULL;
 IADs * pADs = NULL;

 //
 // Convert path to unicode and then bind to the object.
 //

 BAIL_ON_NULL(pszADsPath = AllocateUnicodeString(AnsiADsPath));

 hr = ADsGetObject(
             pszADsPath,
             IID_IADs,
             (void **)&pADs
             );

 if (FAILED(hr)) {

     printf("Failed to bind to object: %S\n", pszADsPath) ;
 }
 else {

     //
     // Dump the object
     //

     hr = DumpObject(pADs);

     if (FAILED(hr)) {

         printf("Unable to read properties of: %S\n", pszADsPath) ;
     }

     pADs->Release();
 }

error:

 FreeUnicodeString(pszADsPath);

 return (FAILED(hr) ? 1 : 0) ;
}

//
// Given an ADs pointer, dump the contents of the object
//

HRESULT
DumpObject(
 IADs * pADs
 )
{
 HRESULT hr;
HRESULT hrSA;
 IADs * pADsProp = NULL;
 VARIANT var;
ZeroMemory(&var,sizeof(var));
VARIANT *   pvarPropName = NULL;
 DWORD i = 0;
VARIANT varProperty;
 IDispatch * pDispatch = NULL;

 //
 // Access the schema for the object
 //

 hr = GetPropertyList(
             pADs,
             &var);
 BAIL_ON_FAILURE(hr);

 //
 // List the Properties
//
hr = SafeArrayAccessData(var.parray, (void **) &pvarPropName);
BAIL_ON_FAILURE(hr);

for (i = 0; i < var.parray->rgsabound[0].cElements; i++){

   //
     // Get a property and print it out. The HRESULT is passed to
     // PrintProperty.
     //

     hr = pADs->Get(
             pvarPropName[i].bstrVal,
             &varProperty
             );
   PrintProperty(
         pvarPropName[i].bstrVal,
         hr,
         varProperty
         );

}

hr = SafeArrayUnaccessData(var.parray);

error:
// Don't destroy hr in case we're here from BAIL_ON_FAILURE
if(var.parray) hrSA = SafeArrayDestroy(var.parray);

return(hr);
}

HRESULT
GetPropertyList(
 IADs * pADs,
 VARIANT * pvar )
{
 HRESULT hr= S_OK;
 BSTR bstrSchemaPath = NULL;
IADsClass * pADsClass = NULL;

 hr = pADs->get_Schema(&bstrSchemaPath);
 BAIL_ON_FAILURE(hr);

 hr = ADsGetObject(
             bstrSchemaPath,
             IID_IADsClass,
             (void **)&pADsClass);
 BAIL_ON_FAILURE(hr);

//Put SafeArray of bstr's into input variant struct
hr = pADsClass->get_MandatoryProperties(pvar);
BAIL_ON_FAILURE(hr);

error:
 if (bstrSchemaPath) {
     SysFreeString(bstrSchemaPath);
 }

 if (pADsClass) {
     pADsClass->Release();
 }

 return(hr);
}


UTIL.CXX

//---------------------------------------------------------------------------
//
//  Microsoft Active Directory 1.0 Sample Code
//
//  Copyright (C) Microsoft Corporation, 1992 - 1995
//
//  File:  util.cxx
//
//  Contents:  Ansi to Unicode conversions and misc helper functions
//
//----------------------------------------------------------------------------

#include "main.hxx"

//
// Local functions
//

int
AnsiToUnicodeString(
    LPSTR pAnsi,
    LPWSTR pUnicode,
    DWORD StringLength
    )
{
    int iReturn;

    if( StringLength == 0 )
        StringLength = strlen( pAnsi );

    iReturn = MultiByteToWideChar(CP_ACP,
                                  MB_PRECOMPOSED,
                                  pAnsi,
                                  StringLength + 1,
                                  pUnicode,
                                  StringLength + 1 );

    //
    // Ensure NULL termination.
    //
    pUnicode[StringLength] = 0;

    return iReturn;
}

int
UnicodeToAnsiString(
    LPWSTR pUnicode,
    LPSTR pAnsi,
    DWORD StringLength
    )
{
    LPSTR pTempBuf = NULL;
    INT   rc = 0;

    if( StringLength == 0 ) {

        //
        // StringLength is just the
        // number of characters in the string
        //
        StringLength = wcslen( pUnicode );
    }

    //
    // WideCharToMultiByte doesn't NULL terminate if we're copying
    // just part of the string, so terminate here.
    //

    pUnicode[StringLength] = 0;

    //
    // Include one for the NULL
    //
    StringLength++;

    //
    // Unfortunately, WideCharToMultiByte doesn't do conversion in place,
    // so allocate a temporary buffer, which we can then copy:
    //

    if( pAnsi == (LPSTR)pUnicode )
    {
        pTempBuf = (LPSTR)LocalAlloc( LPTR, StringLength );
        pAnsi = pTempBuf;
    }

    if( pAnsi )
    {
        rc = WideCharToMultiByte( CP_ACP,
                                  0,
                                  pUnicode,
                                  StringLength,
                                  pAnsi,
                                  StringLength,
                                  NULL,
                                  NULL );
    }

    /* If pTempBuf is non-null, we must copy the resulting string
     * so that it looks as if we did it in place:
     */
    if( pTempBuf && ( rc > 0 ) )
    {
        pAnsi = (LPSTR)pUnicode;
        strcpy( pAnsi, pTempBuf );
        LocalFree( pTempBuf );
    }

    return rc;
}

LPWSTR
AllocateUnicodeString(
    LPSTR  pAnsiString
    )
{
    LPWSTR  pUnicodeString = NULL;

    if (!pAnsiString)
        return NULL;

    pUnicodeString = (LPWSTR)LocalAlloc(
                        LPTR,
                        strlen(pAnsiString)*sizeof(WCHAR) + sizeof(WCHAR)
                        );

    if (pUnicodeString) {

        AnsiToUnicodeString(
            pAnsiString,
            pUnicodeString,
            0
            );
    }

    return pUnicodeString;
}

void
FreeUnicodeString(
    LPWSTR  pUnicodeString
    )
{

    LocalFree(pUnicodeString);

    return;
}

//
// Misc helper functions for displaying data.
//

HRESULT
PrintVariant(
    VARIANT varPropData
    )
{
    HRESULT hr;
    BSTR bstrValue;

    switch (varPropData.vt) {
    case VT_I4:
        printf("%d", varPropData.lVal);
        break;
    case VT_BSTR:
        printf("%S", varPropData.bstrVal);
        break;

    case VT_BOOL:
        printf("%d", V_BOOL(&varPropData));
        break;

    case (VT_ARRAY | VT_VARIANT):
        PrintVariantArray(varPropData);
        break;

    case VT_DATE:
        hr = VarBstrFromDate(
                 varPropData.date,
                 LOCALE_SYSTEM_DEFAULT,
                 LOCALE_NOUSEROVERRIDE,
                 &bstrValue
                 );
        printf("%S", bstrValue);
        break;

    default:
        printf("Data type is %d\n", varPropData.vt);
        break;

    }
    printf("\n");
    return(S_OK);
}

HRESULT
PrintVariantArray(
    VARIANT var
    )
{
    LONG dwSLBound = 0;
    LONG dwSUBound = 0;
    VARIANT v;
    LONG i;
    HRESULT hr = S_OK;

    if(!((V_VT(&var) &  VT_VARIANT) &&  V_ISARRAY(&var))) {
        return(E_FAIL);
    }

    //
    // Check that there is only one dimension in this array
    //

    if ((V_ARRAY(&var))->cDims != 1) {
        hr = E_FAIL;
        BAIL_ON_FAILURE(hr);
    }
    //
    // Check that there is atleast one element in this array
    //

    if ((V_ARRAY(&var))->rgsabound[0].cElements == 0){
        hr = E_FAIL;
        BAIL_ON_FAILURE(hr);
    }

    //
    // We know that this is a valid single dimension array
    //

    hr = SafeArrayGetLBound(V_ARRAY(&var),
                            1,
                            (long FAR *)&dwSLBound
                            );
    BAIL_ON_FAILURE(hr);

    hr = SafeArrayGetUBound(V_ARRAY(&var),
                            1,
                            (long FAR *)&dwSUBound
                            );
    BAIL_ON_FAILURE(hr);

    for (i = dwSLBound; i <= dwSUBound; i++) {
        VariantInit(&v);
        hr = SafeArrayGetElement(V_ARRAY(&var),
                                (long FAR *)&i,
                                &v
                                );
        if (FAILED(hr)) {
            continue;
        }
        if (i < dwSUBound) {
            printf("%S, ", v.bstrVal);
        } else {
            printf("%S", v.bstrVal);
        }
    }
    return(S_OK);

error:
    return(hr);
}

HRESULT
PrintProperty(
    BSTR bstrPropName,
    HRESULT hRetVal,
    VARIANT varPropData
    )
{
    HRESULT hr = S_OK;

    switch (hRetVal) {

    case 0:
        printf("%-32S: ", bstrPropName);
        PrintVariant(varPropData);
        break;

    case E_ADS_CANT_CONVERT_DATATYPE:
        printf("%-32S: ", bstrPropName);
        printf("<Data could not be converted for display>\n");
        break;

    default:
        printf("%-32S: ", bstrPropName);
        printf("<Data not available>\n");
        break;

    }
    return(hr);
}

void
PrintUsage(
    void
    )
{
    printf("usage: adscmd [list|dump] <ADsPath>\n") ;
}


README.TXT

Active Directory Service Interfaces 1.0, Sample C++ Query Application

Overview:

This simple command line based program shows how to use Active Directory to
query and display objects of a Directory Service using IDirectorySearch
interface. Its usage is as follows:

For Example: adsqry /b "LDAP://ntdsdc0/O=Internet/DC=COM/DC=MICROSOFT/DC=NTDEV"
 /f "objectClass=Group" /a "ADsPath, name, description"  /u "CN=NTDS,CN=Users,DC
=NTDEV,DC=MICROSOFT,DC=COM,O=INTERNET" ""

For Example: adsqry /b "'LDAP://ntdsdc0/O=Internet/DC=COM/DC=MICROSOFT/DC=NTDEV'
"  /f "objectClass='Group'" /a "ADsPath, name, description"  /d sql /u "CN=NTDS,
CN=Users,DC=NTDEV,DC=MICROSOFT,DC=COM,O=INTERNET" ""

will dump the ADsPathName, Name and description attributes for all
the group objects found in the directory starting the query at the object
LDAP://O=Internet/DC=COM/DC=MICROSOFT/DC=NTDEV authenticating with user name
CN=NTDS,CN=Users,DC=NTDEV,DC=MICROSOFT,DC=COM,O=INTERNET and NULL password.

The second example does the same thing but specifies the search filter in the
SQL syntax.

List of files

MAIN.CXXmain program
UTIL.HXXhelper functions
MAIN.HXXmain include file
MAKEFILEmakefile


MAIN.CXX

//----------------------------------------------------------------------------
//
//  Microsoft Active Directory 1.1 Sample Code
//
//  Copyright (C) Microsoft Corporation, 1996
//
//  File:       main.cxx
//
//  Contents:   Main for adsqry
//
//
//----------------------------------------------------------------------------

#include "main.hxx"

#define MAX_SIZE 10
//
// Globals representing the properties
//

DBPROPSET          rgDBPropSet[MAX_SIZE], rgCmdPropSet[MAX_SIZE];
DBPROP             rgDBProp[MAX_SIZE], rgCmdProp[MAX_SIZE];

ULONG              cDBPropSet, cCmdPropSet, cDBProp, cCmdProp;

LPWSTR pszCommandText;

GUID               rguidDialect = DBGUID_DEFAULT;

DWORD cErr;

int __cdecl
main( int argc, char ** argv)
{

    HRESULT            hr;
    ULONG              i, j;
    HROW             * phRows;

    WCHAR              pszErrorBuf[MAX_PATH], pszNameBuf[MAX_PATH];
    DWORD              dwError;

    OLECHAR          * szColNames = NULL;
    DBCOLUMNINFO     * prgColInfo = NULL;
    DBCOLUMNINFO     * rgInfo = NULL;
    WCHAR            * pStringBuffer = NULL;
    WCHAR            * pColInfoBuffer = NULL;

    IMalloc          * pIMalloc = NULL;
    IDBInitialize    * pIDBInit = NULL;
    IDBCreateSession * pIDBCS = NULL;
    IDBCreateCommand * pIDBCreateCommand = NULL;
    ICommandText     * pICommandText = NULL;
    ICommand         * pICommand = NULL;
    IRowset          * pIRowset = NULL;
    IAccessor        * pAccessor = NULL;
    IColumnsInfo     * pIColsInfo = NULL;
    ULONG              cCol, cRowsObtained, nAttrs;

    Data             * pMyData = NULL;
    DBBINDSTATUS     * pMyStatus = NULL;
    HACCESSOR          myAccessor = NULL;

    ICommandProperties * pICommandProperties;
    IDBProperties * pIDBProperties;

    hr = ProcessArgs(argc, argv);
    BAIL_ON_FAILURE(hr);

    hr = CoInitialize(NULL);
    if (FAILED(hr)) {
        printf("CoInitialize failed\n");
        exit(1);
    }

    //
    // Instantiate a data source object for LDAP provider
    //
    hr = CoCreateInstance(
             CLSID_ADsDSOObject,
             0,
             CLSCTX_INPROC_SERVER,
             IID_IDBInitialize,
             (void **)&pIDBInit
             );
    if(FAILED(hr)) {
        printf("CoCreateInstance failed \n");
        goto error;
    }

    //
    // Initialize the Data Source
    //
    hr = pIDBInit->Initialize();
    if(FAILED(hr)) {
        printf("IDBIntialize::Initialize failed \n");
        goto error;
    }

    if (cDBPropSet) {
        pIDBInit->QueryInterface(
            IID_IDBProperties,
            (void**) &pIDBProperties);
        if(FAILED(hr)) {
            printf("QueryInterface for IDBProperties failed \n");
            goto error;
        }

        hr = pIDBProperties->SetProperties(
                 cDBPropSet,
                 rgDBPropSet);

        if(FAILED(hr)) {
            printf("IDBProperties->SetProperties failed \n");
            goto error;
        }

        FREE_INTERFACE(pIDBProperties);
    }

    pIDBInit->QueryInterface(
        IID_IDBCreateSession,
        (void**) &pIDBCS);
    if(FAILED(hr)) {
        printf("QueryInterface for IDBCreateSession failed \n");
        goto error;
    }

    FREE_INTERFACE(pIDBInit);

    //
    // Create a session returning a pointer to its CreateCommand interface
    //
    hr = pIDBCS->CreateSession(
             NULL,
             IID_IDBCreateCommand,
             (LPUNKNOWN*) &pIDBCreateCommand
             );
    if(FAILED(hr)) {
        printf("IDBCreateSession::CreateSession failed \n");
        goto error;
    }

    FREE_INTERFACE(pIDBCS);

    //
    // Create a command from the session object
    //
    hr = pIDBCreateCommand->CreateCommand(
             NULL,
             IID_ICommandText,
             (LPUNKNOWN*) &pICommandText
             );

    if(FAILED(hr)) {
        printf(" IDBCreateCommand::CreateCommand failed\n");
        goto error;
    }

    FREE_INTERFACE(pIDBCreateCommand);

    //
    // Set the CommandText for the Query
    //
    hr = pICommandText->SetCommandText(
             rguidDialect,
             pszCommandText
             );

    if(FAILED(hr)) {
        printf("ICommandText::SetCommandText failed \n");
        goto error;
    }

    if (cCmdPropSet) {
        hr = pICommandText->QueryInterface(
                 IID_ICommandProperties,
                 (void**) &pICommandProperties);

        if(FAILED(hr)) {
            printf("QueryInterface for ICommandProperties failed \n");
            goto error;
        }

        hr = pICommandProperties->SetProperties(
                 cCmdPropSet,
                 rgCmdPropSet);

        if(FAILED(hr)) {
            printf("ICommandProperties:;SetProperties failed \n");
            goto error;
        }

        FREE_INTERFACE(pICommandProperties);
    }

    hr = pICommandText->QueryInterface(
             IID_ICommand,
             (void**) &pICommand);

    if(FAILED(hr)) {
        printf("QueryInterface for ICommand failed \n");
        goto error;
    }

    FREE_INTERFACE(pICommandText);

    //
    // Do the Query and get back a rowset
    //
    hr = pICommand->Execute(
             NULL,
             IID_IRowset,
             NULL,
             NULL,
             (LPUNKNOWN *)&pIRowset
             );
    if(FAILED(hr)) {
        printf("ICommand::Execute failed \n");
        goto error;
    }

    FREE_INTERFACE(pICommand);

    hr = pIRowset->QueryInterface(
             IID_IColumnsInfo,
             (void**) &pIColsInfo
             );
    if(FAILED(hr)) {
        printf("QueryInterface for IColumnsInfo failed \n");
        goto error;
    }

    hr = pIColsInfo->GetColumnInfo(
             &cCol,
             &prgColInfo,
             &szColNames
             );
    if(FAILED(hr)) {
        printf("IColumnsInfo::GetColumnInfo failed \n");
        goto error;
    }

    //
    // The no. of attributes is one less than the no. of columns because of
    // the Bookmark column
    //
    nAttrs = cCol - 1;

    pMyStatus = (DBBINDSTATUS *) LocalAlloc(
                                     LPTR,
                                     sizeof(DBBINDSTATUS) * nAttrs
                                     );
    BAIL_ON_NULL(pMyStatus);

    hr = CreateAccessorHelper(
             pIRowset,
             nAttrs,
             prgColInfo,
             &myAccessor,
             pMyStatus
             );
    if(FAILED(hr)) {
        printf("CreateAccessorHelper failed \n");
        goto error;
    }

    pMyData = (Data *) LocalAlloc(
                           LPTR,
                           sizeof(Data) * nAttrs
                           );
    if(!pMyData) {
        hr = E_OUTOFMEMORY;
        goto error;
    }

    //
    //  Get the rows; 256 at a time
    //
    phRows = NULL;
    hr = pIRowset->GetNextRows(
             NULL,
             0,
             256,
             &cRowsObtained,
             &phRows
             );
    if(FAILED(hr)) {
        printf("IRowset::GetNextRows failed \n");
        goto error;
    }

    j = cRowsObtained;
    while (cRowsObtained) {
        for (i = 0; i < cRowsObtained; i++) {
            //
            // Get the data from each row
            //
            hr = pIRowset->GetData(
                     phRows[i],
                     myAccessor,
                     (void*)pMyData
                     );
            if(FAILED(hr)) {
                printf("IRowset::GetData failed \n");
                goto error;
            }

            PrintData(pMyData, nAttrs, prgColInfo);
        }

        pIRowset->ReleaseRows(
                      cRowsObtained,
                      phRows,
                      NULL,
                      NULL,
                      NULL
                      );

        //
        // Get the next 256 rows
        //

        hr = pIRowset->GetNextRows(
                 NULL,
                 0,
                 256,
                 &cRowsObtained,
                 &phRows
                 );
        if(FAILED(hr)) {
            printf("IRowset::GetNextRows failed \n");
            goto error;
        }
        j+=cRowsObtained;
    }

    printf("Rows printed = %d\n", j);

    FREE_STRING(pszCommandText);

    CoGetMalloc(MEMCTX_TASK, &pIMalloc);
    IMALLOC_FREE(pIMalloc, prgColInfo);
    IMALLOC_FREE(pIMalloc, szColNames);

    FREE_INTERFACE(pIMalloc);
    FREE_INTERFACE(pAccessor);
    FREE_INTERFACE(pIColsInfo);
    FREE_INTERFACE(pIRowset);

    LOCAL_FREE(pMyStatus);
    LOCAL_FREE(pMyData);

    //
    // Uninitialize OLE.
    //
    CoUninitialize();

    exit(0);

error:

    CoGetMalloc(MEMCTX_TASK, &pIMalloc);
    IMALLOC_FREE(pIMalloc, prgColInfo);
    IMALLOC_FREE(pIMalloc, szColNames);

    FREE_STRING(pszCommandText);

    FREE_INTERFACE(pIMalloc);
    FREE_INTERFACE(pIDBInit);
    FREE_INTERFACE(pIDBCS);
    FREE_INTERFACE(pIDBCreateCommand);
    FREE_INTERFACE(pICommandText);
    FREE_INTERFACE(pICommand);
    FREE_INTERFACE(pIRowset);
    FREE_INTERFACE(pIColsInfo);
    FREE_INTERFACE(pAccessor);

    LOCAL_FREE(pMyStatus);
    LOCAL_FREE(pMyData);

    printf("Errors stopped the Query; hr = %x", hr);

    if(hr == ERROR_EXTENDED_ERROR) {
        hr = ADsGetLastError(
                 &dwError,
                 pszErrorBuf,
                 MAX_PATH,
                 pszNameBuf,
                 MAX_PATH
                 );
    }

    if(SUCCEEDED(hr)) {
        wprintf(L"Error in %s; %s\n", pszNameBuf, pszErrorBuf);
    }

    exit(1);
    return(0);
}

//+---------------------------------------------------------------------------
//
//  Function:   ProcessArgs
//
//  Synopsis:
//
//----------------------------------------------------------------------------

HRESULT
ProcessArgs(
    int argc,
    char * argv[]
    )
{
    argc--;
    int currArg = 1;
    LPWSTR pTemp = NULL;
    char *pszCurrPref = NULL, *pszCurrPrefValue = NULL;

    LPWSTR pszSearchBase = NULL, pszSearchFilter = NULL, pszAttrList = NULL;
    LPWSTR pszUserName = NULL, pszPassword = NULL;
    DWORD dwAuthFlags;

    cCmdProp = cDBProp = 0;

    while (argc) {
        if (argv[currArg][0] != '/' && argv[currArg][0] != '-')
            BAIL_ON_FAILURE (E_FAIL);
        switch (argv[currArg][1]) {
        case 'b':
            argc--;
            currArg++;
            if (argc <= 0)
                BAIL_ON_FAILURE (E_FAIL);
            pszSearchBase = AllocateUnicodeString(argv[currArg]);
            BAIL_ON_NULL(pszSearchBase);
            break;

        case 'f':
            argc--;
            currArg++;
            if (argc <= 0)
                BAIL_ON_FAILURE (E_FAIL);
            pszSearchFilter = AllocateUnicodeString(argv[currArg]);
            BAIL_ON_NULL(pszSearchFilter);
            break;

        case 'd':
            argc--;
            currArg++;
            if (argc <= 0)
                BAIL_ON_FAILURE (E_FAIL);

            if (!_stricmp(argv[currArg], "sql"))
                rguidDialect = DBGUID_SQL;
            else if (!_stricmp(argv[currArg], "ldap"))
                rguidDialect = DBGUID_LDAPDialect;
            else if (!_stricmp(argv[currArg], "default"))
                rguidDialect = DBGUID_DEFAULT;
            else
                BAIL_ON_FAILURE (E_FAIL);

            break;

        case 'a':
            argc--;
            currArg++;
            if (argc <= 0)
                BAIL_ON_FAILURE (E_FAIL);
            pszAttrList = AllocateUnicodeString(argv[currArg]);
            BAIL_ON_NULL(pszAttrList);

            break;

        case 'u':
            argc--;
            currArg++;
            if (argc <= 0)
                BAIL_ON_FAILURE (E_FAIL);
            pszUserName = AllocateUnicodeString(argv[currArg]);
            BAIL_ON_NULL(pszUserName);
            argc--;
            currArg++;
            if (argc <= 0)
                BAIL_ON_FAILURE (E_FAIL);
            pszPassword = AllocateUnicodeString(argv[currArg]);
            BAIL_ON_NULL(pszPassword);

            rgDBProp[cDBProp].dwPropertyID = DBPROP_AUTH_USERID;
            rgDBProp[cDBProp].dwOptions = DBPROPOPTIONS_REQUIRED;
            rgDBProp[cDBProp].vValue.vt = VT_BSTR;
            V_BSTR (&rgDBProp[cDBProp].vValue) = SysAllocString(pszUserName);
            cDBProp++;

            rgDBProp[cDBProp].dwPropertyID = DBPROP_AUTH_PASSWORD;
            rgDBProp[cDBProp].dwOptions = DBPROPOPTIONS_REQUIRED;
            rgDBProp[cDBProp].vValue.vt = VT_BSTR;
            V_BSTR (&rgDBProp[cDBProp].vValue) = SysAllocString(pszPassword);
            cDBProp++;

            break;

        case 'p':
            argc--;
            currArg++;
            if (argc <= 0)
                BAIL_ON_FAILURE (E_FAIL);

            pszCurrPref = strtok(argv[currArg], "=");
            pszCurrPrefValue = strtok(NULL, "=");
            if (!pszCurrPref || !pszCurrPrefValue)
                BAIL_ON_FAILURE(E_FAIL);

            if (!_stricmp(pszCurrPref, "asynchronous")) {
                rgCmdProp[cCmdProp].dwPropertyID = ADSIPROP_ASYNCHRONOUS;
                rgCmdProp[cCmdProp].dwOptions = DBPROPOPTIONS_REQUIRED;
                rgCmdProp[cCmdProp].vValue.vt = VT_BOOL;
                if (!_stricmp(pszCurrPrefValue, "yes" ))
                    V_BOOL (&rgCmdProp[cCmdProp].vValue) = VARIANT_TRUE;
                else if (!_stricmp(pszCurrPrefValue, "no" ))
                    V_BOOL (&rgCmdProp[cCmdProp].vValue) = VARIANT_FALSE;
                else
                    BAIL_ON_FAILURE(E_FAIL);
                cCmdProp++;
            }
            else if (!_stricmp(pszCurrPref, "attrTypesOnly")) {
                rgCmdProp[cCmdProp].dwPropertyID = ADSIPROP_ATTRIBTYPES_ONLY;
                rgCmdProp[cCmdProp].dwOptions = DBPROPOPTIONS_REQUIRED;
                rgCmdProp[cCmdProp].vValue.vt = VT_BOOL;
                if (!_stricmp(pszCurrPrefValue, "yes" ))
                    V_BOOL (&rgCmdProp[cCmdProp].vValue) = VARIANT_TRUE;
                else if (!_stricmp(pszCurrPrefValue, "no" ))
                    V_BOOL (&rgCmdProp[cCmdProp].vValue) = VARIANT_FALSE;
                else
                    BAIL_ON_FAILURE(E_FAIL);
                cCmdProp++;
            }
            else if (!_stricmp(pszCurrPref, "SecureAuth")) {
                if (!_stricmp(pszCurrPrefValue, "yes" ))
                    dwAuthFlags |= ADS_SECURE_AUTHENTICATION;
                else if (!_stricmp(pszCurrPrefValue, "no" ))
                    dwAuthFlags &= ~ADS_SECURE_AUTHENTICATION;
                else
                    BAIL_ON_FAILURE(E_FAIL);

                rgDBProp[cDBProp].dwPropertyID = DBPROP_AUTH_ENCRYPT_PASSWORD;
                rgDBProp[cDBProp].dwOptions = DBPROPOPTIONS_REQUIRED;
                rgDBProp[cDBProp].vValue.vt = VT_BSTR;
                V_BSTR (&rgDBProp[cDBProp].vValue) = SysAllocString(pszPassword);
                cDBProp++;

            }
            else if (!_stricmp(pszCurrPref, "derefAliases")) {
                rgCmdProp[cCmdProp].dwPropertyID = ADSIPROP_DEREF_ALIASES;
                rgCmdProp[cCmdProp].dwOptions = DBPROPOPTIONS_REQUIRED;
                rgCmdProp[cCmdProp].vValue.vt = VT_BOOL;
                if (!_stricmp(pszCurrPrefValue, "yes" ))
                    V_BOOL (&rgCmdProp[cCmdProp].vValue) = VARIANT_TRUE;
                else if (!_stricmp(pszCurrPrefValue, "no" ))
                    V_BOOL (&rgCmdProp[cCmdProp].vValue) = VARIANT_FALSE;
                else
                    BAIL_ON_FAILURE(E_FAIL);
                cCmdProp++;
            }
            else if (!_stricmp(pszCurrPref, "timeOut")) {
                rgCmdProp[cCmdProp].dwPropertyID = ADSIPROP_TIMEOUT;
                rgCmdProp[cCmdProp].dwOptions = DBPROPOPTIONS_REQUIRED;
                rgCmdProp[cCmdProp].vValue.vt = VT_I4;
                V_I4 (&rgCmdProp[cCmdProp].vValue) = atoi(pszCurrPrefValue);
                cCmdProp++;
            }
            else if (!_stricmp(pszCurrPref, "timeLimit")) {
                rgCmdProp[cCmdProp].dwPropertyID = ADSIPROP_TIME_LIMIT;
                rgCmdProp[cCmdProp].dwOptions = DBPROPOPTIONS_REQUIRED;
                rgCmdProp[cCmdProp].vValue.vt = VT_I4;
                V_I4 (&rgCmdProp[cCmdProp].vValue) = atoi(pszCurrPrefValue);
                cCmdProp++;
            }
            else if (!_stricmp(pszCurrPref, "sizeLimit")) {
                rgCmdProp[cCmdProp].dwPropertyID = ADSIPROP_SIZE_LIMIT;
                rgCmdProp[cCmdProp].dwOptions = DBPROPOPTIONS_REQUIRED;
                rgCmdProp[cCmdProp].vValue.vt = VT_I4;
                V_I4 (&rgCmdProp[cCmdProp].vValue) = atoi(pszCurrPrefValue);
                cCmdProp++;
            }
            else if (!_stricmp(pszCurrPref, "PageSize")) {
                rgCmdProp[cCmdProp].dwPropertyID = ADSIPROP_PAGESIZE;
                rgCmdProp[cCmdProp].dwOptions = DBPROPOPTIONS_REQUIRED;
                rgCmdProp[cCmdProp].vValue.vt = VT_I4;
                V_I4 (&rgCmdProp[cCmdProp].vValue) = atoi(pszCurrPrefValue);
                cCmdProp++;
            }
            else if (!_stricmp(pszCurrPref, "PagedTimeLimit")) {
                rgCmdProp[cCmdProp].dwPropertyID = ADSIPROP_PAGED_TIME_LIMIT;
                rgCmdProp[cCmdProp].dwOptions = DBPROPOPTIONS_REQUIRED;
                rgCmdProp[cCmdProp].vValue.vt = VT_I4;
                V_I4 (&rgCmdProp[cCmdProp].vValue) = atoi(pszCurrPrefValue);
                cCmdProp++;
            }
            else if (!_stricmp(pszCurrPref, "SearchScope")) {
                rgCmdProp[cCmdProp].dwPropertyID = ADSIPROP_SEARCH_SCOPE;
                rgCmdProp[cCmdProp].dwOptions = DBPROPOPTIONS_REQUIRED;
                rgCmdProp[cCmdProp].vValue.vt = VT_I4;
                if (!_stricmp(pszCurrPrefValue, "Base" ))
                    V_I4 (&rgCmdProp[cCmdProp].vValue) = ADS_SCOPE_BASE;
                else if (!_stricmp(pszCurrPrefValue, "OneLevel" ))
                    V_I4 (&rgCmdProp[cCmdProp].vValue) = ADS_SCOPE_ONELEVEL;
                else if (!_stricmp(pszCurrPrefValue, "Subtree" ))
                    V_I4 (&rgCmdProp[cCmdProp].vValue) = ADS_SCOPE_SUBTREE;
                else
                    BAIL_ON_FAILURE(E_FAIL);
                cCmdProp++;
            }
            else if (!_stricmp(pszCurrPref, "ChaseReferrals")) {
                rgCmdProp[cCmdProp].dwPropertyID = ADSIPROP_CHASE_REFERRALS;
                rgCmdProp[cCmdProp].dwOptions = DBPROPOPTIONS_REQUIRED;
                rgCmdProp[cCmdProp].vValue.vt = VT_I4;
                if (!_stricmp(pszCurrPrefValue, "always" ))
                    V_I4 (&rgCmdProp[cCmdProp].vValue) = ADS_CHASE_REFERRALS_ALWAYS;
                else if (!_stricmp(pszCurrPrefValue, "never" ))
                    V_I4 (&rgCmdProp[cCmdProp].vValue) = ADS_CHASE_REFERRALS_NEVER;
                else if (!_stricmp(pszCurrPrefValue, "external" ))
                    V_I4 (&rgCmdProp[cCmdProp].vValue) = ADS_CHASE_REFERRALS_EXTERNAL;
                else if (!_stricmp(pszCurrPrefValue, "subordinate" ))
                    V_I4 (&rgCmdProp[cCmdProp].vValue) = ADS_CHASE_REFERRALS_SUBORDINATE;
                else
                    BAIL_ON_FAILURE(E_FAIL);
                cCmdProp++;
            }
            else
                BAIL_ON_FAILURE(E_FAIL);

            break;

        default:
            BAIL_ON_FAILURE(E_FAIL);
        }

        argc--;
        currArg++;
    }

    //
    // Check for Mandatory arguments;
    //

    if (!pszSearchBase || !pszSearchFilter || !pszAttrList)
        BAIL_ON_FAILURE(E_FAIL);

    if (IsEqualGUID(rguidDialect, DBGUID_SQL) ) {

        pszCommandText = (LPWSTR) AllocADsMem(
                                      (wcslen(pszSearchBase) +
                                      wcslen(pszSearchFilter) +
                                      wcslen(pszAttrList) +
                                      wcslen(L"''") +
                                      wcslen(L"SELECT ") +
                                      wcslen(L" FROM ") +
                                      wcslen(L" WHERE ") +
                                      1) * sizeof(WCHAR)
                                      );
        BAIL_ON_NULL(E_FAIL);

        wsprintf(pszCommandText,
                 L"SELECT %s FROM '%s' WHERE %s",
                 pszAttrList,
                 pszSearchBase,
                 pszSearchFilter
                 );

    } else {

        pszCommandText = (LPWSTR) AllocADsMem(
                                      (wcslen(pszSearchBase) +
                                      wcslen(pszSearchFilter) +
                                      wcslen(pszAttrList) +
                                      5) * sizeof(WCHAR)
                                      );
        BAIL_ON_NULL(E_FAIL);

        wsprintf(pszCommandText,
                 L"<%s>;%s;%s",
                 pszSearchBase,
                 pszSearchFilter,
                 pszAttrList
                 );

    }
    if (cDBProp > 0) {
        cDBPropSet = 1;
        rgDBPropSet[0].rgProperties    = rgDBProp;
        rgDBPropSet[0].cProperties     = cDBProp;
        rgDBPropSet[0].guidPropertySet = DBPROPSET_DBINIT;
    }

    if (cCmdProp > 0) {
        cCmdPropSet = 1;
        rgCmdPropSet[0].rgProperties    = rgCmdProp;
        rgCmdPropSet[0].cProperties     = cCmdProp;
        rgCmdPropSet[0].guidPropertySet = DBPROPSET_ADSISEARCH;
    }

    FreeUnicodeString(pszSearchBase) ;
    FreeUnicodeString(pszSearchFilter) ;
    FreeUnicodeString(pszAttrList) ;

    return (S_OK);

error:

    FreeUnicodeString(pszSearchBase) ;
    FreeUnicodeString(pszSearchFilter) ;
    FreeUnicodeString(pszAttrList) ;

    PrintUsage();
    return E_FAIL;

}


UTIL.CXX

//---------------------------------------------------------------------------
//
//  Microsoft Active Directory 1.1 Sample Code
//
//  Copyright (C) Microsoft Corporation, 1992 - 1995
//
//  File:  util.cxx
//
//  Contents:  Ansi to Unicode conversions and misc helper functions
//
//----------------------------------------------------------------------------//------------------------------------------------------------------------------

#include "main.hxx"

void
PrintUsage(
    void
    )
{

    printf("\nUsage: adsqry /b <baseObject> /f <search_filter> /a <attrlist> [/p <preference=value>] ");
    printf(" [/u <UserName> <Password>] /d <dialect> \n");
    printf("\n   where:\n" );
    printf("   baseObject     = ADsPath of the base of the search\n");
    printf("   search_filter  = search filter string in LDAP format\n" );
    printf("   attrlist       = list of the attributes to display\n" );
    printf("   dialect is one of \"ldap\", \"sql\", or \"default\"\n");
    printf("   preference could be one of:\n");
    printf("   Asynchronous, AttrTypesOnly, DerefAliases, SizeLimit, TimeLimit,\n");
    printf("   TimeOut, PageSize, SearchScope, SecureAuth and EncryptPassword\n");
    printf("   value is yes/no for a Boolean and the respective integer for integers\n");
    printf("   scope is one of \"Base\", \"OneLevel\", or \"Subtree\"\n");

    printf("\nFor Example: adsqry /b \"LDAP://server1/O=Internet/DC=COM/");
    printf("DC=MyCompany/DC=MyDivision\"  /f \"(objectClass=Group)\" /a \"ADsPath, name, description\" ");
    printf(" /u \"CN=FooUser,CN=Users,DC=MyDivision,DC=MyCompany,DC=COM,O=INTERNET\" \"Password\" \n");

    printf("\nFor Example: adsqry /b \"LDAP://O=Internet/DC=COM/");
    printf("DC=MyCompany/DC=MyDivision\"  /f \"objectClass='Group'\" /a \"ADsPath, name, description\" ");
    printf(" /d sql /u \"CN=FooUser,CN=Users,DC=MyDivision,DC=MyCompany,DC=COM,O=INTERNET\" \"Password\" \n");

}

//
// Form the bindings array to specify the way the provider has to put the
// data in consumer's buffers; Create the Accessor from the bindings
//
HRESULT
CreateAccessorHelper(
    IRowset *pIRowset,
    ULONG nAttrs,
    DBCOLUMNINFO *prgColInfo,
    HACCESSOR *phAccessor,
    DBBINDSTATUS *pBindStatus
    )
{

    DBBINDING *prgBindings = NULL;
    HRESULT hr;
    ULONG i;
    IAccessor *pIAccessor = NULL;

    if(!phAccessor || !pBindStatus)
        return(E_INVALIDARG);

    prgBindings = (DBBINDING *) LocalAlloc(
                                   LPTR,
                                   sizeof(DBBINDING) * nAttrs
                                   );
    BAIL_ON_NULL(prgBindings);

    //
    // Set up rest of the attributes
    //
    for (i=0; i < nAttrs; i++) {
        prgBindings[i].iOrdinal = i+1;
        prgBindings[i].wType= prgColInfo[i+1].wType;
        if (prgBindings[i].wType == DBTYPE_DATE || prgBindings[i].wType == DBTYPE_I8)
            prgBindings[i].obValue = sizeof(Data)*i + offsetof(Data, obValue2);
        else
            prgBindings[i].obValue = sizeof(Data)*i + offsetof(Data, obValue);
        prgBindings[i].obLength= sizeof(Data)*i + offsetof(Data, obLength);
        prgBindings[i].obStatus= sizeof(Data)*i + offsetof(Data, status);
        prgBindings[i].dwPart= DBPART_VALUE|DBPART_LENGTH|DBPART_STATUS;

        if(prgBindings[i].wType & DBTYPE_BYREF)
            prgBindings[i].dwMemOwner= DBMEMOWNER_PROVIDEROWNED;
        else
            prgBindings[i].dwMemOwner= DBMEMOWNER_CLIENTOWNED;

        prgBindings[i].dwFlags= 0;
    }

    hr= pIRowset->QueryInterface(
           IID_IAccessor,
           (void**) &pIAccessor
           );
    BAIL_ON_FAILURE(hr);

    //
    // With the bindings create the accessor
    //
    hr = pIAccessor->CreateAccessor(
             DBACCESSOR_ROWDATA,
             nAttrs,
             prgBindings,
             0,
             phAccessor,
             pBindStatus
             );

    pIAccessor->Release();
    LOCAL_FREE(prgBindings);

    return(hr);

error:
    LOCAL_FREE(prgBindings);

    return(hr);

}

//
// Print the data depending on its type.
//

void
PrintData(
    Data *prgData,
    ULONG nAttrs,
    DBCOLUMNINFO *prgColInfo
    )
{

    ULONG i, j;
    HRESULT hr;

    for (i=0; i < nAttrs; i++) {

        if(prgData[i].status == DBSTATUS_S_OK) {

            switch(prgColInfo[i+1].wType) {
                case DBTYPE_I4:
                    wprintf(
                        L"%s = %d \n",
                        prgColInfo[i+1].pwszName,
                        (DWORD) prgData[i].obValue
                        );
                    break;

                case DBTYPE_I8:
                    wprintf(
                        L"%s = %I64d \n",
                        prgColInfo[i+1].pwszName,
                        *((__int64 *) &prgData[i].obValue2)
                        );
                    break;

                case DBTYPE_BOOL:
                    wprintf(
                        L"%s = %s \n",
                        prgColInfo[i+1].pwszName,
                        *((VARIANT_BOOL *) &(prgData[i].obValue)) == VARIANT_TRUE ?
                        L"TRUE" : L"FALSE"
                        );
                    break;

                case DBTYPE_STR | DBTYPE_BYREF:
                    wprintf(
                        L"%s = ",
                        prgColInfo[i+1].pwszName
                        );
                    printf(
                        "%s \n",
                        (char *)prgData[i].obValue
                        );
                    break;

                case DBTYPE_BYTES | DBTYPE_BYREF:
                    wprintf(
                        L"%s = ",
                        prgColInfo[i+1].pwszName
                        );
                    for (j=0; j<prgData[i].obLength; j++) {
                        printf(
                            "%x",
                            ((BYTE *)prgData[i].obValue)[j]
                            );
                    }
                    printf("\n");
                    break;

                case DBTYPE_WSTR | DBTYPE_BYREF:
                    wprintf(
                        L"%s = %s \n",
                        prgColInfo[i+1].pwszName,
                        (WCHAR *) prgData[i].obValue
                        );
                    break;

                    case DBTYPE_DATE:
                    wprintf(
                        L"%s = %e \n",
                        prgColInfo[i+1].pwszName,
                        prgData[i].obValue2
                        );
                break;

                case DBTYPE_VARIANT | DBTYPE_BYREF:
                    wprintf(
                        L"%s = ",
                        prgColInfo[i+1].pwszName
                        );

                    ULONG dwSLBound;
                    ULONG dwSUBound;
                    void HUGEP *pArray;
                    VARIANT *pVariant;

                    pArray = NULL;

                    pVariant = (VARIANT*) prgData[i].obValue;

                    if( !(pVariant->vt & VT_ARRAY))
                        BAIL_ON_FAILURE(hr = E_FAIL);

                    hr = SafeArrayGetLBound(V_ARRAY(pVariant),
                                            1,
                                            (long FAR *) &dwSLBound );
                    BAIL_ON_FAILURE(hr);

                    hr = SafeArrayGetUBound(V_ARRAY(pVariant),
                                            1,
                                            (long FAR *) &dwSUBound );
                    BAIL_ON_FAILURE(hr);

                    hr = SafeArrayAccessData( V_ARRAY(pVariant),
                                              &pArray );
                    BAIL_ON_FAILURE(hr);

                    for (j=dwSLBound; j<=dwSUBound; j++) {
                        switch(pVariant->vt & ~VT_ARRAY) {
                        case VT_BSTR:
                            wprintf(
                                L"%s  #  ",
                                ((BSTR *)pArray)[j]
                                );
                            break;
                        case VT_I4:
                            wprintf(
                                L"%d #  ",
                                ((DWORD *) pArray)[j]
                                );
                            break;
                        case VT_BOOL:
                            wprintf(
                                L"%s  #  ",
                                ((VARIANT_BOOL *) pArray)[j] == VARIANT_TRUE ?
                                L"TRUE" : L"FALSE"
                                );
                            break;
                        case VT_I8:
                            wprintf(
                                L"%I64d #  ",
                                ((__int64 *) pArray)[j]
                                );
                            break;

                        default:
                            wprintf(
                                L"Unsupported  #  \n"
                                );
                        }
                    }
                    printf("\n");

                    SafeArrayUnaccessData( V_ARRAY(pVariant) );

                    break;

                default:
                    wprintf(
                        L"Unsupported type for attribute %s\n",
                        prgColInfo[i+1].pwszName
                        );
                    break;
            }
        }

    }

    if(nAttrs != 0)
        wprintf(L"\n");

    return;

error:
    wprintf(
        L"Error in Printing data for %s\n",
        prgColInfo[i+1].pwszName
        );
    return;

}

int
AnsiToUnicodeString(
    LPSTR pAnsi,
    LPWSTR pUnicode,
    DWORD StringLength
    )
{
    int iReturn;

    if( StringLength == NULL_TERMINATED )
        StringLength = strlen( pAnsi );

    iReturn = MultiByteToWideChar(CP_ACP,
                                  MB_PRECOMPOSED,
                                  pAnsi,
                                  StringLength + 1,
                                  pUnicode,
                                  StringLength + 1 );

    //
    // Ensure NULL termination.
    //
    pUnicode[StringLength] = 0;

    return iReturn;
}

int
UnicodeToAnsiString(
    LPWSTR pUnicode,
    LPSTR pAnsi,
    DWORD StringLength
    )
{
    LPSTR pTempBuf = NULL;
    INT   rc = 0;

    if( StringLength == NULL_TERMINATED ) {

        //
        // StringLength is just the
        // number of characters in the string
        //
        StringLength = wcslen( pUnicode );
    }

    //
    // WideCharToMultiByte doesn't NULL terminate if we're copying
    // just part of the string, so terminate here.
    //

    pUnicode[StringLength] = 0;

    //
    // Include one for the NULL
    //
    StringLength++;

    //
    // Unfortunately, WideCharToMultiByte doesn't do conversion in place,
    // so allocate a temporary buffer, which we can then copy:
    //

    if( pAnsi == (LPSTR)pUnicode )
    {
#ifdef  DBCS
        pTempBuf = (LPSTR)LocalAlloc( LPTR, StringLength * 2 );
#else
        pTempBuf = (LPSTR)LocalAlloc( LPTR, StringLength );
#endif
        pAnsi = pTempBuf;
    }

    if( pAnsi )
    {
        rc = WideCharToMultiByte( CP_ACP,
                                  0,
                                  pUnicode,
                                  StringLength,
                                  pAnsi,
#ifdef  DBCS
                                  StringLength*2,
#else
                                  StringLength,
#endif
                                  NULL,
                                  NULL );
    }

    /* If pTempBuf is non-null, we must copy the resulting string
     * so that it looks as if we did it in place:
     */
    if( pTempBuf && ( rc > 0 ) )
    {
        pAnsi = (LPSTR)pUnicode;
        strcpy( pAnsi, pTempBuf );
        LocalFree( pTempBuf );
    }

    return rc;
}

LPWSTR
AllocateUnicodeString(
    LPSTR  pAnsiString
    )
{
    LPWSTR  pUnicodeString = NULL;

    if (!pAnsiString)
        return NULL;

    pUnicodeString = (LPWSTR)LocalAlloc(
                        LPTR,
                        strlen(pAnsiString)*sizeof(WCHAR) +sizeof(WCHAR)
                        );

    if (pUnicodeString) {

        AnsiToUnicodeString(
            pAnsiString,
            pUnicodeString,
            NULL_TERMINATED
            );
    }

    return pUnicodeString;
}

void
FreeUnicodeString(
    LPWSTR  pUnicodeString
    )
{
    if (!pUnicodeString)
        return;

    LocalFree(pUnicodeString);

    return;
}


README.TXT

Readme.txt for the DSBrowse.exe

DSBrowse.exe is a sample VB application that demonstrates the ease with which VB application developers can quickly put together utilities for viewing/managing Directory Services.

DSBrowse performs the following tasks

1] It can enumerate through multiple namespaces (each corresponding to a Directory Service).  For each object found in a Directory Service hierarchy, it  provides a brief description of the object. Double clicking on a object  (container) will result in enumerating objects within that container.

2] Reading and writing properties on the DS object. Clicking on the properties button allows you to display the properties on an object. You can select a specific property and update it by pressing the "Set" button.

Requirements -
1] You must have Microsoft Visual Basic 4.0a Professional or Enterprise Edition installed on your machine. Microsoft Visual Basic 4.0a Professional or Enterprise Edition is available only on x86 platforms.

List of source  files:

1]dsbrowse.vbp -  Visual Basic Project File
2]Main.Frm- Form for browsing directory service namespaces
3]Property.Frm - Form for getting and setting properties
4]VbADs.Bas-  VisualBasic source code
5]DsVw.Res  - Resource file for the project (x86 only)
6]DsBrowse.Exe - Visual Basic Executable (x86 only)


MAIN.FRM

VERSION 4.00
Begin VB.Form frmBrwsTree
   BackColor       =   &H00C0C0C0&
   BorderStyle     =   1  'Fixed Single
   Caption         =   "Active Directory VB Browser"
   ClientHeight    =   7065
   ClientLeft      =   4125
   ClientTop       =   2790
   ClientWidth     =   7575
   FillStyle       =   0  'Solid
   ForeColor       =   &H00000000&
   Height          =   7470
   Left            =   4065
   LinkTopic       =   "Form1"
   MaxButton       =   0   'False
   MinButton       =   0   'False
   ScaleHeight     =   7065
   ScaleWidth      =   7575
   Top             =   2445
   Width           =   7695
   Begin VB.CommandButton cmdAbort
      Caption         =   "Stop"
      Height          =   375
      Left            =   6360
      TabIndex        =   4
      Top             =   720
      Visible         =   0   'False
      Width           =   1095
   End
   Begin VB.CommandButton cmdProperties
      Caption         =   "Properties..."
      Height          =   375
      Left            =   6360
      TabIndex        =   3
      Top             =   240
      Width           =   1095
   End
   Begin VB.CommandButton cmdExit
      Caption         =   "Exit"
      Height          =   375
      Left            =   6360
      TabIndex        =   2
      Top             =   1200
      Width           =   1095
   End
   Begin VB.Label txtPath
      Appearance      =   0  'Flat
      AutoSize        =   -1  'True
      BackColor       =   &H00C0C0C0&
      Caption         =   "Path"
      BeginProperty Font
         name            =   "Times New Roman"
         charset         =   0
         weight          =   400
         size            =   11.25
         underline       =   0   'False
         italic          =   0   'False
         strikethrough   =   0   'False
      EndProperty
      ForeColor       =   &H80000008&
      Height          =   255
      Left            =   360
      TabIndex        =   7
      Top             =   6720
      Width           =   405
   End
   Begin VB.Label txtName
      Appearance      =   0  'Flat
      BackColor       =   &H00C0C0C0&
      Caption         =   "ADS:"
      BeginProperty Font
         name            =   "Times New Roman"
         charset         =   0
         weight          =   400
         size            =   12
         underline       =   0   'False
         italic          =   0   'False
         strikethrough   =   0   'False
      EndProperty
      ForeColor       =   &H80000008&
      Height          =   375
      Left            =   360
      TabIndex        =   6
      Top             =   6000
      Width           =   6255
   End
   Begin VB.Label Label3
      Caption         =   "Path:"
      BeginProperty Font
         name            =   "Times New Roman"
         charset         =   0
         weight          =   400
         size            =   11.25
         underline       =   0   'False
         italic          =   0   'False
         strikethrough   =   0   'False
      EndProperty
      Height          =   375
      Left            =   240
      TabIndex        =   5
      Top             =   6360
      Width           =   855
   End
   Begin VB.Label Label1
      Caption         =   "Name:"
      BeginProperty Font
         name            =   "Times New Roman"
         charset         =   0
         weight          =   400
         size            =   12
         underline       =   0   'False
         italic          =   0   'False
         strikethrough   =   0   'False
      EndProperty
      Height          =   375
      Left            =   240
      TabIndex        =   1
      Top             =   5640
      Width           =   855
   End
   Begin ComctlLib.ImageList ImageList1
      Left            =   11160
      Top             =   6960
      _Version        =   65536
      _ExtentX        =   1005
      _ExtentY        =   1005
      _StockProps     =   1
      BackColor       =   -2147483643
      MaskColor       =   12632256
   End
   Begin ComctlLib.TreeView TreeView1
      Height          =   5535
      Left            =   0
      TabIndex        =   0
      Top             =   0
      Width           =   6135
      _Version        =   65536
      _ExtentX        =   10821
      _ExtentY        =   9763
      _StockProps     =   196
      Appearance      =   1
      ImageList       =   ""
      MousePointer    =   1
      PathSeparator   =   "\"
      Sorted          =   -1  'True
      Style           =   7
   End
End
Attribute VB_Name = "frmBrwsTree"
Attribute VB_Creatable = False
Attribute VB_Exposed = False
Option Explicit
Private Sub cmdAbort_Click()
'
' Allows the user to cancel a tree expand
'
    bAbort = True
End Sub

Private Sub cmdExit_Click()
'
' Allows user to end program
'
    End
End Sub

Private Sub cmdProperties_Click()
'
' Allows viewing of Active Directory object properties
'
    Dim Class As IADsClass
    Dim Property As IADsProperty
    Dim v As Variant
    Dim Value As Variant
    Dim EmptyVar As Variant

    '
    ' Set Error handler
    '
    On Error Resume Next

    '
    ' Don't allow exit while getting properties
    '
    cmdExit.Visible = False

    '
    ' Set cursor to wait/arrow
    '
    Screen.MousePointer = 13

    '
    ' Get the object and its properties
    '
    Set currentADsObj = GetObject(TreeView1.SelectedItem.Key)
    If Err Then
        MsgBox "Could not get object"
        GoTo skip
    End If
    frmProp.lblName.Caption = currentADsObj.Name
    frmProp.lblPath.Caption = currentADsObj.ADsPath
    frmProp.lblClass.Caption = currentADsObj.Class
    '
    ' Namespaces object and Schema object are the only two objects that
    ' do not have an associated schema
    '
    If currentADsObj.Class = "Schema" Or _
            currentADsObj.Class = "NameSpaces" Then
        MsgBox "Please chose non schema object"
        GoTo skip
    End If

    '
    ' Load the form
    '
    Load frmProp
    Set Class = GetObject(currentADsObj.Schema)
    If Err Then
        MsgBox "Could not get schema object"
        GoTo skip
    End If
    If Class.Container Then
        frmProp.lblIsContainer.Caption = "Yes"
    Else
        frmProp.lblIsContainer.Caption = "No"
    End If
    For Each v In Class.MandatoryProperties
        Set Property = GetObject(Class.Parent + "\" + v)
        Value = EmptyVar
        Value = currentADsObj.Get(Property.Name)
        If VarType(Value) = vbEmpty Then
            frmProp.lstProperties.AddItem Property.Name & "(" & Property.Syntax & _
            "):" & "<Empty>"
        Else
            frmProp.lstProperties.AddItem Property.Name & "(" & Property.Syntax & _
            "):" & Value
        End If
    Next v

    '
    'Make form visible
    '
    frmProp.Visible = True

skip:
    cmdExit.Visible = True
    '
    'Set cursor to default
    '
    Screen.MousePointer = 0
End Sub

Private Sub Form_Load()
'
' Sets up the application main form
'

    '
    ' Define indexs for LoadResPicture API
    '
    Const idxComputer = 101
    Const idxUser = 102
    Const idxDomain = 103
    Const idxSyntax = 104
    Const idxService = 105
    Const idxGroup = 106
    Const idxPrintQueue = 107
    Const idxOU = 108
    Const idxFileShare = 109
    Const idxOrganization = 110
    Const idxCountry = 111
    Const idxTop = 112
    Const idxNamespace = 113
    Const idxDefault = 114
    Const idxNamespaces = 115
    ' App icon index
    Const idxApp = 101

    '
    ' Create temp Node variable
    '
    Dim nodX As Node

    '
    ' Load pictures into ImageList control.
    '
    'Dim pic As Picture
    '
    'Create Image variable
    '
    Dim imgI As ListImage

    '
    ' Set Error handler
    '
    On Error GoTo ErrorHandler

    '
    ' Each image/icon is loaded from the resource file.  If the image
    ' is not present, the default image is used.
    '
    Set imgI = ImageList1.ListImages.Add(, _
        "imgComputer", LoadResPicture(idxComputer, vbResBitmap))
    Set imgI = ImageList1.ListImages.Add(, _
        "imgUser", LoadResPicture(idxUser, vbResBitmap))
    Set imgI = ImageList1.ListImages.Add(, _
        "imgDomain", LoadResPicture(idxDomain, vbResBitmap))
    Set imgI = ImageList1.ListImages.Add(, _
        "imgSchema", LoadResPicture(idxSyntax, vbResBitmap))
    Set imgI = ImageList1.ListImages.Add(, _
        "imgService", LoadResPicture(idxService, vbResBitmap))
    Set imgI = ImageList1.ListImages.Add(, _
        "imgGroup", LoadResPicture(idxGroup, vbResBitmap))
    Set imgI = ImageList1.ListImages.Add(, _
        "imgPrintQueue", LoadResPicture(idxPrintQueue, vbResBitmap))
    Set imgI = ImageList1.ListImages.Add(, _
        "imgFileShare", LoadResPicture(idxFileShare, vbResBitmap))
    Set imgI = ImageList1.ListImages.Add(, _
        "imgOrganization", LoadResPicture(idxOrganization, vbResBitmap))
    Set imgI = ImageList1.ListImages.Add(, _
        "imgCountry", LoadResPicture(idxCountry, vbResBitmap))
    Set imgI = ImageList1.ListImages.Add(, _
        "imgNameSpace", LoadResPicture(idxNamespace, vbResBitmap))
    Set imgI = ImageList1.ListImages.Add(, _
        "imgNamespaces", LoadResPicture(idxNamespaces, vbResBitmap))
    Set imgI = ImageList1.ListImages.Add(, _
        "imgFileService", LoadResPicture(idxDefault, vbResBitmap))
    Set imgI = ImageList1.ListImages.Add(, _
        "imgDefault", LoadResPicture(idxDefault, vbResBitmap))

    frmBrwsTree.Icon = LoadResPicture(idxApp, vbResIcon)

    '
    ' Set TreeView control properties.
    '
    TreeView1.ImageList = ImageList1    ' Initialize ImageList
    TreeView1.Style = tvwTreelinesPlusMinusPictureText ' Style 7
    TreeView1.LineStyle = tvwRootLines
    TreeView1.Indentation = 50
    strPath = "ADS:" ' Default start of browse

    Call enumerate_path(nodX)

    GoTo finish

ErrorHandler:
    errstring = "Had an error:" & Err.Number
    ERRNUMB = Err.Number
    MsgBox errstring
    Resume Next
finish:

End Sub

Private Sub TreeView1_Collapse(ByVal Node As Node)
'
' To do when collapsing a node
'
    txtName.Caption = Node.Text
    txtPath.Caption = Node.Key
End Sub

Private Sub TreeView1_Expand(ByVal Node As Node)
'
' Process a node expand
'
    Dim ChildNode As Node
    On Error GoTo ErrorHandler

    TreeView1.MousePointer = 13
    ' Only expand once so check for "Dummy" child
    If Node.Child = "Dummy" Then

        Node.Sorted = True
        If bStartup = False Then

            cmdExit.Visible = False
            txtName.Caption = Node.Text
            txtPath.Caption = Node.Key

            Set ChildNode = Node.Child
            If ChildNode.Text = "Dummy" Then
                TreeView1.Nodes.Remove (ChildNode.Key)
            End If
            cmdAbort.Visible = True
            cmdAbort.SetFocus
            DoEvents

            strPath = Node.Key

            Call enumerate_path(Node)

        End If
    End If

GoTo finish

ErrorHandler:
    errstring = "Had an error:" & Err.Number
    ERRNUMB = Err.Number
    MsgBox errstring

finish:
    cmdExit.Visible = True
    Node.Sorted = True
    cmdAbort.Visible = False
    TreeView1.MousePointer = 0

End Sub

Private Sub TreeView1_NodeClick(ByVal Node As Node)
'
' Process a node click
'
    txtName.Caption = Node.Text
    txtPath.Caption = Node.Key
End Sub


PROPERTY.FRM

VERSION 4.00
Begin VB.Form frmProp
   BorderStyle     =   1  'Fixed Single
   Caption         =   "Properties"
   ClientHeight    =   4710
   ClientLeft      =   5955
   ClientTop       =   4245
   ClientWidth     =   7080
   Height          =   5115
   Left            =   5895
   LinkTopic       =   "Form1"
   MaxButton       =   0   'False
   MinButton       =   0   'False
   ScaleHeight     =   4710
   ScaleWidth      =   7080
   Top             =   3900
   Width           =   7200
   Begin VB.CommandButton cmdSet
      Caption         =   "Set"
      Height          =   375
      Left            =   5760
      TabIndex        =   13
      Top             =   4080
      Width           =   1215
   End
   Begin VB.TextBox txtValue
      BeginProperty Font
         name            =   "MS Sans Serif"
         charset         =   0
         weight          =   400
         size            =   9.75
         underline       =   0   'False
         italic          =   0   'False
         strikethrough   =   0   'False
      EndProperty
      Height          =   360
      Left            =   840
      TabIndex        =   12
      Top             =   4080
      Width           =   4815
   End
   Begin VB.ListBox lstProperties
      Appearance      =   0  'Flat
      BeginProperty Font
         name            =   "MS Sans Serif"
         charset         =   0
         weight          =   400
         size            =   9.75
         underline       =   0   'False
         italic          =   0   'False
         strikethrough   =   0   'False
      EndProperty
      Height          =   1950
      ItemData        =   "property.frx":0000
      Left            =   120
      List            =   "property.frx":0002
      Sorted          =   -1  'True
      TabIndex        =   9
      Top             =   1920
      Width           =   6735
   End
   Begin VB.CommandButton cmdOK
      Caption         =   "Close"
      Height          =   375
      Left            =   5760
      TabIndex        =   8
      Top             =   480
      Width           =   975
   End
   Begin VB.Label Label8
      Caption         =   "Value:"
      BeginProperty Font
         name            =   "MS Sans Serif"
         charset         =   0
         weight          =   400
         size            =   9.75
         underline       =   0   'False
         italic          =   0   'False
         strikethrough   =   0   'False
      EndProperty
      Height          =   255
      Left            =   120
      TabIndex        =   11
      Top             =   4200
      Width           =   735
   End
   Begin VB.Label Label5
      Caption         =   "Properties"
      BeginProperty Font
         name            =   "MS Sans Serif"
         charset         =   0
         weight          =   400
         size            =   9.75
         underline       =   0   'False
         italic          =   0   'False
         strikethrough   =   0   'False
      EndProperty
      Height          =   255
      Left            =   240
      TabIndex        =   10
      Top             =   1560
      Width           =   3255
   End
   Begin VB.Label lblIsContainer
      BackColor       =   &H00C0C0C0&
      Height          =   255
      Left            =   1200
      TabIndex        =   7
      Top             =   1200
      Width           =   1935
   End
   Begin VB.Label lblClass
      BackColor       =   &H00C0C0C0&
      Height          =   255
      Left            =   1200
      TabIndex        =   6
      Top             =   840
      Width           =   4575
   End
   Begin VB.Label lblPath
      BackColor       =   &H00C0C0C0&
      Height          =   255
      Left            =   1200
      TabIndex        =   5
      Top             =   480
      Width           =   4575
   End
   Begin VB.Label lblName
      BackColor       =   &H00C0C0C0&
      Height          =   255
      Left            =   1200
      TabIndex        =   4
      Top             =   120
      Width           =   1935
   End
   Begin VB.Label Label4
      Alignment       =   1  'Right Justify
      Appearance      =   0  'Flat
      BackColor       =   &H00C0C0C0&
      Caption         =   "IsContainer:"
      ForeColor       =   &H80000008&
      Height          =   255
      Left            =   120
      TabIndex        =   3
      Top             =   1200
      Width           =   855
   End
   Begin VB.Label Label3
      Alignment       =   1  'Right Justify
      BackColor       =   &H00C0C0C0&
      Caption         =   "Class:"
      Height          =   255
      Left            =   120
      TabIndex        =   2
      Top             =   840
      Width           =   855
   End
   Begin VB.Label Label2
      Alignment       =   1  'Right Justify
      Appearance      =   0  'Flat
      BackColor       =   &H00C0C0C0&
      Caption         =   "ADsPath:"
      ForeColor       =   &H80000008&
      Height          =   255
      Left            =   120
      TabIndex        =   1
      Top             =   480
      Width           =   855
   End
   Begin VB.Label Label1
      Alignment       =   1  'Right Justify
      Appearance      =   0  'Flat
      BackColor       =   &H00C0C0C0&
      Caption         =   "Name:"
      ForeColor       =   &H80000008&
      Height          =   255
      Left            =   120
      TabIndex        =   0
      Top             =   120
      Width           =   855
   End
End
Attribute VB_Name = "frmProp"
Attribute VB_Creatable = False
Attribute VB_Exposed = False
Option Explicit
Private Sub cmdCancel_Click()
'
' Allow user to cancel
'

    Unload Me
End Sub

Private Sub cmdOK_Click()
'
' Allow user to close form
'

    Unload Me
End Sub

Private Sub cmdSet_Click()
'
' Process Set Property command
'
    Dim strProp As String
    Dim strSyntax As String
    Dim strOld As String
    Dim Val As Variant
    Dim I As Integer
    Dim pos1 As Integer  'Finds "(" in string to locate syntax
    Dim pos2 As Integer  'Finds ")" in string to locate syntax
    '
    ' Set error handling
    '
    On Error Resume Next

    I = lstProperties.ListIndex
    If currentADsObj Is Nothing Then
        '
        ' There is no object associated with this interface.
        ' Do nothing.
        '
        GoTo finish

    End If
    '
    'Need to strip Prop off Prop(SYNTAX):Value
    '
    strOld = lstProperties.Text
    pos1 = InStr(strOld, "(")
    pos2 = InStr(pos1, strOld, ")")
    strSyntax = Right(strOld, (Len(strOld) - pos1))
    strSyntax = Left(strSyntax, ((pos2 - pos1) - 1))
    strProp = Left(strOld, (pos1 - 1))
    If (strSyntax = "ADsPath") Or (strSyntax = "String") Or (strSyntax = "DirectoryString") Then
        Val = txtValue.Text
    ElseIf (strSyntax = "Counter") Or (strSyntax = "Integer") _
            Or (strSyntax = "Interval") Then
        Val = CLng(txtValue.Text)
    ElseIf (strSyntax = "SmallInterval") Then
        Val = CLng(txtValue.Text)
    ElseIf (strSyntax = "List") Or (strSyntax = "OctetString") Then
        Val = CVar(txtValue.Text)
    ElseIf (strSyntax = "PhoneNumber") Or (strSyntax = "PostalAddress") Then
        Val = txtValue.Text
    ElseIf strSyntax = "Boolean" Then
        Val = CBool(txtValue.Text)
    ElseIf (strSyntax = "EmailAddress") Or (strSyntax = "FaxNumber") Then
        Val = txtValue.Text
    ElseIf strSyntax = "NetAddress" Or strSyntax = "Path" Then
        Val = txtValue.Text
    ElseIf strSyntax = "Time" Then
        Val = CDate(txtValue.Text)
    Else
        '
        ' If none of the syntax cases above are found we bail out silently
        '
        GoTo finish
    End If
    strOld = Left(strOld, (pos2 + 1)) & txtValue.Text
    currentADsObj.Put strProp, Val
    Err = 0
    currentADsObj.SetInfo
    If Err Then
        MsgBox "Could not Set Property, check access "
        GoTo finish
    End If
    lstProperties.RemoveItem I
    lstProperties.AddItem strOld, I

finish:
End Sub

Private Sub Form_Load()

    '
    ' User cannot bring up second properties dialog box
    '
        frmBrwsTree.cmdProperties.Enabled = False
End Sub

Private Sub Form_Unload(Cancel As Integer)

    '
    ' Enable properties command
    '
    frmBrwsTree.cmdProperties.Enabled = True
End Sub

Private Sub lstProperties_Click()
'
'Do on Properties command
'
    Dim pos1 As Integer
    Dim pos2 As Integer
    Dim strVal As String
    Dim strTemp As String

    On Error GoTo finish

    strTemp = lstProperties.List(lstProperties.ListIndex)
    pos1 = InStr(strTemp, "(")
    pos2 = InStr(pos1, strTemp, ")")
    pos1 = InStr(pos2, strTemp, ":")
    strVal = Right(strTemp, (Len(strTemp) - pos1))

    txtValue.Text = strVal
    frmProp.Refresh
finish:
End Sub


VBADS.BAS

Attribute VB_Name = "Module1"
' DSBROWSE
' This is a simple browser for Active Directory which demonstrates
' the use of the basic Active Directory Automation interfaces from
' VB. Browsing starts automatically at the Namespaces
' Collection Object. The user can expand the browser tree
' to any level desired. The properties option allows viewing
' of the current Active Directory object's properties, while the Set
' button allows changing property values.

Option Explicit

Public currentADsObj As IADs
Public currentADsObjCont As IADsContainer
Public currentADsObjSchema   As IADs
Public currentNewObj   As IADs
Public strPath As String
Public bStartup As Boolean
Public errstring As String
Public ERRNUMB As Long
Public bAbort As Boolean
Public Sub enumerate_path(ByVal Node As Node)
'
'Name: enumerate_path
'Input: Node As Node
'Desc: Does the actual enumerating of input node.
'   All child object are enumerated and placed under
'   the input node as children.
'Return: none
'

    '
    ' Set Error handler
    '
    On Error GoTo ErrorHandler

    Dim st As Variant
    Dim nodX As Node    ' Create variable.
    Dim ChildNode As Node    ' Create variable.
    Dim I As Integer    ' Create a counter variable.
    Dim Container As IADsContainer
    Dim Class As IADsClass
    Dim Child As IADs
    Dim classobj As IADsClass
    Dim tempClass As String

    bAbort = False
    '
    ' Get the object selected
    '
    Set currentADsObj = GetObject(strPath)
    frmBrwsTree.txtName.Caption = currentADsObj.Name
    frmBrwsTree.txtPath.Caption = currentADsObj.ADsPath
    On Error Resume Next
    '
    ' Set the node if not passed into this sub as the current
    ' node we are working on (for the collection coming up).
    '
    If Node Is Nothing Then
       Set Node = frmBrwsTree.TreeView1.Nodes.Item(currentADsObj.ADsPath)
       Node.Sorted = True
    End If

    On Error GoTo ErrorHandler

    If frmBrwsTree.TreeView1.Nodes.Count = 0 Then
        '
        ' This means we are enumerating the namespaces container
        ' No nodes expanded yet,
        '
        tempClass = SetClass(currentADsObj.Class)
        Set nodX = frmBrwsTree.TreeView1.Nodes.Add(, , currentADsObj.ADsPath, _
            currentADsObj.Name, tempClass)
        nodX.Sorted = True
        '
        'Get namespace IDs
        '
        Set Container = currentADsObj
        For Each Child In Container
            tempClass = SetClass(Child.Class)
            Set Node = frmBrwsTree.TreeView1.Nodes.Add(nodX, tvwChild, _
                Child.ADsPath, Child.Name, tempClass)
            Node.Sorted = True
            '
            ' If the child is a container, then add a temporary nod
            ' to show that it can be expanded later.
            '
            Set ChildNode = frmBrwsTree.TreeView1.Nodes.Add(Node, _
                tvwChild, Child.ADsPath & "/Dummy", "Dummy", tempClass)
            ChildNode.Sorted = True
        Next Child
        bStartup = True 'disable expand processing
        frmBrwsTree.TreeView1.Nodes(1).Expanded = True
        bStartup = False
        Set Node = Nothing

    Else

        Set Container = Nothing
        Node.Sorted = True
        '
        ' Now, see if it's a container and if so, enumerate
        ' all objects below it.
        '
        On Error Resume Next

        Set Container = currentADsObj
        If Err = 0 Then
            For Each Child In Container
                If Child Is Nothing Then Exit For
                Err = 0
                I = I + 1
                '
                ' Every 80 objects we check for user input
                '
                If I = 80 Then
                    DoEvents
                    If bAbort Then GoTo quitloop
                    I = 0
                End If
                tempClass = SetClass(Child.Class) 'Gets the image
                If tempClass = "" Then tempClass = "Dummy"

                Set nodX = frmBrwsTree.TreeView1.Nodes.Add(Node, tvwChild, _
                    Child.ADsPath, Child.Name, tempClass)
                nodX.Sorted = True
                '
                ' If New node is a container then add temporary child node
                '
                If IsContainer(Child) Then

                    Set ChildNode = frmBrwsTree.TreeView1.Nodes.Add(nodX, _
                        tvwChild, Child.ADsPath & "Dummy", "Dummy", tempClass)
                    ChildNode.Sorted = True
                End If
skip:
           Next Child
        End If

quitloop:
        Set currentADsObj = Nothing
skip2:
    End If
    GoTo finish

ErrorHandler:

        errstring = "Had an error:" & Err.Number
        ERRNUMB = Err.Number
        MsgBox errstring
        Resume Next

finish:

End Sub
Public Function SetClass(classString As String) As String
'Name: SetClass
'Input: classString As String
'Desc: Used to determine what classname is
'   used for the imagelist
'Return: SetClass As String

    SetClass = "img" & classString

    '
    ' Evaluate input class to set image to all classes
    '
    Select Case classString

    Case "Computer"
    Case "NameSpace"
    Case "Namespaces"
    Case "User"
    Case "Domain"
    Case "Service"
    Case "Group"
    Case "PrintQueue"
    Case "FileShare"
    Case "Organization"
    Case "Country"
    Case "Organizational Unit"
        SetClass = "imgOrganization"
    ' Here we set all others
    Case Else
        SetClass = "imgDefault"
    End Select

End Function

Public Function IsContainer(objADS As IADs) As Boolean
'
' This Function returns True or False depending on whether the
' object referred to by objADs is a container or not
'
    Dim Cont As IADsContainer

    On Error Resume Next

    Set Cont = objADS
    If Err Then
        IsContainer = False
    Else
        IsContainer = True
    End If
End Function


README.TXT

Readme for Java support in Active Directory
===========================================
1) Install Active Directory

2) Build dsconn.dll in the dsconn directory with a C compiler

3) Copy dsconn.dll to your system directory (on Windows NT this would be
%SystemRoot%\System32 on Windows 95 this would be Windows\System)

4) cd to the system directory per above

5) Run regsvr32 /s dsconn.dll

6) Run javatlb dsconn.dll

7) Run javatlb activeds.tlb

8) cd to "Program files\Common files\System\ADO" and

9) run javatlb msado10.dll

10) Build the Bizcard applet in the bizcard directory with VJ++

Starting Point for the Java Sample
==================================
To obtain data from the Directory Service, a valid LDAP path must
be provided in the 'Starting Point' field. The following is an
example of a valid LDAP path.

LDAP://NW09T5/C=US/O=NTMARST/OU=NW09T5DOM


DSCNOBJ.H

// DSConnObj.h : Declaration of the CDsconnObject

#include "resource.h"       // main symbols

/////////////////////////////////////////////////////////////////////////////
// DSConn

class CDsconnObject :
public CComDualImpl<IDsconn, &IID_IDsconn, &LIBID_DsconnLib>,
public ISupportErrorInfo,
public CComObjectRoot,
public CComCoClass<CDsconnObject,&CLSID_DsconnSrv>
{
public:
CDsconnObject() { Path=SysAllocString(L"<none>");}
BEGIN_COM_MAP(CDsconnObject)
COM_INTERFACE_ENTRY(IDispatch)
COM_INTERFACE_ENTRY(IDsconn)
COM_INTERFACE_ENTRY(ISupportErrorInfo)
END_COM_MAP()
//DECLARE_NOT_AGGREGATABLE(CDsconnObject)
// 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(CDsconnObject, _T("DSCONN.DsconnObject.1"), _T("DSCONN.DsconnObject"), IDS_DSCONNOBJECT_DESC, THREADFLAGS_BOTH)
// ISupportsErrorInfo
STDMETHOD(InterfaceSupportsErrorInfo)(REFIID riid);

// IDsconn
public:
    STDMETHODIMP get_Path (BSTR* retval);
    STDMETHODIMP CDsconnObject::put_Path (BSTR ObjectPath);
    STDMETHODIMP get_CurrentObject (IUnknown** retval);
    STDMETHODIMP get_Object(BSTR OleDsPath,IUnknown** retval);

    BSTR Path;

};


DSCONN.CPP

// DSConn.cpp : Implementation of DLL Exports.

// 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.

// Note: Proxy/Stub Information
//To build a separate proxy/stub DLL,
//run nmake -f DSConnps.mak in the project directory.

#include "stdafx.h"
#include "resource.h"
#include "initguid.h"
#include "DSConn.h"
#include "DSCnObj.h"

#define IID_DEFINED
#include "DSConn_i.c"

CComModule _Module;

BEGIN_OBJECT_MAP(ObjectMap)
OBJECT_ENTRY(CLSID_DsconnSrv, CDsconnObject)
END_OBJECT_MAP()

/////////////////////////////////////////////////////////////////////////////
// DLL Entry Point

extern "C"
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/)
{
if (dwReason == DLL_PROCESS_ATTACH)
{
_Module.Init(ObjectMap, hInstance);
DisableThreadLibraryCalls(hInstance);
}
else if (dwReason == DLL_PROCESS_DETACH)
_Module.Term();
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;
}


DSCONN.IDL

// DSConn.idl : IDL source for DSConn.dll
//

[
object,
uuid(1C32FF60-22DF-11d0-8C3F-00AA003E0EED),
dual,
helpstring("IDsconn Interface"),
pointer_default(unique)
]
interface IDsconn : IDispatch
{
import "oaidl.idl";
[propput]
HRESULT Path([in] BSTR Path);

        [propget]
HRESULT Path([out, retval] BSTR* Path);

[propget]
        HRESULT CurrentObject([out, retval] IUnknown** obj);

[propget]
HRESULT Object([in] BSTR Path,
[out, retval] IUnknown** obj);

};

[
uuid(1C32FF61-22DF-11d0-8C3F-00AA003E0EED),
version(1.0),
helpstring("DSConn 1.0 Type Library")
]
library DsconnLib
{
importlib("stdole32.tlb");

[
uuid(1C32FF62-22DF-11d0-8C3F-00AA003E0EED),
helpstring("DsconnObject Class")
]
coclass DsconnSrv
{
[default] interface IDsconn;
};

};


DSCNOBJ.CPP

// DSConnObj.cpp : Implementation of CDSConnApp and DLL registration.

#include "stdafx.h"
#include "DSConn.h"
#include "DSCnObj.h"
#include "activeds.h"

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

STDMETHODIMP CDsconnObject::InterfaceSupportsErrorInfo(REFIID riid)
{
static const IID* arr[] =
{
&IID_IDsconn,
};

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

STDMETHODIMP CDsconnObject::get_Path (BSTR* retval)
{
    if (Path != NULL)
*retval = SysAllocString(Path);
    else
*retval = NULL;
    return S_OK;
}

STDMETHODIMP CDsconnObject::put_Path (BSTR ObjectPath)
{
    SysReAllocString(&Path, ObjectPath);
    return S_OK;
}

STDMETHODIMP CDsconnObject::get_CurrentObject (IUnknown** retval)
{

    HRESULT hr;

    //DebugBreak();
    OutputDebugString(TEXT("DS control: Object Fetched\n"));

    //hr = ADsGetObject(L"WinNT:\\\\Redmond\\Jimharr",
      hr = ADsGetObject(Path,
                        IID_IDispatch,(void **)retval);
    if (SUCCEEDED(hr)) {
        OutputDebugString(TEXT("DS control: Object Fetched - Returning Object\n"));
    } else {
        OutputDebugString(TEXT("DS control: Object Fetched - E_FAIL\n"));
        return E_FAIL;
    }
    return S_OK;
}

STDMETHODIMP CDsconnObject::get_Object(BSTR ADsPath,
                                      IUnknown** retval)
{
    HRESULT hr;

    //    DebugBreak();
    OutputDebugString(TEXT("DS control: Get Object\n"));

    SysReAllocString(&Path,ADsPath);

    hr = ADsGetObject(Path,IID_IDispatch,(void **)retval);
    if (SUCCEEDED(hr)) {
        OutputDebugString(TEXT("DS control: GetObject - Returning Object\n"));
    } else {
        OutputDebugString(TEXT("DS control: Get Object - E_FAIL\n"));
        return hr;
    }
    return S_OK;
}


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by DSConn.rc
//
#define IDS_DSCONNOBJECT_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


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"

#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

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


ADSCONST.BAS

Attribute VB_Name = "ADsConst"
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Active Directory Service Interfaces 1.0
'
' Copyright (c) 1995-1997  Microsoft Corporation
'
' ADsConst.Bas - Error codes, Status codes, Class names and
'                Schema definitions for ADSI.
'

'''''''''''''''''''''''' HRESULT Value Definitions '''''''''''''''''''''''''
'
' HRESULT definitions
'
'  Values are 32 bit values layed out as follows:
'
'   3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
'   1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
'  +---+-+-+-----------------------+-------------------------------+
'  |Sev|C|R|     Facility          |               Code            |
'  +---+-+-+-----------------------+-------------------------------+
'
'  where
'
'      Sev - is the severity code
'
'          00 - Success
'          01 - Informational
'          10 - Warning
'          11 - Error
'
'      C - is the Customer code flag
'
'      R - is a reserved bit
'
'      Facility - is the facility code
'
'      Code - is the facility's status code
'
'
' Note: An error codes of the form &H8007xxxx indicates Win32 error xxxx.
'
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Public Const FACILITY_WINDOWS = 8
Public Const FACILITY_STORAGE = 3
Public Const FACILITY_RPC = 1
Public Const FACILITY_SSPI = 9
Public Const FACILITY_WIN32 = 7
Public Const FACILITY_CONTROL = 10
Public Const FACILITY_NULL = 0
Public Const FACILITY_ITF = 4
Public Const FACILITY_DISPATCH = 2

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'
' MessageId: E_ADS_BAD_PATHNAME
'
' MessageText:
'
'  An invalid OLE DS pathname was passed
'
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Public Const E_ADS_BAD_PATHNAME = &H80005000

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'
' MessageId: E_ADS_INVALID_DOMAIN_OBJECT
'
' MessageText:
'
'  An unknown OLE DS domain object was requested
'
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Public Const E_ADS_INVALID_DOMAIN_OBJECT = &H80005001

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'
' MessageId: E_ADS_INVALID_USER_OBJECT
'
' MessageText:
'
'  An unknown OLE DS user object was requested
'
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Public Const E_ADS_INVALID_USER_OBJECT = &H80005002

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'
' MessageId: E_ADS_INVALID_COMPUTER_OBJECT
'
' MessageText:
'
'  An unknown OLE DS computer object was requested
'
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Public Const E_ADS_INVALID_COMPUTER_OBJECT = &H80005003

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'
' MessageId: E_ADS_UNKNOWN_OBJECT
'
' MessageText:
'
'  An unknown OLE DS object was requested
'
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Public Const E_ADS_UNKNOWN_OBJECT = &H80005004

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'
' MessageId: E_ADS_PROPERTY_NOT_SET
'
' MessageText:
'
'  The specified OLE DS property was not set
'
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Public Const E_ADS_PROPERTY_NOT_SET = &H80005005

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'
' MessageId: E_ADS_PROPERTY_NOT_SUPPORTED
'
' MessageText:
'
'  The specified OLE DS property is not supported
'
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Public Const E_ADS_PROPERTY_NOT_SUPPORTED = &H80005006

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'
' MessageId: E_ADS_PROPERTY_INVALID
'
' MessageText:
'
'  The specified OLE DS property is invalid
'
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Public Const E_ADS_PROPERTY_INVALID = &H80005007

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'
' MessageId: E_ADS_BAD_PARAMETER
'
' MessageText:
'
'  One or more input parameters are invalid
'
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Public Const E_ADS_BAD_PARAMETER = &H80005008

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'
' MessageId: E_ADS_OBJECT_UNBOUND
'
' MessageText:
'
'  The specified OLE DS object is not bound to a remote resource
'
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Public Const E_ADS_OBJECT_UNBOUND = &H80005009

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'
' MessageId: E_ADS_PROPERTY_NOT_MODIFIED
'
' MessageText:
'
'  The specified OLE DS object has not been modified
'
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Public Const E_ADS_PROPERTY_NOT_MODIFIED = &H8000500A

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'
' MessageId: E_ADS_PROPERTY_MODIFIED
'
' MessageText:
'
'  The specified OLE DS object has not been modified
'
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Public Const E_ADS_PROPERTY_MODIFIED = &H8000500B

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'
' MessageId: E_ADS_CANT_CONVERT_DATATYPE
'
' MessageText:
'
'  The OLE DS datatype cannot be converted to/from a native DS datatype
'
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Public Const E_ADS_CANT_CONVERT_DATATYPE = &H8000500C

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'
' MessageId: E_ADS_PROPERTY_NOT_FOUND
'
' MessageText:
'
'  The OLE DS property cannot be found in the cache.
'
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Public Const E_ADS_PROPERTY_NOT_FOUND = &H8000500D

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'
' MessageId: E_ADS_OBJECT_EXISTS
'
' MessageText:
'
'  The OLE DS object exists.
'
Public Const E_ADS_OBJECT_EXISTS = &H8000500E

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'
' MessageId: E_ADS_SCHEMA_VIOLATION
'
' MessageText:
'
'  The attempted action violates the DS schema rules.
'
Public Const E_ADS_SCHEMA_VIOLATION = &H8000500F

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'
' Printer Status Values
'
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Public Const ADS_PRINTER_PAUSED = &H1
Public Const ADS_PRINTER_PENDING_DELETION = &H2
Public Const ADS_PRINTER_ERROR = &H3
Public Const ADS_PRINTER_PAPER_JAM = &H4
Public Const ADS_PRINTER_PAPER_OUT = &H5
Public Const ADS_PRINTER_MANUAL_FEED = &H6
Public Const ADS_PRINTER_PAPER_PROBLEM = &H7
Public Const ADS_PRINTER_OFFLINE = &H8
Public Const ADS_PRINTER_IO_ACTIVE = &H100
Public Const ADS_PRINTER_BUSY = &H200
Public Const ADS_PRINTER_PRINTING = &H400
Public Const ADS_PRINTER_OUTPUT_BIN_FULL = &H800
Public Const ADS_PRINTER_NOT_AVAILABLE = &H1000
Public Const ADS_PRINTER_WAITING = &H2000
Public Const ADS_PRINTER_PROCESSING = &H4000
Public Const ADS_PRINTER_INITIALIZING = &H8000
Public Const ADS_PRINTER_WARMING_UP = &H10000
Public Const ADS_PRINTER_TONER_LOW = &H20000
Public Const ADS_PRINTER_NO_TONER = &H40000
Public Const ADS_PRINTER_PAGE_PUNT = &H80000
Public Const ADS_PRINTER_USER_INTERVENTION = &H100000
Public Const ADS_PRINTER_OUT_OF_MEMORY = &H200000
Public Const ADS_PRINTER_DOOR_OPEN = &H400000
Public Const ADS_PRINTER_SERVER_UNKNOWN = &H800000
Public Const ADS_PRINTER_POWER_SAVE = &H1000000

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'
' Job Status Values
'
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Public Const ADS_JOB_PAUSED = &H1
Public Const ADS_JOB_ERROR = &H2
Public Const ADS_JOB_DELETING = &H4
Public Const ADS_JOB_SPOOLING = &H8
Public Const ADS_JOB_PRINTING = &H10
Public Const ADS_JOB_OFFLINE = &H20
Public Const ADS_JOB_PAPEROUT = &H40
Public Const ADS_JOB_PRINTED = &H80
Public Const ADS_JOB_DELETED = &H100

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'
' Service Status Values
'
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Public Const ADS_SERVICE_STOPPED = &H1
Public Const ADS_SERVICE_START_PENDING = &H2
Public Const ADS_SERVICE_STOP_PENDING = &H3
Public Const ADS_SERVICE_RUNNING = &H4
Public Const ADS_SERVICE_CONTINUE_PENDING = &H5
Public Const ADS_SERVICE_PAUSE_PENDING = &H6
Public Const ADS_SERVICE_PAUSED = &H7
Public Const ADS_SERVICE_ERROR = &H8

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'
' Service Type Valid Values
'
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Public Const ADS_SERVICE_OWN_PROCESS = &H10
Public Const ADS_SERVICE_SHARE_PROCESS = &H20
Public Const ADS_SERVICE_KERNEL_DRIVER = &H1
Public Const ADS_SERVICE_FILE_SYSTEM_DRIVER = &H2

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'
' Start Type Valid Values
'
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Public Const ADS_SERVICE_BOOT_START = &H0
Public Const ADS_SERVICE_SYSTEM_START = &H1
Public Const ADS_SERVICE_AUTO_START = &H2
Public Const ADS_SERVICE_DEMAND_START = &H3
Public Const ADS_SERVICE_DISABLED = &H4

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'
' Error Control Values
'
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Public Const ADS_SERVICE_ERROR_IGNORE = 0
Public Const ADS_SERVICE_ERROR_NORMAL = 1
Public Const ADS_SERVICE_ERROR_SEVERE = 2
Public Const ADS_SERVICE_ERROR_CRITICAL = 3

Public Const NAMESPACE_CLASS_NAME         = "Namespace"
Public Const DOMAIN_CLASS_NAME            = "Domain"
Public Const COMPUTER_CLASS_NAME          = "Computer"
Public Const USER_CLASS_NAME              = "User"
Public Const GROUP_CLASS_NAME             = "Group"
Public Const SERVICE_CLASS_NAME           = "Service"
Public Const FILESERVICE_CLASS_NAME       = "FileService"
Public Const SESSION_CLASS_NAME           = "Session"
Public Const RESOURCE_CLASS_NAME          = "Resource"
Public Const FILESHARE_CLASS_NAME         = "FileShare"
Public Const PRINTER_CLASS_NAME           = "PrintQueue"
Public Const PRINTJOB_CLASS_NAME          = "PrintJob"
Public Const SCHEMA_CLASS_NAME            = "Schema"
Public Const CLASS_CLASS_NAME             = "Class"
Public Const PROPERTY_CLASS_NAME          = "Property"
Public Const SYNTAX_CLASS_NAME            = "Syntax"

Public Const NO_SCHEMA                    = ""
Public Const DOMAIN_SCHEMA_NAME           = "Domain"
Public Const COMPUTER_SCHEMA_NAME         = "Computer"
Public Const USER_SCHEMA_NAME             = "User"
Public Const GROUP_SCHEMA_NAME            = "Group"
Public Const SERVICE_SCHEMA_NAME          = "Service"
Public Const PRINTER_SCHEMA_NAME          = "PrintQueue"
Public Const PRINTJOB_SCHEMA_NAME         = "PrintJob"
Public Const FILESERVICE_SCHEMA_NAME      = "FileService"
Public Const SESSION_SCHEMA_NAME          = "Session"
Public Const RESOURCE_SCHEMA_NAME         = "Resource"
Public Const FILESHARE_SCHEMA_NAME        = "FileShare"
Public Const FPNW_FILESERVICE_SCHEMA_NAME = "FPNWFileService"
Public Const FPNW_SESSION_SCHEMA_NAME     = "FPNWSession"
Public Const FPNW_RESOURCE_SCHEMA_NAME    = "FPNWResource"
Public Const FPNW_FILESHARE_SCHEMA_NAME   = "FPNWFileShare"


ADSSMP.CPP

#include <windows.h>


ADSSMP.H

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    ADsSmp.h

Abstract:

Author:

Environment:

    User mode

Revision History :

--*/
#ifndef _ADSSMP_H_
#define _ADSSMP_H_

#ifndef INITGUID
#define INITGUID
#endif

#define _LARGE_INTEGER_SUPPORT_
#define UNICODE
#define _UNICODE
#define INC_OLE2
#define SECURITY_WIN32

#include <windows.h>
#include <windowsx.h>
#include <winspool.h>
#include <cguid.h>
#include <stdlib.h>
#include <limits.h>
#include <io.h>
#include <activeds.h>
#include <crtdbg.h>

#define ADsAssert(x) _ASSERT((x))

#include "libmain.h"
#include "util.h"
#include "memory.h"
#include "intf.h"
#include "cdispmgr.h"
#include "guid.h"
#include "macro.h"
#include "cmacro.h"
#include "common.h"
#include "getobj.h"
#include "smptypes.h"
#include "parse.h"
#include "core.h"
#include "pack.h"
#include "property.h"
#include "object.h"
#include "cprovcf.h"
#include "cprov.h"
#include "cnamcf.h"
#include "cnamesp.h"
#include "smpoper.h"
#include "cprops.h"
#include "cgenobj.h"
#include "cschobj.h"
#include "cclsobj.h"
#include "cprpobj.h"
#include "regdsapi.h"
#include "cenumvar.h"
#include "cenumns.h"
#include "cenumobj.h"
#include "cenumsch.h"
#include "globals.h"

#define     DISPID_REGULAR             1
#define     SampleDS_CLASS_ID          9
#define     SampleDS_SCHEMA_ID         12
#define     SampleDS_PROPERTY_ID       13
#define     SampleDS_OU_ID             15
#define     SampleDS_CLASSPROP_ID      18

#endif


CCLSOBJ.CPP

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    CClsObj.cpp

Abstract:

Author:

Environment:

    User mode

Revision History :

--*/
#include "adssmp.h"
#pragma hdrstop

//  Class CSampleDSClass

DEFINE_IDispatch_Implementation(CSampleDSClass)
DEFINE_IADs_Implementation(CSampleDSClass)

CSampleDSClass::CSampleDSClass():
      _pDispMgr( NULL ),
      _lpClassName(0),
      _dwNumberOfMandatoryAttributes(0),
      _lpMandatoryAttributes(0)

{
    ENLIST_TRACKING(CSampleDSClass);
}

HRESULT
CSampleDSClass::CreateClass(
    BSTR Parent,
    BSTR CommonName,
    LPSampleDS_CLASS_DEF lpClassDefs,
    DWORD dwObjectState,
    REFIID riid,
    void **ppvObj
    )
{
    CSampleDSClass FAR * pClass = NULL;
    HRESULT hr = S_OK;

    hr = AllocateClassObject(&pClass);
    BAIL_ON_FAILURE(hr);

    hr = pClass->InitializeCoreObject(
                Parent,
                CommonName,
                CLASS_CLASS_NAME,
                L"",
                CLSID_SampleDSClass,
                dwObjectState
                );
    BAIL_ON_FAILURE(hr);

    hr = pClass->QueryInterface(riid, ppvObj);
    BAIL_ON_FAILURE(hr);

    pClass->_dwNumberOfMandatoryAttributes =
                    lpClassDefs->dwNumberOfMandatoryAttributes;
    pClass->_lpMandatoryAttributes = CreatePropertyList(
                                          lpClassDefs->lpMandatoryAttributes
                                          );

    pClass->Release();

    RRETURN(hr);

error:

    delete pClass;
    RRETURN(hr);
}

HRESULT
CSampleDSClass::CreateClass(
    BSTR Parent,
    BSTR CommonName,
    HANDLE hTree,
    DWORD dwObjectState,
    REFIID riid,
    void **ppvObj
    )
{
    HRESULT hr = S_OK;
    LPSampleDS_CLASS_DEF lpClassDefs = NULL;

    hr = SampleDSGetClassDefinition(&lpClassDefs,
                                    CommonName);
    BAIL_ON_FAILURE(hr);

    if (!lpClassDefs) {
        hr = E_FAIL;
        BAIL_ON_FAILURE(hr);
    }

    hr = CSampleDSClass::CreateClass(
                    Parent,
                    CommonName,
                    lpClassDefs,
                    dwObjectState,
                    riid,
                    ppvObj
                    );

error:
    if (lpClassDefs) {
        SampleDSFreeClassDefinition(lpClassDefs,
                                    1);
    };
    RRETURN(hr);
}

CSampleDSClass::~CSampleDSClass( )
{
    delete _pDispMgr;
}

STDMETHODIMP
CSampleDSClass::QueryInterface(
    REFIID iid,
    LPVOID FAR* ppv
    )
{
    if (IsEqualIID(iid, IID_IUnknown))
    {
        *ppv = (IADsClass FAR *) this;
    }
    else if (IsEqualIID(iid, IID_IADsClass))
    {
        *ppv = (IADsClass FAR *) this;
    }
    else if (IsEqualIID(iid, IID_IADs))
    {
        *ppv = (IADsClass FAR *) this;
    }
    else if (IsEqualIID(iid, IID_IDispatch))
    {
        *ppv = (IADsClass FAR *) this;
    }
    else
    {
        *ppv = NULL;
        return E_NOINTERFACE;
    }
    AddRef();
    return NOERROR;
}

STDMETHODIMP
CSampleDSClass::SetInfo(THIS)
{
    RRETURN(E_NOTIMPL);
}

STDMETHODIMP
CSampleDSClass::GetInfo(THIS)
{
    RRETURN(E_NOTIMPL);
}

HRESULT
CSampleDSClass::AllocateClassObject(
    CSampleDSClass ** ppClass
    )
{
    CSampleDSClass FAR * pClass = NULL;
    CDispatchMgr FAR * pDispMgr = NULL;
    HRESULT hr = S_OK;

    pClass = new CSampleDSClass();
    if (pClass == NULL) {
        hr = E_OUTOFMEMORY;
    }
    BAIL_ON_FAILURE(hr);

    pDispMgr = new CDispatchMgr;
    if (pDispMgr == NULL) {
        hr = E_OUTOFMEMORY;
    }
    BAIL_ON_FAILURE(hr);

    hr = LoadTypeInfoEntry(
                pDispMgr,
                LIBID_ADs,
                IID_IADsClass,
                (IADsClass *)pClass,
                DISPID_REGULAR
                );
    BAIL_ON_FAILURE(hr);

    hr = LoadTypeInfoEntry(
                pDispMgr,
                LIBID_ADs,
                IID_IADsContainer,
                (IADsContainer *)pClass,
                DISPID_NEWENUM
                );
    BAIL_ON_FAILURE(hr);

    pClass->_pDispMgr = pDispMgr;
    *ppClass = pClass;

    RRETURN(hr);

error:
    delete  pDispMgr;

    RRETURN(hr);

}

STDMETHODIMP
CSampleDSClass::GetInfo(
    THIS_ DWORD dwApiLevel,
    BOOL fExplicit
    )
{
    RRETURN(E_NOTIMPL);
}

STDMETHODIMP
CSampleDSClass::Get(
    THIS_ BSTR bstrName,
    VARIANT FAR* pvProp
    )
{
    RRETURN(E_NOTIMPL);
}

STDMETHODIMP
CSampleDSClass::Put(
    THIS_ BSTR bstrName,
    VARIANT vProp
    )
{
    RRETURN(E_NOTIMPL);
}

PPROPENTRY
CreatePropertyList(
    LPWSTR_LIST  lpStringList
    )
{
    PPROPENTRY pStart = NULL;
    PPROPENTRY pPropEntry = NULL;

    while (lpStringList) {

        pPropEntry = CreatePropertyEntry(
                            lpStringList->lpString,
                            0
                            );

        pPropEntry->pNext = pStart;
        pStart = pPropEntry;

        lpStringList = lpStringList->Next;
    }

    return(pStart);
}

PPROPENTRY
CreatePropertyEntry(
    LPWSTR pszPropertyName,
    DWORD dwSyntaxId
    )
{
    LPWSTR pszTemp = NULL;
    PPROPENTRY pPropName = NULL;

    pszTemp = (LPWSTR)AllocProvStr(
                    pszPropertyName
                    );
    if (!pszTemp) {
        return(NULL);
    }

    pPropName = (PPROPENTRY)AllocProvMem(
                        sizeof(PROPENTRY)
                        );
    if (!pPropName) {
        FreeProvStr(pszTemp);
        return(NULL);
    }

    pPropName->pszPropName = pszTemp;
    pPropName->dwSyntaxId = dwSyntaxId;

    return(pPropName);
}

void
FreePropertyEntry(
    PPROPENTRY pPropName
    )
{
    if (pPropName->pszPropName) {
        FreeProvStr(pPropName->pszPropName);
    }

    FreeProvMem(pPropName);

    return;
}

HRESULT
MakeVariantFromPropList(
    PPROPENTRY pPropList,
    DWORD dwNumEntries,
    VARIANT * pVarList
    )
{
    SAFEARRAYBOUND sabNewArray;
    SAFEARRAY * pFilter = NULL;
    HRESULT hr = S_OK;
    DWORD dwSLBound = 0;
    DWORD dwSUBound = 0;
    DWORD i = 0;
    VARIANT v;

    VariantInit(pVarList);

    sabNewArray.cElements = dwNumEntries;
    sabNewArray.lLbound = 0;

    pFilter = SafeArrayCreate(
                    VT_VARIANT,
                    1,
                    &sabNewArray
                    );

    if (!pFilter) {
        hr = E_OUTOFMEMORY;
        BAIL_ON_FAILURE(hr);
    }

    for (i = dwSLBound; i < (dwSLBound + dwNumEntries); i++) {
        VariantInit(&v);
        V_VT(&v) = VT_BSTR;

        V_BSTR(&v) = SysAllocString(pPropList->pszPropName);

        hr = SafeArrayPutElement(
                pFilter,
                (long*)&i,
                (void *)&v
                );
        BAIL_ON_FAILURE(hr);

        pPropList = pPropList->pNext;

    }

    V_VT(pVarList) = VT_ARRAY | VT_VARIANT;
    V_ARRAY(pVarList) = pFilter;

    RRETURN(S_OK);

error:

    if (pFilter) {
        SafeArrayDestroy(pFilter);
    }

    RRETURN(hr);
}

/* IADsClass methods */

STDMETHODIMP
CSampleDSClass::get_PrimaryInterface( THIS_ BSTR FAR *pbstrGUID )
{
    RRETURN(E_ADS_PROPERTY_NOT_SUPPORTED);
}

STDMETHODIMP
CSampleDSClass::get_CLSID( THIS_ BSTR FAR *pbstrCLSID )
{
    RRETURN(E_ADS_PROPERTY_NOT_SUPPORTED);
}

STDMETHODIMP
CSampleDSClass::put_CLSID( THIS_ BSTR bstrCLSID )
{
    RRETURN(E_ADS_PROPERTY_NOT_SUPPORTED);
}

STDMETHODIMP
CSampleDSClass::get_OID( THIS_ BSTR FAR *pbstrOID )
{
    RRETURN(E_ADS_PROPERTY_NOT_SUPPORTED);
}

STDMETHODIMP
CSampleDSClass::put_OID( THIS_ BSTR bstrOID )
{
    RRETURN(E_ADS_PROPERTY_NOT_SUPPORTED);
}

STDMETHODIMP
CSampleDSClass::get_Abstract( THIS_ VARIANT_BOOL FAR *pfAbstract )
{
    RRETURN(E_ADS_PROPERTY_NOT_SUPPORTED);
}

STDMETHODIMP
CSampleDSClass::put_Abstract( THIS_ VARIANT_BOOL fAbstract )
{
    RRETURN(E_ADS_PROPERTY_NOT_SUPPORTED);
}

STDMETHODIMP
CSampleDSClass::get_MandatoryProperties( THIS_ VARIANT FAR *pvMandatoryProperties )
{
    HRESULT hr = S_OK;

    hr = MakeVariantFromPropList(
            _lpMandatoryAttributes,
            _dwNumberOfMandatoryAttributes,
            pvMandatoryProperties
            );
    RRETURN(hr);
}

STDMETHODIMP
CSampleDSClass::put_MandatoryProperties( THIS_ VARIANT vMandatoryProperties )
{
    RRETURN(E_ADS_PROPERTY_NOT_SUPPORTED);
}

STDMETHODIMP
CSampleDSClass::get_DerivedFrom( THIS_ VARIANT FAR *pvDerivedFrom )
{
    RRETURN(E_ADS_PROPERTY_NOT_SUPPORTED);
}

STDMETHODIMP
CSampleDSClass::put_DerivedFrom( THIS_ VARIANT vDerivedFrom )
{
    RRETURN(E_ADS_PROPERTY_NOT_SUPPORTED);
}

STDMETHODIMP
CSampleDSClass::get_Containment( THIS_ VARIANT FAR *pvContainment )
{
    RRETURN(E_ADS_PROPERTY_NOT_SUPPORTED);
}

STDMETHODIMP
CSampleDSClass::put_Containment( THIS_ VARIANT vContainment )
{
    RRETURN(E_ADS_PROPERTY_NOT_SUPPORTED);
}

STDMETHODIMP
CSampleDSClass::get_Container( THIS_ VARIANT_BOOL FAR *pfContainer )
{
    RRETURN(E_ADS_PROPERTY_NOT_SUPPORTED);
}

STDMETHODIMP
CSampleDSClass::put_Container( THIS_ VARIANT_BOOL fContainer )
{
    RRETURN(E_ADS_PROPERTY_NOT_SUPPORTED);
}

STDMETHODIMP
CSampleDSClass::get_HelpFileName( THIS_ BSTR FAR *pbstrHelpFileName )
{
    RRETURN(E_ADS_PROPERTY_NOT_SUPPORTED);
}

STDMETHODIMP
CSampleDSClass::put_HelpFileName( THIS_ BSTR bstrHelpFile )
{
    RRETURN(E_ADS_PROPERTY_NOT_SUPPORTED);
}

STDMETHODIMP
CSampleDSClass::get_HelpFileContext( THIS_ long FAR *plHelpContext )
{
    RRETURN(E_ADS_PROPERTY_NOT_SUPPORTED);
}

STDMETHODIMP
CSampleDSClass::put_HelpFileContext( THIS_ long lHelpContext )
{
    RRETURN(E_ADS_PROPERTY_NOT_SUPPORTED);
}

STDMETHODIMP
CSampleDSClass::Qualifiers(THIS_ IADsCollection FAR* FAR* ppQualifiers)
{
    RRETURN(E_NOTIMPL);
}

STDMETHODIMP
CSampleDSClass::get_Auxiliary( THIS_ VARIANT_BOOL FAR *pfAuxiliary )
{
    RRETURN(E_NOTIMPL);
}

STDMETHODIMP
CSampleDSClass::put_Auxiliary( THIS_ VARIANT_BOOL fAuxiliary )
{
    RRETURN(E_ADS_PROPERTY_NOT_SUPPORTED);
}

STDMETHODIMP
CSampleDSClass::get_OptionalProperties( THIS_ VARIANT FAR *retval )
{
    RRETURN(E_NOTIMPL);
}

STDMETHODIMP
CSampleDSClass::put_OptionalProperties( THIS_ VARIANT vOptionalProperties )
{
    RRETURN(E_NOTIMPL);
}

STDMETHODIMP
CSampleDSClass::get_NamingProperties( THIS_ VARIANT FAR *retval )
{
    RRETURN(E_NOTIMPL);
}

STDMETHODIMP
CSampleDSClass::put_NamingProperties( THIS_ VARIANT vNamingProperties )
{
    RRETURN(E_NOTIMPL);
}
STDMETHODIMP
CSampleDSClass::get_AuxDerivedFrom( THIS_ VARIANT FAR *pvAuxDerivedFrom )
{
    RRETURN(E_NOTIMPL);
}

STDMETHODIMP
CSampleDSClass::put_AuxDerivedFrom( THIS_ VARIANT vAuxDerivedFrom )
{
    RRETURN(E_ADS_PROPERTY_NOT_SUPPORTED);
}

STDMETHODIMP
CSampleDSClass::get_PossibleSuperiors( THIS_ VARIANT FAR *pvPossSuperiors)
{
    RRETURN(E_NOTIMPL);
}

STDMETHODIMP
CSampleDSClass::put_PossibleSuperiors( THIS_ VARIANT vPossSuperiors )
{
    RRETURN(E_ADS_PROPERTY_NOT_SUPPORTED);
}


CCLSOBJ.H

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    CClsObj.h

Abstract:

Author:

Environment:

    User mode

Revision History :

--*/
#ifndef _CCLSOBJ_H_
#define _CCLSOBJ_H_

typedef struct _propentry{
    DWORD dwSyntaxId;
    LPWSTR pszPropName;
    struct _propentry *pNext;
} PROPENTRY, *PPROPENTRY;

#define SampleDS_CONTAINER_CLASS               0x01
#define SampleDS_EFFECTIVE_CLASS               0x02
#define SampleDS_NONREMOVABLE_CLASS            0x04
#define SampleDS_AMBIGUOUS_NAMING              0x08
#define SampleDS_AMBIGUOUS_CONTAINMENT         0x10

class CSampleDSClass;

class CSampleDSClass : INHERIT_TRACKING,
                     public CCoreADsObject,
                     public IADsClass
{
public:

    /* IUnknown methods */
    STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) ;

    DECLARE_STD_REFCOUNTING

    DECLARE_IDispatch_METHODS

    DECLARE_IADs_METHODS

    DECLARE_IADsClass_METHODS

    CSampleDSClass::CSampleDSClass();

    CSampleDSClass::~CSampleDSClass();

    static
    HRESULT
    CSampleDSClass::CreateClass(
        BSTR Parent,
        BSTR CommonName,
        LPSampleDS_CLASS_DEF lpClassDefs,
        DWORD dwObjectState,
        REFIID riid,
        void **ppvObj
        );

    static
    HRESULT
    CSampleDSClass::CreateClass(
        BSTR Parent,
        BSTR CommonName,
        HANDLE hTree,
        DWORD dwObjectState,
        REFIID riid,
        void **ppvObj
        );

    static
    HRESULT
    CSampleDSClass::AllocateClassObject(
        CSampleDSClass ** ppClass
        );

    STDMETHOD(GetInfo)(
        THIS_ DWORD dwApiLevel,
        BOOL fExplicit
        );

protected:
    VARIANT     _vFilter;
    LPWSTR      _lpClassName;
    DWORD       _dwNumberOfMandatoryAttributes;
    PPROPENTRY  _lpMandatoryAttributes;
    CDispatchMgr FAR * _pDispMgr;
};

PPROPENTRY
CreatePropertyList(
    LPWSTR_LIST  lpStringList
    );

PPROPENTRY
CreatePropertyEntry(
    LPWSTR pszPropertyName,
    DWORD dwSyntaxId
    );

void
FreePropertyEntry(
    PPROPENTRY pPropName
    );

HRESULT
MakeVariantFromPropList(
    PPROPENTRY pPropList,
    DWORD dwNumEntries,
    VARIANT * pVarList
    );
#endif


CDISPMGR.CPP

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    CDispMgr.c

Abstract:

    Dispatch Manager implementation

Author:

Environment:

    User mode

Revision History :

--*/
#include "adssmp.h"
#pragma hdrstop

DISPID
CDispatchMgr::getDISPID(DISPID InputDispId)
{
    return(InputDispId & 0x0000FFFF);
}

DISPID
CDispatchMgr::getTypeinfoID(DISPID InputDispId)
{
    return((InputDispId & 0x7fff0000) >> 16);
}

void *
CDispatchMgr::getInterfacePtr(DISPID TypeinfoID
                                  )
{
    PTYPEINFOENTRY pTypeInfoEntry = NULL;
    pTypeInfoEntry = _pTypeInfoEntry;
    while (pTypeInfoEntry) {
        if (pTypeInfoEntry->TypeInfoId == TypeinfoID) {
            return(pTypeInfoEntry->pInterfacePointer);
        }
        pTypeInfoEntry = pTypeInfoEntry->pNext;
    }
    return(NULL);
}

PTYPEINFOENTRY
CDispatchMgr::FindTypeInfoEntry(LONG TypeInfoId)
{
    PTYPEINFOENTRY pTypeInfoEntry;

    pTypeInfoEntry = _pTypeInfoEntry;
    while (pTypeInfoEntry) {
        if (pTypeInfoEntry->TypeInfoId == TypeInfoId) {
            return(pTypeInfoEntry);
        }
        pTypeInfoEntry = pTypeInfoEntry->pNext;
    }
    return(NULL);
}

HRESULT
CDispatchMgr::AddTypeInfo(void FAR *ptypeinfo,
                               void * pIntfptr)
{
    PTYPEINFOENTRY pTypeInfoEntry = NULL;
    HRESULT hr;

    if (pTypeInfoEntry =FindTypeInfo(ptypeinfo)) {
        return(E_FAIL);
    }
    pTypeInfoEntry = (PTYPEINFOENTRY)LocalAlloc(LPTR,sizeof(TYPEINFOENTRY));
    if (!pTypeInfoEntry) {
        hr = E_OUTOFMEMORY;
        BAIL_IF_ERROR(hr);
    }

    pTypeInfoEntry->ptypeinfo = ptypeinfo;
    pTypeInfoEntry->TypeInfoId = gentypeinfoid();
    pTypeInfoEntry->pInterfacePointer = pIntfptr;

    pTypeInfoEntry->pNext = _pTypeInfoEntry;
    _pTypeInfoEntry = pTypeInfoEntry;

    RRETURN(S_OK);

cleanup:
    RRETURN(hr);
}

STDMETHODIMP
CDispatchMgr::GetTypeInfoCount(unsigned int FAR* pctinfo)
{
    RRETURN(E_NOTIMPL);
}

STDMETHODIMP
CDispatchMgr::GetTypeInfo(unsigned int itinfo, LCID lcid,
        ITypeInfo FAR* FAR* pptinfo)
{
    RRETURN(E_NOTIMPL);
}

STDMETHODIMP
CDispatchMgr::GetIDsOfNames(REFIID iid, LPWSTR FAR* rgszNames,
        unsigned int cNames, LCID lcid, DISPID FAR* rgdispid)
{
    PTYPEINFOENTRY pTypeInfo = NULL;
    HRESULT hr;

    pTypeInfo = _pTypeInfoEntry;
    while (pTypeInfo) {
        hr = DispGetIDsOfNames(((ITypeInfo *)pTypeInfo->ptypeinfo),
                                rgszNames,
                                cNames,
                                rgdispid
                                );
        if (SUCCEEDED(hr)) {
            generate_newids(pTypeInfo->TypeInfoId,
                            rgdispid,
                            cNames
                            );
            RRETURN(hr);
        }
        pTypeInfo = pTypeInfo->pNext;
    }
    RRETURN(E_FAIL);
}

STDMETHODIMP
CDispatchMgr::Invoke(DISPID dispidMember, REFIID iid, LCID lcid,
        unsigned short wFlags, DISPPARAMS FAR* pdispparams,
        VARIANT FAR* pvarResult, EXCEPINFO FAR* pexcepinfo,
        unsigned int FAR* puArgErr)
{
        RRETURN(TypeInfoInvoke(dispidMember,
                               iid,
                               lcid,
                               wFlags,
                               pdispparams,
                               pvarResult,
                               pexcepinfo,
                               puArgErr
                               ));
}

void
CDispatchMgr::generate_newids(LONG TypeInfoId,
        DISPID FAR* rgdispid, unsigned int cNames)
{
    unsigned int i = 0;
    for (i = 0; i < cNames; i++ ) {
        rgdispid[i] = (DISPID)(((unsigned int)TypeInfoId << 16) | rgdispid[i]);
    }
}

STDMETHODIMP
CDispatchMgr::TypeInfoInvoke(DISPID dispidMember, REFIID iid, LCID lcid,
        unsigned short wFlags, DISPPARAMS FAR* pdispparams,
        VARIANT FAR* pvarResult, EXCEPINFO FAR* pexcepinfo,
        unsigned int FAR* puArgErr)
{

    void * pInterfacePtr = NULL;
    DISPID dispid;
    DISPID typeinfoid;
    ITypeInfo *pTypeInfo;
    HRESULT hr = S_OK;

    if (dispidMember <= 0) {
        dispid = dispidMember;
        switch (dispid) {
        case -4:
            if (_pDispidNewEnum) {
                pTypeInfo = (ITypeInfo *)_pDispidNewEnum->ptypeinfo;
                pInterfacePtr = _pDispidNewEnum->pInterfacePointer;
                break;

            }else {
                hr = DISP_E_BADINDEX;
                BAIL_IF_ERROR(hr);

            }
            break;

        default:
             hr = DISP_E_BADINDEX;
             BAIL_IF_ERROR(hr);

        }

    }else {
        typeinfoid = getTypeinfoID(dispidMember);
        dispid = getDISPID(dispidMember);
        pInterfacePtr = getInterfacePtr(typeinfoid);
        hr = getTypeInfo(typeinfoid, &pTypeInfo);
        BAIL_IF_ERROR(hr);
    }

    hr = DispInvoke(pInterfacePtr,
                pTypeInfo,
                dispid,
                wFlags,
                pdispparams,
                pvarResult,
                pexcepinfo,
                puArgErr
                );
    return hr;

cleanup:

    RRETURN(hr);
}

HRESULT
CDispatchMgr::getTypeInfo(DISPID typeinfoid, ITypeInfo FAR * FAR * ppTypeInfo)
{
    PTYPEINFOENTRY pTypeInfoEntry;
    pTypeInfoEntry = _pTypeInfoEntry;
    while (pTypeInfoEntry) {
        if (pTypeInfoEntry->TypeInfoId == typeinfoid) {
            *ppTypeInfo = (ITypeInfo FAR *)pTypeInfoEntry->ptypeinfo;
            RRETURN(S_OK);
        }
        pTypeInfoEntry = pTypeInfoEntry->pNext;
    }
    RRETURN(E_FAIL);
}

CDispatchMgr::CDispatchMgr()
{
    _pTypeInfoEntry = NULL;
    _pDispidNewEnum = NULL;
    _dwTypeInfoId = 0;
}

CDispatchMgr::~CDispatchMgr()
{
    PTYPEINFOENTRY pTypeInfoEntry = NULL;
    PTYPEINFOENTRY pTemp = NULL;
    ITypeInfo *pTypeInfo = NULL;

    pTypeInfoEntry = _pTypeInfoEntry;

    while (pTypeInfoEntry) {

        pTemp = pTypeInfoEntry;

        pTypeInfo = (ITypeInfo *)pTypeInfoEntry->ptypeinfo;
        pTypeInfo->Release();

        pTypeInfoEntry = pTemp->pNext;

        LocalFree(pTemp);
    }
}

HRESULT
CDispatchMgr::MarkAsNewEnum(void *pTypeInfo)
{
    PTYPEINFOENTRY pTypeInfoEntry;

    if (!pTypeInfo) {
        RRETURN(E_FAIL);
    }
    if (!(pTypeInfoEntry = FindTypeInfo(pTypeInfo))) {
        RRETURN(E_FAIL);
    }
    _pDispidNewEnum = pTypeInfoEntry;
    RRETURN(S_OK);
}

PTYPEINFOENTRY
CDispatchMgr::FindTypeInfo(void *pTypeInfo)
{
    PTYPEINFOENTRY pTypeInfoEntry;

    pTypeInfoEntry = _pTypeInfoEntry;
    while (pTypeInfoEntry) {
        if (pTypeInfoEntry->ptypeinfo == pTypeInfo) {
            return(pTypeInfoEntry);
        }
        pTypeInfoEntry = pTypeInfoEntry->pNext;
    }
    return(NULL);
}

LONG
CDispatchMgr::gentypeinfoid()
{
    return (_dwTypeInfoId++);
}


CDISPMGR.H

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    CDispMgr.h

Abstract:

Author:

Environment:

    User mode

Revision History :

--*/
#ifndef _CDISPMGR_H_
#define _CDISPMGR_H_

typedef struct _typeinfoentry
{
    LONG TypeInfoId;
    void FAR *ptypeinfo;
    void * pInterfacePointer;
    struct _typeinfoentry *pNext;
} TYPEINFOENTRY, *PTYPEINFOENTRY;

class CDispatchMgr;

class CDispatchMgr
{
public:

    DECLARE_IDispatch_METHODS

    CDispatchMgr::CDispatchMgr();

    CDispatchMgr::~CDispatchMgr();

    DISPID
    CDispatchMgr::getDISPID(DISPID InputDispId);

    DISPID
    CDispatchMgr::getTypeinfoID(DISPID InputDispId);

    void *
    CDispatchMgr::getInterfacePtr(DISPID TypeinfoID
                                      );
     void
     CDispatchMgr::generate_newids(LONG TypeInfoId,
             DISPID FAR* rgdispid, unsigned int cNames);

    PTYPEINFOENTRY
    CDispatchMgr::FindTypeInfoEntry(LONG TypeInfoId);

    HRESULT
    CDispatchMgr::AddTypeInfo(void FAR *ptypeinfo, void * pIntfptr);

    STDMETHODIMP
    CDispatchMgr::TypeInfoInvoke(DISPID dispidMember, REFIID iid, LCID lcid,
            unsigned short wFlags, DISPPARAMS FAR* pdispparams,
            VARIANT FAR* pvarResult, EXCEPINFO FAR* pexcepinfo,
            unsigned int FAR* puArgErr);

   HRESULT
   CDispatchMgr::getTypeInfo(DISPID typeinfoid, ITypeInfo FAR * FAR * ppTypeInfo) ;

   HRESULT
   CDispatchMgr::MarkAsNewEnum(void *pTypeInfo);

   PTYPEINFOENTRY
   CDispatchMgr::FindTypeInfo(void *pTypeInfo);

   LONG
   CDispatchMgr::gentypeinfoid();

protected:

    LONG        _dwTypeInfoId;
    PTYPEINFOENTRY _pTypeInfoEntry;
    PTYPEINFOENTRY _pDispidNewEnum;
};

#define BAIL_IF_ERROR(hr) \
        if (FAILED(hr)) {       \
                goto cleanup;   \
        }
#endif


CENUMNS.CPP

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    CEnumNS.c

Abstract:

    Sample Provider Namespace Enumerator Code

Author:

Environment:

    User mode

Revision History :

--*/
#include "adssmp.h"
#pragma hdrstop

#define ENUM_BUFFER_SIZE (1024 * 16)

//+---------------------------------------------------------------------------
//
//  Function:   CSampleDSNamespaceEnum::Create
//
//  Synopsis:
//
//  Arguments:  [pCollection]
//              [ppEnumVariant]
//
//  Returns:    HRESULT
//
//  Modifies:
//
//----------------------------------------------------------------------------
HRESULT
CSampleDSNamespaceEnum::Create(
    CSampleDSNamespaceEnum FAR* FAR* ppenumvariant,
    VARIANT var
    )
{
    HRESULT hr = S_OK;
    CSampleDSNamespaceEnum FAR* penumvariant = NULL;

    penumvariant = new CSampleDSNamespaceEnum();

    if (penumvariant == NULL){
        hr = E_OUTOFMEMORY;
        BAIL_ON_FAILURE(hr);
    }

    hr = SampleDSRDNEnum(
                     &penumvariant->_hEnum,
                     NULL
                     );
    BAIL_ON_FAILURE(hr);

    hr = ObjectTypeList::CreateObjectTypeList(
                                        var,
                                        &penumvariant->_pObjList
                                        );
    BAIL_ON_FAILURE(hr);

    *ppenumvariant = penumvariant;
    RRETURN(hr);

error:
    if (penumvariant) {
        delete penumvariant;
    }
    RRETURN(hr);
}

//+---------------------------------------------------------------------------
//
//  Function:   CSampleDSNamespaceEnum::CSampleDSNamespaceEnum
//
//  Synopsis:
//
//
//  Arguments:
//
//
//  Returns:
//
//----------------------------------------------------------------------------
CSampleDSNamespaceEnum::CSampleDSNamespaceEnum()
{
    _pObjList = NULL;
    _dwEntriesRead = 0;
    _dwCurrentEntry = 0;
    _hEnum = NULL;
    _pBuffer = NULL;
}

//+---------------------------------------------------------------------------
//
//  Function:   CSampleDSNamespaceEnum::~CSampleDSNamespaceEnum
//
//  Synopsis:
//
//
//  Arguments:
//
//  Returns:
//
//----------------------------------------------------------------------------
CSampleDSNamespaceEnum::~CSampleDSNamespaceEnum()
{
    if (_pBuffer)
        FreeProvMem(_pBuffer);
}

//+---------------------------------------------------------------------------
//
//  Function:   CSampleDSNamespaceEnum::Next
//
//  Synopsis:   Returns cElements number of requested ADs objects in the
//              array supplied in pvar.
//
//  Arguments:  [cElements] -- The number of elements requested by client
//              [pvar] -- ptr to array of VARIANTs to for return objects
//              [pcElementFetched] -- if non-NULL, then number of elements
//                                 -- actually returned is placed here
//
//  Returns:    HRESULT -- S_OK if number of elements requested are returned
//                      -- S_FALSE if number of elements is < requested
//
//----------------------------------------------------------------------------
STDMETHODIMP
CSampleDSNamespaceEnum::Next(
    ULONG cElements,
    VARIANT FAR* pvar,
    ULONG FAR* pcElementFetched
    )
{
    ULONG cElementFetched = 0;
    HRESULT hr = S_OK;

    hr = EnumObjects(
            cElements,
            pvar,
            &cElementFetched
            );

    if (pcElementFetched) {
        *pcElementFetched = cElementFetched;
    }
    RRETURN(hr);
}

HRESULT
CSampleDSNamespaceEnum::EnumObjects(
    ULONG cElements,
    VARIANT FAR* pvar,
    ULONG FAR* pcElementFetched
    )
{
    DWORD           i;
    ULONG           cRequested = 0;
    ULONG           cTotalFetched = 0;
    HRESULT         hr;

    for (i = 0; i < cElements; i++)  {
        VariantInit(&pvar[i]);
    }
    cRequested = cElements;

        hr = FetchObjects(cElements, pvar, &cTotalFetched);

    if (pcElementFetched) {
        *pcElementFetched = cTotalFetched;
    }

    RRETURN(hr);
}

//
// Load IDispatch pointers of Next cElements Root Objects into pvar array
//
HRESULT
CSampleDSNamespaceEnum::FetchObjects(

    ULONG cElements,
    VARIANT FAR* pvar,
    ULONG FAR* pcElementFetched
    )
{
    HRESULT hr;
    IDispatch *pDispatch = NULL;
    DWORD i = 0;

    while (i < cElements) {

        hr = FetchNextObject(&pDispatch);
        if (hr == S_FALSE) {
            break;
        }

        VariantInit(&pvar[i]);
        pvar[i].vt = VT_DISPATCH;
        pvar[i].pdispVal = pDispatch;
        (*pcElementFetched)++;
        i++;
    }
    return(hr);
}

//+---------------------------------------------------------------------------
//
//  Function:   CSampleDSNamespaceEnum::FetchNextObject
//
//  Synopsis:   Gets IDispatch pointer of next object in namespace.
//
//  Arguments:  [ppDispatch] -- Pointer to where to return IDispatch pointer.
//
//  Returns:    HRESULT -- S_OK if got the next object
//                      -- S_FALSE if not
//
//  Modifies:   [*ppDispatch]
//
//----------------------------------------------------------------------------
HRESULT
CSampleDSNamespaceEnum::FetchNextObject(
    IDispatch ** ppDispatch
    )
{
    HRESULT hr;
    LPTSTR lpRootRDN=NULL ;
    LPTSTR lpClassName=NULL ;
    *ppDispatch = NULL;

    hr = SampleDSNextRDN(_hEnum,
                         &lpRootRDN,
                         &lpClassName
                         );

    if (hr != S_OK) {
        if (_hEnum) {
            SampleDSFreeEnum(_hEnum);
            _hEnum = NULL;
        }
        goto error;
    }

    hr = CSampleDSGenObject::CreateGenericObject(
                                         L"Sample:",
                                        lpRootRDN,
                                        lpClassName,
                                        ADS_OBJECT_BOUND,
                                        IID_IDispatch,
                                        (void **)ppDispatch
                                        );
error:
    if (lpClassName)
        FreeProvMem(lpClassName);
    if (lpRootRDN)
        FreeProvMem(lpRootRDN);
    RRETURN_ENUM_STATUS(hr);
}


CENUMNS.H

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    CEnumNs.h

Abstract:

Author:

Environment:

    User mode

Revision History :

--*/
#ifndef _CENUMNS_H_
#define _CENUMNS_H_

class FAR CSampleDSNamespaceEnum : public CSampleDSEnumVariant
{
public:

    // IEnumVARIANT methods
    STDMETHOD(Next)(ULONG cElements,
                    VARIANT FAR* pvar,
                    ULONG FAR* pcElementFetched);

    static HRESULT Create(CSampleDSNamespaceEnum FAR* FAR*, VARIANT var);

    CSampleDSNamespaceEnum();
    ~CSampleDSNamespaceEnum();

    HRESULT
    PrepBuffer();

    HRESULT
    FetchNextObject(
        IDispatch ** ppDispatch
        );

    HRESULT
    FetchObjects(
        ULONG cElements,
        VARIANT FAR* pvar,
        ULONG FAR* pcElementFetched
        );

    HRESULT
    CSampleDSNamespaceEnum::EnumObjects(
        ULONG cElements,
        VARIANT FAR* pvar,
        ULONG FAR* pcElementFetched
        );

private:

    ObjectTypeList FAR *_pObjList;

    DWORD _dwEntriesRead;
    DWORD _dwCurrentEntry;
    HANDLE _hEnum;
    LPVOID _pBuffer;
    LPVOID _pBufferEnd;
};

#endif


CENUMOBJ.CPP

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    CEnumObj.c

Abstract:

    Generic Object Enumeration CodeStandard IClassFactory implementation

Author:

Environment:

    User mode

Revision History :

--*/
#include "adssmp.h"
#pragma hdrstop

//+---------------------------------------------------------------------------
//
//  Function:   CSampleDSEnumVariant::Create
//
//  Synopsis:
//
//  Arguments:  [pCollection]
//              [ppEnumVariant]
//
//  Returns:    HRESULT
//
//  Modifies:
//
//  History:
//
//----------------------------------------------------------------------------
HRESULT
CSampleDSGenObjectEnum::Create(
    CSampleDSGenObjectEnum FAR* FAR* ppenumvariant,
    BSTR ADsPath,
    VARIANT var
    )
{
    HRESULT hr = NOERROR;
    CSampleDSGenObjectEnum FAR* penumvariant = NULL;
    WCHAR szDSPath[MAX_PATH];
    DWORD dwModificationTime = 0L;
    DWORD dwNumberOfEntries = 0L;
    DWORD dwStatus = 0L;

    *ppenumvariant = NULL;

    penumvariant = new CSampleDSGenObjectEnum();
    if (!penumvariant) {
        hr = E_OUTOFMEMORY;
        BAIL_ON_FAILURE(hr);
    }

    hr = xx_put_BSTR(&penumvariant->_ADsPath, ADsPath);
    BAIL_ON_FAILURE(hr);

    hr = BuildDSFilterArray(
                var,
                (LPBYTE *)&penumvariant->_pDSFilterList
                );
    if (FAILED(hr)) {
        penumvariant->_pDSFilterList = NULL;
    }

    *ppenumvariant = penumvariant;

    hr = BuildDSPathFromADsPath(
                ADsPath,
                szDSPath
                );
    BAIL_ON_FAILURE(hr);

    hr = SampleDSOpenObject(
                    szDSPath,
                    &penumvariant->_hObject,
                    NULL,
                    REG_DS
                    );
    BAIL_ON_FAILURE(hr);

    hr = SampleDSRDNEnum(
                 &penumvariant->_hEnum,
                 penumvariant->_hObject
                 );

    RRETURN(hr);

error:
    if (penumvariant->_hObject) {
        SampleDSCloseObject(penumvariant->_hObject);
    }

    delete penumvariant;

    RRETURN(hr);
}

CSampleDSGenObjectEnum::CSampleDSGenObjectEnum():
                    _ADsPath(NULL)
{
    _pObjList = NULL;
    _dwObjectReturned = 0;
    _dwObjectCurrentEntry = 0;
    _dwObjectTotal = 0;
    _hObject = NULL;
    _hEnum = NULL;
    _lpObjects = NULL;
    _pDSFilterList = NULL;

    _bNoMore = FALSE;
}

CSampleDSGenObjectEnum::~CSampleDSGenObjectEnum()
{
    if (_pDSFilterList) {
        FreeFilterList((LPBYTE)_pDSFilterList);
    }
}

HRESULT
CSampleDSGenObjectEnum::EnumObjects(
    DWORD ObjectType,
    ULONG cElements,
    VARIANT FAR * pvar,
    ULONG FAR * pcElementFetched
    )
{

    //
    // Multi-level detection of Objects may not be necessary for DS code
    //

    RRETURN(EnumGenericObjects(cElements, pvar, pcElementFetched));

}
HRESULT
CSampleDSGenObjectEnum::EnumGenericObjects(
    ULONG cElements,
    VARIANT FAR* pvar,
    ULONG FAR* pcElementFetched
    )
{
    DWORD           i;
    ULONG           cRequested = 0;
    ULONG           cTotalFetched = 0;
    HRESULT         hr;

    for (i = 0; i < cElements; i++)  {
        VariantInit(&pvar[i]);
    }
    cRequested = cElements;

        hr = FetchObjects(cElements, pvar, &cTotalFetched);

    if (pcElementFetched) {
        *pcElementFetched = cTotalFetched;
    }

    RRETURN(hr);
}

//
// Load IDispatch pointers of Next cElements Filtered Objects into pvar array
//
HRESULT
CSampleDSGenObjectEnum::FetchObjects(

    ULONG cElements,
    VARIANT FAR* pvar,
    ULONG FAR* pcElementFetched
    )
{
    HRESULT hr;
    IDispatch *pDispatch = NULL;
    DWORD i = 0;

    while (i < cElements) {

        hr = FetchNextObject(&pDispatch);
        if (hr == S_FALSE) {
            break;
        }

        VariantInit(&pvar[i]);
        pvar[i].vt = VT_DISPATCH;
        pvar[i].pdispVal = pDispatch;
        (*pcElementFetched)++;
        i++;
    }
    return(hr);
}
//+---------------------------------------------------------------------------
//
//  Function:   CSampleDSNamespaceEnum::FetchNextObject
//
//  Synopsis:   Gets IDispatch pointer of next object in namespace subject to
//                              filter.
//
//  Arguments:  [ppDispatch] -- Pointer to where to return IDispatch pointer.
//
//  Returns:    HRESULT -- S_OK if got the next object
//                      -- S_FALSE if not
//
//  Modifies:   [*ppDispatch]
//
//----------------------------------------------------------------------------
HRESULT
CSampleDSGenObjectEnum::FetchNextObject(
    IDispatch ** ppDispatch
    )
 {
    HRESULT hr;
    LPTSTR lpszObjectRDN=NULL ;
    LPTSTR lpszObjectClass = 0;
    DWORD dwClassId = 0;
    IADs * pADs = NULL;
    *ppDispatch = NULL;

    hr = S_OK;
    while (hr == S_OK){
        hr=SampleDSNextRDN(_hEnum,
                           &lpszObjectRDN,
                           &lpszObjectClass
                           );
        if (S_OK==hr &&
            S_OK==IsValidDSFilter(lpszObjectClass)) {
            break;
        }
    }

    //
    // Now create and send back the current object
    //
    if (hr == S_OK) {
        hr = CSampleDSGenObject::CreateGenericObject(_ADsPath,
                                                     lpszObjectRDN,
                                                     lpszObjectClass,
                                                     ADS_OBJECT_BOUND,
                                                     IID_IDispatch,
                                                     (void **)ppDispatch
                                                     );
        BAIL_ON_FAILURE(hr);
    }
    else {
        if (_hEnum) {
            SampleDSFreeEnum(_hEnum);
            _hEnum = NULL;
        }
    }

error:

    //
    // Free the intermediate pADs pointer.
    //
    if (pADs) {
        pADs->Release();
    }
    if (lpszObjectRDN)
        FreeProvMem(lpszObjectRDN);
    if (lpszObjectClass)
        FreeProvMem(lpszObjectClass);
    RRETURN_ENUM_STATUS(hr);
}

//+---------------------------------------------------------------------------
//
//  Function:   CSampleDSGenObjectEnum::Next
//
//  Synopsis:   Returns cElements number of requested NetOle objects in the
//              array supplied in pvar.
//
//  Arguments:  [cElements] -- The number of elements requested by client
//              [pvar] -- ptr to array of VARIANTs to for return objects
//              [pcElementFetched] -- if non-NULL, then number of elements
//                                 -- actually returned is placed here
//
//  Returns:    HRESULT -- S_OK if number of elements requested are returned
//                      -- S_FALSE if number of elements is < requested
//
//
//----------------------------------------------------------------------------
STDMETHODIMP
CSampleDSGenObjectEnum::Next(
    ULONG cElements,
    VARIANT FAR* pvar,
    ULONG FAR* pcElementFetched
    )
{
    ULONG cElementFetched = 0;
    HRESULT hr = S_OK;

    hr = EnumGenericObjects(
            cElements,
            pvar,
            &cElementFetched
            );

    if (pcElementFetched) {
        *pcElementFetched = cElementFetched;
    }
    RRETURN(hr);
}

HRESULT
CSampleDSGenObjectEnum::IsValidDSFilter(LPWSTR ObjectName){

    if (_pDSFilterList){
                for (DWORD i = 0; i < _pDSFilterList->dwNumberOfFilters; i++) {

                        if ( !_wcsicmp(ObjectName,_pDSFilterList->Filters[i].lpObjectClass) ) {
                                RRETURN(S_OK);
                        }
                }
                RRETURN(E_FAIL);
    }
    RRETURN(S_OK);
}

HRESULT
BuildDSFilterArray(
    VARIANT var,
    LPBYTE * ppContigFilter
    )
{
    LONG uDestCount = 0;
    LONG dwSLBound = 0;
    LONG dwSUBound = 0;
    VARIANT v;
    LONG i;
    HRESULT hr = S_OK;

    LPDS_FILTER_LIST pDsFilterList = NULL;
    LPBYTE pContigFilter = NULL;

    if(!((V_VT(&var) &  VT_VARIANT) &&  V_ISARRAY(&var))) {
        RRETURN(E_FAIL);
    }

    //
    // Check that there is only one dimension in this array
    //

    if ((V_ARRAY(&var))->cDims != 1) {
        hr = E_FAIL;
        BAIL_ON_FAILURE(hr);
    }
    //
    // Check that there is atleast one element in this array
    //

    if ((V_ARRAY(&var))->rgsabound[0].cElements == 0){
        hr = E_FAIL;
        BAIL_ON_FAILURE(hr);
    }

    //
    // We know that this is a valid single dimension array
    //

    hr = SafeArrayGetLBound(V_ARRAY(&var),
                            1,
                            (long FAR *)&dwSLBound
                            );
    BAIL_ON_FAILURE(hr);

    hr = SafeArrayGetUBound(V_ARRAY(&var),
                            1,
                            (long FAR *)&dwSUBound
                            );
    BAIL_ON_FAILURE(hr);

    pContigFilter = (LPBYTE)AllocProvMem(
                            sizeof(DS_FILTER_LIST)
                            - sizeof(DS_FILTER)
                            );
    if (!pContigFilter) {

        hr = E_FAIL;
        BAIL_ON_FAILURE(hr);
    }

    for (i = dwSLBound; i <= dwSUBound; i++) {
        VariantInit(&v);
        hr = SafeArrayGetElement(V_ARRAY(&var),
                                (long FAR *)&i,
                                &v
                                );
        if (FAILED(hr)) {
            continue;
        }

        //
        //  Create an entry in the filter block
        //  Append it to the existing block
        //

        pContigFilter = CreateAndAppendFilterEntry(
                            pContigFilter,
                            V_BSTR(&v)
                            );
        if (!pContigFilter) {

            hr = E_FAIL;
            BAIL_ON_FAILURE(hr);
        }

    }

    pDsFilterList = (LPDS_FILTER_LIST)pContigFilter;

    if (!pDsFilterList->dwNumberOfFilters){

        hr = E_FAIL;
        BAIL_ON_FAILURE(hr);
    }

    *ppContigFilter = pContigFilter;

    RRETURN(S_OK);

error:

    if (pContigFilter){

        FreeFilterList(
               pContigFilter
               );

    }

    *ppContigFilter = NULL;

    RRETURN(hr);
}

LPBYTE
CreateAndAppendFilterEntry(
    LPBYTE pContigFilter,
    LPWSTR lpObjectClass
    )
{
    LPWSTR pszFilter = NULL;
    LPDS_FILTER_LIST pDsFilterList = NULL;
    DWORD dwFilterCount = 0;
    LPBYTE pNewContigFilter = NULL;
    LPDS_FILTER pNewEntry = NULL;

    pszFilter = (LPWSTR)AllocProvStr(lpObjectClass);
    if (!pszFilter) {
        return(pContigFilter);
    }

    pDsFilterList = (LPDS_FILTER_LIST)pContigFilter;

    dwFilterCount = pDsFilterList->dwNumberOfFilters;

    pNewContigFilter = (LPBYTE)ReallocProvMem(
                                    pContigFilter,

                                    sizeof(DS_FILTER_LIST) +
                                    (dwFilterCount - 1)* sizeof(DS_FILTER),

                                    sizeof(DS_FILTER_LIST)
                                    + dwFilterCount * sizeof(DS_FILTER)
                                    );
    if (!pNewContigFilter) {
        return(pContigFilter);
    }

    pNewEntry = (LPDS_FILTER)(pNewContigFilter + sizeof(DS_FILTER_LIST)
                        + (dwFilterCount - 1)* sizeof(DS_FILTER));

    pNewEntry->lpObjectClass = pszFilter;

    pDsFilterList = (LPDS_FILTER_LIST)pNewContigFilter;

    pDsFilterList->dwNumberOfFilters = dwFilterCount + 1;

    return(pNewContigFilter);
}

void
FreeFilterList(
    LPBYTE lpContigFilter
    )
{
    LPDS_FILTER_LIST lpDsFilterList = (LPDS_FILTER_LIST)lpContigFilter;
    DWORD dwNumFilters = 0;
    LPDS_FILTER lpDsFilter = NULL;
    DWORD i = 0;

    dwNumFilters = lpDsFilterList->dwNumberOfFilters;

    if (dwNumFilters){

        lpDsFilter = (LPDS_FILTER)(lpContigFilter  + sizeof(DS_FILTER_LIST)
                                      - sizeof(DS_FILTER));

        for (i = 0; i < dwNumFilters; i++) {

            FreeProvStr((lpDsFilter + i)->lpObjectClass);
        }

    }

    FreeProvMem(lpContigFilter);
}


CENUMOBJ.H

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    CEnumObj.h

Abstract:

Author:

Environment:

    User mode

Revision History :

--*/
#ifndef _CENUMOBJ_H_
#define _CENUMOBJ_H_

class FAR CSampleDSGenObjectEnum : public CSampleDSEnumVariant
{
public:
    CSampleDSGenObjectEnum(ObjectTypeList ObjList);
    CSampleDSGenObjectEnum();
    ~CSampleDSGenObjectEnum();

    HRESULT
    EnumObjects(
        ULONG cElements,
        VARIANT FAR * pvar,
        ULONG FAR * pcElementFetched
        );

    static
    HRESULT
    CSampleDSGenObjectEnum::Create(
        CSampleDSGenObjectEnum FAR* FAR* ppenumvariant,
        BSTR ADsPath,
        VARIANT var
        );

private:

        HRESULT
        CSampleDSGenObjectEnum::FetchObjects(
            ULONG cElements,
                VARIANT FAR* pvar,
                ULONG FAR* pcElementFetched
                );

        HRESULT
        CSampleDSGenObjectEnum::FetchNextObject(
                IDispatch ** ppDispatch
                );

    ObjectTypeList FAR *_pObjList;
    LPDS_FILTER_LIST _pDSFilterList;

    HANDLE      _hObject;
    HANDLE      _hEnum;
    LPDS_OBJECT_INFO _lpObjects;
    DWORD       _dwObjectReturned;
    DWORD       _dwObjectCurrentEntry;
    DWORD       _dwObjectTotal;

    BSTR        _ADsPath;

    BOOL        _bNoMore;

    HRESULT
    CSampleDSGenObjectEnum::GetGenObject(
        IDispatch ** ppDispatch
        );

    HRESULT
    EnumGenericObjects(
        ULONG cElements,
        VARIANT FAR* pvar,
        ULONG FAR* pcElementFetched
        );

    HRESULT
    CSampleDSGenObjectEnum::EnumObjects(
        DWORD ObjectType,
        ULONG cElements,
        VARIANT FAR * pvar,
        ULONG FAR * pcElementFetched
        );

    STDMETHOD(Next)(
        ULONG cElements,
        VARIANT FAR* pvar,
        ULONG FAR* pcElementFetched
        );

        HRESULT
        CSampleDSGenObjectEnum::IsValidDSFilter(
                LPWSTR lpObjectClass
                );

};

HRESULT
BuildDSFilterArray(
    VARIANT var,
    LPBYTE * ppContigFilter
    );

void
FreeFilterList(
    LPBYTE lpContigFilter
    );

LPBYTE
CreateAndAppendFilterEntry(
    LPBYTE pContigFilter,
    LPWSTR lpObjectClass
    );
#endif


CENUMSCH.CPP

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    CEnumSch.cpp

Abstract:

    Sample Provider Enum Schema functionality

Author:

Environment:

    User mode

Revision History :

--*/
#include "adssmp.h"
#pragma hdrstop

//+---------------------------------------------------------------------------
//
//  Function:   CSampleDSSchemaEnum::Create
//
//  Synopsis:
//
//  Arguments:  [pCollection]
//              [ppEnumVariant]
//
//  Returns:    HRESULT
//
//  Modifies:
//
//  History:    01-30-95   yihsins     Created.
//
//----------------------------------------------------------------------------
HRESULT
CSampleDSSchemaEnum::Create(
    CSampleDSSchemaEnum FAR* FAR* ppenumvariant,
    BSTR bstrSampleDSTreeName,
    BSTR bstrADsPath,
    BSTR bstrName,
    VARIANT var
    )
{
    HRESULT hr = S_OK;
    CSampleDSSchemaEnum FAR* penumvariant = NULL;

    *ppenumvariant = NULL;

    penumvariant = new CSampleDSSchemaEnum();
    if (!penumvariant)
    {
        hr = E_OUTOFMEMORY;
        BAIL_ON_FAILURE(hr);
    }

    hr = ProvAllocString( bstrSampleDSTreeName, &penumvariant->_bstrSampleDSTreeName);
    BAIL_ON_FAILURE(hr);
    hr = ProvAllocString( bstrADsPath, &penumvariant->_bstrADsPath);
    BAIL_ON_FAILURE(hr);

    hr = ProvAllocString( bstrName, &penumvariant->_bstrName);
    BAIL_ON_FAILURE(hr);

    hr = ObjectTypeList::CreateObjectTypeList(
            var,
            &penumvariant->_pObjList
            );
    BAIL_ON_FAILURE(hr);

    *ppenumvariant = penumvariant;

    RRETURN(hr);

error:

    delete penumvariant;

    RRETURN(hr);
}

CSampleDSSchemaEnum::CSampleDSSchemaEnum()
    : _bstrADsPath( NULL ),
      _bstrName( NULL ),
      _bstrSampleDSTreeName( NULL ),
      _pObjList( NULL )
{
    _lpClassDefs = NULL;
    _dwClassCurrent = 0;
    _dwClassReturned = 0;

    _lpAttrDefs = NULL;
    _dwAttrCurrent = 0;
    _dwAttrReturned = 0;

}

CSampleDSSchemaEnum::~CSampleDSSchemaEnum()
{
   ProvFreeString( _bstrName );
   ProvFreeString( _bstrADsPath );
   ProvFreeString( _bstrSampleDSTreeName );

   if ( _pObjList != NULL )
   {
       delete _pObjList;
       _pObjList = NULL;
   }
}

//+---------------------------------------------------------------------------
//
//  Function:   CSampleDSSchemaEnum::Next
//
//  Synopsis:   Returns cElements number of requested NetOle objects in the
//              array supplied in pvar.
//
//  Arguments:  [cElements] -- The number of elements requested by client
//              [pvar] -- ptr to array of VARIANTs to for return objects
//              [pcElementFetched] -- if non-NULL, then number of elements
//                                 -- actually returned is placed here
//
//  Returns:    HRESULT -- S_OK if number of elements requested are returned
//                      -- S_FALSE if number of elements is < requested
//
//  Modifies:
//
//  History:    11-3-95   yihsins     Created.
//
//----------------------------------------------------------------------------
STDMETHODIMP
CSampleDSSchemaEnum::Next(
    ULONG cElements,
    VARIANT FAR* pvar,
    ULONG FAR* pcElementFetched
    )
{
    ULONG cElementFetched = 0;
    HRESULT hr = S_OK;

    hr = EnumObjects(
                cElements,
                pvar,
                &cElementFetched
                );

    if ( pcElementFetched )
        *pcElementFetched = cElementFetched;

    RRETURN(hr);
}

HRESULT
CSampleDSSchemaEnum::EnumObjects(
    DWORD ObjectType,
    ULONG cElements,
    VARIANT FAR * pvar,
    ULONG FAR * pcElementFetched
    )
{
    switch (ObjectType)
    {
        case SampleDS_CLASS_ID:
            RRETURN (EnumClasses(cElements, pvar, pcElementFetched));

        case SampleDS_PROPERTY_ID:
            RRETURN (EnumProperties(cElements, pvar, pcElementFetched));

        default:
            RRETURN(S_FALSE);
    }
}

HRESULT
CSampleDSSchemaEnum::EnumObjects(
    ULONG cElements,
    VARIANT FAR* pvar,
    ULONG FAR* pcElementFetched
    )
{
    DWORD           i;
    ULONG           cRequested = 0;
    ULONG           cFetchedByPath = 0;
    ULONG           cTotalFetched = 0;
    VARIANT FAR*    pPathvar = pvar;
    HRESULT         hr;
    DWORD           ObjectType;

    for (i = 0; i < cElements; i++)
        VariantInit(&pvar[i]);

    cRequested = cElements;

    while (  SUCCEEDED( _pObjList->GetCurrentObject(&ObjectType))
          && ((hr = EnumObjects( ObjectType,
                                 cRequested,
                                 pPathvar,
                                 &cFetchedByPath)) == S_FALSE )
          )
    {
        pPathvar += cFetchedByPath;
        cRequested -= cFetchedByPath;
        cTotalFetched += cFetchedByPath;

        cFetchedByPath = 0;

        if ( FAILED(_pObjList->Next()) )
        {
            if ( pcElementFetched )
                *pcElementFetched = cTotalFetched;
            RRETURN(S_FALSE);
        }
    }

    if ( pcElementFetched )
        *pcElementFetched = cTotalFetched + cFetchedByPath;

    RRETURN(hr);
}

HRESULT
CSampleDSSchemaEnum::EnumClasses(
    ULONG cElements,
    VARIANT FAR* pvar,
    ULONG FAR* pcElementFetched
    )
{
    HRESULT hr = S_OK;
    DWORD i = 0;
    IDispatch *pDispatch = NULL;

    while ( i < cElements )
    {
        hr = GetClassObject(&pDispatch);
        if ( hr == S_FALSE )
            break;

        VariantInit( &pvar[i] );
        pvar[i].vt = VT_DISPATCH;
        pvar[i].pdispVal = pDispatch;
        (*pcElementFetched)++;
        i++;
    }

    RRETURN(hr);
}

HRESULT
CSampleDSSchemaEnum::GetClassObject(
    IDispatch ** ppDispatch
    )
{
    HRESULT hr = S_OK;
    LPSampleDS_CLASS_DEF lpCurrentObject = NULL;

    if (!_lpClassDefs) {
        _dwClassCurrent = 0;
        hr = SampleDSGetClassDefinition(&_lpClassDefs,
                                        &_dwClassReturned);
        BAIL_ON_FAILURE(hr);
    }

    if (_dwClassCurrent == _dwClassReturned) {
        goto error;
    }

    if (_dwClassCurrent < _dwClassReturned) {

        //
        // Now send back the current object
        //

        lpCurrentObject = _lpClassDefs + _dwClassCurrent;

        hr = CSampleDSClass::CreateClass(
                            _bstrADsPath,
                            lpCurrentObject->lpClassName,
                            lpCurrentObject,
                            ADS_OBJECT_BOUND,
                            IID_IDispatch,
                            (void **)ppDispatch
                            );
        BAIL_ON_FAILURE(hr);
        _dwClassCurrent++;

        RRETURN(S_OK);

    }

error:
    if (_lpClassDefs) {
        SampleDSFreeClassDefinition(_lpClassDefs,
                                    _dwClassReturned);
    }
    *ppDispatch = NULL;
    RRETURN(S_FALSE);
}

HRESULT
CSampleDSSchemaEnum::EnumProperties(
    ULONG cElements,
    VARIANT FAR* pvar,
    ULONG FAR* pcElementFetched
    )
{
    HRESULT hr = S_OK;
    DWORD i = 0;
    IDispatch *pDispatch = NULL;

    while ( i < cElements )
    {
        hr = GetPropertyObject(&pDispatch);
        if ( hr == S_FALSE )
            break;

        VariantInit( &pvar[i] );
        pvar[i].vt = VT_DISPATCH;
        pvar[i].pdispVal = pDispatch;
        (*pcElementFetched)++;
        i++;
    }

    RRETURN(hr);
}

HRESULT
CSampleDSSchemaEnum::GetPropertyObject(
    IDispatch ** ppDispatch
    )
{
    HRESULT hr = S_OK;

    LPSampleDS_ATTR_DEF lpCurrentObject = NULL;

    if (!_lpAttrDefs) {
        _dwAttrCurrent = 0;
        hr = SampleDSGetPropertyDefinition(&_lpAttrDefs,
                                           &_dwAttrReturned);
        BAIL_ON_FAILURE(hr);
    }

    if (_dwAttrCurrent == _dwAttrReturned) {
        goto error;
    }

    if (_dwAttrCurrent < _dwAttrReturned) {

        lpCurrentObject = _lpAttrDefs + _dwAttrCurrent;

        hr = CSampleDSProperty::CreateProperty(
                            _bstrADsPath,
                            lpCurrentObject->lpAttributeName,
                            lpCurrentObject,
                            ADS_OBJECT_BOUND,
                            IID_IDispatch,
                            (void **)ppDispatch
                            );
        BAIL_ON_FAILURE(hr);
        _dwAttrCurrent++;
        RRETURN(S_OK);
    }

error:
    if (_lpAttrDefs)
        SampleDSFreePropertyDefinition(_lpAttrDefs,
                                       _dwAttrReturned);
    *ppDispatch = NULL;
    RRETURN(S_FALSE);
}


CENUMSCH.H

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    EnumSch.h

Abstract:

Author:

Environment:

    User mode

Revision History :

--*/
#ifndef _ENUMSCH_H_
#define _ENUMSCH_H_

class FAR CSampleDSSchemaEnum : public CSampleDSEnumVariant
{
public:
    // IEnumVARIANT methods
    STDMETHOD(Next)(
        ULONG cElements,
        VARIANT FAR* pvar,
        ULONG FAR* pcElementFetched
        );

    static
    HRESULT
    Create(
        CSampleDSSchemaEnum FAR* FAR* ppenumvariant,
        BSTR bstrSampleDSTreeName,
        BSTR bstrADsPath,
        BSTR bstrDomainName,
        VARIANT var
        );

    CSampleDSSchemaEnum();
    ~CSampleDSSchemaEnum();

    HRESULT
    EnumObjects(
        ULONG cElements,
        VARIANT FAR * pvar,
        ULONG FAR * pcElementFetched
        );

private:

    ObjectTypeList FAR *_pObjList;
    //HANDLE _hOperationData;
    //HANDLE _hTree;

    LPSampleDS_ATTR_DEF _lpAttrDefs;
    DWORD _dwAttrCurrent;
    DWORD _dwAttrReturned;

    BSTR        _bstrName;
    BSTR        _bstrSampleDSTreeName;
    BSTR        _bstrADsPath;

    LPSampleDS_CLASS_DEF _lpClassDefs;
    DWORD       _dwClassCurrent;
    DWORD       _dwClassReturned;

    //DWORD       _dwCurrentEntry;
    //DWORD       _dwInfoType;

    HRESULT
    CSampleDSSchemaEnum::GetClassObject(
        IDispatch **ppDispatch
        );

    HRESULT
    EnumClasses(
        ULONG cElements,
        VARIANT FAR* pvar,
        ULONG FAR* pcElementFetched
        );

    HRESULT
    CSampleDSSchemaEnum::EnumObjects(
        DWORD ObjectType,
        ULONG cElements,
        VARIANT FAR * pvar,
        ULONG FAR * pcElementFetched
        );

    HRESULT
    CSampleDSSchemaEnum::EnumProperties(
        ULONG cElements,
        VARIANT FAR* pvar,
        ULONG FAR* pcElementFetched
        );

    HRESULT
    CSampleDSSchemaEnum::GetPropertyObject(
        IDispatch ** ppDispatch
        );
};

#endif


CENUMVAR.CPP

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    CEnumVar.cpp

Abstract:

    Base Implementation for xxxEnumVariant derived classes

Author:

Environment:

    User mode

Revision History :

--*/
#include "adssmp.h"
#pragma hdrstop

//+---------------------------------------------------------------------------
//
//  Function:   CSampleDSEnumVariant::CSampleDSEnumVariant
//
//  Synopsis:
//
//
//  Arguments:
//
//
//  Returns:
//
//  Modifies:
//
//----------------------------------------------------------------------------
CSampleDSEnumVariant::CSampleDSEnumVariant()
{
    //
    // Set the reference count on the enumerator.
    //
    m_cRef = 1;

}

//+---------------------------------------------------------------------------
//
//  Function:   CSampleDSEnumVariant::~CSampleDSEnumVariant
//
//  Synopsis:
//
//
//  Arguments:
//
//  Returns:
//
//  Modifies:
//
//----------------------------------------------------------------------------
CSampleDSEnumVariant::~CSampleDSEnumVariant()
{
    //
    // Bump down the reference count on the Collection object
    //
}

//+---------------------------------------------------------------------------
//
//  Function:   CSampleDSEnumVariant::QueryInterface
//
//  Synopsis:
//
//  Arguments:  [iid]
//              [ppv]
//
//  Returns:    HRESULT
//
//  Modifies:
//
//----------------------------------------------------------------------------
STDMETHODIMP
CSampleDSEnumVariant::QueryInterface(REFIID iid, void FAR* FAR* ppv)
{
    *ppv = NULL;

    if (iid == IID_IUnknown || iid == IID_IEnumVARIANT) {

        *ppv = this;

    }
    else {

        return ResultFromScode(E_NOINTERFACE);
    }

    AddRef();
    return NOERROR;
}

//+---------------------------------------------------------------------------
//
//  Function:   CSampleDSEnumVariant::AddRef
//
//  Synopsis:
//
//  Arguments:
//
//  Returns:    HRESULT
//
//  Modifies:
//
//----------------------------------------------------------------------------
STDMETHODIMP_(ULONG)
CSampleDSEnumVariant::AddRef(void)
{

    return ++m_cRef;
}

//+---------------------------------------------------------------------------
//
//  Function:   CSampleDSEnumVariant::Release
//
//  Synopsis:
//
//
//  Arguments:  [void]
//
//  Returns:
//
//  Modifies:
//
//----------------------------------------------------------------------------
STDMETHODIMP_(ULONG)
CSampleDSEnumVariant::Release(void)
{

    if(--m_cRef == 0){

        delete this;
        return 0;
    }

    return m_cRef;
}

//+---------------------------------------------------------------------------
//
//  Function:   CSampleDSEnumVariant::Skip
//
//  Synopsis:
//
//  Arguments:  [cElements]
//
//  Returns:    HRESULT
//
//  Modifies:
//
//----------------------------------------------------------------------------
STDMETHODIMP
CSampleDSEnumVariant::Skip(ULONG cElements)
{

    RRETURN(ResultFromScode(E_FAIL));

}

//+---------------------------------------------------------------------------
//
//  Function:   CSampleDSEnumVariant::Reset
//
//  Synopsis:
//
//  Arguments:  []
//
//  Returns:    HRESULT
//
//  Modifies:
//
//----------------------------------------------------------------------------
STDMETHODIMP
CSampleDSEnumVariant::Reset()
{

    RRETURN(ResultFromScode(S_OK));
}

//+---------------------------------------------------------------------------
//
//  Function:   CSampleDSEnumVariant::Clone
//
//  Synopsis:
//
//  Arguments:  [pCollection]
//              [ppEnumVariant]
//
//  Returns:    HRESULT
//
//  Modifies:
//
//----------------------------------------------------------------------------
STDMETHODIMP
CSampleDSEnumVariant::Clone(IEnumVARIANT FAR* FAR* ppenum)
{
    RRETURN(ResultFromScode(E_FAIL));
}


CENUMVAR.H

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    CEnumVar.h

Abstract:

Author:

Environment:

    User mode

Revision History :

--*/
#ifndef _CENUMVAR_H_
#define _CENUMVAR_H_

class FAR CSampleDSEnumVariant : public IEnumVARIANT
{
public:
    // IUnknown methods
    STDMETHOD(QueryInterface)(REFIID riid, LPVOID FAR* ppvObj) ;
    STDMETHOD_(ULONG, AddRef)() ;
    STDMETHOD_(ULONG, Release)() ;

    // IEnumVARIANT methods
    STDMETHOD(Next)(ULONG cElements,
                    VARIANT FAR* pvar,
                    ULONG FAR* pcElementFetched) PURE;
    STDMETHOD(Skip)(ULONG cElements);
    STDMETHOD(Reset)();
    STDMETHOD(Clone)(IEnumVARIANT FAR* FAR* ppenum);

    CSampleDSEnumVariant();
    ~CSampleDSEnumVariant();

private:
    ULONG m_cRef;
};

#endif


CGENOBJ.CPP

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    CGenObj.cpp

Abstract:

    Microsoft ADs DS Provider Generic Object

Author:

Environment:

    User mode

Revision History :

--*/
#include "adssmp.h"
#pragma hdrstop

DEFINE_IDispatch_Implementation(CSampleDSGenObject)
DEFINE_IADs_Implementation(CSampleDSGenObject)

CSampleDSGenObject::CSampleDSGenObject():_pPropertyCache(NULL)
{
    VariantInit(&_vFilter);
}

HRESULT
CSampleDSGenObject::CreateGenericObject(
    BSTR Parent,
    BSTR CommonName,
    BSTR ClassName,
    DWORD dwObjectState,
    REFIID riid,
    void **ppvObj
    )
{
    CSampleDSGenObject FAR * pGenObject = NULL;
    HRESULT hr = S_OK;

    hr = AllocateGenObject(&pGenObject);
    BAIL_ON_FAILURE(hr);

    hr = pGenObject->InitializeCoreObject(
                Parent,
                CommonName,
                ClassName,
                L"",
                CLSID_SampleDSGenObject,
                dwObjectState
                );
    BAIL_ON_FAILURE(hr);

    hr = pGenObject->QueryInterface(riid, ppvObj);
    BAIL_ON_FAILURE(hr);

    pGenObject->Release();

    RRETURN(hr);

error:

    delete pGenObject;

    RRETURN(hr);
}

CSampleDSGenObject::~CSampleDSGenObject( )
{

    delete _pDispMgr;
    delete _pPropertyCache;

}

STDMETHODIMP
CSampleDSGenObject::QueryInterface(REFIID iid, LPVOID FAR* ppv)
{
    if (IsEqualIID(iid, IID_IUnknown))
    {
        *ppv = (IADs FAR *) this;
    }
    else if (IsEqualIID(iid, IID_IADsContainer))
    {
        *ppv = (IADsContainer FAR *) this;
    }
    else if (IsEqualIID(iid, IID_IADs))
    {
        *ppv = (IADs FAR *) this;
    }
    else if (IsEqualIID(iid, IID_IDispatch))
    {
        *ppv = (IADs FAR *) this;
    }
    else
    {
        *ppv = NULL;
        return E_NOINTERFACE;
    }
    AddRef();
    return NOERROR;
}

HRESULT
CSampleDSGenObject::SetInfo()
{
    HRESULT hr = S_OK;

    if (GetObjectState() == ADS_OBJECT_UNBOUND) {

        hr = SampleDSCreateObject();
        BAIL_ON_FAILURE(hr);

        //
        // If the create succeded, set the object type to bound
        //

        SetObjectState(ADS_OBJECT_BOUND);

    }else {

        hr = SampleDSSetObject();
        BAIL_ON_FAILURE(hr);
    }

error:

    RRETURN(hr);
}

HRESULT
CSampleDSGenObject::SampleDSSetObject()
{
    WCHAR szSampleDSPathName[MAX_PATH];
    HANDLE hObject = NULL;
    HRESULT hr = S_OK;
    HANDLE hOperationData = NULL;

    hr = BuildDSPathFromADsPath(
                _ADsPath,
                szSampleDSPathName
                );
    BAIL_ON_FAILURE(hr);

    hr = SampleDSOpenObject(
                    szSampleDSPathName,
                    &hObject,
                    NULL,
                    REG_DS
                    );
    BAIL_ON_FAILURE(hr);

    hr = SampleDSCreateBuffer(&hOperationData);
    BAIL_ON_FAILURE(hr);

    hr = _pPropertyCache->SampleDSMarshallProperties(
                            hOperationData
                            );

    BAIL_ON_FAILURE(hr);
    hr = SampleDSModifyObject(hObject,
                              hOperationData
                             );
error:
    if (hObject) {
        hr = SampleDSCloseObject(hObject);
    }
    if (hOperationData) {
        SampleDSFreeBuffer(hOperationData);
    }
    RRETURN(hr);
}

HRESULT
CSampleDSGenObject::SampleDSCreateObject()
{
    WCHAR szSampleDSParentName[MAX_PATH];
    HANDLE hOperationData = NULL;
    HANDLE hObject = NULL;
    HRESULT hr = S_OK;

    hr = BuildDSPathFromADsPath(
                _Parent,
                szSampleDSParentName
                );
    BAIL_ON_FAILURE(hr);

    hr = SampleDSOpenObject(
                    szSampleDSParentName,
                    &hObject,
                    NULL,
                    REG_DS
                    );
    BAIL_ON_FAILURE(hr);

    hr = SampleDSCreateBuffer(&hOperationData);
    BAIL_ON_FAILURE(hr);

    hr = _pPropertyCache->SampleDSMarshallProperties(
                            hOperationData
                            );
    BAIL_ON_FAILURE(hr);

    hr = SampleDSAddObject(
                hObject,
                _Name,
                _ADsClass,
                hOperationData
                );

error:
    if (hObject) {
        hr = SampleDSCloseObject(hObject);
    }
    if (hOperationData) {
        SampleDSFreeBuffer(hOperationData);
    }

    RRETURN(hr);
}

HRESULT
CSampleDSGenObject::GetInfo()
{
    RRETURN(GetInfo(TRUE));
}

HRESULT
CSampleDSGenObject::GetInfo(
    BOOL fExplicit
){
    HANDLE hObject = NULL;
    HRESULT hr = S_OK;
    WCHAR szDSPathName[MAX_PATH];
    HANDLE hOperationData = NULL;

    if (GetObjectState() == ADS_OBJECT_UNBOUND) {
        hr = E_ADS_OBJECT_UNBOUND;
        BAIL_ON_FAILURE(hr);
    }

    hr = BuildDSPathFromADsPath(
                _ADsPath,
                szDSPathName
                );
    BAIL_ON_FAILURE(hr);

    hr = SampleDSOpenObject(
                    szDSPathName,
                    &hObject,
                    NULL,
                    REG_DS
                    );
    BAIL_ON_FAILURE(hr);

    hr = SampleDSReadObject(
        hObject,
        &hOperationData
    );
    BAIL_ON_FAILURE(hr);

    hr = _pPropertyCache->SampleDSUnMarshallProperties(hOperationData,
                                                       fExplicit
                                                       );
    BAIL_ON_FAILURE(hr);
error:

    if (hObject) {
        hr = SampleDSCloseObject(hObject);
    }
    if (hOperationData) {
        SampleDSFreeBuffer(hOperationData);
    }

    RRETURN(hr);
}

/* IADsContainer methods */

STDMETHODIMP
CSampleDSGenObject::get_Count(long FAR* retval)
{
    RRETURN(E_NOTIMPL);
}

STDMETHODIMP
CSampleDSGenObject::get_Filter(THIS_ VARIANT FAR* pVar)
{
    VariantInit(pVar);
    RRETURN(VariantCopy(pVar, &_vFilter));
}

STDMETHODIMP
CSampleDSGenObject::put_Filter(THIS_ VARIANT Var)
{
    RRETURN(VariantCopy(&_vFilter, &Var));
}

STDMETHODIMP
CSampleDSGenObject::put_Hints(THIS_ VARIANT Var)
{
    RRETURN( E_NOTIMPL);
}

STDMETHODIMP
CSampleDSGenObject::get_Hints(THIS_ VARIANT FAR* pVar)
{
    RRETURN(E_NOTIMPL);
}

STDMETHODIMP
CSampleDSGenObject::GetObject(
    BSTR ClassName,
    BSTR RelativeName,
    IDispatch * FAR* ppObject
    )
{
    RRETURN(::RelativeGetObject(_ADsPath,
                                ClassName,
                                RelativeName,
                                ppObject,
                                FALSE));
}

STDMETHODIMP
CSampleDSGenObject::get__NewEnum(
    THIS_ IUnknown * FAR* retval
    )
{
    HRESULT hr;
    IUnknown FAR* punkEnum=NULL;
    IEnumVARIANT * penum = NULL;

    *retval = NULL;

    hr = CSampleDSGenObjectEnum::Create(
                (CSampleDSGenObjectEnum **)&penum,
                _ADsPath,
                _vFilter
                );
    BAIL_ON_FAILURE(hr);

    hr = penum->QueryInterface(
                IID_IUnknown,
                (VOID FAR* FAR*)retval
                );
    BAIL_ON_FAILURE(hr);

    if (penum) {
        penum->Release();
    }

    RRETURN(NOERROR);

error:

    if (penum) {
        delete penum;
    }

    RRETURN(hr);
}

STDMETHODIMP
CSampleDSGenObject::Create(
    THIS_ BSTR ClassName,
    BSTR RelativeName,
    IDispatch * FAR* ppObject
    )
{
    HRESULT hr = S_OK;
    IADs * pADs  = NULL;
    WCHAR szDSTreeName[MAX_PATH];
    DWORD dwSyntaxId = 0;

    //
    // Get the TreeName for this object
    //

    hr = BuildDSTreeNameFromADsPath(
                _ADsPath,
                szDSTreeName
                );
    BAIL_ON_FAILURE(hr);

    //
    // Validate if this class really exists in the schema
    // and validate that this object can be created in this
    // container
    //

    hr = CSampleDSGenObject::CreateGenericObject(
                    _ADsPath,
                    RelativeName,
                    ClassName,
                    ADS_OBJECT_UNBOUND,
                    IID_IDispatch,
                    (void **)ppObject
                    );
    BAIL_ON_FAILURE(hr);

error:

    RRETURN(hr);
}

STDMETHODIMP
CSampleDSGenObject::Delete(
    THIS_ BSTR bstrClassName,
    BSTR bstrRelativeName
    )
{
    WCHAR szDSPathName[MAX_PATH];
    HRESULT hr = S_OK;
    DWORD dwStatus = 0;
    HANDLE hParentObject = NULL;

    hr = BuildDSPathFromADsPath(
                _ADsPath,
                szDSPathName
                );
    BAIL_ON_FAILURE(hr);

    hr = SampleDSOpenObject(
                    szDSPathName,
                    &hParentObject,
                    NULL,
                    REG_DS
                    );
    BAIL_ON_FAILURE(hr);

    hr= SampleDSRemoveObject(
                hParentObject,
                bstrRelativeName);
    BAIL_ON_FAILURE(hr);

error:
    if (hParentObject) {
        dwStatus = SampleDSCloseObject(hParentObject);
    }
    RRETURN(hr);
}

STDMETHODIMP
CSampleDSGenObject::CopyHere(
    THIS_ BSTR SourceName,
    BSTR NewName,
    IDispatch * FAR* ppObject
    )
{
    RRETURN(E_NOTIMPL);
}

STDMETHODIMP
CSampleDSGenObject::MoveHere(
    THIS_ BSTR SourceName,
    BSTR NewName,
    IDispatch * FAR* ppObject
    )
{
    RRETURN(E_NOTIMPL);
}

HRESULT
CSampleDSGenObject::AllocateGenObject(CSampleDSGenObject ** ppGenObject)
{
    CSampleDSGenObject FAR * pGenObject = NULL;
    CDispatchMgr FAR * pDispMgr = NULL;
    CPropertyCache FAR * pPropertyCache = NULL;
    HRESULT hr = S_OK;

    pGenObject = new CSampleDSGenObject();
    if (pGenObject == NULL) {
        hr = E_OUTOFMEMORY;
    }
    BAIL_ON_FAILURE(hr);

    pDispMgr = new CDispatchMgr;
    if (pDispMgr == NULL) {
        hr = E_OUTOFMEMORY;
    }
    BAIL_ON_FAILURE(hr);

    hr = LoadTypeInfoEntry(pDispMgr,
                           LIBID_ADs,
                           IID_IADs,
                           (IADs *)pGenObject,
                           DISPID_REGULAR
                           );
    BAIL_ON_FAILURE(hr);

    hr = LoadTypeInfoEntry(pDispMgr,
                           LIBID_ADs,
                           IID_IADsContainer,
                           (IADsContainer *)pGenObject,
                           DISPID_NEWENUM
                           );
    BAIL_ON_FAILURE(hr);

    hr = CPropertyCache::createpropertycache(
                        (CCoreADsObject FAR *)pGenObject,
                        &pPropertyCache
                        );
    BAIL_ON_FAILURE(hr);

    pGenObject->_pPropertyCache = pPropertyCache;
    pGenObject->_pDispMgr = pDispMgr;
    *ppGenObject = pGenObject;

    RRETURN(hr);

error:
    delete  pDispMgr;
    RRETURN(hr);
}

STDMETHODIMP
CSampleDSGenObject::Get(
    THIS_ BSTR bstrName,
    VARIANT FAR* pvProp
    )
{
    HRESULT hr = S_OK;
    DWORD dwSyntaxId;
    DWORD dwNumValues;
    LPSampleDSOBJECT pNdsSrcObjects = NULL;

    //
    // retrieve data object from cache; if one exists
    //

    if (GetObjectState() == ADS_OBJECT_UNBOUND) {

        hr = _pPropertyCache->unboundgetproperty(
                    bstrName,
                    &dwSyntaxId,
                    &dwNumValues,
                    &pNdsSrcObjects
                    );
        BAIL_ON_FAILURE(hr);

    }else {

        hr = _pPropertyCache->getproperty(
                    bstrName,
                    &dwSyntaxId,
                    &dwNumValues,
                    &pNdsSrcObjects
                    );
        BAIL_ON_FAILURE(hr);
    }

    //
    // translate the Nds objects to variants
    //

    hr = SampleDSTypeToVarTypeCopyConstruct(
                pNdsSrcObjects,
                dwNumValues,
                pvProp
                );
    BAIL_ON_FAILURE(hr);

error:
    if (pNdsSrcObjects) {

        SampleDSTypeFreeSampleDSObjects(
            pNdsSrcObjects,
            dwNumValues
            );
    }

    RRETURN(hr);
}

STDMETHODIMP
CSampleDSGenObject::Put(
    THIS_ BSTR bstrName,
    VARIANT vProp
    )
{
    HRESULT hr = S_OK;
    DWORD dwSyntaxId  = 0;
    DWORD dwIndex = 0;
    LPSampleDSOBJECT pNdsDestObjects = NULL;
    WCHAR szSampleDSTreeName[MAX_PATH];
    DWORD dwNumValues = 0;

    VARIANT * pVarArray = NULL;
    VARIANT * pvProp = NULL;

    //
    // Issue: How do we handle multi-valued support
    //

    if ((V_VT(&vProp) &  VT_VARIANT) &&  V_ISARRAY(&vProp)) {

        hr  = ConvertSafeArrayToVariantArray(
                    vProp,
                    &pVarArray,
                    &dwNumValues
                    );
        BAIL_ON_FAILURE(hr);
        pvProp = pVarArray;

    }else {

        dwNumValues = 1;
        pvProp = &vProp;
    }

    //
    // Get the TreeName for this object
    //

    hr = BuildDSPathFromADsPath(
                _ADsPath,
                szSampleDSTreeName
                );
    BAIL_ON_FAILURE(hr);

    //
    // check if the variant maps to the syntax of this property
    //
    switch (vProp.vt) {
        case VT_BSTR:
                dwSyntaxId = SampleDS_DATATYPE_1;
                break;
        case VT_I4:
                dwSyntaxId = SampleDS_DATATYPE_2;
                break;
        default:
                hr = E_FAIL;
                goto error;
        }
                hr = VarTypeToSampleDSTypeCopyConstruct(
                    dwSyntaxId,
                    pvProp,
                    dwNumValues,
                    &pNdsDestObjects
                    );
    BAIL_ON_FAILURE(hr);

    //
    // Find this property in the cache
    //

    hr = _pPropertyCache->findproperty(
                        bstrName,
                        &dwIndex
                        );

    //
    // If this property does not exist in the
    // cache, add this property into the cache.
    //

    if (FAILED(hr)) {
        hr = _pPropertyCache->addproperty(
                    bstrName,
                    dwSyntaxId,
                    dwNumValues,
                    pNdsDestObjects
                    );
        //
        // If the operation fails for some reason
        // move on to the next property
        //
        BAIL_ON_FAILURE(hr);

    }

    //
    // Now update the property in the cache
    //

    hr = _pPropertyCache->putproperty(
                    bstrName,
                    dwSyntaxId,
                    dwNumValues,
                    pNdsDestObjects
                    );
    BAIL_ON_FAILURE(hr);

error:

    if (pNdsDestObjects) {
        SampleDSTypeFreeSampleDSObjects(
                pNdsDestObjects,
                dwNumValues
                );

    }

    if (pVarArray) {

        DWORD i = 0;

        for (i = 0; i < dwNumValues; i++) {
            VariantClear(pVarArray + i);
        }
        FreeProvMem(pVarArray);
    }

    RRETURN(hr);
}

HRESULT
ConvertSafeArrayToVariantArray(
    VARIANT varSafeArray,
    VARIANT ** ppVarArray,
    PDWORD pdwNumVariants
    )
{
    HRESULT hr = S_OK;
    DWORD dwSLBound = 0;
    DWORD dwSUBound = 0;
    DWORD i = 0;
    DWORD dwNumVariants = 0;
    VARIANT * pVarArray = NULL;

    *pdwNumVariants = 0;
    *ppVarArray  = 0;

    if(!((V_VT(&varSafeArray) &  VT_VARIANT) &&  V_ISARRAY(&varSafeArray))) {
        return(E_FAIL);
    }

    //
    // Check that there is only one dimension in this array
    //

    if ((V_ARRAY(&varSafeArray))->cDims != 1) {
        hr = E_FAIL;
        BAIL_ON_FAILURE(hr);
    }
    //
    // Check that there is atleast one element in this array
    //

    if ((V_ARRAY(&varSafeArray))->rgsabound[0].cElements == 0){
        hr = E_FAIL;
        BAIL_ON_FAILURE(hr);
    }

    hr = SafeArrayGetLBound(V_ARRAY(&varSafeArray),
                            1,
                            (long FAR *)&dwSLBound
                            );
    BAIL_ON_FAILURE(hr);

    hr = SafeArrayGetUBound(V_ARRAY(&varSafeArray),
                            1,
                            (long FAR *)&dwSUBound
                            );
    BAIL_ON_FAILURE(hr);

    dwNumVariants = dwSUBound - dwSLBound + 1;
    pVarArray = (VARIANT*)AllocProvMem(
                                sizeof(VARIANT)*dwNumVariants
                                );
    if (!pVarArray) {
        hr = E_OUTOFMEMORY;
        BAIL_ON_FAILURE(hr);
    }

    for (i = dwSLBound; i <= dwSUBound; i++) {

        VariantInit(pVarArray + i);
        hr = SafeArrayGetElement(V_ARRAY(&varSafeArray),
                                (long FAR *)&i,
                                (pVarArray + i)
                                );
        CONTINUE_ON_FAILURE(hr);
    }

    *ppVarArray = pVarArray;
    *pdwNumVariants = dwNumVariants;

error:

    RRETURN(hr);
}


CGENOBJ.H

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    CGenObj.h

Abstract:

Author:

Environment:

    User mode

Revision History :

--*/
#ifndef _CGENOBJ_H_
#define _CGENOBJ_H_

class CSampleDSGenObject;

class CSampleDSGenObject : INHERIT_TRACKING,
                     public CCoreADsObject,
                     public IADs,
                     public IADsContainer
{
public:

    /* IUnknown methods */
    STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) ;

    DECLARE_STD_REFCOUNTING

    DECLARE_IDispatch_METHODS

    DECLARE_IADs_METHODS

    DECLARE_IADsContainer_METHODS

    CSampleDSGenObject::CSampleDSGenObject();

    CSampleDSGenObject::~CSampleDSGenObject();

    static
    HRESULT
    CSampleDSGenObject::CreateGenericObject(
        BSTR bstrADsPath,
        DWORD dwObjectState,
        REFIID riid,
        void **ppvObj
        );

    static
    HRESULT
    CSampleDSGenObject::CreateGenericObject(
        BSTR Parent,
        BSTR CommonName,
        BSTR ClassName,
        DWORD dwObjectState,
        REFIID riid,
        void **ppvObj
        );

    static
    HRESULT
    CSampleDSGenObject::AllocateGenObject(
        CSampleDSGenObject ** ppGenObject
        );

    STDMETHOD(GetInfo)(
        BOOL fExplicit
        );

    HRESULT
    CSampleDSGenObject::SampleDSSetObject();

    HRESULT
    CSampleDSGenObject::SampleDSCreateObject();

    protected:

        VARIANT     _vFilter;
        CPropertyCache FAR * _pPropertyCache;
        CDispatchMgr FAR * _pDispMgr;
};

HRESULT
ConvertSafeArrayToVariantArray(
    VARIANT varSafeArray,
    VARIANT ** ppVarArray,
    PDWORD pdwNumVariants
    );
#endif


CMACRO.H

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    CMacro.h

Abstract:

Author:

Environment:

    User mode

Revision History :

--*/
#ifndef _CMACRO_H_
#define _CMACRO_H_

#define DEFINE_CONTAINED_IADs_Implementation(cls)                   \
STDMETHODIMP                                                          \
cls::get_Name(THIS_ BSTR FAR* retval)                                 \
{                                                                     \
    RRETURN(_pADs->get_Name(retval));                               \
}                                                                     \
                                                                      \
STDMETHODIMP                                                          \
cls::get_ADsPath(THIS_ BSTR FAR* retval)                            \
{                                                                     \
                                                                      \
    RRETURN(_pADs->get_ADsPath(retval));                          \
}                                                                     \
                                                                      \
STDMETHODIMP                                                          \
cls::get_Class(THIS_ BSTR FAR* retval)                                \
{                                                                     \
                                                                      \
    RRETURN(_pADs->get_Class(retval));                              \
}                                                                     \
                                                                      \
                                                                      \
STDMETHODIMP                                                          \
cls::get_Parent(THIS_ BSTR FAR* retval)                               \
{                                                                     \
    RRETURN(_pADs->get_Parent(retval));                             \
}                                                                     \
                                                                      \
STDMETHODIMP                                                          \
cls::get_Schema(THIS_ BSTR FAR* retval)                               \
{                                                                     \
    RRETURN(_pADs->get_Schema(retval));                             \
}                                                                     \
                                                                      \
STDMETHODIMP                                                          \
cls::get_GUID(THIS_ BSTR FAR* retval)                                 \
{                                                                     \
    RRETURN(_pADs->get_GUID(retval));                               \
}                                                                     \
                                                                      \
STDMETHODIMP                                                          \
cls::Get(THIS_ BSTR bstrName, VARIANT FAR* pvProp)                    \
{                                                                     \
    RRETURN(_pADs->Get(bstrName, pvProp));                          \
}                                                                     \
                                                                      \
STDMETHODIMP                                                          \
cls::Put(THIS_ BSTR bstrName, VARIANT vProp)                          \
{                                                                     \
    RRETURN(_pADs->Put(bstrName, vProp));                           \
}                                                                     \
                                                                      \
STDMETHODIMP                                                          \
cls::GetInfo(THIS_)                                                   \
{                                                                     \
    RRETURN(_pADs->GetInfo());                                      \
}                                                                     \
                                                                      \
STDMETHODIMP                                                          \
cls::SetInfo(THIS_ )                                                  \
{                                                                     \
    RRETURN(_pADs->SetInfo());                                      \
}

#endif


CNAMCF.CPP

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    CNamCF.cpp

Abstract:

Author:

Environment:

    User mode

Revision History :

--*/
#include "adssmp.h"
#pragma hdrstop

//+---------------------------------------------------------------------------
//
//  Function:   CSampleDSNamespaceCF::CreateInstance
//
//  Synopsis:
//
//  Arguments:  [pUnkOuter]
//              [iid]
//              [ppv]
//
//  Returns:    HRESULT
//
//  Modifies:
//
//----------------------------------------------------------------------------
STDMETHODIMP
CSampleDSNamespaceCF::CreateInstance(IUnknown * pUnkOuter, REFIID iid, LPVOID * ppv)
{
    HRESULT     hr = S_OK;

    if (pUnkOuter)
        RRETURN(E_FAIL);

        WCHAR wcaProviderDispName[MAX_PATH] = L"";
        wcscpy(wcaProviderDispName,g_szProviderName);
        wcscat(wcaProviderDispName,L":");

    hr = CSampleDSNamespace::CreateNamespace(
                L"ADs:",
                wcaProviderDispName,                    // Displayed by Enum of Namespaces
                ADS_OBJECT_BOUND,
                iid,
                ppv
                );

    RRETURN(hr);
}


CNAMCF.H

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    CNamCF.h

Abstract:

   Class factory for standard Namespace object.

Author:

Environment:

    User mode

Revision History :

--*/
#ifndef _CNAMCF_H_
#define _CNAMCF_H_

class CSampleDSNamespaceCF : public StdClassFactory
{
public:
    STDMETHOD(CreateInstance)(IUnknown * pUnkOuter, REFIID iid, LPVOID * ppv);
};

#endif


CNAMESP.CPP

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    CNameSp.cpp

Abstract:

    Sample Namespace Object

Author:

Environment:

    User mode

Revision History :

--*/
#include "adssmp.h"
#pragma hdrstop

DEFINE_IDispatch_Implementation(CSampleDSNamespace)
DEFINE_IADs_Implementation(CSampleDSNamespace)

//  Class CSampleDSNamespace
CSampleDSNamespace::CSampleDSNamespace()
{
    VariantInit(&_vFilter);
}

HRESULT
CSampleDSNamespace::CreateNamespace(
    BSTR Parent,
    BSTR NamespaceName,
    DWORD dwObjectState,
    REFIID riid,
    void **ppvObj
    )
{
    CSampleDSNamespace FAR * pNamespace = NULL;
    HRESULT hr = S_OK;

    hr = AllocateNamespaceObject(&pNamespace);
    BAIL_ON_FAILURE(hr);

    hr = pNamespace->InitializeCoreObject(
                Parent,
                NamespaceName,
                L"Namespace",
                L"",
                CLSID_SampleDSNamespace,
                dwObjectState
                );
    BAIL_ON_FAILURE(hr);

    hr = pNamespace->QueryInterface(riid, ppvObj);
    BAIL_ON_FAILURE(hr);

    pNamespace->Release();

    RRETURN(hr);

error:

    delete pNamespace;
    RRETURN(hr);
}

CSampleDSNamespace::~CSampleDSNamespace( )
{
    delete _pDispMgr;
}

STDMETHODIMP
CSampleDSNamespace::QueryInterface(REFIID iid, LPVOID FAR* ppv)
{
    if (IsEqualIID(iid, IID_IUnknown))
    {
        *ppv = (IADs FAR *)this;
    }else if (IsEqualIID(iid, IID_IDispatch))
    {
        *ppv = (IADs FAR *)this;
    }
    else if (IsEqualIID(iid, IID_IADsContainer))
    {
        *ppv = (IADsContainer FAR *) this;
    }
    else if (IsEqualIID(iid, IID_IADs))
    {
        *ppv = (IADs FAR *) this;
    }
    else
    {
        *ppv = NULL;
        return E_NOINTERFACE;
    }
    AddRef();
    return NOERROR;
}

STDMETHODIMP
CSampleDSNamespace::SetInfo(THIS)
{
    RRETURN(E_NOTIMPL);
}

STDMETHODIMP
CSampleDSNamespace::GetInfo(THIS)
{
    RRETURN(E_NOTIMPL);
}

/* IADsContainer methods */

STDMETHODIMP
CSampleDSNamespace::get_Count(long FAR* retval)
{
    RRETURN(E_NOTIMPL);
}

STDMETHODIMP
CSampleDSNamespace::get_Filter(THIS_ VARIANT FAR* pVar)
{
    VariantInit(pVar);
    RRETURN(VariantCopy(pVar, &_vFilter));
}

STDMETHODIMP
CSampleDSNamespace::put_Filter(THIS_ VARIANT Var)
{
    RRETURN(VariantCopy(&_vFilter, &Var));
}

STDMETHODIMP
CSampleDSNamespace::put_Hints(THIS_ VARIANT Var)
{
    RRETURN( E_NOTIMPL);
}

STDMETHODIMP
CSampleDSNamespace::get_Hints(THIS_ VARIANT FAR* pVar)
{
    RRETURN(E_NOTIMPL);
}

STDMETHODIMP
CSampleDSNamespace::GetObject(
    THIS_ BSTR ClassName,
    BSTR RelativeName,
    IDispatch* FAR* ppObject
    )
{
    RRETURN(::RelativeGetObject(_ADsPath,
                                ClassName,
                                RelativeName,
                                ppObject,
                                TRUE));
}

STDMETHODIMP
CSampleDSNamespace::get__NewEnum(THIS_ IUnknown * FAR* retval)
{
    HRESULT hr;
    IUnknown FAR* punkEnum=NULL;
    IEnumVARIANT * penum = NULL;

    *retval = NULL;

    //
    // Create new enumerator for items currently
    // in collection and QI for IUnknown
    //

    hr = CSampleDSNamespaceEnum::Create(
                (CSampleDSNamespaceEnum **)&penum,
                _vFilter
                );
    if (FAILED(hr)){

        goto error;
    }
    hr = penum->QueryInterface(
                IID_IUnknown,
                (VOID FAR* FAR*)retval
                );

    if (FAILED(hr)){
       goto error;
    }

    if (penum) {
        penum->Release();
    }

    return NOERROR;

error:

    if (penum) {
        delete penum;
    }

    return hr;
}

STDMETHODIMP
CSampleDSNamespace::Create(THIS_ BSTR ClassName, BSTR RelativeName, IDispatch * FAR* ppObject)
{
    RRETURN(E_NOTIMPL);
}

STDMETHODIMP
CSampleDSNamespace::Delete(THIS_ BSTR SourceName, BSTR Type)
{
    RRETURN(E_NOTIMPL);
}

STDMETHODIMP
CSampleDSNamespace::CopyHere(THIS_ BSTR SourceName, BSTR NewName, IDispatch * FAR* ppObject)
{
    RRETURN(E_NOTIMPL);
}

STDMETHODIMP
CSampleDSNamespace::MoveHere(THIS_ BSTR SourceName, BSTR NewName, IDispatch * FAR* ppObject)
{
    RRETURN(E_NOTIMPL);
}

HRESULT
CSampleDSNamespace::AllocateNamespaceObject(CSampleDSNamespace ** ppNamespace)
{
    CSampleDSNamespace FAR * pNamespace = NULL;
    CDispatchMgr FAR * pDispMgr = NULL;
    HRESULT hr = S_OK;

    pNamespace = new CSampleDSNamespace();
    if (pNamespace == NULL) {
        hr = E_OUTOFMEMORY;
    }
    BAIL_ON_FAILURE(hr);

    pDispMgr = new CDispatchMgr;
    if (pDispMgr == NULL) {
        hr = E_OUTOFMEMORY;
    }
    BAIL_ON_FAILURE(hr);

    hr = LoadTypeInfoEntry(pDispMgr,
                           LIBID_ADs,
                           IID_IADs,
                           (IADs *)pNamespace,
                           DISPID_REGULAR
                           );
    BAIL_ON_FAILURE(hr);

    hr = LoadTypeInfoEntry(pDispMgr,
                           LIBID_ADs,
                           IID_IADsContainer,
                           (IADsContainer *)pNamespace,
                           DISPID_NEWENUM
                           );
    BAIL_ON_FAILURE(hr);

    pNamespace->_pDispMgr = pDispMgr;
    *ppNamespace = pNamespace;

    RRETURN(hr);

error:
    delete  pDispMgr;

    RRETURN(hr);

}

STDMETHODIMP
CSampleDSNamespace::Get(
    THIS_ BSTR bstrName,
    VARIANT FAR* pvProp
    )
{
    RRETURN(E_NOTIMPL);
}

STDMETHODIMP
CSampleDSNamespace::Put(
    THIS_ BSTR bstrName,
    VARIANT vProp
    )
{
    RRETURN(E_NOTIMPL);
}


CNAMESP.H

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    CNameSp.h

Abstract:

Author:

Environment:

    User mode

Revision History :

--*/
#ifndef _CNAMESP_H_
#define _CNAMESP_H_

class CSampleDSNamespace : INHERIT_TRACKING,
                        public CCoreADsObject,
                        public IADsContainer,
                        public IADs
{
public:

    /* IUnknown methods */
    STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) ;

    DECLARE_STD_REFCOUNTING

    DECLARE_IDispatch_METHODS

    DECLARE_IADs_METHODS

    DECLARE_IADsContainer_METHODS

    CSampleDSNamespace::CSampleDSNamespace();

    CSampleDSNamespace::~CSampleDSNamespace();

    static
    HRESULT
    CSampleDSNamespace::CreateNamespace(
        BSTR Parent,
        BSTR NamespaceName,
        DWORD dwObjectState,
        REFIID riid,
        void **ppvObj
        );

    static
    HRESULT
    CSampleDSNamespace::AllocateNamespaceObject(
        CSampleDSNamespace ** ppNamespace
        );

protected:

    VARIANT     _vFilter;
    CDispatchMgr      *_pDispMgr;
};
#endif


COMMON.CPP

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    Common.cpp

Abstract:

    Contains routines and properties that are common to
    all tshirt objects. thsirt objects get the routines and
    properties through C++ inheritance.

Author:

Environment:

    User mode

Revision History :

--*/
#include "adssmp.h"
#pragma hdrstop

FILTERS Filters[] = {
                    {L"organizational unit", SampleDS_OU_ID},
                    {L"class", SampleDS_CLASS_ID},
                    {L"property", SampleDS_PROPERTY_ID}
                  };

#define MAX_FILTERS  (sizeof(Filters)/sizeof(FILTERS))

PFILTERS  gpFilters = Filters;
DWORD gdwMaxFilters = MAX_FILTERS;

extern WCHAR * g_szProviderName;

HRESULT
xx_put_BSTR(BSTR* lppItemName, BSTR lpName)
{
    if (*lppItemName) {
        SysFreeString(*lppItemName);
        *lppItemName = NULL;
    }

    if (!lpName) {
        RRETURN(S_OK);
    }

    *lppItemName = SysAllocString(lpName);

    if (!*lppItemName) {
        RRETURN(E_FAIL);
    }
    RRETURN(S_OK);
}

HRESULT
xx_put_LONG(long * plnItem, long lnItem)
{
    *plnItem = lnItem;
    RRETURN(S_OK);
}

HRESULT
xx_put_DATE(DATE * pdaItem, DATE daItem)
{
    *pdaItem = daItem;
    RRETURN(S_OK);
}

HRESULT
xx_put_VARIANT_BOOL(VARIANT_BOOL * pfItem, VARIANT_BOOL fItem)
{
    *pfItem = fItem;
    RRETURN(S_OK);
}

HRESULT
xx_put_VARIANT(VARIANT * * ppvItem, VARIANT vItem)
{
    if (!*ppvItem) {
        if (!(*ppvItem = (VARIANT *)AllocProvMem(sizeof(VARIANT)))){
            RRETURN(E_OUTOFMEMORY);
        }
    }
    RRETURN(VariantCopy(*ppvItem, &vItem));
}

HRESULT
BuildADsPath(
    BSTR Parent,
    BSTR Name,
    BSTR *pADsPath
    )
{
    WCHAR ADsPath[MAX_PATH];
    WCHAR ProviderName[MAX_PATH];
    HRESULT hr = S_OK;

    //
    // We will assert if bad parameters are passed to us.
    // This is because this should never be the case. This
    // is an internal call
    //

    ADsAssert(Parent && Name);
    ADsAssert(pADsPath);

    //
    // Special case the Namespace object; if
    // the parent is L"ADs:", then Name = ADsPath
    //

    if (!_wcsicmp(Parent, L"ADs:")) {
        RRETURN(xx_put_BSTR(pADsPath, Name));
    }

    //
    // The rest of the cases we expect valid data,
    // Path, Parent and Name are read-only, the end-user
    // cannot modify this data
    //

    //
    // For first level objects we do not add
    // the first backslash; so we examine that the parent is
    // L"Sample:" and skip the slash otherwise we start with
    // the slash
    //

    wsprintf(ProviderName, L"%s:", g_szProviderName);

    wcscpy(ADsPath, Parent);

    if (_wcsicmp(ADsPath, ProviderName)) {
        wcscat(ADsPath, L"/");
    }else {
        wcscat(ADsPath, L"//");
    }
    wcscat(ADsPath, Name);

    hr = xx_put_BSTR(pADsPath, ADsPath);

    RRETURN(hr);
}

HRESULT
BuildSchemaPath(
    BSTR bstrADsPath,
    BSTR bstrClass,
    BSTR *pSchemaPath
    )
{
    WCHAR ADsSchema[MAX_PATH];
    OBJECTINFO ObjectInfo;
    POBJECTINFO pObjectInfo = &ObjectInfo;
    CLexer Lexer(bstrADsPath);
    HRESULT hr = S_OK;

    wcscpy(ADsSchema, L"");

    if (bstrClass && *bstrClass) {
        memset(pObjectInfo, 0, sizeof(OBJECTINFO));
        hr = ADsObject(&Lexer, pObjectInfo);
        BAIL_ON_FAILURE(hr);

        if (pObjectInfo->RootRDN) {

            wsprintf(ADsSchema,L"%s://",pObjectInfo->ProviderName);
            wcscat(ADsSchema, pObjectInfo->RootRDN);
            wcscat(ADsSchema,L"/schema/");
            wcscat(ADsSchema, bstrClass);

        }
    }

    hr = ProvAllocString( ADsSchema, pSchemaPath);

error:

    if (pObjectInfo) {

        //
        // BugBug - KrishnaG; free up this piece of memory
        //
    }
    RRETURN(hr);
}

HRESULT
BuildADsGuid(
    REFCLSID clsid,
    BSTR *pADsClass
){
    WCHAR ADsClass[MAX_PATH];

    StringFromGUID2(clsid, ADsClass, 256);

    RRETURN(xx_put_BSTR(pADsClass, ADsClass));
}

typedef struct _typeinfotable
{
    GUID iid;
    ITypeInfo * pTypeInfo;
    struct _typeinfotable *pNext;
}TYPEINFO_TABLE, *PTYPEINFO_TABLE;

PTYPEINFO_TABLE gpTypeInfoTable = NULL;

ITypeInfo *
FindTypeInfo(
    PTYPEINFO_TABLE pTypeInfoTable,
    REFIID iid
){
    PTYPEINFO_TABLE pTemp = NULL;

    pTemp = pTypeInfoTable;

    while (pTemp) {
        if (IsEqualIID(iid, pTemp->iid)) {
            return(pTemp->pTypeInfo);
        }
        pTemp = pTemp->pNext;
    }
    return(NULL);
}

PTYPEINFO_TABLE
AddTypeInfo(
    PTYPEINFO_TABLE pTypeInfoTable,
    REFIID iid,
    ITypeInfo * pTypeInfo
){
    PTYPEINFO_TABLE pTemp = NULL;

    pTemp = (PTYPEINFO_TABLE)AllocProvMem(
                    sizeof(TYPEINFO_TABLE)
                    );
    if (!pTemp) {
        return(NULL);
    }

    memcpy(&pTemp->iid, &iid, sizeof(GUID));
    pTemp->pTypeInfo = pTypeInfo;
    pTemp->pNext = pTypeInfoTable;

    return(pTemp);
}

HRESULT
LoadTypeInfoEntry(
    CDispatchMgr *pDispMgr,
    REFIID libid,
    REFIID iid,
    void * pIntf,
    DISPID SpecialId
){
    ITypeInfo * pTypeInfo = NULL;
    HRESULT hr;

    pTypeInfo = FindTypeInfo(
                    gpTypeInfoTable,
                    iid
                    );
    if (!pTypeInfo) {

        hr = LoadTypeInfo(libid, iid, &pTypeInfo);
        BAIL_IF_ERROR(hr);

        gpTypeInfoTable = AddTypeInfo(
                                gpTypeInfoTable,
                                iid,
                                pTypeInfo
                                );
        if (!gpTypeInfoTable) {
            hr = HRESULT_FROM_WIN32(GetLastError());
            BAIL_IF_ERROR(hr);
        }
    }
    pTypeInfo->AddRef();

    hr = pDispMgr->AddTypeInfo(pTypeInfo, pIntf);
    BAIL_IF_ERROR(hr);

    if (SpecialId == -4) {
        hr = pDispMgr->MarkAsNewEnum(pTypeInfo);
    }

    RRETURN(S_OK);

cleanup:

   if (pTypeInfo) {
       pTypeInfo->Release();
   }
    RRETURN(hr);
}

HRESULT
ValidateOutParameter(BSTR * retval)
{
    if (!retval) {
        RRETURN(E_ADS_BAD_PARAMETER);
    }
    RRETURN(S_OK);
}

PKEYDATA
CreateTokenList(
    LPWSTR   pKeyData,
    WCHAR ch
){
    DWORD       cTokens;
    DWORD       cb;
    PKEYDATA    pResult;
    LPWSTR       pDest;
    LPWSTR       psz = pKeyData;
    LPWSTR      *ppToken;
    WCHAR szTokenList[MAX_PATH];

    if (!psz || !*psz)
        return NULL;

    wsprintf(szTokenList, L"%c", ch);

    cTokens=1;

    // Scan through the string looking for commas,
    // ensuring that each is followed by a non-NULL character:

    while ((psz = wcschr(psz, ch)) && psz[1]) {

        cTokens++;
        psz++;
    }

    cb = sizeof(KEYDATA) + (cTokens-1) * sizeof(LPWSTR) +
         wcslen(pKeyData)*sizeof(WCHAR) + sizeof(WCHAR);

    if (!(pResult = (PKEYDATA)AllocProvMem(cb)))
        return NULL;

    // Initialise pDest to point beyond the token pointers:

    pDest = (LPWSTR)((LPBYTE)pResult + sizeof(KEYDATA) +
                                      (cTokens-1) * sizeof(LPWSTR));

    // Then copy the key data buffer there:

    wcscpy(pDest, pKeyData);

    ppToken = pResult->pTokens;

    // Remember, wcstok has the side effect of replacing the delimiter
    // by NULL, which is precisely what we want:

    psz = wcstok (pDest, szTokenList);

    while (psz) {

        *ppToken++ = psz;
        psz = wcstok (NULL, szTokenList);
    }

    pResult->cTokens = cTokens;

    return( pResult );
}

//+------------------------------------------------------------------------
//
//  Function:   LoadTypeInfo
//
//  Synopsis:   Loads a typeinfo from a registered typelib.
//
//  Arguments:  [clsidTL] --  TypeLib GUID
//              [clsidTI] --  TypeInfo GUID
//              [ppTI]    --  Resulting typeInfo
//
//  Returns:    HRESULT
//
//-------------------------------------------------------------------------

HRESULT
LoadTypeInfo(CLSID clsidTL, CLSID clsidTI, LPTYPEINFO *ppTI)
{
    HRESULT     hr;
    ITypeLib *  pTL;

    ADsAssert(ppTI);
    *ppTI = NULL;
    hr = LoadRegTypeLib(clsidTL, 1, 0, LOCALE_SYSTEM_DEFAULT, &pTL);
    if (hr)
        RRETURN(hr);

    hr = pTL->GetTypeInfoOfGuid(clsidTI, ppTI);
    pTL->Release();
    RRETURN(hr);
}


COMMON.H

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    Common.h

Abstract:

   Contains Sample Provider routines and properties that are common to
   all objects.  Objects get the routines and properties through C++
   inheritance.

Author:

Environment:

    User mode

Revision History :

--*/
#ifndef _COMMON_H_
#define _COMMON_H_

#define MAX_DWORD 0xFFFFFFFF
#define SCHEMA_NAME L"Schema"  //Name of Schema object off of RootObject

HRESULT xx_put_BSTR(
    BSTR* lppItemName,
    BSTR lpName
    );

HRESULT xx_put_LONG(
    long *plnItem,
    long Item
    );

HRESULT
xx_put_DATE(
    DATE * pdaItem,
    DATE daItem
    );

HRESULT
xx_put_VARIANT_BOOL(
    VARIANT_BOOL * pfItem,
    VARIANT_BOOL fItem
    );

HRESULT
xx_put_VARIANT(
    VARIANT * * pvItem,
    VARIANT vItem
    );

HRESULT
LoadTypeInfoEntry(
    CDispatchMgr * pDispMgr,
    REFIID libid,
    REFIID iid,
    void * pIntf,
    DISPID SpecialId
    );

HRESULT
MakeUncName(
    LPWSTR szSrcBuffer,
    LPWSTR szTargBuffer
    );

HRESULT
ValidateOutParameter(
    BSTR * retval
    );

HRESULT
BuildADsPath(
    BSTR Parent,
    BSTR Name,
    BSTR *pADsPath
    );

HRESULT
BuildSchemaPath(
    BSTR bstrADsPath,
    BSTR bstrClass,
    BSTR *pSchemaPath
    );

HRESULT
BuildADsGuid(
    REFCLSID clsid,
    BSTR *pADsClass
    );

//
// Accessing Well-known object types
//
//RHA  will have to do filters as whatever user passes in
typedef struct _filters {
    WCHAR szObjectName[MAX_PATH];
    DWORD dwFilterId;
} FILTERS, *PFILTERS;

extern PFILTERS  gpFilters;
extern DWORD gdwMaxFilters;

HRESULT
BuildADsPathFromDSPath(
    LPWSTR szDSRootRDN,
    LPWSTR szDSDNName,
    LPWSTR szADsPathName
    );

HRESULT
BuildDSPathFromADsPath(
    LPWSTR szADsPathName,
    LPWSTR szDSPathName
    );

HRESULT
BuildDSParentPathFromDSPath(
    LPWSTR szDSPathName,
    LPWSTR szDSParentPathName,
    LPWSTR szCommonName
    );

HRESULT
BuildDSPathFromDSParentPath(
    LPWSTR szDSParentPathName,
    LPWSTR szDSObjectCommonName,
    LPWSTR szDSPathName
    );

typedef struct _KEYDATA {
    DWORD   cTokens;
    LPWSTR  pTokens[1];
} KEYDATA, *PKEYDATA;

PKEYDATA
CreateTokenList(
    LPWSTR   pKeyData,
    WCHAR ch
    );

HRESULT LoadTypeInfo(CLSID clsidTL, CLSID clsidTI, LPTYPEINFO *ppTI);

#endif


CORE.CPP

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    Core.cpp

Abstract:

    CCoreADsObject - Implementation

Author:

Environment:

    User mode

Revision History :

--*/
#include "adssmp.h"
#pragma hdrstop

HRESULT
CCoreADsObject::InitializeCoreObject(
        BSTR Parent,
        BSTR Name,
        BSTR ClassName,
        BSTR Schema,
        REFCLSID rclsid,
        DWORD dwObjectState
        )
{
    HRESULT hr = S_OK;
    ADsAssert(Parent);
    ADsAssert(Name);
    ADsAssert(ClassName);

    hr = BuildADsPath(
            Parent,
            Name,
            &_ADsPath
            );
    BAIL_ON_FAILURE(hr);

    hr = BuildADsGuid(
            rclsid,
            &_ADsGuid
            );
    BAIL_ON_FAILURE(hr);

    hr = xx_put_BSTR(&_Parent, Parent);
    BAIL_ON_FAILURE(hr);

    hr = xx_put_BSTR(&_Name, Name);
    BAIL_ON_FAILURE(hr);

    hr = xx_put_BSTR(&_ADsClass, ClassName);
    BAIL_ON_FAILURE(hr);

    hr = BuildSchemaPath(
            _ADsPath,
            ClassName,
            &_Schema
            );
    BAIL_ON_FAILURE(hr);

    _dwObjectState = dwObjectState;

error:
    RRETURN(hr);

}

CCoreADsObject::CCoreADsObject():
                        _Name(NULL),
                        _ADsPath(NULL),
                        _Parent(NULL),
                        _ADsClass(NULL),
                        _Schema(NULL),
                        _ADsGuid(NULL),
                        _dwObjectState(0)
{
}

CCoreADsObject::~CCoreADsObject()
{
    if (_Name) {
        ProvFreeString(_Name);
    }

    if (_ADsPath) {
        ProvFreeString(_ADsPath);
    }

    if (_Parent) {
        ProvFreeString(_Parent);
    }

    if (_ADsClass) {
        ProvFreeString(_ADsClass);
    }

    if (_Schema) {
        ProvFreeString(_Schema);
    }

    if (_ADsGuid) {
        ProvFreeString(_ADsGuid);
    }

}

HRESULT
CCoreADsObject::get_CoreName(BSTR * retval)
{
    HRESULT hr;

    if (FAILED(hr = ValidateOutParameter(retval))){
        RRETURN(hr);
    }

    RRETURN(ProvAllocString(_Name, retval));
}

HRESULT
CCoreADsObject::get_CoreADsPath(BSTR * retval)
{
    HRESULT hr;

    if (FAILED(hr = ValidateOutParameter(retval))){
        RRETURN(hr);
    }

    RRETURN(ProvAllocString(_ADsPath, retval));

}

HRESULT
CCoreADsObject::get_CoreADsClass(BSTR * retval)
{
    HRESULT hr;

    if (FAILED(hr = ValidateOutParameter(retval))){
        RRETURN(hr);
    }

    RRETURN(ProvAllocString(_ADsClass, retval));
}

HRESULT
CCoreADsObject::get_CoreParent(BSTR * retval)
{

    HRESULT hr;

    if (FAILED(hr = ValidateOutParameter(retval))){
        RRETURN(hr);
    }

    RRETURN(ProvAllocString(_Parent, retval));
}

HRESULT
CCoreADsObject::get_CoreSchema(BSTR * retval)
{

    HRESULT hr;

    if (FAILED(hr = ValidateOutParameter(retval))){
        RRETURN(hr);
    }

    if ( _Schema == NULL || *_Schema == 0 )
        RRETURN(E_ADS_PROPERTY_NOT_SUPPORTED);

    RRETURN(ProvAllocString(_Schema, retval));
}

HRESULT
CCoreADsObject::get_CoreGUID(BSTR * retval)
{
    HRESULT hr;

    if (FAILED(hr = ValidateOutParameter(retval))){
        RRETURN(hr);
    }

    RRETURN(ProvAllocString(_ADsGuid, retval));
}

STDMETHODIMP
CCoreADsObject::GetInfo(
    BOOL fExplicit
    )
{
    RRETURN(E_NOTIMPL);
}


CORE.H

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    Core.h

Abstract:

    Core ADS Obect

Author:

Environment:

    User mode

Revision History :

--*/
#ifndef _CORE_H_
#define _CORE_H_

class CCoreADsObject
{

public:

    CCoreADsObject::CCoreADsObject();

    CCoreADsObject::~CCoreADsObject();

    HRESULT
    get_CoreName(BSTR * retval);

    HRESULT
    get_CoreADsPath(BSTR * retval);

    HRESULT
    get_CoreParent(BSTR * retval);

    HRESULT
    get_CoreSchema(BSTR * retval);

    HRESULT
    get_CoreADsClass(BSTR * retval);

    HRESULT
    get_CoreGUID(BSTR * retval);

    DWORD
    CCoreADsObject::GetObjectState()
    {
        return(_dwObjectState);
    }

    void
    CCoreADsObject::SetObjectState(DWORD dwObjectState)
    {
        _dwObjectState = dwObjectState;
    }

    HRESULT
    InitializeCoreObject(
        BSTR Parent,
        BSTR Name,
        BSTR ClassName,
        BSTR Schema,
        REFCLSID rclsid,
        DWORD   dwObjectState
        );

    STDMETHOD(GetInfo)(THIS_ BOOL fExplicit);

protected:

    DWORD       _dwObjectState;

    BSTR        _Name;
    BSTR        _ADsPath;
    BSTR        _Parent;
    BSTR        _ADsClass;
    BSTR        _ADsGuid;
    BSTR        _Schema;

};

#define     ADS_OBJECT_BOUND              1
#define     ADS_OBJECT_UNBOUND            2

#endif


CPROPS.CPP

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    CProps.cpp

Abstract:

    Property Cache functionality

Author:

Environment:

    User mode

Revision History :

--*/
#include "adssmp.h"
#pragma hdrstop

//+------------------------------------------------------------------------
//
//  Function:   CPropertyCache::addproperty
//
//  Synopsis:
//
//
//
//  Arguments:  [szPropertyName]    --
//              [vt]                --
//              [vaData]            --
//
//
//-------------------------------------------------------------------------
HRESULT
CPropertyCache::
addproperty(
    LPWSTR szPropertyName,
    DWORD  dwSyntaxId,
    DWORD  dwNumValues,
    PSampleDSOBJECT pSampleDSObject
    )
{
    HRESULT hr = S_OK;
    PPROPERTY pNewProperty = NULL;

    //
    //  extend the property cache by adding a new property entry
    //

    _pProperties = (PPROPERTY)ReallocProvMem(
                                _pProperties,
                                _cb,
                                _cb + sizeof(PROPERTY)
                                );
    if (!_pProperties) {
        hr = E_OUTOFMEMORY;
        BAIL_ON_FAILURE(hr);
    }

    pNewProperty = (PPROPERTY)((LPBYTE)_pProperties + _cb);

    wcscpy(pNewProperty->szPropertyName, szPropertyName);

    //
    // Update the index
    //

    _dwMaxProperties++;
    _cb += sizeof(PROPERTY);

error:
    RRETURN(hr);
}

//+------------------------------------------------------------------------
//
//  Function:   CPropertyCache::updateproperty
//
//  Synopsis:
//
//
//
//  Arguments:  [szPropertyName]    --
//              [vaData]    --
//
//-------------------------------------------------------------------------
HRESULT
CPropertyCache::
updateproperty(
    LPWSTR szPropertyName,
    DWORD  dwSyntaxId,
    DWORD  dwNumValues,
    PSampleDSOBJECT pSampleDSObject,
    BOOL fExplicit
    )
{
    HRESULT hr;
    DWORD dwIndex;
    PSampleDSOBJECT pNdsTempObject = NULL;
    PPROPERTY pThisProperty = NULL;

    hr = findproperty(
            szPropertyName,
            &dwIndex
            );
    BAIL_ON_FAILURE(hr);

    pThisProperty = _pProperties + dwIndex;

    if (!fExplicit) {
        if (PROPERTY_FLAGS(pThisProperty) & CACHE_PROPERTY_MODIFIED) {
            hr = S_OK;
            goto error;
        }
    }

    if (PROPERTY_SampleDSOBJECT(pThisProperty)) {

        SampleDSTypeFreeSampleDSObjects(
                PROPERTY_SampleDSOBJECT(pThisProperty),
                PROPERTY_NUMVALUES(pThisProperty)
                );
        PROPERTY_SampleDSOBJECT(pThisProperty) = NULL;
    }
    PROPERTY_SYNTAX(pThisProperty) = dwSyntaxId;
    PROPERTY_NUMVALUES(pThisProperty) = dwNumValues;

    hr = SampleDSTypeCopyConstruct(
            pSampleDSObject,
            dwNumValues,
            &pNdsTempObject
            );
    BAIL_ON_FAILURE(hr);

    PROPERTY_SampleDSOBJECT(pThisProperty) = pNdsTempObject;

    PROPERTY_FLAGS(pThisProperty)  &= ~CACHE_PROPERTY_MODIFIED;

error:

    RRETURN(hr);

}

//+------------------------------------------------------------------------
//
//  Function:   CPropertyCache::findproperty
//
//  Synopsis:
//
//
//
//  Arguments:  [szPropertyName] --
//              [pdwIndex]       --
//
//-------------------------------------------------------------------------
HRESULT
CPropertyCache::
findproperty(
    LPWSTR szPropertyName,
    PDWORD pdwIndex
    )

{
    DWORD i = 0;
    PPROPERTY pThisProperty = NULL;

    for (i = 0; i < _dwMaxProperties; i++) {

        pThisProperty = _pProperties + i;

        if (!_wcsicmp(pThisProperty->szPropertyName, szPropertyName)) {
            *pdwIndex = i;
            RRETURN(S_OK);
        }
    }
    *pdwIndex = 0;
    RRETURN(E_ADS_PROPERTY_NOT_FOUND);
}

//+------------------------------------------------------------------------
//
//  Function:   CPropertyCache::getproperty
//
//  Synopsis:
//
//
//
//  Arguments:  [szPropertyName]    --  Property to retrieve from the cache
//              [pvaData]           --  Data returned in a variant
//
//-------------------------------------------------------------------------
HRESULT
CPropertyCache::
getproperty(
    LPWSTR szPropertyName,
    PDWORD  pdwSyntaxId,
    PDWORD  pdwNumValues,
    PSampleDSOBJECT * ppSampleDSObject
    )
{
    HRESULT hr;
    DWORD dwIndex = 0L;
    PPROPERTY pThisProperty = NULL;

    hr = findproperty(
            szPropertyName,
            &dwIndex
            );

    if (hr == E_ADS_PROPERTY_NOT_FOUND) {

        //
        // Now call the GetInfo function
        //

        hr = _pCoreADsObject->GetInfo(
                    FALSE
                    );
        BAIL_ON_FAILURE(hr);

        hr = findproperty(
                    szPropertyName,
                    &dwIndex
                    );

    }
    BAIL_ON_FAILURE(hr);

    pThisProperty = _pProperties + dwIndex;

    if (PROPERTY_SampleDSOBJECT(pThisProperty)) {

        *pdwSyntaxId = (DWORD)PROPERTY_SYNTAX(pThisProperty);
        *pdwNumValues = (DWORD)PROPERTY_NUMVALUES(pThisProperty);

        hr = SampleDSTypeCopyConstruct(
                PROPERTY_SampleDSOBJECT(pThisProperty),
                PROPERTY_NUMVALUES(pThisProperty),
                ppSampleDSObject
                );
        BAIL_ON_FAILURE(hr);

    }else {

        *ppSampleDSObject = NULL;
        *pdwNumValues = 0;
        *pdwSyntaxId = 0;
        hr = E_FAIL;

    }

error:

   RRETURN(hr);
}

//+------------------------------------------------------------------------
//
//  Function:   CPropertyCache::putproperty
//
//  Synopsis:
//
//
//
//  Arguments:  [szPropertyName]    --  Clsid index
//              [vaData]    --  Matching clsid returned in *pclsid
//
//-------------------------------------------------------------------------
HRESULT
CPropertyCache::
putproperty(
    LPWSTR szPropertyName,
    DWORD  dwSyntaxId,
    DWORD  dwNumValues,
    PSampleDSOBJECT pSampleDSOBJECT
    )
{
    HRESULT hr;
    DWORD dwIndex = 0L;
    PSampleDSOBJECT pSampleDSTempObject = NULL;
    PPROPERTY pThisProperty = NULL;

    hr = findproperty(
            szPropertyName,
            &dwIndex
            );
    BAIL_ON_FAILURE(hr);

    pThisProperty = _pProperties + dwIndex;

    if (PROPERTY_SampleDSOBJECT(pThisProperty)) {

        SampleDSTypeFreeSampleDSObjects(
                PROPERTY_SampleDSOBJECT(pThisProperty),
                PROPERTY_NUMVALUES(pThisProperty)
                );
        PROPERTY_SampleDSOBJECT(pThisProperty) = NULL;
    }

    PROPERTY_SYNTAX(pThisProperty) = dwSyntaxId;
    PROPERTY_NUMVALUES(pThisProperty) = dwNumValues;

    hr = SampleDSTypeCopyConstruct(
            pSampleDSOBJECT,
            dwNumValues,
            &pSampleDSTempObject
            );
    BAIL_ON_FAILURE(hr);

    PROPERTY_SampleDSOBJECT(pThisProperty) = pSampleDSTempObject;

    PROPERTY_FLAGS(pThisProperty) |= CACHE_PROPERTY_MODIFIED;

error:
    RRETURN(hr);
}

//+------------------------------------------------------------------------
//
//  Function:   CPropertyCache
//
//  Synopsis:
//
//
//
//  Arguments:
//
//
//-------------------------------------------------------------------------
CPropertyCache::
CPropertyCache():
        _dwMaxProperties(0),
        _pProperties(NULL),
        _cb(0),
        _pCoreADsObject(NULL)
{

}

//+------------------------------------------------------------------------
//
//  Function:   ~CPropertyCache
//
//  Synopsis:
//
//
//
//  Arguments:
//
//
//-------------------------------------------------------------------------
CPropertyCache::
~CPropertyCache()
{
    DWORD i = 0;
    PPROPERTY pThisProperty = NULL;

    if (_pProperties) {

        for (i = 0; i < _dwMaxProperties; i++) {

            pThisProperty = _pProperties + i;

            if (PROPERTY_SampleDSOBJECT(pThisProperty)) {

                SampleDSTypeFreeSampleDSObjects(
                        PROPERTY_SampleDSOBJECT(pThisProperty),
                        PROPERTY_NUMVALUES(pThisProperty)
                        );
                PROPERTY_SampleDSOBJECT(pThisProperty) = NULL;
            }
        }

        FreeProvMem(_pProperties);
    }
}

//+------------------------------------------------------------------------
//
//  Function:
//
//  Synopsis:
//
//
//
//  Arguments:
//
//
//-------------------------------------------------------------------------
HRESULT
CPropertyCache::
createpropertycache(
    CCoreADsObject FAR * pCoreADsObject,
    CPropertyCache FAR *FAR * ppPropertyCache
    )
{
    CPropertyCache FAR * pPropertyCache = NULL;

    pPropertyCache = new CPropertyCache();

    if (!pPropertyCache) {
        RRETURN(E_FAIL);
    }

    pPropertyCache->_pCoreADsObject = pCoreADsObject;

    *ppPropertyCache = pPropertyCache;

    RRETURN(S_OK);
}

//+------------------------------------------------------------------------
//
//  Function:   CPropertyCache::getproperty
//
//  Synopsis:
//
//
//
//  Arguments:  [szPropertyName]    --  Property to retrieve from the cache
//              [pvaData]           --  Data returned in a variant
//
//-------------------------------------------------------------------------
HRESULT
CPropertyCache::
unboundgetproperty(
    LPWSTR szPropertyName,
    PDWORD  pdwSyntaxId,
    PDWORD  pdwNumValues,
    PSampleDSOBJECT * ppSampleDSOBJECT
    )
{
    HRESULT hr;
    DWORD dwIndex = 0L;
    PPROPERTY pThisProperty = NULL;

    hr = findproperty(
            szPropertyName,
            &dwIndex
            );
    BAIL_ON_FAILURE(hr);

    pThisProperty = _pProperties + dwIndex;

    if (PROPERTY_SampleDSOBJECT(pThisProperty)) {

        *pdwSyntaxId = (DWORD)PROPERTY_SYNTAX(pThisProperty);
        *pdwNumValues = (DWORD)PROPERTY_NUMVALUES(pThisProperty);

        hr = SampleDSTypeCopyConstruct(
                PROPERTY_SampleDSOBJECT(pThisProperty),
                PROPERTY_NUMVALUES(pThisProperty),
                ppSampleDSOBJECT
                );
        BAIL_ON_FAILURE(hr);

    }else {

        *ppSampleDSOBJECT = NULL;
        *pdwNumValues = 0;
        *pdwSyntaxId = 0;
        hr = E_FAIL;

    }

error:

   RRETURN(hr);
}

HRESULT
CPropertyCache::
SampleDSMarshallProperties(
    HANDLE hOperationData
    )
{
    LPSampleDS_ATTRS_INFO pAttrsInfo = (LPSampleDS_ATTRS_INFO)hOperationData;
    HRESULT hr = S_OK;
    DWORD i = 0;
    PPROPERTY pThisProperty = NULL;
    BYTE lpBuffer[2048];
    DWORD cProperty = 0;
    LPSampleDS_ATTR_INFO pAttrInfoMemStart = NULL;
    LPSampleDS_ATTR_INFO pAttrInfoMem = NULL;
    pAttrInfoMemStart = (LPSampleDS_ATTR_INFO)AllocProvMem(
                                     sizeof(SampleDS_ATTR_INFO)*_dwMaxProperties);
    if (!pAttrInfoMemStart)
        goto Error;

    pAttrInfoMem = pAttrInfoMemStart;

    for (i = 0; i < _dwMaxProperties ; i++) {

        pThisProperty = _pProperties + i;

        //
        // Bypass any property that has not been
        // modified
        //

        if (PROPERTY_FLAGS(pThisProperty) == 0) {

            continue;
        }

        hr = MarshallSampleDSSynIdToSampleDS(
                PROPERTY_SYNTAX(pThisProperty),
                PROPERTY_SampleDSOBJECT(pThisProperty),
                PROPERTY_NUMVALUES(pThisProperty),
                lpBuffer
                );
        CONTINUE_ON_FAILURE(hr);

        hr = marshallproperty(
                pAttrInfoMem,
                PROPERTY_NAME(pThisProperty),
                lpBuffer,
                PROPERTY_NUMVALUES(pThisProperty),
                PROPERTY_SYNTAX(pThisProperty)
                );
        CONTINUE_ON_FAILURE(hr);
        cProperty++;
        pAttrInfoMem++;
    }

    if (cProperty != _dwMaxProperties) {
        LPSampleDS_ATTR_INFO pAttrInfoNew;
        pAttrInfoNew = (LPSampleDS_ATTR_INFO)AllocProvMem(
                                         sizeof(SampleDS_ATTR_INFO)*cProperty);
        if (!pAttrInfoNew) {
            hr = E_FAIL;
            goto Error;
        }
        memcpy( (void*)pAttrInfoNew,
                (void*)pAttrInfoMemStart,
                sizeof(SampleDS_ATTR_INFO)*cProperty);
        FreeProvMem(pAttrInfoMemStart);
        pAttrInfoMemStart = pAttrInfoNew;
    }
    pAttrsInfo->pAttrInfo = pAttrInfoMemStart;
    pAttrsInfo->dwAttr = cProperty;
    RRETURN(hr);

Error:
    if (pAttrInfoMem)
        FreeProvMem(pAttrInfoMem);
    RRETURN(hr);

}

HRESULT
CPropertyCache::
marshallproperty(
    LPSampleDS_ATTR_INFO pAttrInfo,
    LPWSTR szPropertyName,
    LPBYTE lpValues,
    DWORD  dwNumValues,
    DWORD  dwSyntaxId
    )
{
    pAttrInfo->lpAttributeName = (LPWSTR)AllocProvStr(szPropertyName);
    if (!pAttrInfo->lpAttributeName)
        RRETURN(E_FAIL);

    DWORD dwMemSize;
    switch (dwSyntaxId) {
        case SampleDS_DATATYPE_1:
            dwMemSize = sizeof(SampleDS_TYPE_1);
            break;
        case SampleDS_DATATYPE_2:
            dwMemSize = sizeof(SampleDS_TYPE_2);
            break;
        default:
            goto Error;
    }
    pAttrInfo->lpValue = (BYTE*)AllocProvMem(dwMemSize);
    if (!pAttrInfo->lpValue)
        goto Error;
    memcpy( (void*)pAttrInfo->lpValue,
            (void*)lpValues,
            dwMemSize);

    pAttrInfo->dwSyntaxId = dwSyntaxId;
    pAttrInfo->dwNumberOfValues = dwNumValues;
    RRETURN(S_OK);
Error:
    if (pAttrInfo->lpAttributeName)
        FreeProvMem(pAttrInfo->lpAttributeName);
    if (pAttrInfo->lpValue)
        FreeProvMem(pAttrInfo->lpValue);
    RRETURN(E_FAIL);
}

HRESULT
CPropertyCache::
SampleDSUnMarshallProperties(
    HANDLE hOperationData,
    BOOL fExplicit
    )

{
    LPSampleDS_ATTRS_INFO pAttrsInfo = (LPSampleDS_ATTRS_INFO)hOperationData;

    DWORD dwNumberOfEntries = 0L;
    LPSampleDS_ATTR_INFO lpEntries = pAttrsInfo->pAttrInfo;
    DWORD dwEntry = pAttrsInfo->dwAttr;
    HRESULT hr = S_OK;
    DWORD i = 0;
    DWORD dwStatus = 0L;

    for (i = 0; i < dwEntry; i++) {

        //
        // unmarshall this property into the
        // property cache
        //

        hr = unmarshallproperty(
                    lpEntries[i].lpAttributeName,
                    lpEntries[i].lpValue,
                    lpEntries[i].dwNumberOfValues,
                    lpEntries[i].dwSyntaxId,
                    fExplicit
                    );

        CONTINUE_ON_FAILURE(hr);

    }

    RRETURN(hr);

}

//+------------------------------------------------------------------------
//
//  Function:
//
//  Synopsis:
//
//
//
//  Arguments:
//
//
//-------------------------------------------------------------------------

HRESULT
CPropertyCache::
unmarshallproperty(
    LPWSTR szPropertyName,
    LPBYTE lpValue,
    DWORD  dwNumValues,
    DWORD  dwSyntaxId,
    BOOL fExplicit
    )
{

    DWORD dwIndex = 0;
    HRESULT hr = S_OK;
    PSampleDSOBJECT pSampleDSObject = NULL;

    hr = UnMarshallSampleDSToSampleDSSynId(
                dwSyntaxId,
                dwNumValues,
                lpValue,
                &pSampleDSObject
                );
    BAIL_ON_FAILURE(hr);

    //
    // Find this property in the cache
    //

    hr = findproperty(
                szPropertyName,
                &dwIndex
                );

    //
    // If this property does not exist in the
    // cache, add this property into the cache.
    //

    if (FAILED(hr)) {
        hr = addproperty(
                    szPropertyName,
                    dwSyntaxId,
                    dwNumValues,
                    pSampleDSObject
                    );

        //
        // If the operation fails for some reason
        // move on to the next property
        //
        BAIL_ON_FAILURE(hr);

    }

    //
    // Now update the property in the cache
    //

    hr = updateproperty(
                    szPropertyName,
                    dwSyntaxId,
                    dwNumValues,
                    pSampleDSObject,
                    fExplicit
                    );
    BAIL_ON_FAILURE(hr);

    if (pSampleDSObject) {
        SampleDSTypeFreeSampleDSObjects(
                pSampleDSObject,
                dwNumValues
                );

    }

error:
    RRETURN(hr);
}


CPROPS.H

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    CProps.h

Abstract:

    Property Cache Object

Author:

Environment:

    User mode

Revision History :

--*/
#ifndef _CPROPS_H_
#define _CPROPS_H_

typedef struct _property{
    WCHAR   szPropertyName[MAX_PATH];
    DWORD   dwFlags;
    DWORD   dwNumValues;
    DWORD   dwSyntaxId;
    PSampleDSOBJECT pSampleDSObject;      // Pointer to the SampleDS Object
}PROPERTY, *PPROPERTY;

#define PROPERTY_NAME(pProperty)            pProperty->szPropertyName
#define PROPERTY_VALUES(pProperty)          pProperty->lpValues
#define PROPERTY_NUMVALUES(pProperty)       pProperty->dwNumValues
#define PROPERTY_SYNTAX(pProperty)          pProperty->dwSyntaxId
#define PROPERTY_SampleDSOBJECT(pProperty)  pProperty->pSampleDSObject
#define PROPERTY_FLAGS(pProperty)           pProperty->dwFlags

#define CACHE_PROPERTY_MODIFIED        0x1

class CPropertyCache {

public:

    HRESULT
    CPropertyCache::
    addproperty(
        LPWSTR szPropertyName,
        DWORD  dwSyntaxId,
        DWORD  dwNumValues,
        PSampleDSOBJECT pSampleDSObject
        );

    HRESULT
    CPropertyCache::
    updateproperty(
        LPWSTR szPropertyName,
        DWORD  dwSyntaxId,
        DWORD  dwNumValues,
        PSampleDSOBJECT pSampleDSObject,
        BOOL fExplicit
        );

    HRESULT
    CPropertyCache::
    findproperty(
        LPWSTR szPropertyName,
        PDWORD pdwIndex
        );

    HRESULT
    CPropertyCache::
    getproperty(
        LPWSTR szPropertyName,
        PDWORD  pdwSyntaxId,
        PDWORD  pdwNumValues,
        PSampleDSOBJECT * ppSampleDSObject
        );

    HRESULT
    CPropertyCache::
    unboundgetproperty(
        LPWSTR szPropertyName,
        PDWORD  pdwSyntaxId,
        PDWORD  pdwNumValues,
        PSampleDSOBJECT * ppSampleDSObject
        );

    HRESULT
    CPropertyCache::
    putproperty(
        LPWSTR szPropertyName,
        DWORD  dwSyntaxId,
        DWORD  dwNumValues,
        PSampleDSOBJECT pSampleDSObject
        );

    CPropertyCache::
    CPropertyCache();

    CPropertyCache::
    ~CPropertyCache();

    static
    HRESULT
    CPropertyCache::
    createpropertycache(
        CCoreADsObject FAR * pCoreADsObject,
        CPropertyCache FAR * FAR * ppPropertyCache
        );

    HRESULT
    CPropertyCache::
    unmarshallproperty(
        LPWSTR szPropertyName,
        LPBYTE lpValue,
        DWORD  dwNumValues,
        DWORD  dwSyntaxId,
        BOOL fExplicit
        );

    HRESULT
    CPropertyCache::
    SampleDSUnMarshallProperties(
        HANDLE hOperationData,
        BOOL fExplicit
        );

    HRESULT
    CPropertyCache::
    marshallproperty(
        LPSampleDS_ATTR_INFO pAttrInfo,
        LPWSTR szPropertyName,
        LPBYTE lpValues,
        DWORD  dwNumValues,
        DWORD  dwSyntaxId
        );

    HRESULT
    CPropertyCache::
    SampleDSMarshallProperties(
       HANDLE hOperationData
    );

protected:

    DWORD _dwMaxProperties;

    PPROPERTY _pProperties;
    DWORD   _cb;

    CCoreADsObject FAR * _pCoreADsObject;
};

#endif


CPROV.CPP

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    CProv.cpp

Abstract:

    ADsSampleDS Provider Object

Author:

Environment:

    User mode

Revision History :

--*/
#include "adssmp.h"
#pragma hdrstop

CSampleDSProvider::CSampleDSProvider()
{

}

HRESULT
CSampleDSProvider::Create(CSampleDSProvider FAR * FAR * ppProvider)
{
    CSampleDSProvider FAR * pProvider = NULL;
    HRESULT hr = S_OK;

    //Create the Provider Object

    pProvider = new CSampleDSProvider();
    if (pProvider == NULL) {
        RRETURN(ResultFromScode(E_OUTOFMEMORY));
    }

    if (FAILED(hr)) {
        delete pProvider;
        RRETURN(hr);
    }

    *ppProvider = pProvider;
    RRETURN(hr);
}

CSampleDSProvider::~CSampleDSProvider( )
{
    ENLIST_TRACKING(CSampleDSProvider);
}

STDMETHODIMP
CSampleDSProvider::QueryInterface(REFIID iid, LPVOID FAR* ppv)
{
    if (IsEqualIID(iid, IID_IUnknown))
    {
        *ppv = this;
    }
    else if (IsEqualIID(iid, IID_IParseDisplayName))
    {
        *ppv = (IADs FAR *) this;
    }
    else
    {
        *ppv = NULL;
        return E_NOINTERFACE;
    }
    AddRef();
    return NOERROR;
}

STDMETHODIMP
CSampleDSProvider::ParseDisplayName(IBindCtx* pbc, WCHAR* szDisplayName, ULONG* pchEaten, IMoniker** ppmk)
{
    HRESULT hr;

    *ppmk = NULL;

    if (pchEaten != NULL){
        *pchEaten = 0;
    }

    hr = ResolvePathName(
                pbc,
                szDisplayName,
                pchEaten,
                ppmk
                );

    RRETURN(hr);
}

HRESULT
CSampleDSProvider::ResolvePathName(IBindCtx* pbc,
                WCHAR* szDisplayName,
                ULONG* pchEaten,
                IMoniker** ppmk
                )
{
    HRESULT hr;
    LPUNKNOWN pUnknown = NULL;

    *pchEaten = 0;
    hr = GetObject(szDisplayName, (LPVOID *)&pUnknown);
    BAIL_IF_ERROR(hr);

    hr = CreatePointerMoniker(pUnknown, ppmk);
    BAIL_IF_ERROR(hr);

    *pchEaten += wcslen(szDisplayName);

cleanup:

    if (pUnknown) {
        pUnknown->Release();
    }

    RRETURN (hr);
}


CPROV.H

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    CProv.h

Abstract:

  Provides Moniker support for Ole Ds GetObject

Author:

Environment:

    User mode

Revision History :

--*/
#ifndef _CPROV_H_
#define _CPROV_H_

class CSampleDSProvider :  INHERIT_TRACKING,
                        public IParseDisplayName
{
public:

    /* IUnknown methods */
    STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) ;

    DECLARE_STD_REFCOUNTING

    /* IParseDisplayName */
    STDMETHOD(ParseDisplayName)(THIS_ IBindCtx* pbc,
                                      WCHAR* szDisplayName,
                                      ULONG* pchEaten,
                                      IMoniker** ppmk);
    CSampleDSProvider::CSampleDSProvider();

    CSampleDSProvider::~CSampleDSProvider();

    static HRESULT Create(CSampleDSProvider FAR * FAR * ppProvider);

    HRESULT
    CSampleDSProvider::ResolvePathName(IBindCtx* pbc,
                    WCHAR* szDisplayName,
                    ULONG* pchEaten,
                    IMoniker** ppmk
                    );

protected:

};

#endif


CPROVCF.CPP

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    CProvCF.cpp

Abstract:

    ADs Provider Object Class Factory Code

Author:

Environment:

    User mode

Revision History :

--*/
#include "adssmp.h"
#pragma hdrstop

//+---------------------------------------------------------------------------
//
//  Function:   CSampleDSProviderCF::CreateInstance
//
//  Synopsis:
//
//  Arguments:  [pUnkOuter]
//              [iid]
//              [ppv]
//
//  Returns:    HRESULT
//
//  Modifies:
//
//----------------------------------------------------------------------------
STDMETHODIMP
CSampleDSProviderCF::CreateInstance(IUnknown * pUnkOuter, REFIID iid, LPVOID * ppv)
{
    HRESULT     hr;
    CSampleDSProvider *     pProvider;

    if (pUnkOuter)
        RRETURN(E_FAIL);

    hr = CSampleDSProvider::Create(&pProvider);

    if (FAILED(hr)) {
        RRETURN (hr);
    }

    if (pProvider)
    {
        hr = pProvider->QueryInterface(iid, ppv);
        pProvider->Release();
    }
    else
    {
        *ppv = NULL;
        RRETURN(E_OUTOFMEMORY);
    }

    RRETURN(hr);
}


CPROVCF.H

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    CProvCf.h

Abstract:

    Class Factory for Provider Object
Author:

Environment:

    User mode

Revision History :

--*/
#ifndef _CPROVCF_H_
#define _CPROVCF_H_

class CSampleDSProviderCF : public StdClassFactory
{
public:
    STDMETHOD(CreateInstance)(IUnknown * pUnkOuter, REFIID iid, LPVOID * ppv);
};

#endif


CPRPOBJ.CPP

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    CPrpObj.cpp

Abstract:

Author:

Environment:

    User mode

Revision History :

--*/
#include "adssmp.h"
#pragma hdrstop

// Class CSampleDSProperty

DEFINE_IDispatch_Implementation(CSampleDSProperty)
DEFINE_IADs_Implementation(CSampleDSProperty)

CSampleDSProperty::CSampleDSProperty()
    : _pDispMgr( NULL ),
      _bstrSyntax( NULL )
{

    ENLIST_TRACKING(CSampleDSProperty);
}

CSampleDSProperty::~CSampleDSProperty()
{
    delete _pDispMgr;
}

HRESULT
CSampleDSProperty::CreateProperty(
    BSTR   bstrParent,
    BSTR   bstrName,
    LPSampleDS_ATTR_DEF lpAttrDef,
    DWORD  dwObjectState,
    REFIID riid,
    void **ppvObj
    )
{
    CSampleDSProperty FAR * pProperty = NULL;
    HRESULT hr = S_OK;
    WCHAR szADsSyntax[MAX_PATH];
    WCHAR szSampleDSSyntax[MAX_PATH];

    hr = AllocatePropertyObject( &pProperty );
    BAIL_ON_FAILURE(hr);

    hr = pProperty->InitializeCoreObject(
             bstrParent,
             bstrName,
             PROPERTY_CLASS_NAME,
             L"",
             CLSID_SampleDSProperty,
             dwObjectState
             );
    BAIL_ON_FAILURE(hr);

    hr = pProperty->QueryInterface( riid, ppvObj );
    BAIL_ON_FAILURE(hr);

    MapSyntaxIdtoADsSyntax(
            lpAttrDef->dwSyntaxID,
            szADsSyntax
            );

    hr = ProvAllocString(
                szADsSyntax,
                &pProperty->_bstrSyntax
                );
    BAIL_ON_FAILURE(hr);

    MapSyntaxIdtoSampleDSSyntax(
            lpAttrDef->dwSyntaxID,
            szSampleDSSyntax
            );

    pProperty->Release();

    RRETURN(hr);

error:

    delete pProperty;
    RRETURN(hr);
}

HRESULT
CSampleDSProperty::CreateProperty(
    BSTR   bstrParent,
    BSTR   bstrName,
    HANDLE hTree,
    DWORD  dwObjectState,
    REFIID riid,
    void **ppvObj
    )
{
    DWORD dwStatus = 0;
    HRESULT hr = S_OK;
    LPSampleDS_ATTR_DEF lpAttrDefs = NULL;
    DWORD dwNumberOfEntries = 0;
    DWORD dwInfoType = 0;
    HANDLE hOperationData = NULL;
    hr = SampleDSGetPropertyDefinition(&lpAttrDefs,
                                       bstrName);
    BAIL_ON_FAILURE(hr);

    if (!lpAttrDefs) {
        hr = E_FAIL;
        BAIL_ON_FAILURE(hr);
    }

    hr = CSampleDSProperty::CreateProperty(
                bstrParent,
                bstrName,
                lpAttrDefs,
                dwObjectState,
                riid,
                ppvObj
                );
error:
    if (lpAttrDefs)
        SampleDSFreePropertyDefinition(lpAttrDefs,
                                       1
                                      );
    RRETURN(hr);
}

STDMETHODIMP
CSampleDSProperty::QueryInterface(
    REFIID iid,
    LPVOID FAR* ppv
    )
{
    if (IsEqualIID(iid, IID_IUnknown))
    {
        *ppv = (IADsProperty FAR *) this;
    }
    else if (IsEqualIID(iid, IID_IDispatch))
    {
        *ppv = (IADsProperty FAR *) this;
    }
    else if (IsEqualIID(iid, IID_IADsProperty))
    {
        *ppv = (IADsProperty FAR *) this;
    }
    else if (IsEqualIID(iid, IID_IADs))
    {
        *ppv = (IADsProperty FAR *) this;
    }
    else
    {
        *ppv = NULL;
        return E_NOINTERFACE;
    }

    AddRef();
    return NOERROR;
}

/* IADs methods */

STDMETHODIMP
CSampleDSProperty::SetInfo(THIS)
{
    RRETURN(E_NOTIMPL);
}

STDMETHODIMP
CSampleDSProperty::GetInfo(THIS)
{
    RRETURN(S_OK);
}

HRESULT
CSampleDSProperty::AllocatePropertyObject(
    CSampleDSProperty FAR * FAR * ppProperty
    )
{
    CSampleDSProperty FAR *pProperty = NULL;
    CDispatchMgr FAR *pDispMgr = NULL;
    HRESULT hr = S_OK;

    pProperty = new CSampleDSProperty();
    if ( pProperty == NULL )
        hr = E_OUTOFMEMORY;
    BAIL_ON_FAILURE(hr);

    pDispMgr = new CDispatchMgr;
    if ( pDispMgr == NULL )
        hr = E_OUTOFMEMORY;
    BAIL_ON_FAILURE(hr);

    hr = LoadTypeInfoEntry(
            pDispMgr,
            LIBID_ADs,
            IID_IADsProperty,
            (IADsProperty *) pProperty,
            DISPID_REGULAR
            );
    BAIL_ON_FAILURE(hr);

    pProperty->_pDispMgr = pDispMgr;
    *ppProperty = pProperty;

    RRETURN(hr);

error:

    delete pDispMgr;
    delete pProperty;

    RRETURN(hr);

}

STDMETHODIMP
CSampleDSProperty::Get(
    THIS_ BSTR bstrName,
    VARIANT FAR* pvProp
    )
{
    RRETURN(E_NOTIMPL);
}

STDMETHODIMP
CSampleDSProperty::Put(
    THIS_ BSTR bstrName,
    VARIANT vProp
    )
{
    RRETURN(E_NOTIMPL);
}

HRESULT
MapSyntaxIdtoADsSyntax(
    DWORD dwSyntaxId,
    LPWSTR pszADsSyntax
    )
{

    if (dwSyntaxId > g_cSampleDSSyntaxMap) {
        wcscpy(pszADsSyntax, L"Out of Bounds");
    }else {
        wcscpy(pszADsSyntax, g_aSampleDSSyntaxMap[dwSyntaxId - 1].bstrName);
    }
    RRETURN(S_OK);

}

HRESULT
MapSyntaxIdtoSampleDSSyntax(
    DWORD dwSyntaxId,
    LPWSTR pszSampleDSSyntax
    )
{

    if (dwSyntaxId > g_cSampleDSSyntaxMap) {
        wcscpy(pszSampleDSSyntax, L"Out of Bounds");
    }else {
        wcscpy(pszSampleDSSyntax, g_aSampleDSSyntaxMap[dwSyntaxId - 1].bstrSampleDSName);
    }
    RRETURN(S_OK);

}
/* IADsProperty methods */

STDMETHODIMP
CSampleDSProperty::get_OID( THIS_ BSTR FAR *pbstrOID )
{
    RRETURN(E_ADS_PROPERTY_NOT_SUPPORTED);
}

STDMETHODIMP
CSampleDSProperty::put_OID( THIS_ BSTR bstrOID )
{
    RRETURN(E_ADS_PROPERTY_NOT_SUPPORTED);
}

STDMETHODIMP
CSampleDSProperty::get_Syntax( THIS_ BSTR FAR *pbstrSyntax )
{
    if ( !pbstrSyntax )
        RRETURN(E_ADS_BAD_PARAMETER);

    RRETURN( ProvAllocString( _bstrSyntax, pbstrSyntax ));
}

STDMETHODIMP
CSampleDSProperty::put_Syntax( THIS_ BSTR bstrSyntax )
{
    RRETURN(E_ADS_PROPERTY_NOT_SUPPORTED);
}

STDMETHODIMP
CSampleDSProperty::get_MaxRange( THIS_ long FAR *plMaxRange )
{
    RRETURN(E_ADS_PROPERTY_NOT_SUPPORTED);
}

STDMETHODIMP
CSampleDSProperty::put_MaxRange( THIS_ long lMaxRange )
{
    RRETURN(E_ADS_PROPERTY_NOT_SUPPORTED);
}

STDMETHODIMP
CSampleDSProperty::get_MinRange( THIS_ long FAR *plMinRange )
{
    RRETURN(E_ADS_PROPERTY_NOT_SUPPORTED);
}

STDMETHODIMP
CSampleDSProperty::put_MinRange( THIS_ long lMinRange )
{
    RRETURN(E_ADS_PROPERTY_NOT_SUPPORTED);
}

STDMETHODIMP
CSampleDSProperty::get_MultiValued( THIS_ VARIANT_BOOL FAR *pfMultiValued )
{
    RRETURN(E_ADS_PROPERTY_NOT_SUPPORTED);
}

STDMETHODIMP
CSampleDSProperty::put_MultiValued( THIS_ VARIANT_BOOL fMultiValued )
{
    RRETURN(E_ADS_PROPERTY_NOT_SUPPORTED);
}

STDMETHODIMP
CSampleDSProperty::Qualifiers(THIS_ IADsCollection FAR* FAR* ppQualifiers)
{
    RRETURN(E_NOTIMPL);
}


CPRPOBJ.H

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    CPrpObj.h

Abstract:

    Sample Provider GetObject functionality

Author:

Environment:

    User mode

Revision History :

--*/
#ifndef _CPRPOBJ_H_
#define _CPRPOBJ_H_

struct SYNTAXMAP
{
    BSTR   bstrName;
    BSTR   bstrSampleDSName;
    long   lOleAutoDataType;
};

struct SYNTAXINFO
{
    BSTR   bstrName;
    long   lOleAutoDataType;
};

class CSampleDSProperty : INHERIT_TRACKING,
                       public CCoreADsObject,
                       public IADsProperty
{
public:

    /* IUnknown methods */
    STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) ;
    DECLARE_STD_REFCOUNTING

    /* Other methods */
    DECLARE_IDispatch_METHODS

    DECLARE_IADs_METHODS

    DECLARE_IADsProperty_METHODS

    /* Constructors, Destructors, ... */
    CSampleDSProperty::CSampleDSProperty();

    CSampleDSProperty::~CSampleDSProperty();

    static
    HRESULT
    CSampleDSProperty::CreateProperty(
        BSTR   bstrParent,
        BSTR   bstrName,
        LPSampleDS_ATTR_DEF lpAttrDef,
        DWORD  dwObjectState,
        REFIID riid,
        void **ppvObj
        );

    static
    HRESULT
    CSampleDSProperty::CreateProperty(
        BSTR   bstrParent,
        BSTR   bstrName,
        HANDLE hTree,
        DWORD  dwObjectState,
        REFIID riid,
        void **ppvObj
        );

    static
    HRESULT
    CSampleDSProperty::AllocatePropertyObject(
        CSampleDSProperty **ppProperty
        );

protected:

    CDispatchMgr FAR * _pDispMgr;

    /* Properties */
    VARIANT _vADsNames;
    VARIANT _vDsNames;
    BSTR _bstrSyntax;
};

HRESULT
MapSyntaxIdtoADsSyntax(
    DWORD dwSyntaxId,
    LPWSTR pszADsSyntax
    );

HRESULT
MapSyntaxIdtoSampleDSSyntax(
    DWORD dwSyntaxId,
    LPWSTR pszSampleDSSyntax
    );
#endif


CSCHOBJ.CPP

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    CSchObj.cpp

Abstract:

Author:

Environment:

    User mode

Revision History :

--*/
#include "adssmp.h"
#pragma hdrstop

//  Class CSampleDSSchema

DEFINE_IDispatch_Implementation(CSampleDSSchema)
DEFINE_IADs_Implementation(CSampleDSSchema)

CSampleDSSchema::CSampleDSSchema()
{

    VariantInit(&_vFilter);

    _SampleDSTreeName = NULL;

    ENLIST_TRACKING(CSampleDSSchema);
}

HRESULT
CSampleDSSchema::CreateSchema(
    BSTR Parent,
    BSTR CommonName,
    DWORD dwObjectState,
    REFIID riid,
    void **ppvObj
    )
{
    CSampleDSSchema FAR * pSchema = NULL;
    HRESULT hr = S_OK;
    WCHAR szSampleDSTreeName[MAX_PATH];

    hr = AllocateSchema(&pSchema);
    BAIL_ON_FAILURE(hr);

    hr = pSchema->InitializeCoreObject(
                Parent,
                CommonName,
                L"schema",
                L"",
                CLSID_SampleDSSchema,
                dwObjectState
                );
    BAIL_ON_FAILURE(hr);

    hr = BuildDSPathFromADsPath(
                Parent,
                szSampleDSTreeName
                );
    BAIL_ON_FAILURE(hr);

    hr = ProvAllocString( szSampleDSTreeName,  &pSchema->_SampleDSTreeName);
    BAIL_ON_FAILURE(hr);

    hr = pSchema->QueryInterface(riid, ppvObj);
    BAIL_ON_FAILURE(hr);

    pSchema->Release();

    RRETURN(hr);

error:

    delete pSchema;
    RRETURN(hr);
}

CSampleDSSchema::~CSampleDSSchema( )
{
    if (_SampleDSTreeName) {
        ProvFreeString(_SampleDSTreeName);
    }
    delete _pDispMgr;
}

STDMETHODIMP
CSampleDSSchema::QueryInterface(REFIID iid, LPVOID FAR* ppv)
{
    if (IsEqualIID(iid, IID_IUnknown))
    {
        *ppv = (IADs FAR *) this;
    }
    else if (IsEqualIID(iid, IID_IADsContainer))
    {
        *ppv = (IADsContainer FAR *) this;
    }
    else if (IsEqualIID(iid, IID_IADs))
    {
        *ppv = (IADs FAR *) this;
    }
    else if (IsEqualIID(iid, IID_IDispatch))
    {
        *ppv = (IADs FAR *) this;
    }
    else
    {
        *ppv = NULL;
        return E_NOINTERFACE;
    }
    AddRef();
    return NOERROR;
}

STDMETHODIMP
CSampleDSSchema::SetInfo(THIS)
{
    RRETURN(E_NOTIMPL);
}

STDMETHODIMP
CSampleDSSchema::GetInfo(THIS)
{
    RRETURN(E_NOTIMPL);
}

/* IADsContainer methods */

STDMETHODIMP
CSampleDSSchema::get_Count(long FAR* retval)
{
    RRETURN(E_NOTIMPL);
}

STDMETHODIMP
CSampleDSSchema::get_Filter(THIS_ VARIANT FAR* pVar)
{
    VariantInit(pVar);
    RRETURN(VariantCopy(pVar, &_vFilter));
}

STDMETHODIMP
CSampleDSSchema::put_Filter(THIS_ VARIANT Var)
{
    RRETURN(VariantCopy(&_vFilter, &Var));
}

STDMETHODIMP
CSampleDSSchema::put_Hints(THIS_ VARIANT Var)
{
    RRETURN( E_NOTIMPL);
}

STDMETHODIMP
CSampleDSSchema::get_Hints(THIS_ VARIANT FAR* pVar)
{
    RRETURN(E_NOTIMPL);
}

STDMETHODIMP
CSampleDSSchema::GetObject(
    THIS_ BSTR ClassName,
    BSTR RelativeName,
    IDispatch * FAR* ppObject
    )
{
    RRETURN(::RelativeGetObject(_ADsPath,
                                ClassName,
                                RelativeName,
                                ppObject,
                                FALSE));
}

STDMETHODIMP
CSampleDSSchema::get__NewEnum(
    THIS_ IUnknown * FAR* retval
    )
{
    HRESULT hr;
    IUnknown FAR* punkEnum=NULL;
    IEnumVARIANT * penum = NULL;

    *retval = NULL;

    hr = CSampleDSSchemaEnum::Create(
                (CSampleDSSchemaEnum **)&penum,
                _SampleDSTreeName,
                _ADsPath,
                _Name,
                _vFilter
                );
    BAIL_ON_FAILURE(hr);

    hr = penum->QueryInterface(
                IID_IUnknown,
                (VOID FAR* FAR*)retval
                );
    BAIL_ON_FAILURE(hr);

    if (penum) {
        penum->Release();
    }

    RRETURN(NOERROR);

error:

    if (penum) {
        delete penum;
    }

    RRETURN(hr);
}

STDMETHODIMP
CSampleDSSchema::Create(
    THIS_ BSTR ClassName,
    BSTR RelativeName,
    IDispatch * FAR* ppObject
    )
{
    RRETURN(E_NOTIMPL);
}

STDMETHODIMP
CSampleDSSchema::Delete(
    THIS_ BSTR bstrClassName,
    BSTR bstrRelativeName
    )
{
    RRETURN(E_NOTIMPL);
}

STDMETHODIMP
CSampleDSSchema::CopyHere(
    THIS_ BSTR SourceName,
    BSTR NewName,
    IDispatch * FAR* ppObject
    )
{
    RRETURN(E_NOTIMPL);
}

STDMETHODIMP
CSampleDSSchema::MoveHere(
    THIS_ BSTR SourceName,
    BSTR NewName,
    IDispatch * FAR* ppObject
    )
{
    RRETURN(E_NOTIMPL);
}

HRESULT
CSampleDSSchema::AllocateSchema(
    CSampleDSSchema ** ppSchema
    )
{
    CSampleDSSchema FAR * pSchema = NULL;
    CDispatchMgr FAR * pDispMgr = NULL;
    HRESULT hr = S_OK;

    pSchema = new CSampleDSSchema();
    if (pSchema == NULL) {
        hr = E_OUTOFMEMORY;
    }
    BAIL_ON_FAILURE(hr);

    pDispMgr = new CDispatchMgr;
    if (pDispMgr == NULL) {
        hr = E_OUTOFMEMORY;
    }
    BAIL_ON_FAILURE(hr);

    hr = LoadTypeInfoEntry(pDispMgr,
                           LIBID_ADs,
                           IID_IADs,
                           (IADsDomain *)pSchema,
                           DISPID_REGULAR
                           );
    BAIL_ON_FAILURE(hr);

    hr = LoadTypeInfoEntry(pDispMgr,
                           LIBID_ADs,
                           IID_IADsContainer,
                           (IADsContainer *)pSchema,
                           DISPID_NEWENUM
                           );
    BAIL_ON_FAILURE(hr);

    pSchema->_pDispMgr = pDispMgr;
    *ppSchema = pSchema;

    RRETURN(hr);

error:
    delete  pDispMgr;

    RRETURN(hr);

}

STDMETHODIMP
CSampleDSSchema::GetInfo(
    THIS_ DWORD dwApiLevel,
    BOOL fExplicit
    )
{
    RRETURN(E_NOTIMPL);
}

STDMETHODIMP
CSampleDSSchema::Get(
    THIS_ BSTR bstrName,
    VARIANT FAR* pvProp
    )
{
    RRETURN(E_NOTIMPL);
}

STDMETHODIMP
CSampleDSSchema::Put(
    THIS_ BSTR bstrName,
    VARIANT vProp
    )
{
    RRETURN(E_NOTIMPL);
}


CSCHOBJ.H

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    CSchObj.h

Abstract:

Author:

Environment:

    User mode

Revision History :

--*/
#ifndef _CSCHOBJ_H_
#define _CSCHOBJ_H_

class CSampleDSSchema;

class CSampleDSSchema : INHERIT_TRACKING,
                     public CCoreADsObject,
                     public IADs,
                     public IADsContainer
{
public:

    /* IUnknown methods */
    STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) ;

    DECLARE_STD_REFCOUNTING

    DECLARE_IDispatch_METHODS

    DECLARE_IADs_METHODS

    DECLARE_IADsContainer_METHODS

    CSampleDSSchema::CSampleDSSchema();

    CSampleDSSchema::~CSampleDSSchema();

    static
    HRESULT
    CSampleDSSchema::CreateSchema(
        BSTR Parent,
        BSTR CommonName,
        DWORD dwObjectState,
        REFIID riid,
        void **ppvObj
        );

    static
    HRESULT
    CSampleDSSchema::AllocateSchema(
        CSampleDSSchema ** ppSchema
        );

    STDMETHOD(GetInfo)(
        THIS_ DWORD dwApiLevel,
        BOOL fExplicit
        );

protected:

    VARIANT     _vFilter;
    BSTR        _SampleDSTreeName;

    CDispatchMgr FAR * _pDispMgr;

};

#endif


FORMTRCK.H

//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) 1992, Microsoft Corporation.
//
//  File:       formtrck.hxx
//
//  Contents:   This file defines facilities for a standard implementation
//              of reference-counted objects, incorporating mechanisms for
//              tracking the usage history of the objects.
//
//  Classes:    ObjectTracker
//
//  History:    6-Apr-92       MikeSe  Created
//              08-Aug-94      DonCl   copied from cinc, and renamed to formtrck.hxx
//
//----------------------------------------------------------------------------

#ifndef __FORMTRCK_HXX__
#define __FORMTRCK_HXX__

#include <windows.h>

//+-------------------------------------------------------------------------
//
//  Class:      ObjectTracker (otr)
//
//  Purpose:    Provides basis for tracking (OLE) objects (aka interface handles)
//
//  History:    6-Apr-92 MikeSe         Created
//
//  Notes:      Access to this class is only indirect, through the macros
//              defined later.
//
//--------------------------------------------------------------------------

class ObjectTracker
{
protected:

                     ObjectTracker ();

//# if DBG == 1
#if 0
                    ~ObjectTracker();
    ULONG            StdAddRef ( void );
    ULONG            StdRelease ( void );
    void             TrackClassName ( char * pszName );
    unsigned long    GetRefCount ( void ) {return _ulRefs;};

private:

    BOOL             IsClassTracking( char * pszName );

    struct TrackLink *  _tl;
    static  BOOL        _TrackAll;

public:
        // dumps information on all outstanding objects
    static void        DumpTrackingInfo ( int fDeleteNode = 0 );
    static void        TrackClass(BOOL, char * pszName );

# endif // DBG == 1

protected:
    unsigned long      _ulRefs;
};

//+-------------------------------------------------------------------------
//
//  The following macros encapsulate use of the above
//
//  INHERIT_TRACKING:
//
//      For any class which implements a Cairo interface, add this macro
//      in the class declaration, eg:
//
//              class CMyFoo: INHERIT_TRACKING, IFoo
//
//      Do not repeat this in any subclass. If both INHERIT_UNWIND and
//      INHERIT_TRACKING are used, the former should appear first.

# define INHERIT_TRACKING       protected ObjectTracker

        // The following declarations are for non-retail builds

//# if DBG == 1
#if 0
//
// DECLARE_STD_REFCOUNTING:
//
//      To make use of standard refcounting code, place the above
//      in the class declaration, in the public method section. This
//      macro defines the AddRef and Release methods for the class.
//

#  define DECLARE_STD_REFCOUNTING                                       \
        STDMETHOD_(ULONG, AddRef) ()                                    \
                {                                                       \
                    return StdAddRef();                                 \
                };                                                      \
        STDMETHOD_(ULONG, Release) ()                                   \
                {                                                       \
                    ULONG ul = StdRelease();                            \
                    if ( ul==0 ) delete this;                           \
                    return ul;                                          \
                };

//
//  ENLIST_TRACKING(class name)
//
//      Place an invocation of this in each constructor, at any appropriate
//      point (generally immediately before returning from the constructor).
//
//              ENLIST_TRACKING(CMyFoo)
//

#  define ENLIST_TRACKING(cls)  TrackClassName( #cls )

//
// NB:  In a subclass of a class which has INHERIT_TRACKING, do not
//      use INHERIT_TRACKING again. However, do use ENLIST_TRACKING in
//      in the constructor of the derived class, and use TRACK_ADDREF
//      and TRACK_RELEASE if the subclass overrides the AddRef and Release
//      methods.

//
//  TRACK_CLASS(fTrack, pszClassName)
//
//      Use this to add or remove a class in the list of tracked classes.
//      You can turn tracking of all classes on or off by using a NULL
//      pszClassName.  If fTrack is TRUE, the class will be tracked, if FALSE,
//      the class will no longer be tracked.  The default configuration is
//      that all classes are tracked.
//
//      NOTE: this affects only objects created after this macro is executed.
//

#  define TRACK_CLASS(fTrack, cls) \
                      ObjectTracker::TrackClass( fTrack, cls )

//
//  DUMP_TRACKING_INFO()
//
//      Place this anywhere it would be useful to dump out the object
//      tracking database. By default, this is always issued at program
//      termination.
//

#  define DUMP_TRACKING_INFO()        DUMP_TRACKING_INFO_KEEP()
#  define DUMP_TRACKING_INFO_KEEP()   ObjectTracker::DumpTrackingInfo(0)
#  define DUMP_TRACKING_INFO_DELETE() ObjectTracker::DumpTrackingInfo(1)

//      Output from this is controlled by setting the following mask values
//      in OtInfoLevel

#  define DEB_OT_OBJECTS        0x00000001L
        // display object addresses, reference count and name (Default)

#  define DEB_OT_CALLERS        0x80000000L
        // display call history

// In addition, set the following values to cause debug output during
// operation:

#  define DEB_OT_ERRORS         0x00000002L
        // report errors during tracking operations (Default)

#  define DEB_OT_ACTIONS        0x40000000L
        // report each create, addref and release

#  define DEB_OT_DELETE         0x20000000L
        // display call history at object delete

# else // DBG == 0

inline ObjectTracker::ObjectTracker():_ulRefs(1) {};

#  define DECLARE_STD_REFCOUNTING                                       \
        STDMETHOD_(ULONG, AddRef) ()                                    \
                {                                                       \
                    InterlockedIncrement((long*)&_ulRefs);         \
                    return _ulRefs;                                \
                };                                                      \
        STDMETHOD_(ULONG, Release) ()                                   \
                {                                                          \
                    if ( InterlockedDecrement((long*)&_ulRefs) == 0 ) \
                    {                                                      \
                        delete this;                                       \
                        return 0;                                          \
                    }                                                      \
                    else                                                   \
                        return  _ulRefs;                              \
                };

#  define ENLIST_TRACKING(cls)

#  define DUMP_TRACKING_INFO()

#  define TRACK_CLASS(fTrack, cls)

# endif // DBG == 0

#endif  // of ifndef __OTRACK_HXX__


GETOBJ.CPP

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    GetObj.cpp

Abstract:

    Sample Provider GetObject functionality

Author:

Environment:

    User mode

Revision History :

--*/
#include "adssmp.h"
#pragma hdrstop

//+---------------------------------------------------------------------------
//  Function:   RelativeGetObject
//
//  Synopsis:   Gets object relative to given OLE DS path.
//
//  Arguments:  [BSTR ADsPath]
//              [BSTR ClassName]
//              [BSTR RelativeName]
//              [IUnknown** ppObject]
//              [BOOT bNamespaceRelative]
//
//  Returns:    HRESULT
//
//  Modifies:   *ppObject
//
//----------------------------------------------------------------------------
HRESULT
RelativeGetObject(
    BSTR ADsPath,
    BSTR ClassName,
    BSTR RelativeName,
    IDispatch* FAR* ppObject,
    BOOL bNamespaceRelative
    )
{
    WCHAR szBuffer[MAX_PATH];
    HRESULT hr = S_OK;

    *ppObject = NULL;

    if (!RelativeName || !*RelativeName) {
        RRETURN(E_ADS_UNKNOWN_OBJECT);
    }

    wcscpy(szBuffer, ADsPath);

    if (bNamespaceRelative)
        wcscat(szBuffer, L"//");
    else
        wcscat(szBuffer, L"/");
    wcscat(szBuffer, RelativeName);

    if (ClassName && *ClassName) {
        wcscat(szBuffer,L",");
        wcscat(szBuffer, ClassName);
    }

    hr = ::GetObject(
                szBuffer,
                (LPVOID *)ppObject
                );
    BAIL_ON_FAILURE(hr);

error:
    RRETURN(hr);
}

//+---------------------------------------------------------------------------
//  Function:  GetObject
//
//  Synopsis:  Called by ResolvePathName to return an object
//
//  Arguments:  [LPWSTR szBuffer]
//              [LPVOID *ppObject]
//
//  Returns:    HRESULT
//

//----------------------------------------------------------------------------
HRESULT
GetObject(
    LPWSTR szBuffer,
    LPVOID * ppObject
    )
{
    HRESULT hr;
    DWORD dwStatus = NO_ERROR;
    DWORD dwNumberEntries = 0;
    DWORD dwModificationTime = 0;
    WCHAR szObjectClassName[MAX_PATH];
    WCHAR szDSPath[MAX_PATH];
    WCHAR szParent[MAX_PATH];
    WCHAR szCommonName[MAX_PATH];
    HANDLE hObject = NULL;

    OBJECTINFO ObjectInfo;
    POBJECTINFO pObjectInfo = &ObjectInfo;
    CLexer Lexer(szBuffer);

    IADs * pADs = NULL;
    szObjectClassName[0]=L'\0';

    memset(pObjectInfo, 0, sizeof(OBJECTINFO));
    hr = ADsObject(&Lexer, pObjectInfo);
    BAIL_ON_FAILURE(hr);

    //
    // Validate that this ADs pathname is to be processed by
    // us - as in the provider name is @Sample!
    //

    hr = ValidateProvider(pObjectInfo);
    BAIL_ON_FAILURE(hr);

    hr = ValidateObjectType(pObjectInfo);

    switch (pObjectInfo->ObjectType) {

    case TOKEN_NAMESPACE:
        //
        // This means that this is a namespace object;
        // instantiate the namespace object
        //

        hr = GetNamespaceObject(
                pObjectInfo,
                ppObject
                );
        BAIL_ON_FAILURE(hr);

        break;

    case TOKEN_SCHEMA:

        hr = GetSchemaObject(
                pObjectInfo,
                ppObject
                );
        BAIL_ON_FAILURE(hr);

        break;

    default:
        hr = BuildDSPathFromADsPath(
                    szBuffer,
                    szDSPath
                    );
        BAIL_ON_FAILURE(hr);

        hr  = SampleDSOpenObject(
                        szDSPath,
                        &hObject,
                        szObjectClassName,
                        REG_DS);
        BAIL_ON_FAILURE(hr);

        hr = BuildADsParentPath(
                    szBuffer,
                    szParent,
                    szCommonName
                    );
        BAIL_ON_FAILURE(hr);

        hr = CSampleDSGenObject::CreateGenericObject(
                        szParent,
                        szCommonName,
                        szObjectClassName,
                        ADS_OBJECT_BOUND,
                        IID_IUnknown,
                        (void**)ppObject
                        );
        BAIL_ON_FAILURE(hr);
        break;

    }

error:

    if (pADs) {
        pADs->Release();
    }
    if (hObject) {
        SampleDSCloseObject(hObject);
    }

    RRETURN(hr);
}

HRESULT
BuildADsPathFromDSPath(
    LPWSTR szDSRootRDN,
    LPWSTR szDSDNName,
    LPWSTR szADsPathName
    )
{
    PKEYDATA pKeyData = NULL;
    DWORD dwCount = 0;
    DWORD i = 0;

    if (!szDSRootRDN || !szDSDNName) {
        RRETURN(E_FAIL);
    }

    wsprintf(szADsPathName,L"%s:%s", g_szProviderName, szDSRootRDN);

    pKeyData = CreateTokenList(
                    szDSDNName,
                    L'.'
                    );

    if (pKeyData) {

        dwCount = pKeyData->cTokens;
        for (i = 0; i < dwCount; i++) {
            wcscat(szADsPathName, L"/");
            wcscat(szADsPathName, pKeyData->pTokens[dwCount - 1 - i]);
        }
    }

    if (pKeyData) {
        FreeProvMem(pKeyData);
    }
    RRETURN(S_OK);
}

HRESULT
BuildDSTreeNameFromADsPath(
    LPWSTR szBuffer,
    LPWSTR szSampleDSTreeName
    )
{
    OBJECTINFO ObjectInfo;
    POBJECTINFO pObjectInfo = &ObjectInfo;
    CLexer Lexer(szBuffer);
    DWORD dwNumComponents = 0;
    HRESULT hr;

    memset(pObjectInfo, 0, sizeof(OBJECTINFO));
    hr = ADsObject(&Lexer, pObjectInfo);
    BAIL_ON_FAILURE(hr);

    dwNumComponents = pObjectInfo->NumComponents;

    if (!dwNumComponents && !pObjectInfo->RootRDN) {
        //
        // There are no CNs in this pathname and
        // no tree name specified. This is the
        // namespace object - its parent is the
        // @ADs! object
        //

        hr = E_FAIL;

    } else if (!dwNumComponents && pObjectInfo->RootRDN) {
        //
        // There are no CNs in this pathname and a tree
        // name has been specified. This is the root
        // object - its parent is the  @SampleDS! object

        wsprintf(szSampleDSTreeName,L"\\\\%s", pObjectInfo->RootRDN);

        hr = S_OK;

    }else {
        //
        // There are one or more CNs, a tree name has been
        // specified. In the worst case the parent is the
        // root object. In the best case a long CN.
        //

        wsprintf(szSampleDSTreeName,L"\\\\%s", pObjectInfo->RootRDN);

        hr = S_OK;
    }

error:
    RRETURN(hr);

}

HRESULT
BuildDSPathFromADsPath(
    LPWSTR szADsPathName,
    LPWSTR szDSPathName
    )
{
    OBJECTINFO ObjectInfo;
    POBJECTINFO pObjectInfo = &ObjectInfo;
    CLexer Lexer(szADsPathName);
    DWORD i = 0;
    DWORD dwNumComponents = 0;
    HRESULT hr;

    memset(pObjectInfo, 0, sizeof(OBJECTINFO));
    hr = ADsObject(&Lexer, pObjectInfo);
    BAIL_ON_FAILURE(hr);

    dwNumComponents = pObjectInfo->NumComponents;

    wcscpy(szDSPathName, L"\\");
    wcscat(szDSPathName, pObjectInfo->RootRDN);

    for (i = 0; i < dwNumComponents; i++) {
        wcscat(szDSPathName, L"\\");
                wcscat(szDSPathName, pObjectInfo->ComponentArray[i].szComponent);
    }

error:

    RRETURN(hr);

}

HRESULT
AppendComponent(
   LPWSTR szDSPathName,
   PCOMPONENT pComponent
){
        DebugBreak();
    RRETURN(S_OK);
}

HRESULT
BuildADsParentPath(
    LPWSTR szBuffer,
    LPWSTR szParent,
    LPWSTR szCommonName
    )
{
    OBJECTINFO ObjectInfo;
    POBJECTINFO pObjectInfo = &ObjectInfo;
    CLexer Lexer(szBuffer);
    DWORD i = 0;
    DWORD dwNumComponents = 0;
    HRESULT hr;
    LPWSTR pszComponent = NULL, pszValue = NULL;

    memset(pObjectInfo, 0, sizeof(OBJECTINFO));
    hr = ADsObject(&Lexer, pObjectInfo);
    BAIL_ON_FAILURE(hr);

    dwNumComponents = pObjectInfo->NumComponents;

    if (!dwNumComponents && !pObjectInfo->RootRDN) {
        //
        // There are no CNs in this pathname and
        // no Root name specified. This is the
        // namespace object - its parent is the
        // @ADs! object
        //

        wsprintf(szParent,L"ADs:");

        RRETURN(S_OK);

    } else if (!dwNumComponents && pObjectInfo->RootRDN) {
        //
        // There are no CNs in this pathname and a Root
        // name has been specified. This is the root
        // object - its parent is the  @Sample! object

        wsprintf(szParent, L"%s:", pObjectInfo->ProviderName);

        //
        // And the common name is the RootRDN
        //

        wsprintf(szCommonName,L"%s", pObjectInfo->RootRDN);

        RRETURN(S_OK);

    }else {
        //
        // There are one or more CNs, a Root name has been
        // specified. In the worst case the parent is the
        // root object. In the best case a long CN.
        //

        wsprintf(
            szParent, L"%s://%s",
            pObjectInfo->ProviderName,
            pObjectInfo->RootRDN
            );

        for (i = 0; i < dwNumComponents - 1; i++) {

            wcscat(szParent, L"/");

            pszComponent =  pObjectInfo->ComponentArray[i].szComponent;
            pszValue = pObjectInfo->ComponentArray[i].szValue;

            if (pszComponent && pszValue) {

                wcscat(
                    szParent,
                    pObjectInfo->ComponentArray[i].szComponent
                    );
                wcscat(szParent,L"=");
                wcscat(
                    szParent,
                    pObjectInfo->ComponentArray[i].szValue
                    );
            }else if (pszComponent){

                wcscat(
                    szParent,
                    pObjectInfo->ComponentArray[i].szComponent
                    );

            }else {
                //
                // Error - we should never hit this case!!
                //

            }
        }

        //
        // And the common name is the last component
        //

        pszComponent =  pObjectInfo->ComponentArray[dwNumComponents - 1].szComponent;
        pszValue = pObjectInfo->ComponentArray[dwNumComponents - 1].szValue;

        if (pszComponent && pszValue) {

            wsprintf(szCommonName, L"%s=%s",pszComponent, pszValue);

        }else if (pszComponent){

            wsprintf(szCommonName, L"%s", pszComponent);

        }else {
            //
            // Error - we should never hit this case!!
            //

        }

    }

error:

    RRETURN(hr);

}

HRESULT
InstantiateDerivedObject(
    IADs FAR * pADs,
    REFIID riid,
    void  ** ppObject
    )
{
        *ppObject = 0;

    RRETURN(S_OK);
}

//+---------------------------------------------------------------------------
// Function:    GetNamespaceObject
//
// Synopsis:    called by GetObject
//
// Arguments:   [POBJECTINFO pObjectInfo]
//              [LPVOID * ppObject]
//
// Returns:     HRESULT
//
//----------------------------------------------------------------------------
HRESULT
GetNamespaceObject(
    POBJECTINFO pObjectInfo,
    LPVOID * ppObject
    )
{
    HRESULT hr;

    hr = ValidateNamespaceObject(
                pObjectInfo
                );
    BAIL_ON_FAILURE(hr);

    hr = CoCreateInstance(CLSID_SampleDSNamespace,
                          NULL,
                          CLSCTX_INPROC_SERVER,
                          IID_IUnknown,
                          (void **)ppObject
                          );

error:

    RRETURN(hr);
}

HRESULT
ValidateNamespaceObject(
    POBJECTINFO pObjectInfo
    )
{

    if (!_wcsicmp(pObjectInfo->ProviderName, g_szProviderName)) {
    }
    RRETURN(S_OK);
}

HRESULT
ValidateProvider(
    POBJECTINFO pObjectInfo
    )
{

    //
    // The provider name is case-sensitive.  This is a restriction that OLE
    // has put on us.
    //
    if (!(wcscmp(pObjectInfo->ProviderName, g_szProviderName))) {
        RRETURN(S_OK);
    }
    RRETURN(E_FAIL);
}

//+---------------------------------------------------------------------------
// Function:    GetSchemaObject
//
// Synopsis:    called by GetObject
//
// Arguments:   [POBJECTINFO pObjectInfo]
//              [LPVOID * ppObject]
//
// Returns:     HRESULT
//
// Modifies:      -
//
//----------------------------------------------------------------------------
HRESULT
GetSchemaObject(
    POBJECTINFO pObjectInfo,
    LPVOID * ppObject
    )
{
    HRESULT hr = S_OK;
    WCHAR szParent[MAX_PATH];
    WCHAR szCommonName[MAX_PATH];
    WCHAR szSampleDSPathName[MAX_PATH];
    DWORD dwObjectType = 0;
    HANDLE hObject= NULL;

    hr = ValidateSchemaObject(
                pObjectInfo,
                &dwObjectType
                );
    BAIL_ON_FAILURE(hr);

    hr = BuildADsParentPath(
             pObjectInfo,
             szParent,
             szCommonName
             );
    BAIL_ON_FAILURE(hr);

    switch(dwObjectType) {
    case SampleDS_CLASS_ID:
    case SampleDS_PROPERTY_ID:
    case SampleDS_CLASSPROP_ID:

        wcscpy(szSampleDSPathName, L"\\");
        wcscat(szSampleDSPathName, szCommonName);
        hr = SampleDSOpenObject(
                        szSampleDSPathName,
                        &hObject,
                        NULL,
                        REG_SCHEMA
                        );
        BAIL_ON_FAILURE(hr);

        break;

    default:
        break;
    }

    //
    // Note: The "error:" tag is at the end of the switch statement,
    //       so we can simply break out.
    //

    switch (dwObjectType) {
    case SampleDS_SCHEMA_ID:
        hr = CSampleDSSchema::CreateSchema(
                    szParent,
                    szCommonName,
                    ADS_OBJECT_BOUND,
                    IID_IUnknown,
                    ppObject
                    );
        break;
    case SampleDS_CLASSPROP_ID:
        hr = CSampleDSClass::CreateClass(
                    szParent,
                    szCommonName,
                    hObject,
                    ADS_OBJECT_BOUND,
                    IID_IUnknown,
                    ppObject
                    );
        if (FAILED(hr)) {
            hr = CSampleDSProperty::CreateProperty(
                                    szParent,
                                    szCommonName,
                                    hObject,
                                    ADS_OBJECT_BOUND,
                                    IID_IUnknown,
                                    ppObject
                                    );
        }
        break;

   default:
        hr = E_ADS_UNKNOWN_OBJECT;
        break;
    }

error:
    if (hObject) {
        SampleDSCloseObject(hObject);
    }
    RRETURN(hr);
}

HRESULT
ValidateSchemaObject(
    POBJECTINFO pObjectInfo,
    PDWORD pdwObjectType
    )
{
    DWORD dwNumComponents = 0;

    dwNumComponents = pObjectInfo->NumComponents;

    switch (dwNumComponents) {

    case 1:
        if (!_wcsicmp(pObjectInfo->ComponentArray[0].szComponent, L"schema")) {
            *pdwObjectType = SampleDS_SCHEMA_ID;
            RRETURN(S_OK);
        }
        break;

    case 2:
        *pdwObjectType = SampleDS_CLASSPROP_ID;
        RRETURN(S_OK);

    default:
        break;

    }

    RRETURN(E_FAIL);
}

HRESULT
BuildADsParentPath(
    POBJECTINFO pObjectInfo,
    LPWSTR szParent,
    LPWSTR szCommonName
    )
{
    DWORD i = 0;
    DWORD dwNumComponents = 0;
    LPWSTR pszComponent = NULL, pszValue = NULL;

    dwNumComponents = pObjectInfo->NumComponents;

    if (!dwNumComponents && !pObjectInfo->RootRDN) {
        //
        // There are no CNs in this pathname and
        // no root name specified. This is the
        // namespace object - its parent is the
        // @ADs! object
        //

        wsprintf(szParent,L"ADs:");

        RRETURN(S_OK);

    } else if (!dwNumComponents && pObjectInfo->RootRDN) {
        //
        // There are no CNs in this pathname and a root
        // name has been specified. This is the root
        // object - its parent is the  @Sample! object

        wsprintf(szParent, L"%s:", pObjectInfo->ProviderName);

        //
        // And the common name is the RootRDN. Remember the
        // "//" will be added on  when we reconstruct the full
        // pathname
        //

        wsprintf(szCommonName,L"%s", pObjectInfo->RootRDN);

        RRETURN(S_OK);

    }else {
        //
        // There are one or more CNs, a root name has been
        // specified. In the worst case the parent is the
        // root object. In the best case a long CN.
        //

        wsprintf(
            szParent, L"%s://%s",
            pObjectInfo->ProviderName,
            pObjectInfo->RootRDN
            );

        for (i = 0; i < dwNumComponents - 1; i++) {

            wcscat(szParent, L"/");

            pszComponent =  pObjectInfo->ComponentArray[i].szComponent;
            pszValue = pObjectInfo->ComponentArray[i].szValue;

            if (pszComponent && pszValue) {

                wcscat(
                    szParent,
                    pObjectInfo->ComponentArray[i].szComponent
                    );
                wcscat(szParent,L"=");
                wcscat(
                    szParent,
                    pObjectInfo->ComponentArray[i].szValue
                    );
            }else if (pszComponent){

                wcscat(
                    szParent,
                    pObjectInfo->ComponentArray[i].szComponent
                    );

            }else {
                //
                // Error - we should never hit this case!!
                //

            }
        }

        //
        // And the common name is the last component
        //

        pszComponent =  pObjectInfo->ComponentArray[dwNumComponents - 1].szComponent;
        pszValue = pObjectInfo->ComponentArray[dwNumComponents - 1].szValue;

        if (pszComponent && pszValue) {

            wsprintf(szCommonName, L"%s=%s",pszComponent, pszValue);

        }else if (pszComponent){

            wsprintf(szCommonName, L"%s", pszComponent);

        }else {
            //
            // Error - we should never hit this case!!
            //

        }

    }

    RRETURN(S_OK);
}

HRESULT
ValidateObjectType(
    POBJECTINFO pObjectInfo
    )
{

    pObjectInfo->ObjectType = TOKEN_DSOBJECT;

    if (pObjectInfo->ProviderName && !pObjectInfo->RootRDN
            && !pObjectInfo->NumComponents) {
        pObjectInfo->ObjectType = TOKEN_NAMESPACE;
    }else if (pObjectInfo->ProviderName && pObjectInfo->RootRDN
                && pObjectInfo->NumComponents) {

        if (!_wcsicmp(pObjectInfo->ComponentArray[0].szComponent,L"schema")) {
            pObjectInfo->ObjectType = TOKEN_SCHEMA;
        }

    }

    RRETURN(S_OK);
}

HRESULT
BuildSampleDSRootRDNFromADsPath(
    LPWSTR szBuffer,
    LPWSTR szDSRootRDN
    )
{
    OBJECTINFO ObjectInfo;
    POBJECTINFO pObjectInfo = &ObjectInfo;
    CLexer Lexer(szBuffer);
    DWORD dwNumComponents = 0;
    HRESULT hr;

    memset(pObjectInfo, 0, sizeof(OBJECTINFO));
    hr = ADsObject(&Lexer, pObjectInfo);
    BAIL_ON_FAILURE(hr);

    dwNumComponents = pObjectInfo->NumComponents;

    if (!dwNumComponents && !pObjectInfo->RootRDN) {
        //
        // There are no CNs in this pathname and
        // no root name specified. This is the
        // namespace object - its parent is the
        // @ADs! object
        //

        hr = E_FAIL;

    } else if (!dwNumComponents && pObjectInfo->RootRDN) {
        //
        // There are no CNs in this pathname and a root
        // name has been specified. This is the root
        // object - its parent is the  @Sample! object

        wsprintf(szDSRootRDN,L"\\\\%s", pObjectInfo->RootRDN);

        hr = S_OK;

    }else {
        //
        // There are one or more CNs, a root name has been
        // specified. In the worst case the parent is the
        // root object. In the best case a long CN.
        //

        wsprintf(szDSRootRDN,L"\\\\%s", pObjectInfo->RootRDN);

        hr = S_OK;
    }

error:
    RRETURN(hr);

}

HRESULT
BuildDSPathFromADsPath(
    LPWSTR szADsPathName,
    LPWSTR szDSRootRDN,
    LPWSTR szDSPathName
    )
{
    OBJECTINFO ObjectInfo;
    POBJECTINFO pObjectInfo = &ObjectInfo;
    CLexer Lexer(szADsPathName);
    DWORD i = 0;
    DWORD dwNumComponents = 0;
    HRESULT hr;

    memset(pObjectInfo, 0, sizeof(OBJECTINFO));
    hr = ADsObject(&Lexer, pObjectInfo);
    BAIL_ON_FAILURE(hr);

    dwNumComponents = pObjectInfo->NumComponents;

    wcscpy(szDSRootRDN, L"\\");
    wcscat(szDSRootRDN, pObjectInfo->RootRDN);

    *szDSPathName = L'\0';

    for (i = dwNumComponents; i >  0; i--) {
                wcscat(szDSPathName, pObjectInfo->ComponentArray[i-1].szComponent);
        if (i>1) wcscat(szDSPathName, L"\\");
    }

error:

    RRETURN(hr);
}


GETOBJ.H

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    GetObj.h

Abstract:

Author:

Environment:

    User mode

Revision History :

--*/
#ifndef _GETOBJ_H_
#define _GETOBJ_H_

#define MAXCOMPONENTS               20

#define TOKEN_IDENTIFIER                1
#define TOKEN_COMMA                     2
#define TOKEN_BSLASH                    3
#define TOKEN_END                       4
#define TOKEN_OU                        5
#define TOKEN_USER                      6
#define TOKEN_GROUP                     7

#define TOKEN_ATSIGN                   11
#define TOKEN_EXCLAMATION              12
#define TOKEN_COLON                    13
#define TOKEN_FSLASH                   14
#define TOKEN_PROVIDER                 15

#define TOKEN_SCHEMA                   17
#define TOKEN_CLASS                    18
#define TOKEN_PROPERTY                 21
#define TOKEN_SYNTAX                   22

#define TOKEN_PERIOD                   24
#define TOKEN_EQUAL                    25
#define TOKEN_NAMESPACE                26
#define TOKEN_DSOBJECT                 28

typedef struct _component {
    LPWSTR szComponent;
    LPWSTR szValue;
}COMPONENT, *PCOMPONENT;

typedef struct _objectinfo {
    LPWSTR  ProviderName;
    LPWSTR  RootRDN;
    DWORD   ObjectType;
    DWORD   NumComponents;
    COMPONENT  ComponentArray[MAXCOMPONENTS];
} OBJECTINFO, *POBJECTINFO;

HRESULT
RelativeGetObject(
    BSTR ADsPath,
    BSTR ClassName,
    BSTR RelativeName,
    IDispatch* FAR* ppObject,
    BOOL bNamespaceRelative
    );

HRESULT
GetObject(
    LPWSTR szBuffer,
    LPVOID * ppObject
    );

HRESULT
GetNamespaceObject(
    POBJECTINFO pObjectInfo,
    LPVOID * ppObject
    );

HRESULT
ValidateNamespaceObject(
    POBJECTINFO pObjectInfo
    );

HRESULT
GetSchemaObject(
    POBJECTINFO pObjectInfo,
    LPVOID * ppObject
    );

HRESULT
ValidateSchemaObject(
    POBJECTINFO pObjectInfo,
    PDWORD pdwObjectType
    );

HRESULT
ValidateProvider(
    POBJECTINFO pObjectInfo
    );

HRESULT
AddComponent(
    POBJECTINFO pObjectInfo,
    LPWSTR szComponent,
    LPWSTR szValue
    );

HRESULT
AddProviderName(
    POBJECTINFO pObjectInfo,
    LPWSTR szToken
    );

HRESULT
BuildADsParentPath(
    LPWSTR szBuffer,
    LPWSTR szParent,
    LPWSTR szCommonName
    );

HRESULT
BuildDSPathFromADsPath(
    LPWSTR szADsPathName,
    LPWSTR szSampleDSPathName
    );

HRESULT
BuildADsParentPath(
    POBJECTINFO pObjectInfo,
    LPWSTR szParent,
    LPWSTR szCommonName
    );

HRESULT
BuildDSTreeNameFromADsPath(
    LPWSTR szBuffer,
    LPWSTR szSampleDSTreeName
    );

HRESULT
ValidateObjectType(
    POBJECTINFO pObjectInfo
    );

HRESULT
BuildSampleDSRootRDNFromADsPath(
    LPWSTR szBuffer,
    LPWSTR szDSTreeName
    );

HRESULT
AppendComponent(
   LPWSTR szDSPathName,
   PCOMPONENT pComponent
   );

HRESULT
BuildDSPathFromADsPath(
    LPWSTR szADsPathName,
    LPWSTR szDSRootName,
    LPWSTR szDSPathName
    );

HRESULT
InstantiateDerivedObject(
    IADs FAR * pADs,
    REFIID riid,
    void  ** ppObject
    );
#endif


GLOBALS.CPP

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    globals.cpp

Abstract:

Author:

Environment:

    User mode

Revision History :

--*/
#include "adssmp.h"
#pragma hdrstop

WCHAR * g_szProviderName = L"Sample";

KWDLIST KeywordList[MAX_KEYWORDS] =
{
    { TOKEN_USER, L"user"},
    { TOKEN_GROUP, L"group"},
    { TOKEN_OU, L"organizational unit"}
};

SYNTAXMAP g_aSampleDSSyntaxMap[] =
{
  /* 1 */
  { TEXT("String"),  TEXT("SampleDS Distinguished Name"),  VT_BSTR},

  /* 2 */
  { TEXT("Integer"),    TEXT("SampleDS Integer"),           VT_I4}

};

DWORD g_cSampleDSSyntaxMap = (sizeof(g_aSampleDSSyntaxMap)/sizeof(g_aSampleDSSyntaxMap[0]));


GLOBALS.H

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    Globals.h

Abstract:

Author:

Environment:

    User mode

Revision History :

--*/
#ifndef _GLOBALS_H_
#define _GLOBALS_H_

extern  WCHAR *g_szProviderName;
extern KWDLIST KeywordList[];
extern SYNTAXMAP g_aSampleDSSyntaxMap[];
extern DWORD g_cSampleDSSyntaxMap;

#endif


GUID.CPP

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    guid.cpp

Abstract:

    Sample CLSIDS

Author:

Environment:

    User mode

Revision History :

--*/
#include "adssmp.h"
#pragma hdrstop
#define INITGUID
#include <ole2.h>

DEFINE_GUID(CLSID_SampleDSNamespace,0xF46430D0L,0xCBfB,0x11CE,0xA9,0xF7,0x00,0xAA,0x00,0xB6,0x76,0x89);
DEFINE_GUID(CLSID_SampleDSProvider,0xF46430D1L,0xCBfB,0x11CE,0xA9,0xF7,0x00,0xAA,0x00,0xB6,0x76,0x89);
DEFINE_GUID(LIBID_SampleDSOle,0xF46430D2L,0xCBfB,0x11CE,0xA9,0xF7,0x00,0xAA,0x00,0xB6,0x76,0x89);
DEFINE_GUID(CLSID_SampleDSGenObject,0xF46430D4L,0xCBfB,0x11CE,0xA9,0xF7,0x00,0xAA,0x00,0xB6,0x76,0x89);
DEFINE_GUID(CLSID_SampleDSSchema,0xac32c348L,0x27f8,0x11d0,0xb9,0x3e,0x00,0xc0,0x4f,0xd8,0xd5,0xa8);
DEFINE_GUID(CLSID_SampleDSClass,0xa75afc78L,0x27f8,0x11d0,0xb9,0x3e,0x00,0xc0,0x4f,0xd8,0xd5,0xa8);
DEFINE_GUID(CLSID_SampleDSProperty,0xaa80d74cL,0x27f8,0x11d0,0xb9,0x3e,0x00,0xc0,0x4f,0xd8,0xd5,0xa8);


GUID.H

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    Guid.h

Abstract:

    Extern references for Sample Guids

Author:

Environment:

    User mode

Revision History :

--*/
#ifndef _GUID_H_
#define _GUID_H_

#ifdef __cplusplus
extern "C" {
#endif

//-------------------------------------------
//
// SampleDSOle CLSIDs
//
//-------------------------------------------
extern const CLSID LIBID_SampleDSOle;
extern const CLSID CLSID_SampleDSNamespace;
extern const CLSID CLSID_SampleDSProvider;
extern const CLSID CLSID_SampleDSGenObject;
extern const CLSID CLSID_SampleDSSchema;
extern const CLSID CLSID_SampleDSClass;
extern const CLSID CLSID_SampleDSProperty;

#ifdef __cplusplus
}
#endif

#endif


INTF.H

/*++s

Copyright (c) 1996 Microsoft Corporation

Module Name:

    IntF.cpp

Abstract:

    Interface for ADS

Author:

Environment:

    User mode

Revision History :

--*/
#ifndef _INTF_H_
#define _INTF_H_

#define DECLARE_IUnknown_METHODS \
        STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) ;

#define DECLARE_IDispatch_METHODS \
        STDMETHOD(GetTypeInfoCount)(THIS_ UINT FAR* pctinfo) ; \
        \
        STDMETHOD(GetTypeInfo)(\
        THIS_ \
        UINT itinfo,\
        LCID lcid,\
        ITypeInfo FAR* FAR* pptinfo) ;\
        \
        STDMETHOD(GetIDsOfNames)( \
        THIS_ \
        REFIID riid,\
        OLECHAR FAR* FAR* rgszNames,\
        UINT cNames,\
        LCID lcid, \
        DISPID FAR* rgdispid) ;\
        \
        STDMETHOD(Invoke)(\
        THIS_\
        DISPID dispidMember,\
        REFIID riid,\
        LCID lcid,\
        WORD wFlags,\
        DISPPARAMS FAR* pdispparams,\
        VARIANT FAR* pvarResult,\
        EXCEPINFO FAR* pexcepinfo,\
        UINT FAR* puArgErr) ;

#define DECLARE_IADs_METHODS  \
        STDMETHOD(get_Name)(THIS_ BSTR FAR* retval) ;        \
        STDMETHOD(get_ADsPath)(THIS_ BSTR FAR* retval); \
        STDMETHOD(get_GUID)(THIS_ BSTR FAR* retval) ;\
        STDMETHOD(get_Class)(THIS_ BSTR FAR* retval);\
        STDMETHOD(get_Parent)(THIS_ BSTR FAR* retval);\
        STDMETHOD(get_Schema)(THIS_ BSTR FAR* retval);\
        STDMETHOD(SetInfo)(THIS) ;                           \
        STDMETHOD(GetInfo)(THIS) ; \
        STDMETHOD(Get)(THIS_ BSTR bstrName, VARIANT FAR* pvProp) ; \
        STDMETHOD(Put)(THIS_ BSTR bstrName, VARIANT vProp) ;        \
        STDMETHOD(GetEx)(THIS_ BSTR bstrName, VARIANT FAR* pvProp) ; \
        STDMETHOD(PutEx)(THIS_ long lnControlCode, BSTR bstrName, VARIANT vProp) ; \
        STDMETHOD(GetInfoEx)(THIS_ VARIANT vProperties, long lnReserved);

#define DECLARE_IADsStatus_METHODS\
    STDMETHOD(get_Code)(THIS_ long FAR* plStatusCode);\
    STDMETHOD(get_String)(THIS_ BSTR FAR* pbstrStatusString);

#define DECLARE_IADsContainer_METHODS \
        STDMETHOD(get_Count)(THIS_ long FAR* retval) ;      \
        STDMETHOD(get_Filter)(THIS_ VARIANT FAR* pVar) ;    \
        STDMETHOD(put_Filter)(THIS_ VARIANT Var) ;          \
        STDMETHOD(get_Hints)(THIS_ VARIANT FAR* pvFilter);  \
        STDMETHOD(put_Hints)(THIS_ VARIANT vHints)       ;  \
        STDMETHOD(GetObject)(THIS_ BSTR ClassName, BSTR RelativeName, IDispatch * FAR* ppObject) ;\
        STDMETHOD(get__NewEnum)(THIS_ IUnknown * FAR* retval) ; \
        STDMETHOD(Create)(THIS_ BSTR ClassName, BSTR RelativeName, IDispatch * FAR* ppObject) ;\
        STDMETHOD(Delete)(THIS_ BSTR bstrClassName, BSTR bstrRelativeName) ;\
        STDMETHOD(CopyHere)(THIS_ BSTR SourceName, BSTR NewName, IDispatch * FAR* ppObject) ;\
        STDMETHOD(MoveHere)(THIS_ BSTR SourceName, BSTR NewName, IDispatch * FAR* ppObject) ;

#define DECLARE_IADsNamespaces_METHODS \
    STDMETHOD(get_DefaultContainer)(THIS_ BSTR FAR* retval);\
    STDMETHOD(put_DefaultContainer)(THIS_ BSTR bstrDefaultContainer);

/* IADsUser methods */
#define DECLARE_IADsUser_METHODS      \
   STDMETHOD(get_BadLoginAddress)(THIS_ BSTR FAR* retval) ;\
    STDMETHOD(get_BadLoginCount)(THIS_ long FAR* retval) ;\
    STDMETHOD(get_LastLogin)(THIS_ DATE FAR* retval) ;\
    STDMETHOD(get_LastLogoff)(THIS_ DATE FAR* retval) ;\
    STDMETHOD(get_LastFailedLogin)(THIS_ DATE FAR* retval) ;\
    STDMETHOD(get_PasswordLastChanged)(THIS_ DATE FAR* retval) ;\
    STDMETHOD(get_Description)(THIS_ BSTR FAR* retval) ;\
    STDMETHOD(put_Description)(THIS_ BSTR bstrDescription) ;\
    STDMETHOD(get_Division)(THIS_ BSTR FAR* retval) ;\
    STDMETHOD(put_Division)(THIS_ BSTR bstrDivision) ;\
    STDMETHOD(get_Department)(THIS_ BSTR FAR* retval) ;\
    STDMETHOD(put_Department)(THIS_ BSTR bstrDepartment) ;\
    STDMETHOD(get_EmployeeID)(THIS_ BSTR FAR* retval) ;\
    STDMETHOD(put_EmployeeID)(THIS_ BSTR bstrEmployeeID) ;\
    STDMETHOD(get_FullName)(THIS_ BSTR FAR* retval) ;\
    STDMETHOD(put_FullName)(THIS_ BSTR bstrFullName) ;\
    STDMETHOD(get_FirstName)(THIS_ BSTR FAR* retval) ;\
    STDMETHOD(put_FirstName)(THIS_ BSTR bstrFirstName) ;\
    STDMETHOD(get_LastName)(THIS_ BSTR FAR* retval) ;\
    STDMETHOD(put_LastName)(THIS_ BSTR bstrLastName) ;\
    STDMETHOD(get_OtherName)(THIS_ BSTR FAR* retval) ;\
    STDMETHOD(put_OtherName)(THIS_ BSTR bstrOtherName) ;\
    STDMETHOD(get_NamePrefix)(THIS_ BSTR FAR* retval) ;\
    STDMETHOD(put_NamePrefix)(THIS_ BSTR bstrNamePrefix) ;\
    STDMETHOD(get_NameSuffix)(THIS_ BSTR FAR* retval) ;\
    STDMETHOD(put_NameSuffix)(THIS_ BSTR bstrNameSuffix) ;\
    STDMETHOD(get_Title)(THIS_ BSTR FAR* retval) ;\
    STDMETHOD(put_Title)(THIS_ BSTR bstrTitle) ;\
    STDMETHOD(get_Manager)(THIS_ BSTR FAR* retval) ;\
    STDMETHOD(put_Manager)(THIS_ BSTR bstrManager) ;\
    STDMETHOD(get_TelephoneNumber)(THIS_ VARIANT FAR* retval) ;\
    STDMETHOD(put_TelephoneNumber)(THIS_ VARIANT vTelephoneNumber) ;\
    STDMETHOD(get_TelephoneHome)(THIS_ VARIANT FAR* retval) ;\
    STDMETHOD(put_TelephoneHome)(THIS_ VARIANT vTelephoneHome );\
    STDMETHOD(get_TelephoneMobile)(THIS_ VARIANT FAR* retval) ;\
    STDMETHOD(put_TelephoneMobile)(THIS_ VARIANT vTelephoneMobile) ;\
    STDMETHOD(get_TelephonePager)(THIS_ VARIANT FAR* retval) ;\
    STDMETHOD(put_TelephonePager)(THIS_ VARIANT vTelephonePager) ;\
    STDMETHOD(get_FaxNumber)(THIS_ VARIANT FAR* retval) ;\
    STDMETHOD(put_FaxNumber)(THIS_ VARIANT vFaxNumber) ;\
    STDMETHOD(get_OfficeLocations)(THIS_ VARIANT FAR* retval) ;\
    STDMETHOD(put_OfficeLocations)(THIS_ VARIANT vOfficeLocation) ;\
    STDMETHOD(get_PostalAddresses)(THIS_ VARIANT FAR* retval) ;\
    STDMETHOD(put_PostalAddresses)(THIS_ VARIANT vPostalAddresses) ;\
    STDMETHOD(get_PostalCodes)(THIS_ VARIANT FAR* retval) ;\
    STDMETHOD(put_PostalCodes)(THIS_ VARIANT vPostalCodes) ;\
    STDMETHOD(get_SeeAlso)(THIS_ VARIANT FAR* retval) ;\
    STDMETHOD(put_SeeAlso)(THIS_ VARIANT vSeeAlso) ;\
    STDMETHOD(get_AccountDisabled)(THIS_ VARIANT_BOOL FAR* retval) ;\
    STDMETHOD(put_AccountDisabled)(THIS_ VARIANT_BOOL fAccountDisabled) ;\
    STDMETHOD(get_AccountExpirationDate)(THIS_ DATE FAR* retval) ;\
    STDMETHOD(put_AccountExpirationDate)(THIS_ DATE daAccountExpirationDate) ;\
    STDMETHOD(get_GraceLoginsAllowed)(THIS_ long FAR* retval) ;\
    STDMETHOD(put_GraceLoginsAllowed)(THIS_ long lnGraceLoginsAllowed) ;\
    STDMETHOD(get_GraceLoginsRemaining)(THIS_ long FAR* retval) ;\
    STDMETHOD(put_GraceLoginsRemaining)(THIS_ long lnGraceLoginsRemaining) ;\
    STDMETHOD(get_IsAccountLocked)(THIS_ VARIANT_BOOL FAR* retval) ;\
    STDMETHOD(put_IsAccountLocked)(THIS_ VARIANT_BOOL fIsAccountLocked) ;\
    STDMETHOD(get_LoginHours)(THIS_ VARIANT FAR* retval) ;\
    STDMETHOD(put_LoginHours)(THIS_ VARIANT vLoginHours) ;\
    STDMETHOD(get_LoginWorkstations)(THIS_ VARIANT FAR* retval) ;\
    STDMETHOD(put_LoginWorkstations)(THIS_ VARIANT vLoginWorkstations) ;\
    STDMETHOD(get_MaxLogins)(THIS_ long FAR* retval) ;\
    STDMETHOD(put_MaxLogins)(THIS_ long lnMaxLogins) ;\
    STDMETHOD(get_MaxStorage)(THIS_ long FAR* retval) ;\
    STDMETHOD(put_MaxStorage)(THIS_ long lnMaxStorage) ;\
    STDMETHOD(get_PasswordExpirationDate)(THIS_ DATE FAR* retval) ;\
    STDMETHOD(put_PasswordExpirationDate)(THIS_ DATE daPasswordExpirationDate) ;\
    STDMETHOD(get_PasswordMinimumLength)(THIS_ long FAR* retval) ;\
    STDMETHOD(put_PasswordMinimumLength)(THIS_ long lnPasswordMinimumLength) ;\
    STDMETHOD(get_PasswordRequired)(THIS_ VARIANT_BOOL FAR* retval) ;\
    STDMETHOD(put_PasswordRequired)(THIS_ VARIANT_BOOL fPasswordRequired) ;\
    STDMETHOD(get_RequireUniquePassword)(THIS_ VARIANT_BOOL FAR* retval) ;\
    STDMETHOD(put_RequireUniquePassword)(THIS_ VARIANT_BOOL fRequireUniquePassword) ;\
    STDMETHOD(get_EmailAddress)(THIS_ BSTR FAR* retval) ;\
    STDMETHOD(put_EmailAddress)(THIS_ BSTR bstrEmailAddress) ;\
    STDMETHOD(get_HomeDirectory)(THIS_ BSTR FAR* retval) ;\
    STDMETHOD(put_HomeDirectory)(THIS_ BSTR bstrHomeDirectory) ;\
    STDMETHOD(get_Languages)(THIS_ VARIANT FAR* retval) ;\
    STDMETHOD(put_Languages)(THIS_ VARIANT vLanguages) ;\
    STDMETHOD(get_Profile)(THIS_ BSTR FAR* retval) ;\
    STDMETHOD(put_Profile)(THIS_ BSTR bstrProfile) ;\
    STDMETHOD(get_LoginScript)(THIS_ BSTR FAR* retval) ;\
    STDMETHOD(put_LoginScript)(THIS_ BSTR bstrLoginScript) ;\
    STDMETHOD(get_Picture)(THIS_ VARIANT FAR* retval) ;\
    STDMETHOD(put_Picture)(THIS_ VARIANT vPicture) ;\
    STDMETHOD(get_HomePage)(THIS_ BSTR FAR* retval) ;\
    STDMETHOD(put_HomePage)(THIS_ BSTR bstrHomePage) ;\
    STDMETHOD(Groups)(THIS_ IADsMembers FAR* FAR* ppGroups) ;\
    STDMETHOD(SetPassword)(THIS_ BSTR NewPassword) ;\
    STDMETHOD(ChangePassword)(THIS_ BSTR bstrOldPassword, BSTR bstrNewPassword) ;\

/* IADsDomain methods */
#define DECLARE_IADsDomain_METHODS \
        STDMETHOD(get_MinPasswordLength)(THIS_ long FAR* retval) ;\
        STDMETHOD(put_MinPasswordLength)(THIS_ long lnMinPasswordLength) ;\
        STDMETHOD(get_MinPasswordAge)(THIS_ long FAR* retval) ;\
        STDMETHOD(put_MinPasswordAge)(THIS_ long lnMinPasswordAge) ;\
        STDMETHOD(get_MaxPasswordAge)(THIS_ long FAR* retval) ;\
        STDMETHOD(put_MaxPasswordAge)(THIS_ long lnMaxPasswordAge) ;\
        STDMETHOD(get_MaxBadPasswordsAllowed)(THIS_ long FAR* retval) ;\
        STDMETHOD(put_MaxBadPasswordsAllowed)(THIS_ long lnMaxBadPasswordsAllowed) ;\
        STDMETHOD(get_PasswordHistoryLength)(THIS_ long FAR* retval) ;\
        STDMETHOD(put_PasswordHistoryLength)(THIS_ long lnPasswordHistoryLength) ;\
        STDMETHOD(get_PasswordAttributes)(THIS_ long FAR* retval) ;\
        STDMETHOD(put_PasswordAttributes)(THIS_ long lnPasswordAttributes) ;\
        STDMETHOD(get_AutoUnlockInterval)(THIS_ long FAR* retval) ;\
        STDMETHOD(put_AutoUnlockInterval)(THIS_ long lnAutoUnlockInterval) ;\
        STDMETHOD(get_LockoutObservationInterval)(THIS_ long FAR* retval) ;\
        STDMETHOD(put_LockoutObservationInterval)(THIS_ long lnLockoutObservationInterval) ;\
        STDMETHOD(get_IsWorkgroup)(THIS_ VARIANT_BOOL FAR* retval);

/* IADsComputer methods */
#define DECLARE_IADsComputer_METHODS \
        STDMETHOD(get_ComputerID)(THIS_ BSTR FAR* retval) ; \
        STDMETHOD(get_Site)(THIS_ BSTR FAR* retval) ; \
        STDMETHOD(get_Description)(THIS_ BSTR FAR* retval) ; \
        STDMETHOD(put_Description)(THIS_ BSTR bstrDescription) ; \
        STDMETHOD(get_Location)(THIS_ BSTR FAR* retval) ; \
        STDMETHOD(put_Location)(THIS_ BSTR bstrLocation) ; \
        STDMETHOD(get_PrimaryUser)(THIS_ BSTR FAR* retval) ;\
        STDMETHOD(put_PrimaryUser)(THIS_ BSTR bstrPrimaryUser) ;\
        STDMETHOD(get_Owner)(THIS_ BSTR FAR* retval) ;\
        STDMETHOD(put_Owner)(THIS_ BSTR bstrOwner) ;\
        STDMETHOD(get_Division)(THIS_ BSTR FAR* retval) ; \
        STDMETHOD(put_Division)(THIS_ BSTR bstrDivision) ; \
        STDMETHOD(get_Department)(THIS_ BSTR FAR* retval) ; \
        STDMETHOD(put_Department)(THIS_ BSTR bstrDepartment) ; \
        STDMETHOD(get_Role)(THIS_ BSTR FAR* retval) ; \
        STDMETHOD(put_Role)(THIS_ BSTR bstrRole) ; \
        STDMETHOD(get_OperatingSystem)(THIS_ BSTR FAR* retval); \
        STDMETHOD(put_OperatingSystem)(THIS_ BSTR bstrOperatingSystem); \
        STDMETHOD(get_OperatingSystemVersion)(THIS_ BSTR FAR* retval); \
        STDMETHOD(put_OperatingSystemVersion)(THIS_ BSTR bstrOperatingSystemVersion); \
        STDMETHOD(get_Model)(THIS_ BSTR FAR* retval) ; \
        STDMETHOD(put_Model)(THIS_ BSTR bstrModel) ; \
        STDMETHOD(get_Processor)(THIS_ BSTR FAR* retval) ; \
        STDMETHOD(put_Processor)(THIS_ BSTR bstrProcessor) ; \
        STDMETHOD(get_ProcessorCount)(THIS_ BSTR FAR* retval) ; \
        STDMETHOD(put_ProcessorCount)(THIS_ BSTR bstrProcessorCount) ; \
        STDMETHOD(get_MemorySize)(THIS_ BSTR FAR* retval) ; \
        STDMETHOD(put_MemorySize)(THIS_ BSTR bstrMemorySize) ; \
        STDMETHOD(get_StorageCapacity)(THIS_ BSTR FAR* retval) ; \
        STDMETHOD(put_StorageCapacity)(THIS_ BSTR bstrStorageCapacity); \
        STDMETHOD(get_NetAddresses)(THIS_ VARIANT FAR* retval); \
        STDMETHOD(put_NetAddresses)(THIS_ VARIANT vNetAddresses);

#define DECLARE_IADsComputerOperations_METHODS \
        STDMETHOD(Status)(THIS_ IDispatch * FAR* ppObject) ; \
        STDMETHOD(Shutdown)(THIS_ VARIANT_BOOL bReboot) ;

#define DECLARE_IADsGroup_METHODS \
        STDMETHOD(get_Description)(THIS_ BSTR FAR* retval) ; \
        STDMETHOD(put_Description)(THIS_ BSTR bstrDescription) ; \
        STDMETHOD(Members)(THIS_ IADsMembers FAR* FAR* ppMembers) ; \
        STDMETHOD(IsMember)(THIS_ BSTR bstrMember, VARIANT_BOOL FAR* bMember) ;\
        STDMETHOD(Add)(THIS_ BSTR bstrNewItem) ;\
        STDMETHOD(Remove)(THIS_ BSTR bstrItemToBeRemoved) ;

#define DECLARE_IADsMembers_METHODS \
        STDMETHOD(get_Count)(THIS_ long FAR* plCount) ;\
        STDMETHOD(get__NewEnum)(THIS_ IUnknown * FAR* ppEnumerator) ;\
        STDMETHOD(get_Filter)(THIS_ VARIANT FAR* pvFilter) ;\
        STDMETHOD(put_Filter)(THIS_ VARIANT pvFilter) ;\

#define DECLARE_IADsPrintQueue_METHODS \
    STDMETHOD(get_Model)(THIS_ BSTR FAR* retval) ;\
    STDMETHOD(put_Model)(THIS_ BSTR bstrModel) ;\
    STDMETHOD(get_Datatype)(THIS_ BSTR FAR* retval) ;\
    STDMETHOD(put_Datatype)(THIS_ BSTR bstrDatatype) ;\
    STDMETHOD(get_Description)(THIS_ BSTR FAR* retval) ;\
    STDMETHOD(put_Description)(THIS_ BSTR bstrDescription) ;\
    STDMETHOD(get_Location)(THIS_ BSTR FAR* retval) ;\
    STDMETHOD(put_Location)(THIS_ BSTR bstrLocation) ;\
    STDMETHOD(get_Priority)(THIS_ long FAR* retval) ;\
    STDMETHOD(put_Priority)(THIS_ long lnPriority) ;\
    STDMETHOD(get_StartTime)(THIS_ DATE FAR* retval) ;\
    STDMETHOD(put_StartTime)(THIS_ DATE daStartTime) ;\
    STDMETHOD(get_UntilTime)(THIS_ DATE FAR* retval) ;\
    STDMETHOD(put_UntilTime)(THIS_ DATE daUntilTime) ;\
    STDMETHOD(get_DefaultJobPriority)(THIS_ long FAR* retval) ;\
    STDMETHOD(put_DefaultJobPriority)(THIS_ long lnDefaultJobPriority) ;\
    STDMETHOD(get_BannerPage)(THIS_ BSTR FAR* retval) ;\
    STDMETHOD(put_BannerPage)(THIS_ BSTR bstrBannerPage) ;\
    STDMETHOD(get_PrinterPath)(THIS_ BSTR FAR* retval) ;\
    STDMETHOD(put_PrinterPath)(THIS_ BSTR bstrHostComputer) ;\
    STDMETHOD(get_PrintProcessor)(THIS_ BSTR FAR* retval);\
    STDMETHOD(put_PrintProcessor)(THIS_ BSTR bstrPrintProcessor);\
    STDMETHOD(get_PrintDevices)(THIS_ VARIANT FAR* retval);\
    STDMETHOD(put_PrintDevices)(THIS_ VARIANT vPorts);\
    STDMETHOD(get_NetAddresses)(THIS_ VARIANT FAR* retval);\
    STDMETHOD(put_NetAddresses)(THIS_ VARIANT vNetAddresses);\

#define DECLARE_IADsPrintQueueOperations_METHODS \
    STDMETHOD(get_Status)(THIS_ long FAR* retval) ;\
    STDMETHOD(PrintJobs)(THIS_ IADsCollection * FAR* ppObject) ;\
    STDMETHOD(Pause)(THIS) ;\
    STDMETHOD(Resume)(THIS) ;\
    STDMETHOD(Purge)(THIS) ;

#define DECLARE_IADsPrintJob_METHODS \
    STDMETHOD(get_HostPrintQueue)(THIS_ BSTR FAR* retval) ;\
    STDMETHOD(get_User)(THIS_ BSTR FAR* retval) ;\
    STDMETHOD(get_UserPath)(THIS_ BSTR FAR* retval);\
    STDMETHOD(get_Size)(THIS_ long FAR* retval) ;\
    STDMETHOD(get_TimeSubmitted)(THIS_ DATE FAR* retval) ;\
    STDMETHOD(get_TotalPages)(THIS_ long FAR* retval) ;\
    STDMETHOD(get_Description)(THIS_ BSTR FAR* retval) ;\
    STDMETHOD(put_Description)(THIS_ BSTR bstrDescription) ;\
    STDMETHOD(get_Priority)(THIS_ long FAR* retval) ;\
    STDMETHOD(put_Priority)(THIS_ long lnPriority) ;\
    STDMETHOD(get_StartTime)(THIS_ DATE FAR* retval) ;\
    STDMETHOD(put_StartTime)(THIS_ DATE daStartTime) ;\
    STDMETHOD(get_UntilTime)(THIS_ DATE FAR* retval) ;\
    STDMETHOD(put_UntilTime)(THIS_ DATE daUntilTime) ;\
    STDMETHOD(get_Notify)(THIS_ BSTR FAR* retval) ;\
    STDMETHOD(put_Notify)(THIS_ BSTR bstrNotify) ;\
    STDMETHOD(get_NotifyPath)(THIS_ BSTR FAR* retval);\
    STDMETHOD(put_NotifyPath)(THIS_ BSTR bstrNotifyPath);\

#define DECLARE_IADsPrintJobOperations_METHODS \
    STDMETHOD(get_Position)(THIS_ long FAR* retval) ;\
    STDMETHOD(put_Position)(THIS_ long lnPosition) ;\
    STDMETHOD(get_TimeElapsed)(THIS_ long FAR* retval) ;\
    STDMETHOD(get_PagesPrinted)(THIS_ long FAR* retval) ;\
    STDMETHOD(get_Status)(THIS_ long FAR* retval) ;\
    STDMETHOD(Pause)(THIS) ;\
    STDMETHOD(Resume)(THIS) ;\
    STDMETHOD(Remove)(THIS) ;

#define DECLARE_IADsCollection_METHODS \
    STDMETHOD(get__NewEnum)(THIS_ IUnknown * FAR* ppEnumerator) ;\
    STDMETHOD(GetObject)(THIS_ BSTR bstrName, VARIANT FAR* pvItem);\
    STDMETHOD(Add)(THIS_ BSTR bstrName, VARIANT vItem);\
    STDMETHOD(Remove)(THIS_ BSTR bstrItemToBeRemoved);

#define DECLARE_IEnumVARIANT_METHODS \
    STDMETHOD(Next)(ULONG cElements, \
                    VARIANT FAR* pvar,\
                    ULONG FAR* pcElementFetched);\
    STDMETHOD(Skip)(ULONG cElements);\
    STDMETHOD(Reset)();\
    STDMETHOD(Clone)(IEnumVARIANT FAR* FAR* ppenum);

#define DECLARE_IADsService_METHODS \
    STDMETHOD(get_HostComputer)(THIS_ BSTR FAR* pbstrHostComputer) ;\
    STDMETHOD(put_HostComputer)(THIS_ BSTR bstrHostComputer) ;\
    STDMETHOD(get_DisplayName)(THIS_ BSTR FAR* pbstrDisplayName) ;\
    STDMETHOD(put_DisplayName)(THIS_ BSTR bstrDisplayName) ;\
    STDMETHOD(get_Version)(THIS_ BSTR FAR* pbstrVersion) ;\
    STDMETHOD(put_Version)(THIS_ BSTR bstrVersion) ;\
    STDMETHOD(get_ServiceType)(THIS_ long FAR* plServiceType) ;\
    STDMETHOD(put_ServiceType)(THIS_ long lServiceType) ;\
    STDMETHOD(get_StartType)(THIS_ long FAR* plStartType) ;\
    STDMETHOD(put_StartType)(THIS_ long lStartType) ;\
    STDMETHOD(get_Path)(THIS_ BSTR FAR* pbstrPath) ;\
    STDMETHOD(put_Path)(THIS_ BSTR bstrPath) ;\
    STDMETHOD(get_StartupParameters)(THIS_ BSTR FAR* pbstrStartupParameters) ;\
    STDMETHOD(put_StartupParameters)(THIS_ BSTR bstrStartupParameters) ;\
    STDMETHOD(get_ErrorControl)(THIS_ long FAR* plErrorControl) ;\
    STDMETHOD(put_ErrorControl)(THIS_ long lErrorControl) ;\
    STDMETHOD(get_LoadOrderGroup)(THIS_ BSTR FAR* pbstrLoadOrderGroup) ;\
    STDMETHOD(put_LoadOrderGroup)(THIS_ BSTR bstrLoadOrderGroup) ;\
    STDMETHOD(get_ServiceAccountName)(THIS_ BSTR FAR* pbstrServiceAccountName) ;\
    STDMETHOD(put_ServiceAccountName)(THIS_ BSTR bstrServiceAccountName) ;\
    STDMETHOD(get_ServiceAccountPath)(THIS_ BSTR FAR* retval);\
    STDMETHOD(put_ServiceAccountPath)(THIS_ BSTR bstrServiceAccountPath);\
    STDMETHOD(get_Dependencies)(THIS_ VARIANT FAR* pv);\
    STDMETHOD(put_Dependencies)(THIS_ VARIANT v); \

#define  DECLARE_IADsServiceOperations_METHODS \
    STDMETHOD(get_Status)(THIS_ long FAR* retval) ;\
    STDMETHOD(SetPassword)(THIS_ BSTR bstrNewPassword) ;\
    STDMETHOD(Start)(THIS) ;\
    STDMETHOD(Stop)(THIS) ;\
    STDMETHOD(Pause)(THIS) ;\
    STDMETHOD(Continue)(THIS) ;

#define  DECLARE_IADsFileService_METHODS \
    STDMETHOD(get_Description)(THIS_ BSTR FAR* pbstrDescription) ;\
    STDMETHOD(put_Description)(THIS_ BSTR bstrDescription) ;\
    STDMETHOD(get_MaxUserCount)(THIS_ long FAR* plMaxUserCount) ;\
    STDMETHOD(put_MaxUserCount)(THIS_ long lMaxUserCount) ;

#define  DECLARE_IADsFileServiceOperations_METHODS \
    STDMETHOD(Sessions)(THIS_ IADsCollection FAR* FAR* ppSessions) ;\
    STDMETHOD(Resources)(THIS_ IADsCollection FAR* FAR* ppResources) ;

#define  DECLARE_IADsSession_METHODS \
    STDMETHOD(get_User)(THIS_ BSTR FAR* retval) ;\
    STDMETHOD(get_UserPath)(THIS_ BSTR FAR* retval);\
    STDMETHOD(get_Computer)(THIS_ BSTR FAR* retval) ;\
    STDMETHOD(get_ComputerPath)(THIS_ BSTR FAR* retval);\
    STDMETHOD(get_ConnectTime)(THIS_ LONG FAR* retval) ;\
    STDMETHOD(get_IdleTime)(THIS_ LONG FAR* retval) ;

#define  DECLARE_IADsFileShare_METHODS \
    STDMETHOD(get_CurrentUserCount)(THIS_ LONG FAR* retval) ;\
    STDMETHOD(get_Description)(THIS_ BSTR FAR* retval) ;\
    STDMETHOD(put_Description)(THIS_ BSTR bstrDescription) ;\
    STDMETHOD(get_HostComputer)(THIS_ BSTR FAR* retval) ;\
    STDMETHOD(put_HostComputer)(THIS_ BSTR bstrHostComputer) ;\
    STDMETHOD(get_Path)(THIS_ BSTR FAR* retval) ;\
    STDMETHOD(put_Path)(THIS_ BSTR bstrPath) ;\
    STDMETHOD(get_MaxUserCount)(THIS_ LONG FAR* retval) ;\
    STDMETHOD(put_MaxUserCount)(THIS_ LONG  lMaxUserCount) ;

#define DECLARE_IADsResource_METHODS \
    STDMETHOD(get_User)(THIS_ BSTR FAR* retval) ;\
    STDMETHOD(get_UserPath)(THIS_ BSTR FAR* retval);\
    STDMETHOD(get_Path)(THIS_ BSTR FAR* retval) ;\
    STDMETHOD(get_LockCount)(THIS_ long FAR* retval) ;

#define DECLARE_IADsClass_METHODS \
        STDMETHOD(get_PrimaryInterface)(THIS_ BSTR FAR* pbstrGUID) ;\
        STDMETHOD(get_CLSID)(THIS_ BSTR FAR* pbstrCLSID) ;\
        STDMETHOD(put_CLSID)(THIS_ BSTR bstrCLSID) ;\
        STDMETHOD(get_OID)(THIS_ BSTR FAR* pbstrOID) ;\
        STDMETHOD(put_OID)(THIS_ BSTR bstrOID) ;\
        STDMETHOD(get_Abstract)(THIS_ VARIANT_BOOL FAR* pfAbstract) ;\
        STDMETHOD(put_Abstract)(THIS_ VARIANT_BOOL fAbstract) ;\
        STDMETHOD(get_Auxiliary)(THIS_ VARIANT_BOOL FAR* pfAuxiliary) ;\
        STDMETHOD(put_Auxiliary)(THIS_ VARIANT_BOOL fAuxiliary) ;\
        STDMETHOD(get_MandatoryProperties)(THIS_ VARIANT FAR* pvMandatoryProperties) ;\
        STDMETHOD(put_MandatoryProperties)(THIS_ VARIANT vMandatoryProperties) ;\
        STDMETHOD(get_OptionalProperties)(THIS_ VARIANT FAR* pvOptionalProperties) ;\
        STDMETHOD(put_OptionalProperties)(THIS_ VARIANT vOptionalProperties) ;\
        STDMETHOD(get_NamingProperties)(THIS_ VARIANT FAR* pvNamingProperties);\
        STDMETHOD(put_NamingProperties)(THIS_ VARIANT vNamingProperties) ;\
        STDMETHOD(get_DerivedFrom)(THIS_ VARIANT FAR* pvDerivedFrom) ;\
        STDMETHOD(put_DerivedFrom)(THIS_ VARIANT vDerivedFrom) ;\
        STDMETHOD(get_AuxDerivedFrom)(THIS_ VARIANT FAR* pvAuxDerivedFrom) ;\
        STDMETHOD(put_AuxDerivedFrom)(THIS_ VARIANT vAuxDerivedFrom) ;\
        STDMETHOD(get_PossibleSuperiors)(THIS_ VARIANT FAR* pvPossSuperiors);\
        STDMETHOD(put_PossibleSuperiors)(THIS_ VARIANT vPossSuperiors) ;\
        STDMETHOD(get_Containment)(THIS_ VARIANT FAR* pvContainment);\
        STDMETHOD(put_Containment)(THIS_ VARIANT vContainment) ;\
        STDMETHOD(get_Container)(THIS_ VARIANT_BOOL FAR* pfContainer);\
        STDMETHOD(put_Container)(THIS_ VARIANT_BOOL fContainer) ;\
        STDMETHOD(get_HelpFileName)(THIS_ BSTR FAR* pbstrHelpfile) ;\
        STDMETHOD(put_HelpFileName)(THIS_ BSTR bstrHelpfile) ;\
        STDMETHOD(get_HelpFileContext)(THIS_ long FAR* plHelpContext) ;\
        STDMETHOD(put_HelpFileContext)(THIS_ long lHelpContext) ; \
        STDMETHOD(Qualifiers)(THIS_ IADsCollection FAR* FAR* ppQualifiers) ;

#define DECLARE_IADsProperty_METHODS \
        STDMETHOD(get_OID)(THIS_ BSTR FAR* pbstrOID) ;\
        STDMETHOD(put_OID)(THIS_ BSTR bstrOID) ;\
        STDMETHOD(get_Syntax)(THIS_ BSTR FAR* pbstrSyntax) ;\
        STDMETHOD(put_Syntax)(THIS_ BSTR bstrSyntax) ;\
        STDMETHOD(get_MaxRange)(THIS_ long FAR* plMaxRange) ;\
        STDMETHOD(put_MaxRange)(THIS_ long lMaxRange) ;\
        STDMETHOD(get_MinRange)(THIS_ long FAR* plMinRange) ;\
        STDMETHOD(put_MinRange)(THIS_ long lMinRange) ;\
        STDMETHOD(get_MultiValued)(THIS_ VARIANT_BOOL FAR* pfMultiValued) ;\
        STDMETHOD(put_MultiValued)(THIS_ VARIANT_BOOL fMultiValued) ;\
        STDMETHOD(Qualifiers)(THIS_ IADsCollection FAR* FAR* ppQualifiers) ;

#define DECLARE_IADsSyntax_METHODS \
        STDMETHOD(get_OleAutoDataType)(THIS_ long FAR* plOleAutoDataType) ;\
        STDMETHOD(put_OleAutoDataType)(THIS_ long lOleAutoDataType) ;

#define DECLARE_IADsLocality_METHODS \
        STDMETHOD(get_Description)(THIS_ BSTR FAR* retval) ;\
        STDMETHOD(put_Description)(THIS_ BSTR bstrDescription) ;\
        STDMETHOD(get_LocalityName)(THIS_ BSTR FAR* retval) ;\
        STDMETHOD(put_LocalityName)(THIS_ BSTR bstrLocalityName) ;\
        STDMETHOD(get_PostalAddress)(THIS_ BSTR FAR* retval) ;\
        STDMETHOD(put_PostalAddress)(THIS_ BSTR bstrPostalAddress) ;\
        STDMETHOD(get_SeeAlso)(THIS_ VARIANT FAR* retval) ;\
        STDMETHOD(put_SeeAlso)(THIS_ VARIANT vSeeAlso) ;

#define DECLARE_IADsO_METHODS \
        STDMETHOD(get_Description)(THIS_ BSTR FAR* retval) ;\
        STDMETHOD(put_Description)(THIS_ BSTR bstrDescription) ;\
        STDMETHOD(get_LocalityName)(THIS_ BSTR FAR* retval) ;\
        STDMETHOD(put_LocalityName)(THIS_ BSTR bstrLocalityName) ;\
        STDMETHOD(get_PostalAddress)(THIS_ BSTR FAR* retval) ;\
        STDMETHOD(put_PostalAddress)(THIS_ BSTR bstrPostalAddress) ;\
        STDMETHOD(get_TelephoneNumber)(THIS_ BSTR FAR* retval) ;\
        STDMETHOD(put_TelephoneNumber)(THIS_ BSTR bstrTelephoneNumber) ;\
        STDMETHOD(get_FaxNumber)(THIS_ BSTR FAR* retval) ;\
        STDMETHOD(put_FaxNumber)(THIS_ BSTR bstrFaxNumber) ;\
        STDMETHOD(get_SeeAlso)(THIS_ VARIANT FAR* retval) ;\
        STDMETHOD(put_SeeAlso)(THIS_ VARIANT vSeeAlso) ;

#define DECLARE_IADsOU_METHODS \
        STDMETHOD(get_Description)(THIS_ BSTR FAR* retval) ;\
        STDMETHOD(put_Description)(THIS_ BSTR bstrDescription) ;\
        STDMETHOD(get_LocalityName)(THIS_ BSTR FAR* retval) ;\
        STDMETHOD(put_LocalityName)(THIS_ BSTR bstrLocalityName) ;\
        STDMETHOD(get_PostalAddress)(THIS_ BSTR FAR* retval) ;\
        STDMETHOD(put_PostalAddress)(THIS_ BSTR bstrPostalAddress) ;\
        STDMETHOD(get_TelephoneNumber)(THIS_ BSTR FAR* retval) ;\
        STDMETHOD(put_TelephoneNumber)(THIS_ BSTR bstrTelephoneNumber) ;\
        STDMETHOD(get_FaxNumber)(THIS_ BSTR FAR* retval) ;\
        STDMETHOD(put_FaxNumber)(THIS_ BSTR bstrFaxNumber) ;\
        STDMETHOD(get_SeeAlso)(THIS_ VARIANT FAR* retval) ;\
        STDMETHOD(put_SeeAlso)(THIS_ VARIANT vSeeAlso) ;\
        STDMETHOD(get_BusinessCategory)(THIS_ BSTR FAR* retval) ;\
        STDMETHOD(put_BusinessCategory)(THIS_ BSTR bstrBusinessCategory) ;

#define DECLARE_IDSNamespace_METHODS \
        STDMETHOD(OpenDSObject)(     \
            THIS_ LPWSTR lpszDNName, \
            LPWSTR lpszUserName,     \
            LPWSTR lpszPassword,     \
            REFIID riid,             \
            void * * ppADsObj      \
            );

#endif


LIBMAIN.CPP

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    LibMain.cpp

Abstract:

    LibMain for ADsSmp.dll

Author:

Environment:

    User mode

Revision History :

--*/
#include "adssmp.h"
#pragma hdrstop

//  Globals
HINSTANCE g_hInst = NULL;

ULONG g_ulObjCount = 0L;        // Number of objects alive in adssmp.dll
ULONG g_ulLockCount = 0L;       // Number of server locks on adssmp.dll

//+------------------------------------------------------------------------
//
//  Macro that calculates the number of elements in a statically-defined
//  array.
//-------------------------------------------------------------------------
#define ARRAY_SIZE(_a)  (sizeof(_a) / sizeof(_a[0]))

//+------------------------------------------------------------------------
// Create static Namespace and Provider class factory objects
//-------------------------------------------------------------------------
CSampleDSProviderCF g_cfProvider;
CSampleDSNamespaceCF g_cfNamespace;

//+------------------------------------------------------------------------
//
//  ads class factory lookup table
//
//-------------------------------------------------------------------------

struct CLSTABLE
{
    const CLSID *   pclsid;
    IClassFactory * pCF;
};

CLSTABLE g_aclscache[] =
{
    &CLSID_SampleDSProvider,                      &g_cfProvider,
    &CLSID_SampleDSNamespace,                     &g_cfNamespace,
};

//+---------------------------------------------------------------
//
//  Function:   DllGetClassObject
//
//  Synopsis:   Standard DLL entrypoint for locating class factories
//
//----------------------------------------------------------------

STDAPI
DllGetClassObject(REFCLSID clsid, REFIID iid, LPVOID FAR* ppv)
{
    HRESULT         hr;
    size_t          i;

    for (i = 0; i < ARRAY_SIZE(g_aclscache); i++)
    {
        if (IsEqualCLSID(clsid, *g_aclscache[i].pclsid))
        {
            hr = g_aclscache[i].pCF->QueryInterface(iid, ppv);
            RRETURN(hr);
        }
    }

    *ppv = NULL;

    return E_NOINTERFACE;
}

//+---------------------------------------------------------------
//
//  Function:   DllCanUnloadNow
//
//  Synopsis:   Standard DLL entrypoint to determine if DLL can be unloaded
//
//---------------------------------------------------------------

STDAPI DllCanUnloadNow(void) {
    if (0L==g_ulObjCount && 0L==g_ulLockCount) return ResultFromScode(S_OK);
    else return ResultFromScode(S_FALSE);
}

//+---------------------------------------------------------------
//
//  Function:   DllMain
//
//  Synopsis:   Standard DLL initialization entrypoint
//
//---------------------------------------------------------------

BOOL WINAPI
LibMain(HINSTANCE hInst, DWORD dwReason, LPVOID pvReserved)
{

    // Check our compatibility with the OLE runtime.
    // We are compatible with any later major version,
    // or the same major version with equal or greater minor version.
    if (!IsCompatibleOleVersion(OLE_MAJ_VER, OLE_MIN_VER))
    {
        return FALSE;
    }

    switch (dwReason){
    case DLL_PROCESS_ATTACH:

        g_hInst = hInst;
        break;

    case DLL_PROCESS_DETACH:

    default:
        break;
    }

    return TRUE;
}

BOOL
IsCompatibleOleVersion(WORD wMaj, WORD wMin)
{
    // Check our compatibility with the OLE runtime.
    // We are compatible with any later major version,
    // or the same major version with equal or greater minor version.
    DWORD ov = OleBuildVersion();
    return HIWORD(ov) > wMaj || (HIWORD(ov) == wMaj && LOWORD(ov) >= wMin);
}

//+---------------------------------------------------------------------------
//
//  Function:   DllMain
//
//  Synopsis:   entry point for NT - post .546
//
//----------------------------------------------------------------------------
EXTERN_C BOOL WINAPI
DllMain(HANDLE hDll, DWORD dwReason, LPVOID lpReserved)
{
    return LibMain((HINSTANCE)hDll, dwReason, lpReserved);
}


LIBMAIN.H

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    LibMain.h

Abstract:

Author:

Environment:

    User mode

Revision History :

--*/

#ifndef _LIBMAIN_H_
#define _LIBMAIN_H_

//+------------------------------------------------------------------------
//
//  Globals in DLL
//
//-------------------------------------------------------------------------

extern HINSTANCE g_hInst;           // Instance of dll
extern CRITICAL_SECTION g_csMem;    // for MemAlloc

//
// Functions to manipulate object count variable g_ulObjCount.  This variable
// is used in the implementation of DllCanUnloadNow.
// NOTE: Please leave the externs within the functions so that it is not
// visible outside the dll project.
//

inline void
INC_OBJECT_COUNT(void)
{
    extern ULONG g_ulObjCount;
    g_ulObjCount++;
}

inline void
DEC_OBJECT_COUNT(void)
{
    extern ULONG g_ulObjCount;
    ADsAssert(g_ulObjCount > 0);
    g_ulObjCount--;
}

inline ULONG
GET_OBJECT_COUNT(void)
{
    extern ULONG g_ulObjCount;
    return g_ulObjCount;
}

#endif // #ifndef _LIBMAIN_H_


MACRO.H

#define BAIL_IF_ERROR(hr) \
        if (FAILED(hr)) {       \
                goto cleanup;   \
        }\

#define BAIL_ON_FAILURE(hr) \
        if (FAILED(hr)) {       \
                goto error;   \
        }\

#define CONTINUE_ON_FAILURE(hr) \
        if (FAILED(hr)) {       \
                continue;   \
        }\

#define BAIL_ON_SUCCESS(hr) \
        if (SUCCEEDED(hr)) {       \
                goto error;   \
        }\

#define QUERY_INTERFACE(hr, ptr, iid, ppObj) \
        hr = ptr->QueryInterface(iid, (void **)ppObj); \
        if (FAILED(hr)) {    \
                goto cleanup;\
        }\

#define RRETURN_ENUM_STATUS(hr) \
        if (hr != S_OK) {       \
            RRETURN(S_FALSE);   \
        }                       \
        RRETURN(S_OK);

#define DEFINE_IDispatch_Implementation_Unimplemented(cls) \
STDMETHODIMP                                                          \
cls::GetTypeInfoCount(unsigned int FAR* pctinfo)        \
{                                                                     \
    RRETURN(E_NOTIMPL);                                               \
}                                                                     \
                                                                      \
STDMETHODIMP                                                          \
cls::GetTypeInfo(unsigned int itinfo, LCID lcid,        \
        ITypeInfo FAR* FAR* pptinfo)                                  \
{                                                                     \
    RRETURN(E_NOTIMPL);                                               \
}                                                                     \
                                                                      \
STDMETHODIMP                                                          \
cls::GetIDsOfNames(REFIID iid, LPWSTR FAR* rgszNames,   \
        unsigned int cNames, LCID lcid, DISPID FAR* rgdispid)         \
{                                                                     \
    RRETURN(E_NOTIMPL);                                               \
}                                                                     \
                                                                      \
STDMETHODIMP                                                          \
cls::Invoke(DISPID dispidMember, REFIID iid, LCID lcid, \
        unsigned short wFlags, DISPPARAMS FAR* pdispparams,           \
        VARIANT FAR* pvarResult, EXCEPINFO FAR* pexcepinfo,           \
        unsigned int FAR* puArgErr)                                   \
{                                                                     \
    RRETURN(E_NOTIMPL);                                               \
}

#define DEFINE_IDispatch_Implementation(cls)                          \
STDMETHODIMP                                                          \
cls::GetTypeInfoCount(unsigned int FAR* pctinfo)                      \
{                                                                     \
        RRETURN(_pDispMgr->GetTypeInfoCount(pctinfo));                \
}                                                                     \
                                                                      \
STDMETHODIMP                                                          \
cls::GetTypeInfo(unsigned int itinfo, LCID lcid,                      \
        ITypeInfo FAR* FAR* pptinfo)                                  \
{                                                                     \
        RRETURN(_pDispMgr->GetTypeInfo(itinfo,                        \
                                       lcid,                          \
                                       pptinfo                        \
                                       ));                            \
}                                                                     \
STDMETHODIMP                                                          \
cls::GetIDsOfNames(REFIID iid, LPWSTR FAR* rgszNames,                 \
        unsigned int cNames, LCID lcid, DISPID FAR* rgdispid)         \
{                                                                     \
        RRETURN(_pDispMgr->GetIDsOfNames(iid,                         \
                                         rgszNames,                   \
                                         cNames,                      \
                                         lcid,                        \
                                         rgdispid                     \
                                         ));                          \
}                                                                     \
                                                                      \
STDMETHODIMP                                                          \
cls::Invoke(DISPID dispidMember, REFIID iid, LCID lcid,               \
        unsigned short wFlags, DISPPARAMS FAR* pdispparams,           \
        VARIANT FAR* pvarResult, EXCEPINFO FAR* pexcepinfo,           \
        unsigned int FAR* puArgErr)                                   \
{                                                                     \
        RRETURN (_pDispMgr->Invoke(dispidMember,                      \
                                   iid,                               \
                                   lcid,                              \
                                   wFlags,                            \
                                   pdispparams,                       \
                                   pvarResult,                        \
                                   pexcepinfo,                        \
                                   puArgErr                           \
                                   ));                                \
}

#define DEFINE_IADs_Implementation(cls)                             \
STDMETHODIMP                                                          \
cls::get_Name(THIS_ BSTR FAR* retval)                                 \
{                                                                     \
    RRETURN(get_CoreName(retval));                                    \
}                                                                     \
                                                                      \
STDMETHODIMP                                                          \
cls::get_ADsPath(THIS_ BSTR FAR* retval)                            \
{                                                                     \
                                                                      \
    RRETURN(get_CoreADsPath(retval));                               \
}                                                                     \
                                                                      \
STDMETHODIMP                                                          \
cls::get_Class(THIS_ BSTR FAR* retval)                                \
{                                                                     \
                                                                      \
    RRETURN(get_CoreADsClass(retval));                              \
}                                                                     \
                                                                      \
                                                                      \
STDMETHODIMP                                                          \
cls::get_Parent(THIS_ BSTR FAR* retval)                               \
{                                                                     \
    RRETURN(get_CoreParent(retval));                                  \
}                                                                     \
                                                                      \
STDMETHODIMP                                                          \
cls::get_Schema(THIS_ BSTR FAR* retval)                               \
{                                                                     \
    RRETURN(get_CoreSchema(retval));                                  \
}                                                                     \
                                                                      \
STDMETHODIMP                                                          \
cls::get_GUID(THIS_ BSTR FAR* retval)                                 \
{                                                                     \
    RRETURN(get_CoreGUID(retval));                                    \
}                                                                     \
                                                                      \
STDMETHODIMP                                                          \
cls::GetEx(THIS_ BSTR bstrName, VARIANT FAR* pvProp)                  \
{                                                                     \
    RRETURN(E_NOTIMPL);                                               \
}                                                                     \
                                                                      \
STDMETHODIMP                                                          \
cls::PutEx(THIS_ long lnControlCode, BSTR bstrName, VARIANT vProp)    \
{                                                                     \
    RRETURN(E_NOTIMPL);                                               \
}                                                                     \
STDMETHODIMP                                                          \
cls::GetInfoEx(THIS_ VARIANT vProperties, long lnReserved)            \
{                                                                     \
    RRETURN(E_NOTIMPL);                                               \
}


MEMORY.CPP

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    Memory.cpp

Abstract:

    This module provides all the memory management functions for all Sample
    provider components

Author:

Environment:

    User mode

Revision History :

--*/
#include "adssmp.h"

#define WORD_ALIGN_DOWN(addr) ((LPBYTE)((DWORD)addr &= ~1))
#define DWORD_ALIGN_UP(size) ((size+3)&~3)

LPVOID
AllocProvMem(
    DWORD cb
)
{
    LPDWORD  pMem;
    DWORD    cbNew ;

#if DBG
    cb = DWORD_ALIGN_UP(cb);

    cbNew = cb + (sizeof(DWORD) + sizeof(LONGLONG));
#else
    cbNew = cb ;
#endif

    pMem=(LPDWORD)LocalAlloc(LPTR, cbNew);

#if DBG

    if (!pMem) {
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        return 0;
    }

    *pMem=cb;
    *(LPDWORD)((LPBYTE)pMem+cbNew-sizeof(DWORD))=0xdeadfeed;
    pMem = (LPDWORD) (((LPBYTE)pMem)+sizeof(LONGLONG));
#endif

    return (LPVOID)(pMem);
}

BOOL
FreeProvMem(
   LPVOID pMem
)
{
    LPBYTE   pNewMem;

    pNewMem = (LPBYTE)pMem;

#if DBG
    DWORD    cb;
    DWORD    cbNew = 0;

    pNewMem -= sizeof(LONGLONG) ;

    cb = *((LPDWORD)pNewMem);
    cbNew = cb + sizeof(DWORD) + sizeof(LONGLONG);

    if (*(LPDWORD)(pNewMem + cbNew - sizeof(DWORD)) != 0xdeadbeef) {
        ADsAssert(!"Freeing memory not allocated by AllocProvMem") ;
        return FALSE;
    }

    // fill freed memory with known pattern
    memset(pMem, 0x5C, cb);

#endif

    return(LocalFree((LPVOID)pNewMem) == NULL);
}

LPVOID
ReallocProvMem(
   LPVOID pOldMem,
   DWORD cbOld,
   DWORD cbNew
)
{
    LPVOID pNewMem;

    pNewMem=AllocProvMem(cbNew);

    if (pOldMem && pNewMem) {
        memcpy(pNewMem, pOldMem, min(cbNew, cbOld));
        FreeProvMem(pOldMem);
    }

    return pNewMem;
}

LPWSTR
AllocProvStr(
    LPWSTR pStr
)
{
   LPWSTR pMem;

   if (!pStr)
      return 0;

   if (pMem = (LPWSTR)AllocProvMem( wcslen(pStr)*sizeof(WCHAR) + sizeof(WCHAR) ))
      wcscpy(pMem, pStr);

   return pMem;
}

BOOL
FreeProvStr(
   LPWSTR pStr
)
{
   return pStr ? FreeProvMem(pStr)
               : FALSE;
}

BOOL
ReallocProvStr(
   LPWSTR *ppStr,
   LPWSTR pStr
)
{
   FreeProvStr(*ppStr);
   *ppStr=AllocProvStr(pStr);

   return TRUE;
}

STDAPI
ProvAllocString(const OLECHAR * pch, BSTR * pBSTR)
{
    HRESULT hr = S_OK;

    ADsAssert(pBSTR);
    if (!pch)
    {
        *pBSTR = NULL;
        return S_OK;
    }

    *pBSTR = SysAllocString(pch);
    hr = (*pBSTR) ? S_OK : E_OUTOFMEMORY;
    RRETURN(hr);
}


MEMORY.H

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    Memory.h

Abstract:

Author:

Environment:

    User mode

Revision History :

--*/
#ifndef _MEMORY_H_
#define _MEMORY_H_

LPVOID
AllocProvMem(
    DWORD cb
);

BOOL
FreeProvMem(
   LPVOID pMem
);

LPVOID
ReallocProvMem(
   LPVOID pOldMem,
   DWORD cbOld,
   DWORD cbNew
);

LPWSTR
AllocProvStr(
    LPWSTR pStr
);

BOOL
FreeProvStr(
   LPWSTR pStr
);

BOOL
ReallocProvStr(
   LPWSTR *ppStr,
   LPWSTR pStr
);

//+---------------------------------------------------------------------------
//
//  Function:   ADsFreeString
//
//  Synopsis:   Frees a BSTR.
//
//
//----------------------------------------------------------------------------

inline void
ProvFreeString(BSTR bstr)
{
    SysFreeString(bstr);
}

STDAPI ProvAllocString(const OLECHAR * pch, BSTR * pBSTR);

#endif


OBJECT.CPP

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    Object.cpp

Abstract:

    ObjectTypeList class

Author:

Environment:

    User mode

Revision History :

--*/
#include "adssmp.h"
#pragma hdrstop

ObjectTypeList::ObjectTypeList()
{
    _pObjList = NULL;
    _dwCurrentIndex = 0;
    _dwMaxElements = 0;
    _dwUBound  = 0;
    _dwLBound = 0;

}

HRESULT
ObjectTypeList::CreateObjectTypeList(
    VARIANT vFilter,
    ObjectTypeList ** ppObjectTypeList
){
    ObjectTypeList * pObjectTypeList = NULL;
    HRESULT hr = S_OK;

    pObjectTypeList = new ObjectTypeList;

    if (!pObjectTypeList) {
        hr = E_OUTOFMEMORY;
        BAIL_ON_FAILURE(hr);
    }

    hr = BuildObjectArray(
            vFilter,
            &pObjectTypeList->_pObjList,
            &pObjectTypeList->_dwMaxElements
            );

    if (FAILED(hr)) {

        hr = BuildDefaultObjectArray(
                gpFilters,
                gdwMaxFilters,
                &pObjectTypeList->_pObjList,
                &pObjectTypeList->_dwMaxElements
                );

        BAIL_ON_FAILURE(hr);
    }

    hr = SafeArrayGetUBound(
                pObjectTypeList->_pObjList,
                1,
                (long FAR *)&pObjectTypeList->_dwUBound
                );
    BAIL_ON_FAILURE(hr);

    hr = SafeArrayGetLBound(
                pObjectTypeList->_pObjList,
                1,
                (long FAR *)&pObjectTypeList->_dwLBound
                );
    BAIL_ON_FAILURE(hr);

    pObjectTypeList->_dwCurrentIndex = pObjectTypeList->_dwLBound;

    *ppObjectTypeList = pObjectTypeList;

    RRETURN(S_OK);

error:
    if (pObjectTypeList) {
        delete pObjectTypeList;
    }
    RRETURN(hr);

}

ObjectTypeList::~ObjectTypeList()
{
    HRESULT hr = S_OK;
    if (_pObjList) {
        hr = SafeArrayDestroy(_pObjList);
    }
}

HRESULT
ObjectTypeList::GetCurrentObject(PDWORD pdwObject)
{
    HRESULT hr = S_OK;

    if (_dwCurrentIndex > _dwUBound) {
        return(E_FAIL);
    }

    hr = SafeArrayGetElement(
                    _pObjList,
                    (long FAR *)&_dwCurrentIndex,
                    (void *)pdwObject
                    );
    RRETURN(hr);
}

HRESULT
ObjectTypeList::Next()
{
    HRESULT hr = S_OK;

    _dwCurrentIndex++;

    if (_dwCurrentIndex > _dwUBound) {
        return(E_FAIL);
    }

    return(hr);
}

HRESULT
ObjectTypeList::Reset()
{
    HRESULT hr = S_OK;

    return(hr);

}

HRESULT
IsValidFilter(
    LPWSTR ObjectName,
    DWORD *pdwFilterId,
    PFILTERS pFilters,
    DWORD dwMaxFilters
){

    DWORD i = 0;

    for (i = 0; i < dwMaxFilters; i++) {

        if (!_wcsicmp(ObjectName, (pFilters + i)->szObjectName)) {
            *pdwFilterId = (pFilters + i)->dwFilterId;
            RRETURN(S_OK);
        }

    }
    *pdwFilterId = 0;
    RRETURN(E_FAIL);
}

HRESULT
BuildDefaultObjectArray(
    PFILTERS  pFilters,
    DWORD dwMaxFilters,
    SAFEARRAY ** ppFilter,
    DWORD * pdwNumElements
){
    DWORD i;
    HRESULT hr = S_OK;
    SAFEARRAYBOUND sabNewArray;
    SAFEARRAY * pFilter = NULL;

    sabNewArray.cElements = dwMaxFilters;
    sabNewArray.lLbound =  0;

    pFilter =   SafeArrayCreate(
                        VT_I4,
                        1,
                        &sabNewArray
                        );
    if (!pFilter){
        hr = E_OUTOFMEMORY;
        BAIL_ON_FAILURE(hr);
    }

    for (i = 0; i < dwMaxFilters; i++) {

        hr = SafeArrayPutElement(
                pFilter,
                (long *)&i,
                (void *)&((pFilters + i)->dwFilterId)
            );
        BAIL_ON_FAILURE(hr);
    }

    *ppFilter = pFilter;
    *pdwNumElements = dwMaxFilters;

    RRETURN(S_OK);

error:
    if (pFilter) {
        SafeArrayDestroy(pFilter);
    }

    *ppFilter = NULL;
    *pdwNumElements = 0;
    RRETURN(hr);
}

HRESULT
BuildObjectArray(
    VARIANT var,
    SAFEARRAY ** ppFilter,
    DWORD * pdwNumElements
){
    LONG uDestCount = 0;
    LONG dwSLBound = 0;
    LONG dwSUBound = 0;
    VARIANT v;
    LONG i;
    HRESULT hr = S_OK;
    SAFEARRAYBOUND sabNewArray;
    DWORD dwFilterId;
    SAFEARRAY * pFilter = NULL;

    if(!((V_VT(&var) &  VT_VARIANT) &&  V_ISARRAY(&var))) {
        RRETURN(E_FAIL);
    }

    //
    // Check that there is only one dimension in this array
    //

    if ((V_ARRAY(&var))->cDims != 1) {
        hr = E_FAIL;
        BAIL_ON_FAILURE(hr);
    }
    //
    // Check that there is atleast one element in this array
    //

    if ((V_ARRAY(&var))->rgsabound[0].cElements == 0){
        hr = E_FAIL;
        BAIL_ON_FAILURE(hr);
    }

    //
    // We know that this is a valid single dimension array
    //

    hr = SafeArrayGetLBound(V_ARRAY(&var),
                            1,
                            (long FAR *)&dwSLBound
                            );
    BAIL_ON_FAILURE(hr);

    hr = SafeArrayGetUBound(V_ARRAY(&var),
                            1,
                            (long FAR *)&dwSUBound
                            );
    BAIL_ON_FAILURE(hr);

    sabNewArray.cElements = dwSUBound - dwSLBound + 1;
    sabNewArray.lLbound = dwSLBound;

    pFilter = SafeArrayCreate(
                    VT_I4,
                    1,
                    &sabNewArray
                    );

    if (!pFilter) {
        hr = E_OUTOFMEMORY;
        BAIL_ON_FAILURE(hr);
    }

    for (i = dwSLBound; i <= dwSUBound; i++) {
        VariantInit(&v);
        hr = SafeArrayGetElement(V_ARRAY(&var),
                                (long FAR *)&i,
                                &v
                                );
        if (FAILED(hr)) {
            continue;
        }

        hr = IsValidFilter(
                V_BSTR(&v),
                &dwFilterId,
                gpFilters,
                gdwMaxFilters
                );

        if (FAILED(hr)) {
            continue;
        }

        hr = SafeArrayPutElement(
                pFilter,
                (long*)&uDestCount,
                (void *)&dwFilterId
                );

        if(FAILED(hr)){
            continue;
        }

        uDestCount++;

    }

    //
    // There was nothing of value that could be retrieved from the
    // filter.
    //

    if (!uDestCount ) {
        hr = E_FAIL;
        BAIL_ON_FAILURE(hr);
    }

    *pdwNumElements  = uDestCount;
    *ppFilter = pFilter;

    RRETURN(S_OK);

error:

    if (pFilter) {

        SafeArrayDestroy(pFilter);
    }
    *ppFilter = NULL;
    *pdwNumElements = 0;
    RRETURN(hr);
}


OBJECT.H

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    Object.h

Abstract:

Author:

Environment:

    User mode

Revision History :

--*/
#ifndef _OBJECT_H_
#define _OBJECT_H_

class FAR ObjectTypeList
{
private:
   SAFEARRAY FAR * _pObjList;
   DWORD    _dwCurrentIndex;
   DWORD    _dwLBound;
   DWORD    _dwUBound;
   DWORD    _dwMaxElements;

public:
    ObjectTypeList();

    static
    HRESULT
    ObjectTypeList::CreateObjectTypeList(
        VARIANT vFilter,
        ObjectTypeList ** ppObjectTypeList
        );

    ~ObjectTypeList();

    HRESULT
    GetCurrentObject(
        PDWORD pdwObject
        );

    HRESULT
    Next();

    HRESULT
    Reset();

};

HRESULT
BuildObjectArray(
    VARIANT var,
    SAFEARRAY ** ppFilter,
    DWORD * pdwNumElements
    );

HRESULT
BuildDefaultObjectArray(
    PFILTERS  pFilters,
    DWORD dwMaxFilters,
    SAFEARRAY ** ppFilter,
    DWORD * pdwNumElements
    );

HRESULT
IsValidFilter(
    LPWSTR ObjectName,
    DWORD *pdwFilterId,
    PFILTERS pFilters,
    DWORD dwMaxFilters
    );

#endif


PACK.CPP

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    Pack.cpp

Abstract:

Author:

Environment:

    User mode

Revision History :

--*/
#include "adssmp.h"
#pragma hdrstop

HRESULT
PackStringinVariant(
    BSTR bstrString,
    VARIANT * pvarInputData
    )
{
    HRESULT hr = S_OK;

    if ( bstrString == NULL )
        RRETURN(E_FAIL);

    VariantInit(pvarInputData);

    pvarInputData->vt = VT_BSTR;

    if (!bstrString) {
        V_BSTR(pvarInputData) = NULL;
        RRETURN(S_OK);
    }

    hr = ProvAllocString(bstrString, &(V_BSTR(pvarInputData)));

    RRETURN(hr);
}

HRESULT
UnpackStringfromVariant(
    VARIANT varSrcData,
    BSTR * pbstrDestString
    )
{
    HRESULT hr = S_OK;

    if( varSrcData.vt != VT_BSTR){
        RRETURN(E_ADS_CANT_CONVERT_DATATYPE);
    }

    if (!V_BSTR(&varSrcData)) {
        *pbstrDestString = NULL;
        RRETURN(S_OK);
    }

    hr = ProvAllocString(V_BSTR(&varSrcData), pbstrDestString);

    RRETURN(hr);
}

HRESULT
PackLONGinVariant(
    LONG  lValue,
    VARIANT * pvarInputData
    )
{
    VariantInit(pvarInputData);

    pvarInputData->vt = VT_I4;
    V_I4(pvarInputData) = lValue;

    RRETURN(S_OK);
}

HRESULT
UnpackLONGfromVariant(
    VARIANT varSrcData,
    LONG * plValue
    )
{
    if( varSrcData.vt != VT_I4){
        RRETURN(E_ADS_CANT_CONVERT_DATATYPE);
    }

    *plValue = V_I4(&varSrcData);

    RRETURN(S_OK);
}

HRESULT
PackDATEinVariant(
    DATE  daValue,
    VARIANT * pvarInputData
    )
{
    VariantInit(pvarInputData);

    pvarInputData->vt = VT_DATE;
    V_DATE(pvarInputData) = daValue;

    RRETURN(S_OK);
}

HRESULT
UnpackDATEfromVariant(
    VARIANT varSrcData,
    DATE * pdaValue
    )
{
    if( varSrcData.vt != VT_DATE){
        RRETURN(E_ADS_CANT_CONVERT_DATATYPE);
    }

    *pdaValue = V_DATE(&varSrcData);

    RRETURN(S_OK);
}

HRESULT
PackVARIANT_BOOLinVariant(
    VARIANT_BOOL  fValue,
    VARIANT * pvarInputData
    )
{
    VariantInit(pvarInputData);

    pvarInputData->vt = VT_BOOL;
    V_BOOL(pvarInputData) = fValue;

    RRETURN(S_OK);
}

HRESULT
UnpackVARIANT_BOOLfromVariant(
    VARIANT varSrcData,
    VARIANT_BOOL * pfValue
    )
{
    if( varSrcData.vt != VT_BOOL){
        RRETURN(E_ADS_CANT_CONVERT_DATATYPE);
    }

    *pfValue = V_BOOL(&varSrcData);

    RRETURN(S_OK);
}

HRESULT
PackVARIANTinVariant(
    VARIANT vaValue,
    VARIANT * pvarInputData
    )
{
    VariantInit(pvarInputData);

    pvarInputData->vt = VT_VARIANT;
    RRETURN( VariantCopy( pvarInputData, &vaValue ));
}

HRESULT
UnpackVARIANTfromVariant(
    VARIANT varSrcData,
    VARIANT * pvaValue
    )
{
    VariantInit( pvaValue );

    RRETURN( VariantCopy( pvaValue, &varSrcData ));
}


PACK.H

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    Pack.h

Abstract:

Author:

Environment:

    User mode

Revision History :

--*/
#ifndef _PACK_H_
#define _PACK_H_

HRESULT
PackStringinVariant(
    BSTR bstrString,
    VARIANT * pvarInputData
    );

HRESULT
UnpackStringfromVariant(
    VARIANT varSrcData,
    BSTR * pbstrDestString
    );

HRESULT
PackLONGinVariant(
    LONG  lValue,
    VARIANT * pvarInputData
    );

HRESULT
UnpackLONGfromVariant(
    VARIANT varSrcData,
    LONG * plValue
    );

HRESULT
PackDATEinVariant(
    DATE  daValue,
    VARIANT * pvarInputData
    );

HRESULT
UnpackDATEfromVariant(
    VARIANT varSrcData,
    DATE * pdaValue
    );

HRESULT
PackVARIANT_BOOLinVariant(
    VARIANT_BOOL  fValue,
    VARIANT * pvarInputData
    );

HRESULT
UnpackVARIANT_BOOLfromVariant(
    VARIANT varSrcData,
    VARIANT_BOOL * pfValue
    );

HRESULT
PackVARIANTinVariant(
    VARIANT  vaValue,
    VARIANT * pvarInputData
    );

HRESULT
UnpackVARIANTfromVariant(
    VARIANT varSrcData,
    VARIANT * pvaValue
    );

#endif


PARSE.CPP

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    Parse.cpp

Abstract:

    DS Pathname Parser

    The Pathname Parser is a key component in ADs providers. It checks for
    syntactic validity of an ADs pathname that has been passed to this
    provider. If the syntax is valid, then an OBJECTINFO structure is
    constructed. This OBJECTINFO structure contains a componentized version
    of the ADs pathname for this object.

    Note all that is being done is a syntax check. Rather than special-case
    every single new nuance to pathnames, all path checking must conform to
    the grammar rules laid out by the parser.

Author:

Environment:

    User mode

Revision History :

--*/
#include "adssmp.h"
#pragma hdrstop

//+---------------------------------------------------------------------------
//  Function:   ADsObject
//
//  Synopsis:   parses an ADs pathname passed to this provider. This function
//              parses the following grammar rules
//
//              <ADsObject> -> <ProviderName> <SampleDSObject>
//
//
//  Arguments:  [CLexer * pTokenizer] - a lexical analyzer object
//              [POBJECTINFO pObjectInfo] - a pointer to an OBJECTINFO structure
//
//  Returns:    [HRESULT] 0 if successful, error HRESULT if not
//
//  Modifies:   pTokenizer (consumes the input buffer)
//
//----------------------------------------------------------------------------
HRESULT
ADsObject(CLexer * pTokenizer, POBJECTINFO pObjectInfo)
{
    WCHAR szToken[MAX_TOKEN_LENGTH];
    DWORD dwToken;
    HRESULT hr;

    hr = ProviderName(pTokenizer, pObjectInfo);
    BAIL_IF_ERROR(hr);

    hr = pTokenizer->GetNextToken(szToken, &dwToken);
    BAIL_IF_ERROR(hr);

    switch (dwToken) {

    case TOKEN_END:
        RRETURN(S_OK);

    case TOKEN_COMMA:
        hr = Type(pTokenizer, pObjectInfo);
        BAIL_IF_ERROR(hr);
        hr = pTokenizer->GetNextToken(szToken, &dwToken);
        BAIL_IF_ERROR(hr);
        if (dwToken == TOKEN_END) {
            RRETURN(S_OK);
        }else {
            RRETURN(E_ADS_BAD_PATHNAME);
        }

    default:
        hr = pTokenizer->PushBackToken();

        hr = SampleDSObject(pTokenizer, pObjectInfo);
        BAIL_IF_ERROR(hr);

        hr = pTokenizer->GetNextToken(szToken, &dwToken);
        BAIL_IF_ERROR(hr);

        switch (dwToken) {
        case TOKEN_END:
            RRETURN(S_OK);

        case TOKEN_COMMA:
            hr = Type(pTokenizer, pObjectInfo);
            BAIL_IF_ERROR(hr);
            hr = pTokenizer->GetNextToken(szToken, &dwToken);
            BAIL_IF_ERROR(hr);
            if (dwToken == TOKEN_END) {
                RRETURN(S_OK);
            }else {
                RRETURN(E_ADS_BAD_PATHNAME);
            }

        default:
            RRETURN(E_FAIL);

        }
    }

cleanup:
    RRETURN(hr);

}

//+---------------------------------------------------------------------------
//  Function:   SampleDSObject
//
//  Synopsis:   parses an ADs pathname passed to this provider. This function
//              parses the following grammar rules
//
//              <SampleDSObject> -> "\\""identifier""\" <SampleDSObject>
//
//
//  Arguments:  [CLexer * pTokenizer] - a lexical analyzer object
//              [POBJECTINFO pObjectInfo] - a pointer to an OBJECTINFO structure
//
//  Returns:    [HRESULT] 0 if successful, error HRESULT if not
//
//  Modifies:   pTokenizer (consumes the input buffer)
//
//  History:    11-3-95   krishnag     Created.
//
//----------------------------------------------------------------------------
HRESULT
SampleDSObject(CLexer * pTokenizer, POBJECTINFO pObjectInfo)
{
    WCHAR szToken[MAX_TOKEN_LENGTH];
    DWORD dwToken;
    HRESULT hr;

    hr = pTokenizer->GetNextToken(szToken, &dwToken);
    BAIL_IF_ERROR(hr);
    if ((dwToken != TOKEN_FSLASH) &&  (dwToken != TOKEN_BSLASH)) {
        RRETURN(E_ADS_BAD_PATHNAME);
    }

    hr = pTokenizer->GetNextToken(szToken, &dwToken);
    BAIL_IF_ERROR(hr);
    if ((dwToken != TOKEN_FSLASH) && (dwToken != TOKEN_BSLASH)) {
        RRETURN(E_ADS_BAD_PATHNAME);
    }

    hr = pTokenizer->GetNextToken(szToken, &dwToken);
    BAIL_IF_ERROR(hr);
    if (dwToken != TOKEN_IDENTIFIER) {
        RRETURN(E_ADS_BAD_PATHNAME);
    }

    hr = AddRootRDN(pObjectInfo, szToken);
    BAIL_IF_ERROR(hr);

    hr = pTokenizer->GetNextToken(szToken, &dwToken);
    BAIL_IF_ERROR(hr);

    //
    // If we get an TOKEN_END, then we have a tree name only \\<tree_name>
    //

    if (dwToken == TOKEN_END || dwToken == TOKEN_COMMA) {
        hr = pTokenizer->PushBackToken();
        RRETURN(S_OK);
    }

    if ((dwToken != TOKEN_BSLASH) && (dwToken != TOKEN_FSLASH)) {
        RRETURN(E_ADS_BAD_PATHNAME);
    }

    hr = PathName(pTokenizer, pObjectInfo);
    BAIL_IF_ERROR(hr);

cleanup:
    RRETURN(hr);
}

HRESULT
ProviderName(CLexer * pTokenizer, POBJECTINFO pObjectInfo)
{
    WCHAR szToken[MAX_TOKEN_LENGTH];
    DWORD dwToken;
    HRESULT hr;

    hr = pTokenizer->GetNextToken(szToken, &dwToken);
    BAIL_IF_ERROR(hr);

    if (dwToken == TOKEN_ATSIGN) {

        hr = pTokenizer->GetNextToken(szToken, &dwToken);
        BAIL_IF_ERROR(hr);

        if (dwToken != TOKEN_IDENTIFIER) {
            RRETURN(E_ADS_BAD_PATHNAME);
        }

        hr = AddProviderName(pObjectInfo, szToken);

        hr = pTokenizer->GetNextToken(szToken, &dwToken);
        BAIL_IF_ERROR(hr);

        if (dwToken != TOKEN_EXCLAMATION) {
            RRETURN(E_ADS_BAD_PATHNAME);
        }

    }else if (dwToken == TOKEN_IDENTIFIER) {

        hr = AddProviderName(pObjectInfo, szToken);

        hr = pTokenizer->GetNextToken(szToken, &dwToken);
        BAIL_IF_ERROR(hr);

        if (dwToken != TOKEN_COLON) {
            RRETURN(E_ADS_BAD_PATHNAME);
        }

    }else {
        RRETURN(E_ADS_BAD_PATHNAME);
    }

    RRETURN(S_OK);

cleanup:

    RRETURN(hr);
}

// PathName -> Component \\ PathName
// PathName -> Component
HRESULT
DsPathName(CLexer * pTokenizer, POBJECTINFO pObjectInfo)
{
    WCHAR szToken[MAX_TOKEN_LENGTH];
    DWORD dwToken;
    HRESULT hr;

    hr = pTokenizer->GetNextToken(szToken, &dwToken);
    BAIL_IF_ERROR(hr);
    if ((dwToken != TOKEN_FSLASH) || (dwToken != TOKEN_BSLASH)) {
        RRETURN(E_ADS_BAD_PATHNAME);
    }

    hr = pTokenizer->GetNextToken(szToken, &dwToken);
    BAIL_IF_ERROR(hr);
    if ((dwToken != TOKEN_FSLASH) || (dwToken != TOKEN_BSLASH)) {
        RRETURN(E_ADS_BAD_PATHNAME);
    }

    hr = PathName(pTokenizer, pObjectInfo);
    BAIL_IF_ERROR(hr);

    RRETURN(S_OK);

cleanup:

    RRETURN(hr);
}

HRESULT
PathName(CLexer * pTokenizer, POBJECTINFO pObjectInfo)
{
    HRESULT hr;
    WCHAR szToken[MAX_TOKEN_LENGTH];
    DWORD dwToken;

    hr = Component(pTokenizer, pObjectInfo);
    BAIL_IF_ERROR(hr);

    hr = pTokenizer->GetNextToken(szToken, &dwToken);

    if ((dwToken == TOKEN_BSLASH) || (dwToken == TOKEN_FSLASH)) {
        RRETURN (PathName(pTokenizer, pObjectInfo));
    }else {
        hr = pTokenizer->PushBackToken();
        RRETURN (S_OK);
    }
cleanup:
    RRETURN(hr);
}

HRESULT
Component(CLexer * pTokenizer, POBJECTINFO pObjectInfo)
{
    WCHAR szValue[MAX_TOKEN_LENGTH];
    WCHAR szEqual[MAX_TOKEN_LENGTH];
    WCHAR szComponent[MAX_TOKEN_LENGTH];
    DWORD dwToken;
    HRESULT hr;

    hr = pTokenizer->GetNextToken(szComponent, &dwToken);
    BAIL_IF_ERROR(hr);

    if (dwToken != TOKEN_IDENTIFIER) {
        RRETURN(E_ADS_BAD_PATHNAME);
    }

    hr = pTokenizer->GetNextToken(szEqual, &dwToken);
    BAIL_IF_ERROR(hr);

    if (dwToken == TOKEN_EQUAL) {

        hr = pTokenizer->GetNextToken(szValue, &dwToken);
        BAIL_IF_ERROR(hr);

        if (dwToken != TOKEN_IDENTIFIER) {
            RRETURN(E_ADS_BAD_PATHNAME);
        }

        hr = AddComponent(pObjectInfo, szComponent, szValue);
        BAIL_IF_ERROR(hr);

    }else {

        hr = AddComponent(pObjectInfo, szComponent, NULL);
        BAIL_IF_ERROR(hr);

        hr = pTokenizer->PushBackToken();
        BAIL_IF_ERROR(hr);
    }

    RRETURN(S_OK);

cleanup:
    RRETURN(hr);
}

CLexer::CLexer(LPWSTR szBuffer):
                _ptr(NULL),
                _Buffer(NULL),
                _dwLastTokenLength(0),
                _dwLastToken(0),
                _dwEndofString(0)
{
    if (!szBuffer || !*szBuffer) {
        return;
    }
    _Buffer = AllocProvStr(szBuffer);
    _ptr = _Buffer;
}

CLexer::~CLexer()
{
    FreeProvStr(_Buffer);
}

HRESULT
CLexer::GetNextToken(LPWSTR szToken, LPDWORD pdwToken)
{
    WCHAR c;
    DWORD state = 0;
    LPWSTR pch = szToken;

    memset(szToken, 0, sizeof(WCHAR) * MAX_TOKEN_LENGTH);
    _dwLastTokenLength = 0;
    while (1) {
        c = NextChar();
        switch (state) {
        case  0:
            *pch++ = c;
            _dwLastTokenLength++;

            if (c == L'\\') {
                *pdwToken = TOKEN_BSLASH;
                _dwLastToken = *pdwToken;
                RRETURN(S_OK);
            }else if (c == L'/') {
                *pdwToken = TOKEN_FSLASH;
                _dwLastToken = *pdwToken;
                RRETURN(S_OK);
            }else if (c == L',') {
                *pdwToken = TOKEN_COMMA;
                _dwLastToken = *pdwToken;
                RRETURN(S_OK);
            }else if (c == L'@'){
                *pdwToken = TOKEN_ATSIGN;
                _dwLastToken = *pdwToken;
                RRETURN(S_OK);
            }else if (c == L'='){
                *pdwToken = TOKEN_EQUAL;
                _dwLastToken = *pdwToken;
                RRETURN(S_OK);
            }else if (c == L'.'){
                *pdwToken = TOKEN_PERIOD;
                _dwLastToken = *pdwToken;
                RRETURN(S_OK);
            }else if (c == L'!'){
                *pdwToken = TOKEN_EXCLAMATION;
                _dwLastToken = *pdwToken;
                RRETURN(S_OK);
            }else if (c == L':'){
                *pdwToken = TOKEN_COLON;
                _dwLastToken = *pdwToken;
                RRETURN(S_OK);
            }else if (c == L'\0'){
                *pdwToken = TOKEN_END;
                _dwLastToken = *pdwToken;
                RRETURN(S_OK);
            }else {
                state = 1;
            }
            break;

        case 1:
            if (c == L'\\' || c == L'\0' || c == L',' ||
                    c == L'@' || c == L'!'|| c == L'=' || c == L'.' ||
                    c == L':' || c == L'/') {
                PushbackChar();

                *pdwToken = TOKEN_IDENTIFIER;
                _dwLastToken = *pdwToken;
                RRETURN (S_OK);
            }else {
                *pch++ = c;
                _dwLastTokenLength++;
                state = 1;
                break;
            }
        default:
            RRETURN(E_FAIL);
        }
    }
}

WCHAR
CLexer::NextChar()
{
    if (_ptr == NULL || *_ptr == L'\0') {
        _dwEndofString = TRUE;
        return(L'\0');
    }
    return(*_ptr++);
}

HRESULT
CLexer::PushBackToken()
{
    if (_dwLastToken == TOKEN_END) {
        RRETURN(S_OK);
    }
    _ptr -= _dwLastTokenLength;

    RRETURN(S_OK);
}

void
CLexer::PushbackChar()
{
    if (_dwEndofString) {
        return;
    }
    _ptr--;

}

BOOL
CLexer::IsKeyword(LPWSTR szToken, LPDWORD pdwToken)
{
    DWORD i = 0;

    for (i = 0; i < MAX_KEYWORDS; i++) {
        if (!_wcsicmp(szToken, KeywordList[i].Keyword)) {
            *pdwToken = KeywordList[i].dwTokenId;
            return(TRUE);
        }
    }
    *pdwToken = 0;
    return(FALSE);
}

HRESULT
AddComponent(POBJECTINFO pObjectInfo, LPWSTR szComponent, LPWSTR szValue)
{
    if (!szComponent || !*szComponent) {
        RRETURN(E_FAIL);
    }

    if (pObjectInfo->NumComponents < MAXCOMPONENTS) {

        pObjectInfo->ComponentArray[pObjectInfo->NumComponents].szComponent =
                        AllocProvStr(szComponent);

        pObjectInfo->ComponentArray[pObjectInfo->NumComponents].szValue =
                        AllocProvStr(szValue);

        pObjectInfo->NumComponents++;

        RRETURN(S_OK);

    }else {
        RRETURN(E_FAIL);
    }
}

HRESULT
AddProviderName(POBJECTINFO pObjectInfo, LPWSTR szToken)
{
    if (!szToken || !*szToken) {
        RRETURN(E_FAIL);
    }

    pObjectInfo->ProviderName = AllocProvStr(szToken);

    RRETURN(S_OK);
}

HRESULT
AddRootRDN(POBJECTINFO pObjectInfo, LPWSTR szToken)
{
    if (!szToken || !*szToken) {
        RRETURN(E_FAIL);
    }

    pObjectInfo->RootRDN = AllocProvStr(szToken);

    RRETURN(S_OK);
}

HRESULT
SetType(POBJECTINFO pObjectInfo, DWORD dwToken)
{
    pObjectInfo->ObjectType = dwToken;
    RRETURN(S_OK);
}

// Type -> "user", "group","printer","service", "fileservice"

//+---------------------------------------------------------------------------
// Function:    Type
//
// Synopsis:    Parses Type-> "user" | "group" etc
//
// Arguments:   [CLexer * pTokenizer]
//              [POBJECTINFo pObjectInfo]
//
// Returns:     HRESULT
//
// Modifies:
//
// History:
//
//----------------------------------------------------------------------------
HRESULT
Type(CLexer * pTokenizer, POBJECTINFO pObjectInfo)
{
    WCHAR szToken[MAX_PATH];
    DWORD dwToken;
    HRESULT hr;

    hr = pTokenizer->GetNextToken(szToken, &dwToken);
    BAIL_IF_ERROR(hr);

    if (dwToken == TOKEN_IDENTIFIER ) {
        RRETURN(hr);
    }

    RRETURN(E_FAIL);

cleanup:
    RRETURN(hr);
}


PARSE.H

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    Parse.h

Abstract:

Author:

Environment:

    User mode

Revision History :

--*/
#ifndef _PARSE_H_
#define _PARSE_H_

#define MAX_TOKEN_LENGTH       80
#define MAX_KEYWORDS           14

typedef struct _kwdlist {
    DWORD   dwTokenId;
    LPWSTR  Keyword;
} KWDLIST, *PKWDLIST;

class FAR CLexer
{
public:
    CLexer(LPWSTR szBuffer);
    ~CLexer();

    BOOL
    CLexer::IsKeyword(LPWSTR szToken, LPDWORD pdwToken);

    WCHAR
    CLexer::NextChar();

    void
    CLexer::PushbackChar();

    HRESULT
    CLexer::GetNextToken(LPWSTR szToken, LPDWORD pdwToken);

    HRESULT
    CLexer::PushBackToken();

private:

    LPWSTR _ptr;
    LPWSTR _Buffer;
    DWORD  _dwLastTokenLength;
    DWORD  _dwLastToken;
    DWORD  _dwEndofString;

};

HRESULT
ADsObject(CLexer * pTokenizer, POBJECTINFO pObjectInfo);

HRESULT
SampleDSObject(CLexer * pTokenizer, POBJECTINFO pObjectInfo);

HRESULT
DsPathName(CLexer * pTokenizer, POBJECTINFO pObjectInfo);

HRESULT
PathName(CLexer * pTokenizer, POBJECTINFO pObjectInfo);

HRESULT
Component(CLexer * pTokenizer, POBJECTINFO pObjectInfo);

HRESULT
Type(CLexer * pTokenizer, POBJECTINFO pObjectInfo);

HRESULT
ProviderName(CLexer * pTokenizer, POBJECTINFO pObjectInfo);

HRESULT
AddRootRDN(POBJECTINFO pObjectInfo, LPWSTR szToken);

HRESULT
SetType(POBJECTINFO pObjectInfo, DWORD dwToken);

HRESULT
SchemaPathName(CLexer * pTokenizer, POBJECTINFO pObjectInfo);

HRESULT
SchemaComponent(CLexer * pTokenizer, POBJECTINFO pObjectInfo);

#endif


PROPERTY.CPP

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    Property.cpp

Abstract:

Author:

Environment:

    User mode

Revision History :

--*/
#include "adssmp.h"
#pragma hdrstop

#define VALIDATE_PTR(pPtr) \
    if (!pPtr) { \
        hr = E_ADS_BAD_PARAMETER;\
    }\
    BAIL_ON_FAILURE(hr);

HRESULT
put_BSTR_Property(
    IADs * pADsObject,
    BSTR   bstrPropertyName,
    BSTR   pSrcStringProperty
    )
{
    HRESULT hr = S_OK;
    VARIANT varInputData;

    hr = PackStringinVariant(
            pSrcStringProperty,
            &varInputData
            );
    BAIL_ON_FAILURE(hr);

    hr = pADsObject->Put(
            bstrPropertyName,
            varInputData
            );
    BAIL_ON_FAILURE(hr);

error:
    RRETURN(hr);
}

HRESULT
get_BSTR_Property(
    IADs * pADsObject,
    BSTR   bstrPropertyName,
    BSTR *ppDestStringProperty
    )
{
    HRESULT hr = S_OK;
    VARIANT varOutputData;

    VariantInit( &varOutputData );

    hr = pADsObject->Get(
            bstrPropertyName,
            &varOutputData
            );
    BAIL_ON_FAILURE(hr);

    hr = UnpackStringfromVariant(
            varOutputData,
            ppDestStringProperty
            );
    BAIL_ON_FAILURE(hr);

error:
    RRETURN(hr);
}

HRESULT
put_LONG_Property(
    IADs * pADsObject,
    BSTR   bstrPropertyName,
    LONG   lSrcProperty
    )
{
    HRESULT hr = S_OK;
    VARIANT varInputData;

    hr = PackLONGinVariant(
            lSrcProperty,
            &varInputData
            );
    BAIL_ON_FAILURE(hr);

    hr = pADsObject->Put(
            bstrPropertyName,
            varInputData
            );
    BAIL_ON_FAILURE(hr);

error:
    RRETURN(hr);
}

HRESULT
get_LONG_Property(
    IADs * pADsObject,
    BSTR  bstrPropertyName,
    PLONG plDestProperty
    )
{
    HRESULT hr = S_OK;
    VARIANT varOutputData;

    VariantInit( &varOutputData );

    hr = pADsObject->Get(
            bstrPropertyName,
            &varOutputData
            );
    BAIL_ON_FAILURE(hr);

    hr = UnpackLONGfromVariant(
            varOutputData,
            plDestProperty
            );
    BAIL_ON_FAILURE(hr);

error:
    RRETURN(hr);

}

HRESULT
put_DATE_Property(
    IADs * pADsObject,
    BSTR bstrPropertyName,
    DATE   daSrcProperty
    )
{
    HRESULT hr = S_OK;
    VARIANT varInputData;

    hr = PackDATEinVariant(
            daSrcProperty,
            &varInputData
            );
    BAIL_ON_FAILURE(hr);

    hr = pADsObject->Put(
            bstrPropertyName,
            varInputData
            );
    BAIL_ON_FAILURE(hr);

error:
    RRETURN(hr);
}

HRESULT
get_DATE_Property(
    IADs * pADsObject,
    BSTR bstrPropertyName,
    PDATE pdaDestProperty
    )
{
    HRESULT hr = S_OK;
    VARIANT varOutputData;

    VariantInit( &varOutputData );

    hr = pADsObject->Get(
            bstrPropertyName,
            &varOutputData
            );
    BAIL_ON_FAILURE(hr);

    hr = UnpackDATEfromVariant(
            varOutputData,
            pdaDestProperty
            );
    BAIL_ON_FAILURE(hr);

error:
    RRETURN(hr);
}

HRESULT
put_VARIANT_BOOL_Property(
    IADs * pADsObject,
    BSTR bstrPropertyName,
    VARIANT_BOOL   fSrcProperty
    )
{
    HRESULT hr = S_OK;
    VARIANT varInputData;

    hr = PackVARIANT_BOOLinVariant(
            fSrcProperty,
            &varInputData
            );
    BAIL_ON_FAILURE(hr);

    hr = pADsObject->Put(
            bstrPropertyName,
            varInputData
            );
    BAIL_ON_FAILURE(hr);

error:
    RRETURN(hr);
}

HRESULT
get_VARIANT_BOOL_Property(
    IADs * pADsObject,
    BSTR bstrPropertyName,
    PVARIANT_BOOL pfDestProperty
    )
{
    HRESULT hr = S_OK;
    VARIANT varOutputData;

    VariantInit( &varOutputData );

    hr = pADsObject->Get(
            bstrPropertyName,
            &varOutputData
            );
    BAIL_ON_FAILURE(hr);

    hr = UnpackVARIANT_BOOLfromVariant(
            varOutputData,
            pfDestProperty
            );
    BAIL_ON_FAILURE(hr);

error:
    RRETURN(hr);
}

HRESULT
put_VARIANT_Property(
    IADs * pADsObject,
    BSTR   bstrPropertyName,
    VARIANT   vSrcProperty
    )
{
    HRESULT hr = S_OK;
    VARIANT varInputData;

    hr = PackVARIANTinVariant(
            vSrcProperty,
            &varInputData
            );
    BAIL_ON_FAILURE(hr);

    hr = pADsObject->Put(
            bstrPropertyName,
            varInputData
            );
    BAIL_ON_FAILURE(hr);

error:
    RRETURN(hr);
}

HRESULT
get_VARIANT_Property(
    IADs * pADsObject,
    BSTR bstrPropertyName,
    PVARIANT pvDestProperty
    )
{
    HRESULT hr = S_OK;
    VARIANT varOutputData;

    VariantInit( &varOutputData );

    hr = pADsObject->Get(
            bstrPropertyName,
            &varOutputData
            );
    BAIL_ON_FAILURE(hr);

    hr = UnpackVARIANTfromVariant(
            varOutputData,
            pvDestProperty
            );
    BAIL_ON_FAILURE(hr);

error:
    RRETURN(hr);
}


PROPERTY.H

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    property.h

Abstract:

Author:

Environment:

    User mode

Revision History :

--*/
#ifndef _PROPERTY_H_
#define _PROPERTY_H_

typedef VARIANT_BOOL * PVARIANT_BOOL;

typedef VARIANT * PVARIANT;

typedef DATE *PDATE;

HRESULT
put_BSTR_Property(
    IADs * pADsObject,
    BSTR   bstrPropertyName,
    BSTR   pSrcStringProperty
    );

HRESULT
get_BSTR_Property(
    IADs * pADsObject,
    BSTR   bstrPropertyName,
    BSTR *ppDestStringProperty
    );

HRESULT
put_LONG_Property(
    IADs * pADsObject,
    BSTR   bstrPropertyName,
    LONG   lSrcProperty
    );

HRESULT
get_LONG_Property(
    IADs * pADsObject,
    BSTR  bstrPropertyName,
    PLONG plDestProperty
    );

HRESULT
put_DATE_Property(
    IADs * pADsObject,
    BSTR bstrPropertyName,
    DATE   daSrcProperty
    );

HRESULT
get_DATE_Property(
    IADs * pADsObject,
    BSTR bstrPropertyName,
    PDATE pdaDestProperty
    );

HRESULT
put_VARIANT_BOOL_Property(
    IADs * pADsObject,
    BSTR bstrPropertyName,
    VARIANT_BOOL   fSrcProperty
    );

HRESULT
get_VARIANT_BOOL_Property(
    IADs * pADsObject,
    BSTR bstrPropertyName,
    PVARIANT_BOOL pfDestProperty
    );

HRESULT
put_VARIANT_Property(
    IADs * pADsObject,
    BSTR   bstrPropertyName,
    VARIANT   vSrcProperty
    );

HRESULT
get_VARIANT_Property(
    IADs * pADsObject,
    BSTR bstrPropertyName,
    PVARIANT pvDestProperty
    );
#endif


REGDSAPI.CPP

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    RegDSAPI.cpp

Abstract:

    Sample Provider Registry DS APIs

Author:

Environment:

    User mode

Revision History :

--*/
#include "adssmp.h"
#pragma hdrstop
#include "string.h"

#define SAMPLEDS_REGDSPATH       L"SOFTWARE\\Microsoft\\ADs\\SampleDS\\DS"
#define SAMPLEDS_REGSCHEMAPATH   L"SOFTWARE\\Microsoft\\ADs\\SampleDS\\Schema"
#define SAMPLEDS_REG_TYPE        L"TYPE"
#define SAMPLEDS_REG_MANPROP     L"Mandatory Properties"
#define SAMPLEDS_REG_PROPERTY    L"PROPERTY"
#define SAMPLEDS_REG_CLASS       L"CLASS"
#define SAMPLEDS_REG_SYNTAX      L"Syntax"

/*++

Routine Description:

    Open an object in the DS. Class of the object is also returned
    in szClass if the parameter is not NULL.

Arguments:
    szClass will be used to store the object class if it is not NULL.
    szClass has to be a LPWSTR of length MAX_PATH.

Return Value:

--*/
HRESULT
SampleDSOpenObject(
    LPWSTR szRegPath,
    HANDLE *phKey,
    LPWSTR szClass,
    DWORD  dwType
    )
{
    if (!szRegPath ||
        !phKey)
        RRETURN(E_FAIL);

    WCHAR szFullRegPath[MAX_PATH] = L"";

    switch (dwType) {
        case REG_DS:
            wcscpy(
               szFullRegPath,
               SAMPLEDS_REGDSPATH
               );
            break;
        case REG_SCHEMA:
            wcscpy(
               szFullRegPath,
               SAMPLEDS_REGSCHEMAPATH
               );
            break;
        default:
            RRETURN(E_FAIL);
    };

    if (*szRegPath != L'\0')
            wcscat(szFullRegPath,
            szRegPath
            );

    if (RegOpenKeyEx(
            HKEY_LOCAL_MACHINE,
            szFullRegPath,
            0,
            KEY_ALL_ACCESS,
            (PHKEY)phKey
            ) != ERROR_SUCCESS) {
        RRETURN(E_FAIL);
    }

    if (szClass){
        DWORD dwDataType;
        DWORD dwClass = MAX_PATH * sizeof(WCHAR);
        if (RegQueryValueEx(
                (HKEY)*phKey,
                SAMPLEDS_REG_TYPE,
                NULL,
                &dwDataType,
                (BYTE*)szClass,
                &dwClass
                ) != ERROR_SUCCESS) {
            goto Error;
        }
        if (dwDataType != REG_SZ) {
            goto Error;
        }
    }
    RRETURN(S_OK);
Error:
    RegCloseKey((HKEY)*phKey);
    RRETURN (E_FAIL);
}

/*++

Routine Description:

    Close an object in the DS

Arguments:

Return Value:

--*/
HRESULT
SampleDSCloseObject(
    HANDLE hKey
    )
{
    if (RegCloseKey((HKEY)hKey
                    ) != ERROR_SUCCESS)
        RRETURN(E_FAIL);
    RRETURN(S_OK);
}

/*++

Routine Description:

    Setup the enumeration handle of the objects in the DS

Arguments:

Return Value:

--*/
HRESULT
SampleDSRDNEnum(
    HANDLE *phEnum,
    HANDLE hContainerKey
    )
{
    if (!phEnum)
        RRETURN(E_FAIL);

    LPREGDS_ENUM lprdsenum = new REGDS_ENUM;
    if (lprdsenum == NULL)
        RRETURN(E_OUTOFMEMORY);

    HANDLE hKey = hContainerKey;
    if (hKey == NULL) {
        if ( SampleDSOpenObject(L"",
                                &hKey,
                                NULL,
                                REG_DS
                                ) != ERROR_SUCCESS) {
            goto Error;
        }
    }

    lprdsenum->dwIndex = 0;
    lprdsenum->hContainerKey= hKey;

    *((LPREGDS_ENUM *) phEnum) = lprdsenum;

    RRETURN(S_OK);
Error:
    delete lprdsenum;
    *phEnum = NULL;
    RRETURN(E_FAIL);
}

/*++

Routine Description:

    Get the next object in the DS using the enumeration handle created
    by SampleDSRDNRnum

Arguments:

Return Value:

--*/
HRESULT
SampleDSNextRDN(
    HANDLE hEnum,
    LPWSTR *pszName,
    LPWSTR *pszClass
    )
{
    HRESULT hr = E_FAIL;
    DWORD dwName = MAX_PATH;
    LRESULT lResult;
    FILETIME ftLastWrite;

    if (!hEnum || !pszName)
        RRETURN(hr);

    *pszName = (LPWSTR) AllocProvMem(sizeof(WCHAR)*dwName);
    if (*pszName == NULL)
        RRETURN(E_OUTOFMEMORY);

    lResult = RegEnumKeyEx(
                   (HKEY)LPREGDS_ENUM(hEnum)->hContainerKey,
                    LPREGDS_ENUM(hEnum)->dwIndex,
                    *pszName,
                    &dwName,
                    0,
                    NULL,
                    NULL,
                    &ftLastWrite
                    );
    if (lResult != ERROR_SUCCESS) {
        if (lResult == ERROR_NO_MORE_ITEMS)
            hr = S_FALSE;       // Not an error, no items left
        goto Error;
    } else {
        LPREGDS_ENUM(hEnum)->dwIndex++;
        if (pszClass != NULL) {
            DWORD dwClass = MAX_PATH;
            *pszClass = (LPWSTR) AllocProvMem(sizeof(WCHAR)*dwClass);
            if (*pszClass == NULL) {
                hr = E_OUTOFMEMORY;
                goto Error;
            }
            if (SampleDSGetTypeText(
                        (HKEY)LPREGDS_ENUM(hEnum)->hContainerKey,
                        *pszName,
                        *pszClass,
                        &dwClass
                        ) != S_OK) {
                goto Error;
            }
        }
    }
    RRETURN(S_OK);

Error:
    if (*pszName) {
        FreeProvMem(*pszName);
        *pszName = NULL;
    }
    if (*pszClass) {
        FreeProvMem(*pszClass);
        *pszClass = NULL;
    }
    RRETURN(hr);
}

/*++

Routine Description:

    Free the enumeration handle

Arguments:

Return Value:

--*/
HRESULT
SampleDSFreeEnum(HANDLE hEnum)
{
    if (!hEnum)
        RRETURN(E_FAIL);

    LPREGDS_ENUM lprdsenum = (LPREGDS_ENUM)hEnum;
    if (lprdsenum->hContainerKey) {
        SampleDSCloseObject(lprdsenum->hContainerKey);
    }
    delete lprdsenum;
    RRETURN(S_OK);
}

/*++

Routine Description:

    Modify properties of an object in the DS

Arguments:

Return Value:

--*/
HRESULT
SampleDSModifyObject(HANDLE hKey,
                     HANDLE hOperationData
                     )
{
    if (!hKey)
        RRETURN(E_FAIL);

    if (!hOperationData)
        RRETURN(S_OK);

    LPSampleDS_ATTRS_INFO pAttrsInfo = (LPSampleDS_ATTRS_INFO)hOperationData;
    LPSampleDS_ATTR_INFO pInfo = pAttrsInfo->pAttrInfo;
    DWORD cAttrInfo = pAttrsInfo->dwAttr;

    BYTE* pbData;
    DWORD dwData;
    DWORD dwType;

    for (DWORD i = 0;i < cAttrInfo;i++) {
        switch (pInfo->dwSyntaxId) {
            case SampleDS_DATATYPE_1:
                {
                SampleDS_TYPE_1* pData = (SampleDS_TYPE_1*)pInfo->lpValue;
                pbData = (BYTE*)pData->DNString;
                dwData = (wcslen(pData->DNString) + 1) * sizeof(WCHAR);
                dwType = REG_SZ;
                break;
                }
            case SampleDS_DATATYPE_2:
                {
                SampleDS_TYPE_2* pData = (SampleDS_TYPE_2*)pInfo->lpValue;
                pbData = (BYTE*)&(pData->Integer);
                dwData = sizeof(DWORD);
                dwType = REG_DWORD;
                break;
                }
            default:
                RRETURN(E_FAIL);
        }
        if (RegSetValueEx(
                    (HKEY)hKey,
                    pInfo->lpAttributeName,
                    NULL,
                    dwType,
                    pbData,
                    dwData
                    ) != ERROR_SUCCESS)
            RRETURN(E_FAIL);
        pInfo++;
    }

    RRETURN(NO_ERROR);
}

/*++

Routine Description:

    Read the properties of an object in the DS

Arguments:

Return Value:

--*/
HRESULT
SampleDSReadObject(
    HANDLE hkey,
    HANDLE *phOperationData
    )
{
    DWORD cValues;
    LPSampleDS_ATTR_INFO pInfo = NULL;
    DWORD i;
    LPWSTR pszValue = NULL;
    BYTE*  pbData = NULL;
    DWORD cProp = 0;

    if (RegQueryInfoKey(
                (HKEY)hkey,
                NULL,
                NULL,
                NULL,
                NULL,
                NULL,
                NULL,
                &cValues,
                NULL,
                NULL,
                NULL,
                NULL
                ) != ERROR_SUCCESS) {
        RRETURN(E_FAIL);
    };

    if (cValues == 0) {
        *phOperationData = NULL;
        RRETURN(NO_ERROR);
    }

    DWORD dwData;
    DWORD dwValue;

    pInfo = (LPSampleDS_ATTR_INFO)AllocProvMem(
                                      sizeof(SampleDS_ATTR_INFO)*cValues
                                      );
    if (!pInfo)
        RRETURN(E_OUTOFMEMORY);

    for (i = 0; i < cValues; i++) {
        DWORD dwType;
        pszValue = (LPWSTR)AllocProvMem(sizeof(WCHAR)*MAX_PATH);
        pbData = (BYTE*)AllocProvMem(sizeof(WCHAR)*MAX_PATH);
        if (pbData == NULL || pszValue == NULL)
            goto Error;

        dwData = sizeof(WCHAR) * MAX_PATH;
        dwValue = sizeof(WCHAR) * MAX_PATH;

        if (RegEnumValue(
                (HKEY)hkey,
                i,
                pszValue,
                &dwValue,
                NULL,
                &dwType,
                pbData,
                &dwData
                ) != ERROR_SUCCESS)
            goto Error;

        if (!_wcsicmp(
                pszValue,
                SAMPLEDS_REG_TYPE)) {
            FreeProvMem(pszValue);
            FreeProvMem(pbData);
            continue;
        };

        switch (dwType) {
            case REG_DWORD:
                {
                SampleDS_TYPE_2 *pData = NULL;
                pData = (SampleDS_TYPE_2*)AllocProvMem(
                                               sizeof(SampleDS_TYPE_2));
                if (pData == NULL)
                    goto Error;
                pInfo[cProp].lpValue = (BYTE*)pData;
                pData->Integer = *(DWORD*)pbData;
                pInfo[cProp].dwSyntaxId = SampleDS_DATATYPE_2;
                FreeProvMem(pbData);
                pbData = NULL;
                break;
                }
            case REG_SZ:
                {
                SampleDS_TYPE_1 *pData = NULL;
                pData = (SampleDS_TYPE_1*)AllocProvMem(
                                               sizeof(SampleDS_TYPE_1));
                if (pData == NULL)
                    goto Error;
                pInfo[cProp].lpValue = (BYTE*)pData;
                pData->DNString = (LPWSTR)pbData;
                pInfo[cProp].dwSyntaxId = SampleDS_DATATYPE_1;
                break;
                }
            default:
                goto Error;
        }
        pInfo[cProp].lpAttributeName = pszValue;
        pInfo[cProp].dwNumberOfValues = 1;
        cProp++;
    }

    // Ownership has already been passed
    pbData = NULL;
    pszValue = NULL;

    LPSampleDS_ATTRS_INFO pAttrsInfo;
    pAttrsInfo = (LPSampleDS_ATTRS_INFO)AllocProvMem(sizeof(SampleDS_ATTRS_INFO));
    if (!pAttrsInfo)
        goto Error;

    pAttrsInfo->pAttrInfo = pInfo;
    pAttrsInfo->dwAttr = cProp;
    *phOperationData = pAttrsInfo;

    RRETURN(NO_ERROR);

Error:
    for (DWORD j=0; j<cProp; j++) {
        if (pInfo[j].lpAttributeName)
            FreeProvMem(pInfo[j].lpAttributeName);
        if (pInfo[j].lpValue) {
            if (pInfo[j].dwSyntaxId == SampleDS_DATATYPE_1) {
                if (((SampleDS_TYPE_1*)(pInfo[j].lpValue))->DNString)
                    FreeProvMem(((SampleDS_TYPE_1*)
                                    (pInfo[j].lpValue))->DNString);
            }
            FreeProvMem(pInfo[j].lpValue);
        }
    }
    if (pszValue)
        FreeProvMem(pszValue);
    if (pbData)
        FreeProvMem(pbData);
    FreeProvMem(pInfo);
    RRETURN(E_FAIL);
}

/*++

Routine Description:

    Get all the property defintions from the Schema

Arguments:

Return Value:

--*/
HRESULT
SampleDSGetPropertyDefinition(
    LPSampleDS_ATTR_DEF* ppAttrDefsReturn,
    DWORD *pnumObject)
{
    WCHAR szRegPath[MAX_PATH] = SAMPLEDS_REGSCHEMAPATH;
    DWORD numEntries;
    HKEY hKey;
    LPSampleDS_ATTR_DEF pAttrDefsCurrent;
    LPSampleDS_ATTR_DEF pAttrDefsStart;
    LPWSTR szPropNameCur;
    DWORD dwPropNameCur = MAX_PATH;
    DWORD numProperties = 0;

    if( RegOpenKeyEx(
            HKEY_LOCAL_MACHINE,
            szRegPath,
            0,
            KEY_ALL_ACCESS,
            &hKey
            ) != ERROR_SUCCESS) {
        RRETURN(E_FAIL);
    }

    if( RegQueryInfoKey(
                hKey,
                NULL,
                NULL,
                NULL,
                &numEntries,
                NULL,
                NULL,
                NULL,
                NULL,
                NULL,
                NULL,
                NULL
                ) != ERROR_SUCCESS) {
        goto Error;
    }

    pAttrDefsStart = (LPSampleDS_ATTR_DEF)AllocProvMem(
                                    sizeof(SampleDS_ATTR_DEF)*numEntries);
    if (!pAttrDefsStart)
        goto Error;
    pAttrDefsCurrent = pAttrDefsStart;
    DWORD i;
    for (i=0; i<numEntries; i++) {
        szPropNameCur = (LPWSTR)AllocProvMem(sizeof(WCHAR)*dwPropNameCur);
        if (!szPropNameCur)
            goto Error;

        if (RegEnumKey(
                hKey,
                i,
                szPropNameCur,
                dwPropNameCur
                ) != ERROR_SUCCESS)
            goto Error;

        if (SampleDSGetPropertyInfo(
                hKey,
                szPropNameCur,
                pAttrDefsCurrent
                ) != ERROR_SUCCESS) {
            FreeProvMem(szPropNameCur);
            continue;
        }
        pAttrDefsCurrent->lpAttributeName = szPropNameCur;
        numProperties++;
        pAttrDefsCurrent++;
    }

    // Ownership has been passed
    szPropNameCur = NULL;

    if (numProperties != numEntries) {
        LPSampleDS_ATTR_DEF pAttrDefsFinal;
        pAttrDefsFinal = (LPSampleDS_ATTR_DEF)AllocProvMem(
                                    sizeof(SampleDS_ATTR_DEF)*numProperties);
        if (!pAttrDefsFinal)
            goto Error;
        memcpy(
            (void*)pAttrDefsFinal,
            (void*)pAttrDefsStart,
            sizeof(SampleDS_ATTR_DEF)*numProperties
            );
        *ppAttrDefsReturn = pAttrDefsFinal;
        FreeProvMem(pAttrDefsStart);
    }
    else {
        *ppAttrDefsReturn = pAttrDefsStart;
    }
    *pnumObject = numProperties;
    RegCloseKey(hKey);
    RRETURN(S_OK);

Error:
    RegCloseKey(hKey);
    LPSampleDS_ATTR_DEF pAttrDefsDelete = pAttrDefsStart;
    for (DWORD j=0; j<numProperties; j++) {
        if (pAttrDefsDelete->lpAttributeName)
            FreeProvMem(pAttrDefsDelete->lpAttributeName);
        pAttrDefsDelete++;
    }
    if (szPropNameCur)
        FreeProvMem(szPropNameCur);
    if (pAttrDefsStart)
        FreeProvMem(pAttrDefsStart);
    RRETURN(E_FAIL);
}

/*++

Routine Description:

    Get a particular property defintion from the schema

Arguments:

Return Value:

--*/
HRESULT
SampleDSGetPropertyDefinition(
    LPSampleDS_ATTR_DEF* ppAttrDefReturn,
    LPWSTR szPropName)
{
    WCHAR szRegPath[MAX_PATH] = SAMPLEDS_REGSCHEMAPATH;
    DWORD numEntries;
    HKEY hKey;
    LPSampleDS_ATTR_DEF pAttrDefCurrent;
    LPWSTR szPropNameCur;
    DWORD i;

    if( RegOpenKeyEx(
            HKEY_LOCAL_MACHINE,
            szRegPath,
            0,
            KEY_ALL_ACCESS,
            &hKey
            ) != ERROR_SUCCESS) {
        RRETURN(E_FAIL);
    }

    if( RegQueryInfoKey(
            hKey,
            NULL,
            NULL,
            NULL,
            &numEntries,
            NULL,
            NULL,
            NULL,
            NULL,
            NULL,
            NULL,
            NULL
            ) != ERROR_SUCCESS) {
        goto Error;
    }

    pAttrDefCurrent= (LPSampleDS_ATTR_DEF)AllocProvMem(
                                               sizeof(SampleDS_ATTR_DEF));
    if (!pAttrDefCurrent)
        goto Error;
    szPropNameCur = (LPWSTR)AllocProvMem(sizeof(WCHAR)*MAX_PATH);
    if (!szPropNameCur)
        goto Error;

    for (i=0; i<numEntries; i++) {
        if (RegEnumKey(hKey,
            i,
            szPropNameCur,
            MAX_PATH) != ERROR_SUCCESS)
            goto Error;

        if (!_wcsicmp(
                szPropNameCur,
                szPropName)) {
            if (SampleDSGetPropertyInfo(
                                hKey,
                                szPropNameCur,
                                pAttrDefCurrent
                                ) != ERROR_SUCCESS)
                goto Error;
            pAttrDefCurrent->lpAttributeName = szPropNameCur;
            *ppAttrDefReturn = pAttrDefCurrent;
            RegCloseKey(hKey);
            RRETURN(S_OK);
        }
    }
Error:
    RegCloseKey(hKey);
    if (szPropNameCur)
        FreeProvMem(szPropNameCur);
    if (pAttrDefCurrent)
        FreeProvMem(pAttrDefCurrent);
    RRETURN(E_FAIL);
}

/*++

Routine Description:

    Free memory allocated by GetPropertyDefinition

Arguments:

Return Value:

--*/
HRESULT
SampleDSFreePropertyDefinition(LPSampleDS_ATTR_DEF pAttrDefs,
                               DWORD numObject)
{
    if (!pAttrDefs)
        RRETURN(E_FAIL);

    LPSampleDS_ATTR_DEF pAttrDefCurrent = pAttrDefs;
    for (DWORD j=0; j<numObject; j++) {
        if (pAttrDefCurrent->lpAttributeName)
            FreeProvMem(pAttrDefCurrent->lpAttributeName);
        pAttrDefCurrent++;
    }
    FreeProvMem(pAttrDefs);
    RRETURN(S_OK);
}

/*++

Routine Description:

    Get the type of an object in text format

Arguments:

Return Value:

--*/
HRESULT
SampleDSGetTypeText(HKEY hKey,
                    LPWSTR szPropertyName,
                    LPWSTR szClassName,
                    DWORD *pdwClassName)
{
    HKEY hKeyProperty;

    if (RegOpenKey(
            hKey,
            szPropertyName,
            &hKeyProperty
            ) != ERROR_SUCCESS) {
        RRETURN(E_FAIL);
    }

    DWORD dwType;
    if (RegQueryValueEx(
            hKeyProperty,
            SAMPLEDS_REG_TYPE,
            NULL,
            &dwType,
            (BYTE*)szClassName,
            pdwClassName
            ) != ERROR_SUCCESS) {
        goto Error;
    }

    if (dwType != REG_SZ) {
        goto Error;
    }
    RegCloseKey(hKeyProperty);
    RRETURN(S_OK);
Error:
    RegCloseKey(hKeyProperty);
    RRETURN(E_FAIL);

}

/*++

Routine Description:

    Get the type of an object

Arguments:

Return Value:

--*/
HRESULT
SampleDSGetType(HKEY hKey,
                LPWSTR szPropertyName,
                DWORD *pdwType)
{
    HRESULT hrReturn = S_OK;
    LPWSTR szClassName;
    DWORD dwClassName = MAX_PATH * sizeof(WCHAR);

    szClassName = (LPWSTR)AllocProvMem(dwClassName);
    if (!szClassName) {
        hrReturn = E_OUTOFMEMORY;
        goto Error;
    }

    if (SampleDSGetTypeText(
                    hKey,
                    szPropertyName,
                    szClassName,
                    &dwClassName
                    ) == E_FAIL) {
        hrReturn = E_FAIL;
        goto Error;
    }

    if (!_wcsicmp(
            szClassName,
            SAMPLEDS_REG_PROPERTY)) {
        *pdwType = SAMPLEDS_PROPERTY;
    }
    else if (!_wcsicmp(
                szClassName,
                SAMPLEDS_REG_CLASS)) {
        *pdwType = SAMPLEDS_CLASS;
    }
    else {
        *pdwType = SAMPLEDS_UNKNOWN;
    }
Error:
    if (szClassName)
        FreeProvMem(szClassName);
    RRETURN(hrReturn);
}

/*++

Routine Description:

    Get information about a property from the schema

Arguments:

Return Value:

--*/
HRESULT
SampleDSGetPropertyInfo(HKEY hKey,
                        LPWSTR szPropertyName,
                        LPSampleDS_ATTR_DEF pAttrDef)
{
    HKEY hKeyClass;
    DWORD dwSyntax;
    DWORD dwType;
    DWORD dwSyntaxSize = sizeof(DWORD);
    DWORD dwSchemaType;

    if ((SampleDSGetType(
                    hKey,
                    szPropertyName,
                    &dwSchemaType
                    ) == E_FAIL) ||
        (dwSchemaType != SAMPLEDS_PROPERTY))
        RRETURN(E_FAIL);

    if (RegOpenKeyEx(
                hKey,
                szPropertyName,
                NULL,
                KEY_READ,
                &hKeyClass
                ) != ERROR_SUCCESS)
        RRETURN(E_FAIL);

    if (RegQueryValueEx(
                    hKeyClass,
                    SAMPLEDS_REG_SYNTAX,
                    NULL,
                    &dwType,
                    (BYTE*)&dwSyntax,
                    &dwSyntaxSize
                    ) == ERROR_SUCCESS) {
        pAttrDef->dwSyntaxID = dwSyntax;
        RegCloseKey(hKeyClass);
        RRETURN(S_OK);
    }
    RegCloseKey(hKeyClass);
    RRETURN(E_FAIL);
}

/*++

Routine Description:

    Free the memory used by a LPWSTR_LIST

Arguments:

Return Value:

--*/
void FreeList(LPWSTR_LIST pList)
{
    if (pList) {
        if (pList->lpString) {
            FreeProvStr(pList->lpString);
                }
        FreeList(pList->Next);
        FreeProvMem(pList);
        };
}

/*++

Routine Description:

Arguments:

Return Value:

--*/
HRESULT
SampleDSGetClassDefinition(
    LPSampleDS_CLASS_DEF* ppClassDefsReturn,
    DWORD *pnumObject)
{
    WCHAR szRegPath[MAX_PATH] = SAMPLEDS_REGSCHEMAPATH;
    DWORD numEntries;
    HKEY hKey;
    LPSampleDS_CLASS_DEF pClassDefsCurrent;
    LPSampleDS_CLASS_DEF pClassDefsStart;
    LPWSTR szClassName;
    DWORD dwClassName = MAX_PATH;
    DWORD numClass = 0;

    if( RegOpenKeyEx(
                HKEY_LOCAL_MACHINE,
                szRegPath,
                0,
                KEY_ALL_ACCESS,
                &hKey
                ) != ERROR_SUCCESS) {
        RRETURN(E_FAIL);
    }

    if( RegQueryInfoKey(
                    hKey,
                    NULL,
                    NULL,
                    NULL,
                    &numEntries,
                    NULL,
                    NULL,
                    NULL,
                    NULL,
                    NULL,
                    NULL,
                    NULL
                    ) != ERROR_SUCCESS) {
        goto Error;
    }

    pClassDefsStart = (LPSampleDS_CLASS_DEF)AllocProvMem(
                                      sizeof(SampleDS_CLASS_DEF)*numEntries);
    if (!pClassDefsStart)
        goto Error;
    pClassDefsCurrent = pClassDefsStart;
    DWORD i;
    for (i=0; i<numEntries; i++) {
        szClassName = (LPWSTR)AllocProvMem(sizeof(WCHAR)*dwClassName);
        if (!szClassName)
            goto Error;

        if (RegEnumKey(
                    hKey,
                    i,
                    szClassName,
                    dwClassName
                    ) != ERROR_SUCCESS)
            goto Error;

        if (SampleDSGetClassInfo(
                            hKey,
                            szClassName,
                            pClassDefsCurrent
                            ) != ERROR_SUCCESS) {
            FreeProvMem(szClassName);
            continue;
        }
        pClassDefsCurrent->lpClassName = szClassName;
        numClass++;
        pClassDefsCurrent++;
    }
    szClassName = NULL;

    if (numClass != numEntries) {
        LPSampleDS_CLASS_DEF pClassDefsFinal;
        pClassDefsFinal = (LPSampleDS_CLASS_DEF)AllocProvMem(
                                     sizeof(SampleDS_CLASS_DEF)*numClass);
        if (!pClassDefsFinal)
            goto Error;
        memcpy(
            (void*)pClassDefsFinal,
            (void*)pClassDefsStart,
            sizeof(SampleDS_CLASS_DEF)*numClass
            );
        *ppClassDefsReturn = pClassDefsFinal;
        FreeProvMem(pClassDefsStart);
    }
    else {
        *ppClassDefsReturn = pClassDefsStart;
    }
    *pnumObject = numClass;
    RegCloseKey(hKey);
    RRETURN(S_OK);

Error:
    RegCloseKey(hKey);
    LPSampleDS_CLASS_DEF pClassDefsDelete = pClassDefsStart;
    DWORD j;
    for (j=0; j<numClass; j++) {
        if (pClassDefsDelete->lpClassName)
            FreeProvMem(pClassDefsDelete->lpClassName);
        if (pClassDefsDelete->lpMandatoryAttributes)
            FreeList(pClassDefsDelete->lpMandatoryAttributes);
        pClassDefsDelete++;
    }
    if (szClassName)
        FreeProvMem(szClassName);
    if (pClassDefsStart)
        FreeProvMem(pClassDefsStart);
    RRETURN(E_FAIL);
}

/*++

Routine Description:

    Get the class defintion for a particular class

Arguments:

Return Value:

--*/
HRESULT
SampleDSGetClassDefinition(
    LPSampleDS_CLASS_DEF* ppClassDefReturn,
    LPWSTR szClassName)
{
    WCHAR szRegPath[MAX_PATH] = SAMPLEDS_REGSCHEMAPATH;
    DWORD numEntries;
    HKEY hKey;
    LPSampleDS_CLASS_DEF pClassDefCurrent;
    LPWSTR szClassNameCurrent;
    DWORD i;

    if( RegOpenKeyEx(
                HKEY_LOCAL_MACHINE,
                szRegPath,
                0,
                KEY_ALL_ACCESS,
                &hKey
                ) != ERROR_SUCCESS) {
        RRETURN(E_FAIL);
    }

    if( RegQueryInfoKey(
                    hKey,
                    NULL,
                    NULL,
                    NULL,
                    &numEntries,
                    NULL,
                    NULL,
                    NULL,
                    NULL,
                    NULL,
                    NULL,
                    NULL
                    ) != ERROR_SUCCESS) {
        goto Error;
    }

    pClassDefCurrent= (LPSampleDS_CLASS_DEF)AllocProvMem(
                                             sizeof(SampleDS_CLASS_DEF));
    if (!pClassDefCurrent)
        goto Error;
    szClassNameCurrent = (LPWSTR)AllocProvMem(sizeof(WCHAR)*MAX_PATH);
    if (!szClassNameCurrent)
        goto Error;

    for (i=0; i<numEntries; i++) {
        if (RegEnumKey(hKey,
            i,
            szClassNameCurrent,
            MAX_PATH) != ERROR_SUCCESS)
            goto Error;

        if (!_wcsicmp(szClassNameCurrent,szClassName)) {
            if (SampleDSGetClassInfo(
                                hKey,
                                szClassNameCurrent,
                                pClassDefCurrent
                                ) != ERROR_SUCCESS)
                goto Error;
            pClassDefCurrent->lpClassName = szClassNameCurrent;
            *ppClassDefReturn = pClassDefCurrent;
            RegCloseKey(hKey);
            RRETURN(S_OK);
        }
    }
Error:
    RegCloseKey(hKey);
    if (szClassNameCurrent)
        FreeProvMem(szClassNameCurrent);
    if (pClassDefCurrent)
        FreeProvMem(pClassDefCurrent);
    RRETURN(E_FAIL);
}

/*++

Routine Description:

    Free memory allocated by GetClassDefinition

Arguments:

Return Value:

--*/
HRESULT
SampleDSFreeClassDefinition(LPSampleDS_CLASS_DEF pClassDefs,
                            DWORD numObject)
{
    if (!pClassDefs)
        RRETURN(E_FAIL);

    LPSampleDS_CLASS_DEF pClassDefCurrent = pClassDefs;
    for (DWORD j=0; j<numObject; j++) {
        if (pClassDefCurrent->lpClassName)

FreeProvMem(pClassDefCurrent->lpClassName);
        if (pClassDefCurrent->lpMandatoryAttributes)
            FreeList(pClassDefCurrent->lpMandatoryAttributes);
        pClassDefCurrent++;
    }
    FreeProvMem(pClassDefs);
    RRETURN(S_OK);
}

/*++

Routine Description:

    Get the next token in a REG_MULTISZ string returned by the
    Regsitry getvalue function

Arguments:

Return Value:

--*/
LPWSTR nexttoken(LPWSTR szCurrent)
{
    static LPWSTR szRemainder = NULL;
    LPWSTR szReturn = NULL;

    if (szCurrent) {
        szRemainder = szCurrent;
    }
    else if (szRemainder == NULL) {
        return NULL;
    }

    if (*szRemainder) {
        szReturn = szRemainder;
        while (*++szRemainder);
        szRemainder++;
    }
    return szReturn;
}

/*++

Routine Description:

    Get information about a particular class from the schema

Arguments:

Return Value:

--*/
HRESULT
SampleDSGetClassInfo(HKEY hKey,
                     LPWSTR szClassName,
                     LPSampleDS_CLASS_DEF pClassDef)
{
    HKEY hKeyClass;
    DWORD dwType;
    LPWSTR szProperties;
    DWORD dwProperties = MAX_PATH * 3;
    LPWSTR_LIST pListStart = NULL;

    DWORD dwManProp = 0;
    LPWSTR_LIST pListCurrent = NULL;
    LPWSTR szPropCurrent = NULL;
    LPWSTR szProperty = NULL;
    LPWSTR szPropertyNew = NULL;

    DWORD dwSchemaType;
    if ((SampleDSGetType(
                    hKey,
                    szClassName,
                    &dwSchemaType) == E_FAIL) ||
        (dwSchemaType != SAMPLEDS_CLASS))
        RRETURN(E_FAIL);

    if (RegOpenKeyEx(
                hKey,
                szClassName,
                NULL,
                KEY_READ,
                &hKeyClass
                ) != ERROR_SUCCESS)
        RRETURN(E_FAIL);

    szProperties= (LPWSTR)AllocProvMem(sizeof(WCHAR)*dwProperties);
    if (!szProperties)
        goto Error;

    if (RegQueryValueEx(
                    hKeyClass,
                    SAMPLEDS_REG_MANPROP,
                    NULL,
                    &dwType,
                    (BYTE*)szProperties,
                    &dwProperties
                    ) != ERROR_SUCCESS) {
        goto Error;
    }
    if (dwType != REG_MULTI_SZ) {
        goto Error;
    }

    szPropCurrent = szProperties;
    szProperty = nexttoken(szPropCurrent);
    szPropertyNew;
    if (szProperty) {
        pListStart = (LPWSTR_LIST)AllocProvMem(sizeof(WSTR_LIST));
        if (!pListStart)
            goto Error;
        pListCurrent = pListStart;
        szPropertyNew = AllocProvStr(szProperty);
        if (!szPropertyNew)
            goto Error;
        pListCurrent->lpString = szPropertyNew;
        szProperty = nexttoken(NULL);
        dwManProp++;
        while( szProperty != NULL ) {
            LPWSTR_LIST pListNew = (LPWSTR_LIST)AllocProvMem(
                                                  sizeof(WSTR_LIST));
            if (!pListNew)
                goto Error;
            pListCurrent->Next = pListNew;
            szPropertyNew = AllocProvStr(szProperty);
            if (!szPropertyNew)
                goto Error;
            pListNew->lpString = szPropertyNew;
            pListCurrent = pListNew;
            szProperty = nexttoken(NULL);
            dwManProp++;
        }
       pListCurrent->Next = NULL;
    }
    pClassDef->lpMandatoryAttributes = pListStart;
    pClassDef->dwNumberOfMandatoryAttributes = dwManProp;
    RegCloseKey(hKeyClass);
    RRETURN(S_OK);

Error:
    RegCloseKey(hKeyClass);
    if (pListStart)
        FreeList(pListStart);
    if (szProperties)
        FreeProvMem(szProperties);
    RRETURN(E_FAIL);
}

/*++

Routine Description:

    Add an object in the DS

Arguments:

Return Value:

--*/
HRESULT
SampleDSAddObject(HANDLE hKey,
                  LPWSTR szObject,
                  LPWSTR szClass,
                  HANDLE hOperationData
                  )
{
    if (!szObject || !szClass || !hKey)
        RRETURN(E_FAIL);

    HKEY hKeyNew;
    if (RegCreateKey(
                (HKEY)hKey,
                szObject,
                &hKeyNew
                ) != ERROR_SUCCESS) {
        RRETURN(E_FAIL);
    };

    if (RegSetValueEx(
                hKeyNew,
                SAMPLEDS_REG_TYPE,
                NULL,
                REG_SZ,
                (BYTE*)szClass,
                (wcslen(szClass) + 1) * sizeof(WCHAR)
                ) != ERROR_SUCCESS) {
        RRETURN(E_FAIL);
    };
    if (hOperationData) {
        if (SampleDSModifyObject(
                            hKeyNew,
                            hOperationData
                            ) != S_OK) {
            RRETURN(E_FAIL);
        };
    };
    RRETURN(S_OK);
}

/*++

Routine Description:

    Delete a registry key and all its subkeys

Arguments:

Return Value:

--*/
LONG RegDeleteAllKeys(HKEY hKeyDelete,
                      LPWSTR pszSubKey)
{
    HKEY  hKeyChild;
    TCHAR szTmp[MAX_PATH];
    LONG  lResult;
    DWORD dwTmpSize = MAX_PATH;

    lResult = RegOpenKeyEx(
                       hKeyDelete,
                       pszSubKey,
                       NULL,
                       KEY_ALL_ACCESS,
                       &hKeyChild
                       );
    if (lResult != ERROR_SUCCESS)
        return lResult;

    lResult = RegEnumKey(
                     hKeyChild,
                     0,
                     szTmp,
                     dwTmpSize
                     );

    while (lResult != ERROR_NO_MORE_ITEMS) {
        RegDeleteAllKeys(
                 hKeyChild,
                 szTmp
                 );

        lResult = RegEnumKey(
                         hKeyChild,
                         0,
                         szTmp,
                         dwTmpSize
                         );
    }

    RegCloseKey(hKeyChild);
    lResult = RegDeleteKey(
                       hKeyDelete,
                       pszSubKey
                       );

    return lResult;
}

/*++

Routine Description:

    Remove an object from the DS

Arguments:

Return Value:

--*/
HRESULT
SampleDSRemoveObject(HANDLE hKey,
                     LPWSTR szObject)
{
    if (RegDeleteAllKeys(
                    (HKEY)hKey,
                    szObject
                    ) == ERROR_SUCCESS)
        RRETURN(S_OK);
    else
        RRETURN(E_FAIL);
}

/*++

Routine Description:

    Create Memory Buffer for operation data

Arguments:

Return Value:

--*/
HRESULT
SampleDSCreateBuffer(HANDLE *phOperationData)
{
    LPSampleDS_ATTRS_INFO pAttrsInfo;
    pAttrsInfo = (LPSampleDS_ATTRS_INFO)AllocProvMem(sizeof(SampleDS_ATTRS_INFO));
    if (!pAttrsInfo)
        RRETURN(E_OUTOFMEMORY);
    *phOperationData = pAttrsInfo;
    RRETURN(S_OK);
}

/*++

Routine Description:

    Free memroy pointed to by operationdata

Arguments:

Return Value:

--*/
HRESULT
SampleDSFreeBuffer(HANDLE hOperationData)
{
    if (!hOperationData)
        RRETURN(E_FAIL);

    LPSampleDS_ATTRS_INFO pAttrsInfo;
    pAttrsInfo = (LPSampleDS_ATTRS_INFO)hOperationData;

    LPSampleDS_ATTR_INFO pAttrInfo;
    DWORD cAttr;
    pAttrInfo = pAttrsInfo->pAttrInfo;
    cAttr = pAttrsInfo->dwAttr;

    if (pAttrInfo) {
        for (DWORD i = 0;i<cAttr;i++) {
            if (pAttrInfo->lpAttributeName)
                FreeProvMem(pAttrInfo->lpAttributeName);
            if (pAttrInfo->lpValue) {
                if (pAttrInfo->dwSyntaxId == SampleDS_DATATYPE_1) {
                    if (((SampleDS_TYPE_1*)(pAttrInfo->lpValue))->DNString)
                        FreeProvMem(((SampleDS_TYPE_1*)
                                        (pAttrInfo->lpValue))->DNString);
                }
                FreeProvMem(pAttrInfo->lpValue);
            }
            pAttrInfo++;
        }
    }
    FreeProvMem(pAttrsInfo->pAttrInfo);
    FreeProvMem(pAttrsInfo);
    RRETURN(S_OK);
}


REGDSAPI.H

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    RegDsAPI.h

Abstract:

Author:

Environment:

    User mode

Revision History :

--*/
#ifndef _REGDSAPI_
#define _REGDSAPI_

#define REG_DS      1
#define REG_SCHEMA  2

#define SAMPLEDS_UNKNOWN  0
#define SAMPLEDS_PROPERTY 1
#define SAMPLEDS_CLASS    2

typedef struct REGDS_ENUM{
    DWORD  dwIndex;
    HANDLE hContainerKey;
} *LPREGDS_ENUM;

HRESULT
SampleDSOpenObject(
    LPWSTR szRegPath,
    HANDLE *phKey,
    LPWSTR szClass,
    DWORD  dwType);

HRESULT
SampleDSCloseObject(
    HANDLE hKey);

HRESULT
SampleDSRDNEnum(
    HANDLE *phEnum,
    HANDLE hContainerKey);

HRESULT
SampleDSNextRDN(
    HANDLE hEnum,
    LPWSTR *pszName,
    LPWSTR *pszClass);

HRESULT
SampleDSFreeEnum(HANDLE hEnum);

HRESULT
SampleDSModifyObject(HANDLE hKey,
                     HANDLE hOperationData
                     );

HRESULT
SampleDSReadObject(
    HANDLE hkey,
    HANDLE *phOperationData
    );

HRESULT
SampleDSGetPropertyDefinition(
    LPSampleDS_ATTR_DEF* ppAttrDefReturn,
    DWORD *pnumObject
);

HRESULT
SampleDSGetPropertyDefinition(
    LPSampleDS_ATTR_DEF* ppAttrDefReturn,
    LPWSTR szPropName
);

HRESULT
SampleDSGetType(HKEY hKey,
                LPWSTR szPropertyName,
                DWORD *pdwType
                );

HRESULT
SampleDSGetPropertyInfo(HKEY hKey,
                        LPWSTR szPropertyName,
                        LPSampleDS_ATTR_DEF pAttrDef
                        );

HRESULT
SampleDSGetClassDefinition(
    LPSampleDS_CLASS_DEF* ppClassDefsReturn,
    DWORD *pnumObject
    );

HRESULT
SampleDSGetClassDefinition(
    LPSampleDS_CLASS_DEF* ppClassDefReturn,
    LPWSTR szClassName
    );

HRESULT
SampleDSGetClassInfo(HKEY hKey,
                     LPWSTR szClassName,
                     LPSampleDS_CLASS_DEF pClassDef
                     );
HRESULT
SampleDSAddObject(HANDLE hKey,
                  LPWSTR szObject,
                  LPWSTR szClass,
                  HANDLE hOperationData
                  );

HRESULT
SampleDSRemoveObject(
     HANDLE hKey,
     LPWSTR szObject);

HRESULT
SampleDSGetTypeText(HKEY hKey,
                    LPWSTR szPropertyName,
                    LPWSTR szClassName,
                    DWORD *pdwClassName);
HRESULT
SampleDSCreateBuffer(HANDLE *phOperationData);

HRESULT
SampleDSFreeBuffer(HANDLE hOperationData);

HRESULT
SampleDSFreeClassDefinition(LPSampleDS_CLASS_DEF pClassDefs,
                            DWORD numObject);

HRESULT
SampleDSFreePropertyDefinition(LPSampleDS_ATTR_DEF pPropertyDefs,
                               DWORD numObject);

#endif


SMPOPER.CPP

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    SmpOper.cpp

Abstract:

    SampleDS DataType Operation routines

Author:

Environment:

    User mode

Revision History :

--*/
#include "adssmp.h"
#pragma hdrstop

HRESULT
SampleDSTypeFreeSampleDSSynId1(
    PSampleDSOBJECT lpSampleDSDestObject
    )
{
    HRESULT hr = S_OK;

    FreeProvStr(lpSampleDSDestObject->SampleDSValue.value_1.DNString);

    RRETURN(hr);
}

HRESULT
SampleDSTypeFreeSampleDSSynId2(
    PSampleDSOBJECT lpSampleDSDestObject
    )
{
    HRESULT hr = S_OK;

    //
    // Do nothing - Integer
    //

    RRETURN(hr);

}

HRESULT
SampleDSTypeClear(
    PSampleDSOBJECT lpSampleDSDestObject
    )
{
    HRESULT hr = S_OK;
    switch (lpSampleDSDestObject->SampleDSType) {
    case 1:
        hr = SampleDSTypeFreeSampleDSSynId1(
                lpSampleDSDestObject
                );
        break;
    case 2:
        hr = SampleDSTypeFreeSampleDSSynId2(
                lpSampleDSDestObject
                );
        break;

    default:
        hr = E_FAIL;
        break;
    }

    RRETURN(hr);
}

void
SampleDSTypeFreeSampleDSObjects(
    PSampleDSOBJECT pSampleDSObject,
    DWORD dwNumValues
    )
{
    DWORD i = 0;

    for (i = 0; i < dwNumValues; i++ ) {
         SampleDSTypeClear(pSampleDSObject + i);
    }

    FreeProvMem(pSampleDSObject);

    return;
}

HRESULT
SampleDSTypeCopySampleDSSynId1(
    PSampleDSOBJECT lpSampleDSSrcObject,
    PSampleDSOBJECT lpSampleDSDestObject
    )
{
    HRESULT hr = S_OK;

    lpSampleDSDestObject->SampleDSType = lpSampleDSSrcObject->SampleDSType;

    lpSampleDSDestObject->SampleDSValue.value_1.DNString =
                    (LPWSTR)AllocProvStr(
                            lpSampleDSSrcObject->SampleDSValue.value_1.DNString
                            );
    RRETURN(hr);
}

HRESULT
SampleDSTypeCopySampleDSSynId2(
    PSampleDSOBJECT lpSampleDSSrcObject,
    PSampleDSOBJECT lpSampleDSDestObject
    )
{
    HRESULT hr = S_OK;

    lpSampleDSDestObject->SampleDSType = lpSampleDSSrcObject->SampleDSType;

    lpSampleDSDestObject->SampleDSValue.value_2.Integer =
                        lpSampleDSSrcObject->SampleDSValue.value_2.Integer;

    RRETURN(hr);

}

HRESULT
SampleDSTypeCopy(
    PSampleDSOBJECT lpSampleDSSrcObject,
    PSampleDSOBJECT lpSampleDSDestObject
    )
{
    HRESULT hr = S_OK;
    switch (lpSampleDSSrcObject->SampleDSType) {
    case 1:
        hr = SampleDSTypeCopySampleDSSynId1(
                lpSampleDSSrcObject,
                lpSampleDSDestObject
                );
        break;
    case 2:
        hr = SampleDSTypeCopySampleDSSynId2(
                lpSampleDSSrcObject,
                lpSampleDSDestObject
                );
        break;

    default:
        hr = E_FAIL;
        break;
    }

    RRETURN(hr);
}

HRESULT
SampleDSTypeCopyConstruct(
    LPSampleDSOBJECT pSampleDSSrcObjects,
    DWORD dwNumObjects,
    LPSampleDSOBJECT * ppSampleDSDestObjects
    )
{

    DWORD i = 0;
    LPSampleDSOBJECT pSampleDSDestObjects = NULL;
    HRESULT hr = S_OK;

    pSampleDSDestObjects = (LPSampleDSOBJECT)AllocProvMem(
                                    dwNumObjects * sizeof(SampleDSOBJECT)
                                    );

    if (!pSampleDSDestObjects) {
        RRETURN(E_FAIL);
    }

     for (i = 0; i < dwNumObjects; i++ ) {
         hr = SampleDSTypeCopy(pSampleDSSrcObjects + i,
                               pSampleDSDestObjects + i
                               );
     }

     *ppSampleDSDestObjects = pSampleDSDestObjects;

     RRETURN(S_OK);

}

HRESULT
UnMarshallSampleDSToSampleDSSynId(
    DWORD dwSyntaxId,
    DWORD dwNumValues,
    LPBYTE lpValue,
    PSampleDSOBJECT * ppSampleDSObject
    )
{
    LPBYTE lpByte = lpValue;
    DWORD  i = 0;
    PSampleDSOBJECT pSampleDSObject = NULL;

    pSampleDSObject = (PSampleDSOBJECT)AllocProvMem(
                            dwNumValues * sizeof(SampleDSOBJECT)
                            );

    if (!pSampleDSObject) {
        RRETURN(E_FAIL);
    }

    for (i = 0; i < dwNumValues; i++) {

        lpByte = CopySampleDSToSampleDSSynId(
                         dwSyntaxId,
                         lpByte,
                         (pSampleDSObject + i)
                         );

    }

    *ppSampleDSObject = pSampleDSObject;

    RRETURN(S_OK);
}

LPBYTE
CopySampleDS1ToSampleDSSynId1(
    LPBYTE lpByte,
    PSampleDSOBJECT lpSampleDSObject
    )
{
    LPSampleDS_TYPE_1 lpData_1 = (LPSampleDS_TYPE_1) lpByte;

    lpSampleDSObject->SampleDSType = 1;

    lpSampleDSObject->SampleDSValue.value_1.DNString =
                    (LPWSTR)AllocProvStr(lpData_1->DNString);

    lpByte = (LPBYTE ) lpData_1 + sizeof(SampleDS_TYPE_1);

    return(lpByte);
}

LPBYTE
CopySampleDS2ToSampleDSSynId2(
    LPBYTE lpByte,
    PSampleDSOBJECT lpSampleDSObject
    )

{
    LPSampleDS_TYPE_2 lpData_2 = (LPSampleDS_TYPE_2) lpByte;

    lpSampleDSObject->SampleDSType = 2;

    lpSampleDSObject->SampleDSValue.value_2.Integer = lpData_2->Integer;

    lpByte = (LPBYTE ) lpData_2 + sizeof(SampleDS_TYPE_2);

    return(lpByte);
}

LPBYTE
CopySampleDSToSampleDSSynId(
    DWORD dwSyntaxId,
    LPBYTE lpByte,
    PSampleDSOBJECT lpSampleDSObject
    )
{
    switch (dwSyntaxId) {
    case 1:
        lpByte = CopySampleDS1ToSampleDSSynId1(
                         lpByte,
                         lpSampleDSObject
                         );
        break;
    case 2:
        lpByte = CopySampleDS2ToSampleDSSynId2(
                         lpByte,
                         lpSampleDSObject
                         );
        break;

    default:
        break;

    }

    return(lpByte);
}

HRESULT
MarshallSampleDSSynIdToSampleDS(
    DWORD dwSyntaxId,
    PSampleDSOBJECT pSampleDSObject,
    DWORD dwNumValues,
    LPBYTE lpValue
    )
{

    DWORD  i = 0;

    for (i = 0; i < dwNumValues; i++) {

        lpValue = CopySampleDSSynIdToSampleDS(
                         dwSyntaxId,
                         lpValue,
                         (pSampleDSObject + i)
                         );

    }

    RRETURN(S_OK);
}

LPBYTE
CopySampleDSSynId1ToSampleDS1(
    LPBYTE lpByte,
    PSampleDSOBJECT lpSampleDSObject
    )
{
    LPSampleDS_TYPE_1 lpData_1 = (LPSampleDS_TYPE_1) lpByte;

    lpData_1->DNString =
                (LPWSTR)AllocProvStr(
                        lpSampleDSObject->SampleDSValue.value_1.DNString
                        );

    lpByte = (LPBYTE ) lpData_1 + sizeof(SampleDS_TYPE_1);

    return(lpByte);

}

LPBYTE
CopySampleDSSynId2ToSampleDS2(
    LPBYTE lpByte,
    PSampleDSOBJECT lpSampleDSObject
    )

{
    LPSampleDS_TYPE_2 lpData_2 = (LPSampleDS_TYPE_2) lpByte;

    lpData_2->Integer = lpSampleDSObject->SampleDSValue.value_2.Integer;

    lpByte = (LPBYTE ) lpData_2 + sizeof(SampleDS_TYPE_2);

    return(lpByte);
}

LPBYTE
CopySampleDSSynIdToSampleDS(
    DWORD dwSyntaxId,
    LPBYTE lpByte,
    PSampleDSOBJECT lpSampleDSObject
    )
{
    switch (dwSyntaxId) {
    case 1:
        lpByte = CopySampleDSSynId1ToSampleDS1(
                         lpByte,
                         lpSampleDSObject
                         );
        break;

    case 2:
        lpByte = CopySampleDSSynId2ToSampleDS2(
                         lpByte,
                         lpSampleDSObject
                         );
        break;

    default:
        break;

    }

    return(lpByte);
}

void
VarTypeFreeVarObjects(
    VARIANT *pVarObject,
    DWORD dwNumValues
    )
{
    DWORD i = 0;

    for (i = 0; i < dwNumValues; i++ ) {
         VariantClear(pVarObject + i);
    }

    FreeProvMem(pVarObject);

    return;
}

HRESULT
SampleDSTypeToVarTypeCopySampleDSSynId1(
    PSampleDSOBJECT lpSampleDSSrcObject,
    VARIANT *lpVarDestObject
    )
{
    HRESULT hr = S_OK;

    lpVarDestObject->vt = VT_BSTR;

    hr = ProvAllocString(
            lpSampleDSSrcObject->SampleDSValue.value_1.DNString,
            &(lpVarDestObject->bstrVal)
            );

    RRETURN(hr);
}

HRESULT
SampleDSTypeToVarTypeCopySampleDSSynId2(
    PSampleDSOBJECT lpSampleDSSrcObject,
    PVARIANT lpVarDestObject
    )
{
    HRESULT hr = S_OK;

    lpVarDestObject->vt = VT_I4;

    lpVarDestObject->lVal =
                lpSampleDSSrcObject->SampleDSValue.value_2.Integer;

    RRETURN(hr);

}

HRESULT
SampleDSTypeToVarTypeCopy(
    PSampleDSOBJECT lpSampleDSSrcObject,
    VARIANT* lpVarDestObject
    )
{
    HRESULT hr = S_OK;
    switch (lpSampleDSSrcObject->SampleDSType) {
    case 1:
        hr = SampleDSTypeToVarTypeCopySampleDSSynId1(
                lpSampleDSSrcObject,
                lpVarDestObject
                );
        break;
    case 2:
        hr = SampleDSTypeToVarTypeCopySampleDSSynId2(
                lpSampleDSSrcObject,
                lpVarDestObject
                );
        break;

    default:
        hr = E_FAIL;
        break;
    }

    RRETURN(hr);
}

HRESULT
SampleDSTypeToVarTypeCopyConstruct(
    LPSampleDSOBJECT pSampleDSSrcObjects,
    DWORD dwNumObjects,
    VARIANT *pVarDestObjects
    )
{
    long i = 0;
    HRESULT hr = S_OK;

    VariantInit(pVarDestObjects);

    if ( dwNumObjects == 1 )
    {
        hr = SampleDSTypeToVarTypeCopy( pSampleDSSrcObjects,
                                   pVarDestObjects );

        RRETURN(hr);
    }

    //
    // The following are for handling are multi-value properties
    //

    SAFEARRAY *aList = NULL;
    SAFEARRAYBOUND aBound;

    aBound.lLbound = 0;
    aBound.cElements = dwNumObjects;

    aList = SafeArrayCreate( VT_VARIANT, 1, &aBound );

    if ( aList == NULL )
    {
        hr = E_OUTOFMEMORY;
        BAIL_ON_FAILURE(hr);
    }

    for ( i = 0; i < (long) dwNumObjects; i++ )
    {
        VARIANT v;

        VariantInit(&v);
        hr = SampleDSTypeToVarTypeCopy( pSampleDSSrcObjects + i,
                                   &v );
        BAIL_ON_FAILURE(hr);

        hr = SafeArrayPutElement( aList, &i, &v );
        BAIL_ON_FAILURE(hr);
    }

    V_VT(pVarDestObjects) = VT_ARRAY | VT_VARIANT;
    V_ARRAY(pVarDestObjects) = aList;

    RRETURN(S_OK);

error:

    if ( aList )
        SafeArrayDestroy( aList );

    RRETURN(hr);
}

HRESULT
VarTypeToSampleDSTypeCopySampleDSSynId1(
    PVARIANT lpVarSrcObject,
    PSampleDSOBJECT lpSampleDSDestObject
    )
{
    HRESULT hr = S_OK;

    if(lpVarSrcObject->vt != VT_BSTR){
        RRETURN(hr = E_ADS_CANT_CONVERT_DATATYPE);
    }

    lpSampleDSDestObject->SampleDSType = 1;

    lpSampleDSDestObject->SampleDSValue.value_1.DNString =
                        AllocProvStr(
                            lpVarSrcObject->bstrVal
                        );

    RRETURN(hr);
}

HRESULT
VarTypeToSampleDSTypeCopySampleDSSynId2(
    PVARIANT lpVarSrcObject,
    PSampleDSOBJECT lpSampleDSDestObject
    )
{
    HRESULT hr = S_OK;

    if(lpVarSrcObject->vt != VT_I4){
        RRETURN(hr = E_ADS_CANT_CONVERT_DATATYPE);
    }

    lpSampleDSDestObject->SampleDSType = 2;

    lpSampleDSDestObject->SampleDSValue.value_2.Integer =
                        lpVarSrcObject->lVal;

    RRETURN(hr);
}

HRESULT
VarTypeToSampleDSTypeCopy(
    DWORD dwSampleDSType,
    PVARIANT lpVarSrcObject,
    PSampleDSOBJECT lpSampleDSDestObject
    )
{
    HRESULT hr = S_OK;
    switch (dwSampleDSType){
    case 1:
        hr = VarTypeToSampleDSTypeCopySampleDSSynId1(
                lpVarSrcObject,
                lpSampleDSDestObject
                );
        break;

    case 2:
        hr = VarTypeToSampleDSTypeCopySampleDSSynId2(
                lpVarSrcObject,
                lpSampleDSDestObject
                );
        break;
    default:
        hr = E_FAIL;
        break;
    }

    RRETURN(hr);
}

HRESULT
VarTypeToSampleDSTypeCopyConstruct(
    DWORD dwSampleDSType,
    LPVARIANT pVarSrcObjects,
    DWORD dwNumObjects,
    LPSampleDSOBJECT * ppSampleDSDestObjects
    )
{

    DWORD i = 0;
    LPSampleDSOBJECT pSampleDSDestObjects = NULL;
    HRESULT hr = S_OK;

    pSampleDSDestObjects = (LPSampleDSOBJECT)AllocProvMem(
                                    dwNumObjects * sizeof(SampleDSOBJECT)
                                    );

    if (!pSampleDSDestObjects) {
        RRETURN(E_FAIL);
    }

     for (i = 0; i < dwNumObjects; i++ ) {
         hr = VarTypeToSampleDSTypeCopy(
                    dwSampleDSType,
                    pVarSrcObjects + i,
                    pSampleDSDestObjects + i
                    );
         BAIL_ON_FAILURE(hr);

     }

     *ppSampleDSDestObjects = pSampleDSDestObjects;

     RRETURN(S_OK);

error:

     if (pSampleDSDestObjects) {

        SampleDSTypeFreeSampleDSObjects(
                pSampleDSDestObjects,
                dwNumObjects
                );
     }

     *ppSampleDSDestObjects = NULL;

     RRETURN(hr);
}


SMPOPER.H

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    SmpOper.h

Abstract:

Author:

Environment:

    User mode

Revision History :

--*/
#ifndef _SMPOPER_H_
#define _SMPOPER_H_

HRESULT
SampleDSTypeFreeSampleDSSynId1(
    PSampleDSOBJECT lpSampleDSDestObject
    );

HRESULT
SampleDSTypeFreeSampleDSSynId2(
    PSampleDSOBJECT lpSampleDSDestObject
    );

HRESULT
SampleDSTypeClear(
    PSampleDSOBJECT lpSampleDSDestObject
    );

void
SampleDSTypeFreeSampleDSObjects(
    PSampleDSOBJECT pSampleDSObject,
    DWORD dwNumValues
    );

HRESULT
SampleDSTypeCopySampleDSSynId1(
    PSampleDSOBJECT lpSampleDSSrcObject,
    PSampleDSOBJECT lpSampleDSDestObject
    );

HRESULT
SampleDSTypeCopySampleDSSynId2(
    PSampleDSOBJECT lpSampleDSSrcObject,
    PSampleDSOBJECT lpSampleDSDestObject
    );

HRESULT
SampleDSTypeCopy(
    PSampleDSOBJECT lpSampleDSSrcObject,
    PSampleDSOBJECT lpSampleDSDestObject
    );

HRESULT
SampleDSTypeCopyConstruct(
    LPSampleDSOBJECT pSampleDSSrcObjects,
    DWORD dwNumObjects,
    LPSampleDSOBJECT * ppSampleDSDestObjects
    );

HRESULT
UnMarshallSampleDSToSampleDSSynId(
    DWORD dwSyntaxId,
    DWORD dwNumValues,
    LPBYTE lpValue,
    PSampleDSOBJECT * ppSampleDSObject
    );

LPBYTE
CopySampleDS1ToSampleDSSynId1(
    LPBYTE lpByte,
    PSampleDSOBJECT lpSampleDSObject
    );

LPBYTE
CopySampleDS2ToSampleDSSynId2(
    LPBYTE lpByte,
    PSampleDSOBJECT lpSampleDSObject
    );

LPBYTE
CopySampleDSToSampleDSSynId(
    DWORD dwSyntaxId,
    LPBYTE lpByte,
    PSampleDSOBJECT lpSampleDSObject
    );

HRESULT
MarshallSampleDSSynIdToSampleDS(
    DWORD dwSyntaxId,
    PSampleDSOBJECT pSampleDSObject,
    DWORD dwNumValues,
    LPBYTE lpValue
    );

LPBYTE
CopySampleDSSynId1ToSampleDS1(
    LPBYTE lpByte,
    PSampleDSOBJECT lpSampleDSObject
    );

LPBYTE
CopySampleDSSynId2ToSampleDS2(
    LPBYTE lpByte,
    PSampleDSOBJECT lpSampleDSObject
    );

LPBYTE
CopySampleDSSynIdToSampleDS(
    DWORD dwSyntaxId,
    LPBYTE lpByte,
    PSampleDSOBJECT lpSampleDSObject
    );

void
VarTypeFreeVarObjects(
    VARIANT *pVarObject,
    DWORD dwNumValues
    );

HRESULT
SampleDSTypeToVarTypeCopySampleDSSynId1(
    PSampleDSOBJECT lpSampleDSSrcObject,
    VARIANT *lpVarDestObject
    );

HRESULT
SampleDSTypeToVarTypeCopySampleDSSynId2(
    PSampleDSOBJECT lpSampleDSSrcObject,
    PVARIANT lpVarDestObject
    );

HRESULT
SampleDSTypeToVarTypeCopy(
    PSampleDSOBJECT lpSampleDSSrcObject,
    VARIANT* lpVarDestObject
    );

HRESULT
SampleDSTypeToVarTypeCopyConstruct(
    LPSampleDSOBJECT pSampleDSSrcObjects,
    DWORD dwNumObjects,
    VARIANT *pVarDestObjects
    );

HRESULT
VarTypeToSampleDSTypeCopySampleDSSynId1(
    PVARIANT lpVarSrcObject,
    PSampleDSOBJECT lpSampleDSDestObject
    );

HRESULT
VarTypeToSampleDSTypeCopySampleDSSynId2(
    PVARIANT lpVarSrcObject,
    PSampleDSOBJECT lpSampleDSDestObject
    );

HRESULT
VarTypeToSampleDSTypeCopy(
    DWORD dwSampleDSType,
    PVARIANT lpVarSrcObject,
    PSampleDSOBJECT lpSampleDSDestObject
    );

HRESULT
VarTypeToSampleDSTypeCopyConstruct(
    DWORD dwSampleDSType,
    LPVARIANT pVarSrcObjects,
    DWORD dwNumObjects,
    LPSampleDSOBJECT * ppSampleDSDestObjects
    );

#endif


SMPTYPES.H

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    SmpTypes.h

Abstract:

    Definitions for Sample DS Data Types and structures used by
    the SampleDS Regsitry APIs to exchange data

Author:

Environment:

    User mode

Revision History :

--*/
#ifndef __SampleDSTYPES_H
#define __SampleDSTYPES_H

// Two data types are supported
#define SampleDS_DATATYPE_1  1
#define SampleDS_DATATYPE_2  2

typedef struct
{
    LPWSTR DNString;

} SampleDS_TYPE_1, * LPSampleDS_TYPE_1;

typedef struct
{
    DWORD Integer;

} SampleDS_TYPE_2, * LPSampleDS_TYPE_2;

typedef struct _SampleDStype{
    DWORD SampleDSType;
    union {
        SampleDS_TYPE_1 value_1;
        SampleDS_TYPE_2 value_2;
    }SampleDSValue;
}SampleDSOBJECT, *PSampleDSOBJECT, *LPSampleDSOBJECT;

#define SampleDS_MAX_TYPE1_LEN        32

typedef struct WSTR_LIST_ELEM
{
    struct WSTR_LIST_ELEM * Next;
    LPWSTR lpString;

}  WSTR_LIST,* LPWSTR_LIST;

typedef struct
{
    LPWSTR  lpAttributeName;
    DWORD   dwSyntaxID;

} SampleDS_ATTR_DEF, * LPSampleDS_ATTR_DEF;

typedef struct
{
    LPWSTR lpAttributeName;
    DWORD  dwSyntaxId;
    DWORD  dwNumberOfValues;
    LPBYTE lpValue;

} SampleDS_ATTR_INFO, * LPSampleDS_ATTR_INFO;

typedef struct
{
    LPSampleDS_ATTR_INFO pAttrInfo;
    DWORD dwAttr;
} SampleDS_ATTRS_INFO, * LPSampleDS_ATTRS_INFO;

typedef struct
{
    LPWSTR lpObjectFullName;
    LPWSTR lpObjectName;
    LPWSTR lpObjectClass;

} DS_OBJECT_INFO, * LPDS_OBJECT_INFO;

typedef struct
{
    LPWSTR lpObjectClass;

} DS_FILTER, * LPDS_FILTER;

typedef struct
{
    DWORD      dwNumberOfFilters;
    DS_FILTER Filters[1];

} DS_FILTER_LIST, * LPDS_FILTER_LIST;

typedef struct
{
    LPWSTR  lpClassName;
    DWORD   dwNumberOfMandatoryAttributes;
    LPWSTR_LIST lpMandatoryAttributes;
} SampleDS_CLASS_DEF, * LPSampleDS_CLASS_DEF;

#endif


STDFACT.CPP

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    StdFact.cpp

Abstract:

    Standard IClassFactory implementation

Author:

Environment:

    User mode

Revision History :

--*/
#include "adssmp.h"

STDMETHODIMP
StdClassFactory::QueryInterface(REFIID riid, LPVOID FAR* ppv)
{
    if (!ppv)
        RRETURN(E_INVALIDARG);

    if (IsEqualIID(riid, IID_IUnknown) ||
        IsEqualIID(riid, IID_IClassFactory))
    {
        *ppv = (IClassFactory *) this;
    }
    else
    {
        *ppv = NULL;
        return E_NOINTERFACE;
    }

    ((IUnknown *)*ppv)->AddRef();
    return S_OK;
}

STDMETHODIMP_(ULONG)
StdClassFactory::AddRef(void)
{
    ADsAssert(_ulRefs);

    if (_ulRefs == 1)
        INC_OBJECT_COUNT();

    return ++_ulRefs;
}

STDMETHODIMP_(ULONG)
StdClassFactory::Release(void)
{
    ADsAssert(_ulRefs > 1);

    if (--_ulRefs == 1)
        DEC_OBJECT_COUNT();

    return _ulRefs;
}

STDMETHODIMP
StdClassFactory::LockServer (BOOL fLock)
{
    if (fLock)
        INC_OBJECT_COUNT();
    else
        DEC_OBJECT_COUNT();
    return NOERROR;
}

CDynamicCF::CDynamicCF(void)
{
    _ulRefs = 1;

    INC_OBJECT_COUNT();
}

CDynamicCF::~CDynamicCF(void)
{
    DEC_OBJECT_COUNT();
}

STDMETHODIMP
CDynamicCF::QueryInterface(REFIID riid, LPVOID FAR* ppv)
{
    if (!ppv)
        RRETURN(E_INVALIDARG);

    if (IsEqualIID(riid, IID_IUnknown) ||
        IsEqualIID(riid, IID_IClassFactory))
    {
        *ppv = (IClassFactory *) this;
    }
    else
    {
        *ppv = NULL;
        return E_NOINTERFACE;
    }

    ((IUnknown *)*ppv)->AddRef();
    return S_OK;
}

STDMETHODIMP
CDynamicCF::LockServer (BOOL fLock)
{
    if (fLock)
    {
        _ulRefs++;
    }
    else
    {
        _ulRefs--;
    }

    return S_OK;
}

#ifdef DOCGEN

STDMETHODIMP
StdClassFactory::CreateInstance(LPUNKNOWN pUnkOuter,
        REFIID iid,
        LPVOID FAR* ppv) {};
#endif  // DOCGEN


UTIL.H

/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    Util.h

Abstract:

    Definition of Utilities for use

Author:

Environment:

    User mode

Revision History :

--*/
#ifndef _UTIL_H_
#define _UTIL_H_

#include "formtrck.h"

//+---------------------------------------------------------------------
//
//   Generally useful #defines and inline functions for OLE2.
//
//------------------------------------------------------------------------

// These are the major and minor version returned by OleBuildVersion
#define OLE_MAJ_VER 0x0003
#define OLE_MIN_VER 0x003A

//---------------------------------------------------------------
//  SCODE and HRESULT macros
//---------------------------------------------------------------

#define OK(r)       (SUCCEEDED(r))
#define NOTOK(r)    (FAILED(r))

//---------------------------------------------------------------
//  IUnknown
//---------------------------------------------------------------

#define InterlockedIncrement(plong) (++(*(plong)))
#define InterlockedDecrement(plong) (--(*(plong)))

#define ADsIncrement(__ul) InterlockedIncrement((long *) &__ul)
#define ADsDecrement(__ul) InterlockedDecrement((long *) &__ul)

#define DECLARE_ADs_IUNKNOWN_METHODS                              \
    STDMETHOD(QueryInterface) (REFIID riid, LPVOID * ppv);          \
    STDMETHOD_(ULONG, AddRef) (void);                               \
    STDMETHOD_(ULONG, Release) (void);

#define DECLARE_ADs_STANDARD_IUNKNOWN(cls)                        \
    STDMETHOD(QueryInterface) (REFIID riid, LPVOID * ppv);          \
    ULONG _ulRefs;                                                  \
    STDMETHOD_(ULONG, AddRef) (void)                                \
        {                                                           \
            ADsIncrement(_ulRefs);                                \
            return _ulRefs;                                         \
        }                                                           \
    STDMETHOD_(ULONG, Release) (void)                               \
        {                                                           \
            if (!ADsDecrement(_ulRefs))                           \
            {                                                       \
                ADsIncrement(_ulRefs);                            \
                delete this;                                        \
                return 0;                                           \
            }                                                       \
            return _ulRefs;                                         \
        }

#define DECLARE_ADs_DELEGATING_IUNKNOWN(cls)                      \
    IUnknown * _pUnkOuter;                                          \
    STDMETHOD(QueryInterface) (REFIID iid, LPVOID * ppv)            \
        { return _pUnkOuter->QueryInterface(iid, ppv); }            \
    STDMETHOD_(ULONG, AddRef) (void)                                \
        { return _pUnkOuter->AddRef(); }                            \
    STDMETHOD_(ULONG, Release) (void)                               \
        { return _pUnkOuter->Release(); }

#if DBG == 0
//
// Retail versions of these macros
//

#define DECLARE_ADs_PRIVATE_IUNKNOWN(cls)                         \
    class PrivateUnknown : public IUnknown                          \
    {                                                               \
    private:                                                        \
        ULONG   _ulRefs;                                            \
        cls *   My##cls(void)                                       \
            { return CONTAINING_RECORD(this, cls, _PrivUnk); }      \
                                                                    \
    public:                                                         \
        PrivateUnknown(void)                                        \
            { _ulRefs = 1; }                                        \
                                                                    \
        DECLARE_ADs_IUNKNOWN_METHODS                              \
    };                                                              \
    friend class PrivateUnknown;                                    \
    PrivateUnknown _PrivUnk;

#define IMPLEMENT_ADs_PRIVATE_IUNKNOWN(cls)                       \
    STDMETHODIMP_(ULONG) cls::PrivateUnknown::AddRef( )             \
    {                                                               \
        ADsIncrement(_ulRefs);                                    \
        return _ulRefs;                                             \
    }                                                               \
    STDMETHODIMP_(ULONG) cls::PrivateUnknown::Release( )            \
    {                                                               \
        if (!ADsDecrement(_ulRefs))                               \
        {                                                           \
            ADsIncrement(_ulRefs);                                \
            delete My##cls();                                       \
            return 0;                                               \
        }                                                           \
        return _ulRefs;                                             \
    }

#define DECLARE_ADs_COMPOUND_IUNKNOWN(cls)                        \
    class PrivateUnknown : public IUnknown                          \
    {                                                               \
        friend class cls;                                           \
                                                                    \
    public:                                                         \
        PrivateUnknown(void)                                        \
            { _ulRefs = 1; _ulAllRefs = 1; }                        \
                                                                    \
        DECLARE_ADs_IUNKNOWN_METHODS                              \
                                                                    \
    private:                                                        \
        ULONG   _ulRefs;                                            \
        ULONG   _ulAllRefs;                                         \
                                                                    \
        cls *   My##cls(void)                                       \
            { return CONTAINING_RECORD(this, cls, _PrivUnk); }      \
    };                                                              \
    friend class PrivateUnknown;                                    \
    PrivateUnknown _PrivUnk;                                        \
                                                                    \
    ULONG   SubAddRef(void);                                        \
    ULONG   SubRelease(void);

#define IMPLEMENT_ADs_COMPOUND_IUNKNOWN(cls)                      \
    STDMETHODIMP_(ULONG) cls::PrivateUnknown::AddRef( )             \
    {                                                               \
        ADsIncrement(_ulAllRefs);                                 \
        ADsIncrement(_ulRefs);                                    \
        return _ulRefs;                                             \
    }                                                               \
    STDMETHODIMP_(ULONG) cls::PrivateUnknown::Release( )            \
    {                                                               \
        if (!ADsDecrement(_ulRefs))                               \
        {                                                           \
            My##cls()->Passivate();                                 \
        }                                                           \
        if (!ADsDecrement(_ulAllRefs))                            \
        {                                                           \
            ADsIncrement(_ulAllRefs);                             \
            delete My##cls();                                       \
            return 0;                                               \
        }                                                           \
        return _ulRefs;                                             \
    }                                                               \
    ULONG cls::SubAddRef( )                                         \
    {                                                               \
        return ADsIncrement(_PrivUnk._ulAllRefs);                 \
    }                                                               \
    ULONG cls::SubRelease( )                                        \
    {                                                               \
        ULONG ul;                                                   \
                                                                    \
        ul = ADsDecrement(_PrivUnk._ulAllRefs);                   \
        if (!ul)                                                    \
        {                                                           \
            ADsIncrement(_PrivUnk._ulAllRefs);                    \
            delete this;                                            \
        }                                                           \
                                                                    \
        return ul;                                                  \
    }

#else  // DBG == 0

//
// Debug versions of these macros
//

#define DECLARE_ADs_PRIVATE_IUNKNOWN(cls)                         \
    class PrivateUnknown : protected ObjectTracker,                 \
                           public IUnknown                          \
    {                                                               \
    private:                                                        \
        cls *   My##cls(void)                                       \
            { return CONTAINING_RECORD(this, cls, _PrivUnk); }      \
                                                                    \
    public:                                                         \
        PrivateUnknown(void)                                        \
            { _ulRefs = 1; TrackClassName(#cls); }                  \
                                                                    \
        DECLARE_ADs_IUNKNOWN_METHODS                              \
    };                                                              \
    friend class PrivateUnknown;                                    \
    PrivateUnknown _PrivUnk;

#define IMPLEMENT_ADs_PRIVATE_IUNKNOWN(cls)                       \
    STDMETHODIMP_(ULONG) cls::PrivateUnknown::AddRef( )             \
    {                                                               \
        StdAddRef();                                                \
        return _ulRefs;                                             \
    }                                                               \
    STDMETHODIMP_(ULONG) cls::PrivateUnknown::Release( )            \
    {                                                               \
        if (!StdRelease())                                          \
        {                                                           \
            ADsIncrement(_ulRefs);                                \
            delete My##cls();                                       \
            return 0;                                               \
        }                                                           \
        return _ulRefs;                                             \
    }

#define DECLARE_ADs_COMPOUND_IUNKNOWN(cls)                        \
    class PrivateUnknown : protected ObjectTracker,                 \
                           public IUnknown                          \
    {                                                               \
        friend class cls;                                           \
                                                                    \
    public:                                                         \
        PrivateUnknown(void)                                        \
            { _ulNRefs = 1; _ulRefs = 1; TrackClassName(#cls); }    \
                                                                    \
        DECLARE_ADs_IUNKNOWN_METHODS                              \
                                                                    \
    private:                                                        \
        ULONG   _ulNRefs;                                           \
                                                                    \
        cls *   My##cls(void)                                       \
            { return CONTAINING_RECORD(this, cls, _PrivUnk); }      \
    };                                                              \
    friend class PrivateUnknown;                                    \
    PrivateUnknown _PrivUnk;                                        \
                                                                    \
    ULONG   SubAddRef(void);                                        \
    ULONG   SubRelease(void);

#define IMPLEMENT_ADs_COMPOUND_IUNKNOWN(cls)                      \
    STDMETHODIMP_(ULONG) cls::PrivateUnknown::AddRef( )             \
    {                                                               \
        StdAddRef();                                                \
        ADsIncrement(_ulNRefs);                                   \
        return _ulNRefs;                                            \
    }                                                               \
    STDMETHODIMP_(ULONG) cls::PrivateUnknown::Release( )            \
    {                                                               \
        if (!ADsDecrement(_ulNRefs))                              \
        {                                                           \
            My##cls()->Passivate();                                 \
        }                                                           \
        if (!StdRelease())                                          \
        {                                                           \
            ADsIncrement(_ulRefs);                                \
            delete My##cls();                                       \
            return 0;                                               \
        }                                                           \
        return _ulNRefs;                                            \
    }                                                               \
    ULONG cls::SubAddRef( )                                         \
    {                                                               \
        return _PrivUnk.StdAddRef();                                \
    }                                                               \
    ULONG cls::SubRelease( )                                        \
    {                                                               \
        ULONG ul;                                                   \
                                                                    \
        ul = _PrivUnk.StdRelease();                                 \
        if (!ul)                                                    \
        {                                                           \
            ADsIncrement(_PrivUnk._ulRefs);                       \
            delete this;                                            \
        }                                                           \
                                                                    \
        return ul;                                                  \
    }

#endif // DBG == 0

#define DECLARE_ADs_SUBOBJECT_IUNKNOWN(cls, parent_cls, member)   \
    DECLARE_ADs_IUNKNOWN_METHODS                                  \
    parent_cls * My##parent_cls(void);                              \
    void Detach(void)                                               \
        { ; }

#define IMPLEMENT_ADs_SUBOBJECT_IUNKNOWN(cls, parent_cls, member) \
    inline parent_cls * cls::My##parent_cls(void)                   \
    {                                                               \
        return CONTAINING_RECORD(this, parent_cls, member);         \
    }                                                               \
    STDMETHODIMP_(ULONG) cls::AddRef( )                             \
        { return My##parent_cls()->SubAddRef(); }                   \
    STDMETHODIMP_(ULONG) cls::Release( )                            \
        { return My##parent_cls()->SubRelease(); }

//+------------------------------------------------------------------------
//
//  NO_COPY *declares* the constructors and assignment operator for copying.
//  By not *defining* these functions, you can prevent your class from
//  accidentally being copied or assigned -- you will be notified by
//  a linkage error.
//
//-------------------------------------------------------------------------

#define NO_COPY(cls)    \
    cls(const cls&);    \
    cls& operator=(const cls&);

//+---------------------------------------------------------------------
//
//  Miscellaneous useful OLE helper and debugging functions
//
//----------------------------------------------------------------------

//
//  Some convenient OLE-related definitions and declarations
//

typedef  unsigned short far * LPUSHORT;

#define OLEMISC_STREAMABLE 1024

IsCompatibleOleVersion(WORD wMaj, WORD wMin);

//#if DBG == 1
#if 0
STDAPI  CheckAndReturnResult(
                HRESULT hr,
                LPSTR   lpstrFile,
                UINT    line,
                int     cSuccess,
                ...);

STDAPI_(void)   CheckResult(HRESULT hr, LPSTR lpstrFile, UINT line);
STDAPI_(void)   PrintIID(DWORD dwFlags, REFIID riid);
STDAPI          PrintHRESULT(DWORD dwFlags, HRESULT hr);

#define SRETURN(hr) \
    return CheckAndReturnResult((hr), __FILE__, __LINE__, -1)
#define RRETURN(hr) \
    return CheckAndReturnResult((hr), __FILE__, __LINE__, 0)
#define RRETURN1(hr, s1) \
    return CheckAndReturnResult((hr), __FILE__, __LINE__, 1, (s1))
#define RRETURN2(hr, s1, s2) \
    return CheckAndReturnResult((hr), __FILE__, __LINE__, 2, (s1), (s2))
#define RRETURN3(hr, s1, s2, s3) \
    return CheckAndReturnResult((hr), __FILE__, __LINE__, 3, (s1), (s2), (s3))

#define WARN_ERROR(hr)  CheckResult((hr), __FILE__, __LINE__)

#define TRETURN(hr)         return PrintHRESULT(DEB_TRACE, (hr))
#define TRACEIID(iid)       PrintIID(DEB_TRACE, iid)
#define TRACEHRESULT(hr)    PrintHRESULT(DEB_TRACE, (hr))

#else   // DBG == 0

#define SRETURN(hr)                 return (hr)
#define RRETURN(hr)                 return (hr)
#define RRETURN1(hr, s1)            return (hr)
#define RRETURN2(hr, s1, s2)        return (hr)
#define RRETURN3(hr, s1, s2, s3)    return (hr)

#define WARN_ERROR(hr)

#define TRETURN(hr)     return (hr)
#define TRACEIID(iid)
#define TRACEHRESULT(hr)

#endif  // DBG

//+---------------------------------------------------------------------
//
//  Interface wrapper for tracing method invocations
//
//----------------------------------------------------------------------

#if DBG == 1

LPVOID WatchInterface(REFIID riid, LPVOID pv, LPWSTR lpstr);
#define WATCHINTERFACE(iid, p, lpstr)  WatchInterface(iid, p, lpstr)

#else   // DBG == 0

#define WATCHINTERFACE(iid, p, lpstr)  (p)

#endif  // DBG

//+---------------------------------------------------------------------
//
//  Standard IClassFactory implementation
//
//----------------------------------------------------------------------

//+---------------------------------------------------------------
//
//  Class:      StdClassFactory
//
//  Purpose:    Standard implementation of a class factory object
//
//  Notes:          **************!!!!!!!!!!!!!!!!!*************
//              TAKE NOTE --- The implementation of Release on this
//              class does not perform a delete.  This is so you can
//              make the class factory a global static variable.
//              Use the CDynamicCF class below for an object
//              which is not global static data.
//
//              ALSO - The refcount is initialized to 0, NOT 1!
//
//---------------------------------------------------------------

class StdClassFactory: public IClassFactory
{
public:
    StdClassFactory(void) : _ulRefs(1) {};

    // IUnknown methods
    DECLARE_ADs_IUNKNOWN_METHODS;

    // IClassFactory methods
    STDMETHOD(LockServer) (BOOL fLock);

    // CreateInstance is left pure virtual.

protected:
    ULONG _ulRefs;
};

//+---------------------------------------------------------------------------
//
//  Class:      CDynamicCF (DYNCF)
//
//  Purpose:    Class factory which exists on the heap, and whose Release
//              method does the normal thing.
//
//  Interface:  DECLARE_ADs_STANDARD_IUNKNOWN -- IUnknown methods
//
//              LockServer             -- Per IClassFactory.
//              CDynamicCF             -- ctor.
//              ~CDynamicCF            -- dtor.
//
//  History:    6-22-94   adams   Created
//              7-13-94   adams   Moved from ADs\inc\dyncf.hxx
//
//----------------------------------------------------------------------------

class CDynamicCF: public IClassFactory
{
public:
    // IUnknown methods
    DECLARE_ADs_STANDARD_IUNKNOWN(CDynamicCF)

    // IClassFactory methods
    STDMETHOD(LockServer) (BOOL fLock);

protected:
            CDynamicCF(void);
    virtual ~CDynamicCF(void);
};

#endif //__UTILS_H_


README.TXT

DLC

SUMMARY
=======

The SIMPLEX sample is a single-threaded DLC program. You need two instances
of this application: one to send and one to receive. This is the typical DLC
situation: simplex or half-duplex. By default, both sides use SAP 4.

MORE INFORMATION
================

Start the receiver with the command:

    simplex

Start the transmitter with the command:

    simplex /t02608c4c970e

In this example, the node address is in canonical form (ethernet format) as
displayed by the command "net config wksta", not the token-ring format that
the DLC API expects. If the test application is being run over a token ring,
then you would supply the token-ring format:

    simplex /t10005a7b08b4

Command line options are:

/a# - use adapter #
/b# - change the buffer pool size from the default 20K to #
/o  - options:
/or# - set receive READ option
/ot# - set transmit READ option
/r# - send to remote SAP # (transmitter only)
/s# - open local SAP #
/t# - send to station address # (transmitter only)
/z# - transmit packets of size #, else use random size (transmitter only)


DLCDEBUG.H

/*++

Copyright 1991-1998 Microsoft Corporation

Module Name:

    dlcdebug.h

Abstract:

    Contains debugging prototypes and manifests for ACSLAN

Author:

    Richard L Firth (rfirth) 28-May-1992

Revision History:

--*/

#define ARRAY_ELEMENTS(a)   (sizeof(a)/sizeof((a)[0]))
#define LAST_ELEMENT(a)     (ARRAY_ELEMENTS(a)-1)

#if DBG

#define PRIVATE

#define ACSLAN_DEBUG_ENV_VAR    "ACSLAN_DEBUG_FLAGS"
#define ACSLAN_DUMP_FILE_VAR    "ACSLAN_DUMP_FILE"
#define ACSLAN_DUMP_FILTER_VAR  "ACSLAN_DUMP_FILTER"

#define DEBUG_DUMP_INPUT_CCB    0x00000001L // dump CCB input to AcsLan
#define DEBUG_DUMP_OUTPUT_CCB   0x00000002L // dump CCB output from AcsLan
#define DEBUG_DUMP_TX_INFO      0x00000004L // dump transmit buffers
#define DEBUG_DUMP_RX_INFO      0x00000008L // dump receive buffers
#define DEBUG_DUMP_TX_DATA      0x00000010L // dump data buffer in TRANSMIT commands
#define DEBUG_DUMP_RX_DATA      0x00000020L // dump received data frames
#define DEBUG_DUMP_DATA_CHAIN   0x00000040L // dump entire chain of received data buffers
#define DEBUG_DUMP_FRAME_CHAIN  0x00000080L // dump entire chain of received frames
#define DEBUG_DUMP_TX_ASCII     0x00000100L // dump transmitted data as hex & ASCII
#define DEBUG_DUMP_RX_ASCII     0x00000200L // dump received data as hex & ASCII
#define DEBUG_DUMP_ASYNC_CCBS   0x00000400L // dump READ async. completed CCBs
#define DEBUG_RETURN_CODE       0x01000000L // dump return code from AcsLan/NtAcsLan
#define DEBUG_DUMP_NTACSLAN     0x02000000L // dump CCBs for NtAcsLan, not AcsLan
#define DEBUG_DUMP_ACSLAN       0x04000000L // dump CCBs for AcsLan, not NtAcsLan
#define DEBUG_DUMP_TIME         0x08000000L // dump relative time between commands
#define DEBUG_DLL_INFO          0x10000000L // dump info about DLL attach/detach
#define DEBUG_BREAKPOINT        0x20000000L // break at conditional breakpoints
#define DEBUG_TO_FILE           0x40000000L // dump info to file
#define DEBUG_TO_TERMINAL       0x80000000L // dump info to console

#define IF_DEBUG(c)             if (AcslanDebugFlags & DEBUG_##c)
#define PUT(x)                  AcslanDebugPrint x
#define DUMPCCB                 DumpCcb

//
// misc.
//

#define DEFAULT_FIELD_WIDTH     16          // amount of description before a number

//
// DumpData options
//

#define DD_DEFAULT_OPTIONS      0x00000000  // use defaults
#define DD_NO_ADDRESS           0x00000001  // don't display address of data
#define DD_LINE_BEFORE          0x00000002  // linefeed before first dumped line
#define DD_LINE_AFTER           0x00000004  // linefeed after last dumped line
#define DD_INDENT_ALL           0x00000008  // indent all lines
#define DD_NO_ASCII             0x00000010  // don't dump ASCII respresentation
#define DD_UPPER_CASE           0x00000020  // upper-case hex dump (F4 instead of f4)
#define DD_DOT_DOT_SPACE        0x00000040  // fill unused hex space with '..'

//
// Filters for individual CCB commands: 4 flags max, because each command is
// represented by a single ASCII character
//

#define CF_DUMP_CCB_IN          0x00000001  // dump CCB on input
#define CF_DUMP_CCB_OUT         0x00000002  // dump CCB on output
#define CF_DUMP_PARMS_IN        0x00000004  // dump parameter table on input
#define CF_DUMP_PARMS_OUT       0x00000008  // dump parameter table on output

//
// global data
//

#ifndef ACSLAN_DEBUG_FLAGS
#define ACSLAN_DEBUG_FLAGS      (DEBUG_DUMP_INPUT_CCB \
                                 | DEBUG_DUMP_OUTPUT_CCB \
                                 | DEBUG_DUMP_TX_INFO \
                                 | DEBUG_DUMP_RX_INFO \
                                 | DEBUG_DUMP_TX_DATA \
                                 | DEBUG_DUMP_RX_DATA \
                                 | DEBUG_DUMP_DATA_CHAIN \
                                 | DEBUG_DUMP_FRAME_CHAIN \
                                 | DEBUG_DUMP_TX_ASCII \
                                 | DEBUG_DUMP_RX_ASCII \
                                 | DEBUG_DUMP_ASYNC_CCBS \
                                 | DEBUG_RETURN_CODE \
                                 | DEBUG_DUMP_NTACSLAN \
                                 | DEBUG_DUMP_ACSLAN \
                                 | DEBUG_DUMP_TIME \
                                 | DEBUG_TO_TERMINAL \
                                 )
#endif

extern DWORD AcslanDebugFlags;
extern FILE* hDumpFile;

//
// prototypes
//

VOID
GetAcslanDebugFlags(
    VOID
    );

VOID
SetAcslanDebugFlags(
    IN DWORD Flags
    );

VOID
AcslanDebugPrint(
    IN LPSTR Format,
    IN ...
    );

VOID
DumpCcb(
    IN PLLC_CCB Ccb,
    IN BOOL DumpAll,
    IN BOOL CcbIsInput
    );

VOID
DumpData(
    IN LPSTR Title,
    IN PBYTE Address,
    IN DWORD Length,
    IN DWORD Options,
    IN DWORD Indent
    );

LPSTR
MapCcbRetcode(
    IN BYTE Retcode
    );

#else

#define PRIVATE                 static

#define IF_DEBUG(c)             if (0)
#define PUT(x)
#define DUMPCCB                 (void)

#endif


DLCDEBUG.C

/*++

Copyright 1991-1998 Microsoft Corporation

Module Name:

    dlcdebug.c

Abstract:

    CCB dump routines for NT-level CCB

    Contents:
        GetAcslanDebugFlags
        SetAcslanDebugFlags
        AcslanDebugPrint
        AcslanDebugPrintString
        DumpCcb
        MapCcbRetcode
        DumpData
        (DefaultParameterTableDump)
        (DumpParameterTableHeader)
        (DumpBufferCreateParms)
        (DumpBufferFreeParms)
        (DumpBufferGetParms)
        (DumpDirInitializeParms)
        (DumpDirOpenAdapterParms)
        (MapEthernetType)
        (DumpDirOpenDirectParms)
        (DumpDirReadLogParms)
        (MapLogType)
        (DumpDirSetExceptionFlagsParms)
        (DumpDirSetFunctionalAddressParms)
        (DumpDirSetGroupAddressParms)
        (DumpDirStatusParms)
        (MapAdapterType)
        (DumpDirTimerCancelParms)
        (DumpDirTimerCancelGroupParms)
        (DumpDirTimerSetParms)
        (DumpDlcCloseSapParms)
        (DumpDlcCloseStationParms)
        (DumpDlcConnectStationParms)
        (DumpDlcFlowControlParms)
        (MapFlowControl)
        (DumpDlcModifyParms)
        (DumpDlcOpenSapParms)
        (DumpDlcOpenStationParms)
        (DumpDlcReallocateParms)
        (DumpDlcResetParms)
        (DumpDlcSetThresholdParms)
        (DumpDlcStatisticsParms)
        (DumpReadParms)
        (MapOptionIndicator)
        (MapReadEvent)
        (MapDlcStatus)
        (DumpReadCancelParms)
        (DumpReceiveParms)
        (MapRcvReadOption)
        (MapReceiveOptions)
        (DumpReceiveCancelParms)
        (DumpReceiveModifyParms)
        (DumpTransmitDirFrameParms)
        (DumpTransmitIFrameParms)
        (DumpTransmitTestCmdParms)
        (DumpTransmitUiFrameParms)
        (DumpTransmitXidCmdParms)
        (DumpTransmitXidRespFinalParms)
        (DumpTransmitXidRespNotFinalParms)
        (DumpTransmitFramesParms)
        (DumpTransmitParms)
        (DumpTransmitQueue)
        DumpReceiveDataBuffer
        (MapMessageType)

Author:

    Richard L Firth (rfirth) 28-May-1992

Revision History:

--*/

#if DBG

// #include <nt.h>
// #include <ntrtl.h>      // ASSERT, DbgPrint
// #include <nturtl.h>
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <dlcapi.h>     // Official DLC API definition
#include "dlcdebug.h"

//
// defines
//

#define DBGDBG  0
#define MyDbgPrint  puts

//
// macros
//

//
// HEX_TO_NUM - converts an ASCII hex character (0-9A-Fa-f) to corresponding
// number (0-15). Assumes c is hexadecimal digit on input
//

#define HEX_TO_NUM(c)   ((c) - ('0' + (((c) <= '9') ? 0 : ((islower(c) ? 'a' : 'A') - ('9' + 1)))))

//
// local prototypes
//

VOID
AcslanDebugPrintString(
    IN LPSTR String
    );

PRIVATE
VOID
DefaultParameterTableDump(
    IN PVOID Parameters
    );

PRIVATE
VOID
DumpParameterTableHeader(
    IN LPSTR CommandName,
    IN PVOID Table
    );

PRIVATE
VOID
DumpBufferCreateParms(
    IN PVOID Parameters
    );

PRIVATE
VOID
DumpBufferFreeParms(
    IN PVOID Parameters
    );

PRIVATE
VOID
DumpBufferGetParms(
    IN PVOID Parameters
    );

PRIVATE
VOID
DumpDirInitializeParms(
    IN PVOID Parameters
    );

PRIVATE
VOID
DumpDirOpenAdapterParms(
    IN PVOID Parameters
    );

PRIVATE
LPSTR
MapEthernetType(
    IN LLC_ETHERNET_TYPE EthernetType
    );

PRIVATE
VOID
DumpDirOpenDirectParms(
    IN PVOID Parameters
    );

PRIVATE
VOID
DumpDirReadLogParms(
    IN PVOID Parameters
    );

PRIVATE
LPSTR
MapLogType(
    IN USHORT Type
    );

PRIVATE
VOID
DumpDirSetExceptionFlagsParms(
    IN PVOID Parameters
    );

PRIVATE
VOID
DumpDirSetFunctionalAddressParms(
    IN PVOID Parameters
    );

PRIVATE
VOID
DumpDirSetGroupAddressParms(
    IN PVOID Parameters
    );

PRIVATE
VOID
DumpDirStatusParms(
    IN PVOID Parameters
    );

PRIVATE
LPSTR
MapAdapterType(
    IN USHORT AdapterType
    );

PRIVATE
VOID
DumpDirTimerCancelParms(
    IN PVOID Parameters
    );

PRIVATE
VOID
DumpDirTimerCancelGroupParms(
    IN PVOID Parameters
    );

PRIVATE
VOID
DumpDirTimerSetParms(
    IN PVOID Parameters
    );

PRIVATE
VOID
DumpDlcCloseSapParms(
    IN PVOID Parameters
    );

PRIVATE
VOID
DumpDlcCloseStationParms(
    IN PVOID Parameters
    );

PRIVATE
VOID
DumpDlcConnectStationParms(
    IN PVOID Parameters
    );

PRIVATE
VOID
DumpDlcFlowControlParms(
    IN PVOID Parameters
    );

PRIVATE
LPSTR
MapFlowControl(
    IN BYTE FlowControl
    );

PRIVATE
VOID
DumpDlcModifyParms(
    IN PVOID Parameters
    );

PRIVATE
VOID
DumpDlcOpenSapParms(
    IN PVOID Parameters
    );

PRIVATE
VOID
DumpDlcOpenStationParms(
    IN PVOID Parameters
    );

PRIVATE
VOID
DumpDlcReallocateParms(
    IN PVOID Parameters
    );

PRIVATE
VOID
DumpDlcResetParms(
    IN PVOID Parameters
    );

PRIVATE
VOID
DumpDlcSetThresholdParms(
    IN PVOID Parameters
    );

PRIVATE
VOID
DumpDlcStatisticsParms(
    IN PVOID Parameters
    );

PRIVATE
VOID
DumpReadParms(
    IN PVOID Parameters
    );

PRIVATE
LPSTR
MapOptionIndicator(
    IN UCHAR OptionIndicator
    );

PRIVATE
LPSTR
MapReadEvent(
    UCHAR Event
    );

PRIVATE
LPSTR
MapDlcStatus(
    WORD Status
    );

PRIVATE
VOID
DumpReadCancelParms(
    IN PVOID Parameters
    );

PRIVATE
VOID
DumpReceiveParms(
    IN PVOID Parameters
    );

PRIVATE
LPSTR
MapReceiveOptions(
    IN UCHAR Options
    );

PRIVATE
LPSTR
MapRcvReadOption(
    IN UCHAR Option
    );

PRIVATE
VOID
DumpReceiveCancelParms(
    IN PVOID Parameters
    );

PRIVATE
VOID
DumpReceiveModifyParms(
    IN PVOID Parameters
    );

PRIVATE
VOID
DumpTransmitDirFrameParms(
    IN PVOID Parameters
    );

PRIVATE
VOID
DumpTransmitIFrameParms(
    IN PVOID Parameters
    );

PRIVATE
VOID
DumpTransmitTestCmdParms(
    IN PVOID Parameters
    );

PRIVATE
VOID
DumpTransmitUiFrameParms(
    IN PVOID Parameters
    );

PRIVATE
VOID
DumpTransmitXidCmdParms(
    IN PVOID Parameters
    );

PRIVATE
VOID
DumpTransmitXidRespFinalParms(
    IN PVOID Parameters
    );

PRIVATE
VOID
DumpTransmitXidRespNotFinalParms(
    IN PVOID Parameters
    );

PRIVATE
VOID
DumpTransmitFramesParms(
    IN PVOID Parameters
    );

PRIVATE
VOID
DumpTransmitParms(
    IN PVOID Parameters
    );

PRIVATE
VOID
DumpTransmitQueue(
    IN PLLC_XMIT_BUFFER Buffer
    );

PRIVATE
LPSTR
MapXmitReadOption(
    IN UCHAR Option
    );

VOID
DumpReceiveDataBuffer(
    IN PVOID Buffer
    );

PRIVATE
LPSTR
MapMessageType(
    UCHAR MessageType
    );

//
// explanations of error codes returned in CCB_RETCODE fields. Explanations
// taken more-or-less verbatim from IBM Local Area Network Technical Reference
// table B-1 ppB-2 to B-5. Includes all errors
//

static LPSTR CcbRetcodeExplanations[] = {
    "Success",
    "Invalid command code",
    "Duplicate command, one already outstanding",
    "Adapter open, should be closed",
    "Adapter closed, should be open",
    "Required parameter missing",
    "Invalid/incompatible option",
    "Command cancelled - unrecoverable failure",
    "Unauthorized access priority",
    "Adapter not initialized, should be",
    "Command cancelled by user request",
    "Command cancelled, adapter closed while command in progress",
    "Command completed Ok, adapter not open",
    "Invalid error code 0x0D",
    "Invalid error code 0x0E",
    "Invalid error code 0x0F",
    "Adapter open, NetBIOS not operational",
    "Error in DIR.TIMER.SET or DIR.TIMER.CANCEL",
    "Available work area exceeded",
    "Invalid LOG.ID",
    "Invalid shared RAM segment or size",
    "Lost log data, buffer too small, log reset",
    "Requested buffer size exceeds pool length",
    "Command invalid, NetBIOS operational",
    "Invalid SAP buffer length",
    "Inadequate buffers available for request",
    "USER_LENGTH value too large for buffer length",
    "The CCB_PARM_TAB pointer is invalid",
    "A pointer in the CCB parameter table is invalid",
    "Invalid CCB_ADAPTER value",
    "Invalid functional address",
    "Invalid error code 0x1F",
    "Lost data on receive, no buffers available",
    "Lost data on receive, inadequate buffer space",
    "Error on frame transmission, check TRANSMIT.FS data",
    "Error on frame transmit or strip process",
    "Unauthorized MAC frame",
    "Maximum commands exceeded",
    "Unrecognized command correlator",
    "Link not transmitting I frames, state changed from link opened",
    "Invalid transmit frame length",
    "Invalid error code 0x29",
    "Invalid error code 0x2a",
    "Invalid error code 0x2b",
    "Invalid error code 0x2c",
    "Invalid error code 0x2d",
    "Invalid error code 0x2e",
    "Invalid error code 0x2f",
    "Inadequate receive buffers for adapter to open",
    "Invalid error code 0x31",
    "Invalid NODE_ADDRESS",
    "Invalid adapter receive buffer length defined",
    "Invalid adapter transmit buffer length defined",
    "Invalid error code 0x35",
    "Invalid error code 0x36",
    "Invalid error code 0x37",
    "Invalid error code 0x38",
    "Invalid error code 0x39",
    "Invalid error code 0x3a",
    "Invalid error code 0x3b",
    "Invalid error code 0x3c",
    "Invalid error code 0x3d",
    "Invalid error code 0x3e",
    "Invalid error code 0x3f",
    "Invalid STATION_ID",
    "Protocol error, link in invalid state for command",
    "Parameter exceeded maximum allowed",
    "Invalid SAP value or value already in use",
    "Invalid routing information field",
    "Requested group membership in non-existent group SAP",
    "Resources not available",
    "Sap cannot close unless all link stations are closed",
    "Group SAP cannot close, individual SAPs not closed",
    "Group SAP has reached maximum membership",
    "Sequence error, incompatible command in progress",
    "Station closed without remote acknowledgement",
    "Sequence error, cannot close, DLC commands outstanding",
    "Unsuccessful link station connection attempted",
    "Member SAP not found in group SAP list",
    "Invalid remote address, may not be a group address",
    "Invalid pointer in CCB_POINTER field",
    "Invalid error code 0x51",
    "Invalid application program ID",
    "Invalid application program key code",
    "Invalid system key code",
    "Buffer is smaller than buffer size given in DLC.OPEN.SAP",
    "Adapter's system process is not installed",
    "Inadequate stations available",
    "Invalid CCB_PARAMETER_1 parameter",
    "Inadequate queue elements to satisfy request",
    "Initialization failure, cannot open adapter",
    "Error detected in chained READ command",
    "Direct stations not assigned to application program",
    "Dd interface not installed",
    "Requested adapter is not installed",
    "Chained CCBs must all be for same adapter",
    "Adapter initializing, command not accepted",
    "Number of allowed application programs has been exceeded",
    "Command cancelled by system action",
    "Direct stations not available",
    "Invalid DDNAME parameter",
    "Inadequate GDT selectors to satisfy request",
    "Invalid error code 0x66",
    "Command cancelled, CCB resources purged",
    "Application program ID not valid for interface",
    "Segment associated with request cannot be locked",
    "Invalid error code 0x6a",
    "Invalid error code 0x6b",
    "Invalid error code 0x6c",
    "Invalid error code 0x6d",
    "Invalid error code 0x6e",
    "Invalid error code 0x6f",
    "Invalid error code 0x70",
    "Invalid error code 0x71",
    "Invalid error code 0x72",
    "Invalid error code 0x73",
    "Invalid error code 0x74",
    "Invalid error code 0x75",
    "Invalid error code 0x76",
    "Invalid error code 0x77",
    "Invalid error code 0x78",
    "Invalid error code 0x79",
    "Invalid error code 0x7a",
    "Invalid error code 0x7b",
    "Invalid error code 0x7c",
    "Invalid error code 0x7d",
    "Invalid error code 0x7e",
    "Invalid error code 0x7f",
    "Invalid buffer address",
    "Buffer already released",
    "Invalid error code 0x82",
    "Invalid error code 0x83",
    "Invalid error code 0x84",
    "Invalid error code 0x85",
    "Invalid error code 0x86",
    "Invalid error code 0x87",
    "Invalid error code 0x88",
    "Invalid error code 0x89",
    "Invalid error code 0x8a",
    "Invalid error code 0x8b",
    "Invalid error code 0x8c",
    "Invalid error code 0x8d",
    "Invalid error code 0x8e",
    "Invalid error code 0x8f",
    "Invalid error code 0x90",
    "Invalid error code 0x91",
    "Invalid error code 0x92",
    "Invalid error code 0x93",
    "Invalid error code 0x94",
    "Invalid error code 0x95",
    "Invalid error code 0x96",
    "Invalid error code 0x97",
    "Invalid error code 0x98",
    "Invalid error code 0x99",
    "Invalid error code 0x9a",
    "Invalid error code 0x9b",
    "Invalid error code 0x9c",
    "Invalid error code 0x9d",
    "Invalid error code 0x9e",
    "Invalid error code 0x9f",
    "BIND error",
    "Invalid version",
    "NT Error status"
};

#define NUMBER_OF_ERROR_MESSAGES    ARRAY_ELEMENTS(CcbRetcodeExplanations)
#define LAST_DLC_ERROR_CODE         LAST_ELEMENT(CcbRetcodeExplanations)

//
// preset the debug flags to nothing, or to whatever is defined at compile-time
//

DWORD   AcslanDebugFlags = ACSLAN_DEBUG_FLAGS;
FILE*   hDumpFile = NULL;
BYTE    AcslanDumpCcb[LLC_MAX_DLC_COMMAND];
BOOL    HaveCcbFilter = FALSE;

VOID
GetAcslanDebugFlags(
    VOID
    )

/*++

Routine Description:

    This routine is called whenever we have a DLL_PROCESS_ATTACH to the DLL

    checks if there is an environment variable called ACSLAN_DEBUG_FLAGS, and
    loads whatever it finds (as a 32-bit hex number) into AcslanDebugFlags

    checks if there is an environment variable called ACSLAN_DUMP_FILE. If
    there is, then output which otherwise would have gone to the terminal
    goes to the file. The file is opened in "w" mode by default - open for
    write. Current file is destroyed if it exists

    If there is an environment variable called ACSLAN_DUMP_FILTER then this is
    loaded into the AcslanDumpCcb array. This is a string of numbers, the
    position of which corresponds to the CCB command. For each position, 1
    means dump the CCB and 2 means dump the CCB and its parameters. This is an
    additional filter control over that contained in ACSLAN_DEBUG_FLAGS which
    allows control on a per CCB basis. Example:

        ACSLAN_DUMP_CCB=1x0221

    means:

        for CCB 0 (DIR.INTERRUPT) dump the CCB only (no parameter table anyway)
                1 (???) don't do anything (x==0)
                2 (???) don't do anything (x==0)
                3 (DIR.OPEN.ADAPTER) dump the CCB and parameter table
                4 (DIR.CLOSE.ADAPTER) dump the CCB and parameter table
                    (no parameter table, doesn't matter)
                5 (DIR.SET.MULTICAST.ADDRESS) dump the CCB

    etc. We currently recognize up to CCB command 0x36 (54, PURGE.RESOURCES)

Arguments:

    None.

Return Value:

    None.

--*/

{
    LPSTR envVar;
    SYSTEMTIME systemTime;

    if (envVar = getenv(ACSLAN_DEBUG_ENV_VAR)) {
        if (!strnicmp(envVar, "0x", 2)) {
            envVar += 2;
        }
        for (AcslanDebugFlags = 0; isxdigit(*envVar); ++envVar) {
            AcslanDebugFlags = AcslanDebugFlags * 16 + HEX_TO_NUM(*envVar);
//                + (*envVar
//                    - ('0' + ((*envVar <= '9') ? 0
//                        : ((islower(*envVar) ? 'a' : 'A') - ('9' + 1)))));
        }
    }

    if (envVar = getenv(ACSLAN_DUMP_FILE_VAR)) {

        char* comma;
        static char openMode[3] = "w";

        if (comma = strchr(envVar, ',')) {
            strcpy(openMode, comma+1);
            *comma = 0;
        }
        if ((hDumpFile = fopen(envVar, openMode)) == NULL) {
            MyDbgPrint("Error: can't create dump file\n");
        } else {
            AcslanDebugFlags |= DEBUG_TO_FILE;
            AcslanDebugFlags &= ~DEBUG_TO_TERMINAL;
        }
    }

#if DBGDBG
    IF_DEBUG(DLL_INFO) {
        PUT(("GetAcslanDebugFlags: AcslanDebugFlags = %#x\n", AcslanDebugFlags));
    }
#endif

    if (envVar = getenv(ACSLAN_DUMP_FILTER_VAR)) {

        int i, len;
        BYTE ch;

        if (strlen(envVar) > sizeof(AcslanDumpCcb) - 1) {

            //
            // if there's too much info then inform the debugger but set up the
            // string anyway
            //

            PUT(("GetAcslanDebugFlags: Error: ACSLAN_DUMP_CCB variable too long: \"%s\"\n", envVar));
            strncpy(AcslanDumpCcb, envVar, sizeof(AcslanDumpCcb) - 1);
            AcslanDumpCcb[sizeof(AcslanDumpCcb) - 1] = 0;
        } else {
            strcpy(AcslanDumpCcb, envVar);
        }

#if DBGDBG
        IF_DEBUG(DLL_INFO) {
            PUT(("GetAcslanDebugFlags: AcslanDumpCcb = %s\n", AcslanDumpCcb));
        }
#endif

        //
        // if there are less characters in the string than CCB commands then
        // default the rest of the commands
        //

        len = strlen(AcslanDumpCcb);
        for (i = len; i < sizeof(AcslanDumpCcb); ++i) {
            AcslanDumpCcb[i] = 0xff;
        }
        AcslanDumpCcb[i] = 0;

        //
        // convert the ASCII characters to numbers
        //

        for (i = 0; i < len; ++i) {
            ch = AcslanDumpCcb[i];
            if (isxdigit(ch)) {
                ch = HEX_TO_NUM(ch);
            } else {
                ch = (BYTE)0xff;
            }
            AcslanDumpCcb[i] = ch;

#if DBGDBG
            PUT(("Command %d: filter=%02x\n", i, ch));
#endif

        }

        HaveCcbFilter = TRUE;
    }

    IF_DEBUG(DLL_INFO) {
        GetSystemTime(&systemTime);
        PUT(("PROCESS STARTED @ %02d:%02d:%02d\n",
            systemTime.wHour, systemTime.wMinute, systemTime.wSecond
            ));
    }
}

VOID
SetAcslanDebugFlags(
    IN DWORD Flags
    )
{
    AcslanDebugFlags = Flags;
}

VOID
AcslanDebugPrint(
    IN LPSTR Format,
    IN ...
    )

/*++

Routine Description:

    Print the debug information to the terminal or debug terminal, depending
    on DEBUG_TO_TERMINAL flag (0x80000000)

Arguments:

    Format  - printf/DbgPrint style format string (ANSI)
    ...     - optional parameters

Return Value:

    None.

--*/

{
    char printBuffer[1024];
    va_list list;

    va_start(list, Format);
    vsprintf(printBuffer, Format, list);

    IF_DEBUG(TO_FILE) {
        fputs(printBuffer, hDumpFile);
    } else IF_DEBUG(TO_TERMINAL) {
        printf(printBuffer);
    } else {
        MyDbgPrint(printBuffer);
    }
}

VOID
AcslanDebugPrintString(
    IN LPSTR String
    )

/*++

Routine Description:

    Print the debug information to the terminal or debug terminal, depending
    on DEBUG_TO_TERMINAL flag (0x80000000)

Arguments:

    String  - formatted string (ANSI)

Return Value:

    None.

--*/

{
    IF_DEBUG(TO_FILE) {
        fputs(String, hDumpFile);
    } else IF_DEBUG(TO_TERMINAL) {
        printf(String);
    } else {
        MyDbgPrint(String);
    }
}

VOID
DumpCcb(
    IN PLLC_CCB Ccb,
    IN BOOL DumpAll,
    IN BOOL CcbIsInput
    )

/*++

Routine Description:

    Dumps a CCB and any associated parameter table. Also displays the symbolic
    CCB command and an error code description if the CCB is being returned to
    the caller. Dumps NT format CCBs (flat 32-bit pointers)

Arguments:

    Ccb         - flat 32-bit pointer to CCB2 to dump
    DumpAll     - if TRUE, dumps parameter tables and buffers, else just CCB
    CcbIsInput  - if TRUE, CCB is from user: don't display error code explanation

Return Value:

    None.

--*/

{
    LPSTR   cmdname = "UNKNOWN CCB!";
    BOOL    haveParms = FALSE;
    VOID    (*DumpParms)(PVOID) = DefaultParameterTableDump;
    PVOID   parameterTable = NULL;
    BOOL    parmsInCcb = FALSE;
    BYTE    command;
    DWORD   numberOfTicks;
    BOOL    ccbIsBad = FALSE;
    BOOL    dumpInputParms = TRUE;
    BOOL    dumpOutputParms = TRUE;

    static DWORD LastTicks;
    static BOOL FirstTimeFunctionCalled = TRUE;

    try {
        command = Ccb->uchDlcCommand;
    } except(1) {
        ccbIsBad = TRUE;
    }

    //
    // bomb out if we get a bad CCB address
    //

    if (ccbIsBad) {
        PUT(("*** Error: Bad Address for CCB @ %08x ***\n", Ccb));
        return;
    }

#if DBGDBG
    PUT(("DumpCcb(%x, %d, %d)\n", Ccb, DumpAll, CcbIsInput));
#endif

    IF_DEBUG(DUMP_TIME) {

        DWORD ticksNow = GetTickCount();

        if (FirstTimeFunctionCalled) {
            numberOfTicks = 0;
            FirstTimeFunctionCalled = FALSE;
        } else {
            numberOfTicks = ticksNow - LastTicks;
        }
        LastTicks = ticksNow;
    }

    switch (command) {
    case LLC_BUFFER_CREATE:
        cmdname = "BUFFER.CREATE";
        haveParms = TRUE;
        DumpParms = DumpBufferCreateParms;
        break;

    case LLC_BUFFER_FREE:
        cmdname = "BUFFER.FREE";
        haveParms = TRUE;
        DumpParms = DumpBufferFreeParms;
        break;

    case LLC_BUFFER_GET:
        cmdname = "BUFFER.GET";
        haveParms = TRUE;
        DumpParms = DumpBufferGetParms;
        break;

    case LLC_DIR_CLOSE_ADAPTER:
        cmdname = "DIR.CLOSE.ADAPTER";
        haveParms = FALSE;
        break;

    case LLC_DIR_CLOSE_DIRECT:
        cmdname = "DIR.CLOSE.DIRECT";
        haveParms = FALSE;
        break;

    case LLC_DIR_INITIALIZE:
        cmdname = "DIR.INITIALIZE";
        haveParms = TRUE;
        DumpParms = DumpDirInitializeParms;
        break;

    case LLC_DIR_INTERRUPT:
        cmdname = "DIR.INTERRUPT";
        haveParms = FALSE;
        break;

    case LLC_DIR_OPEN_ADAPTER:
        cmdname = "DIR.OPEN.ADAPTER";
        haveParms = TRUE;
        DumpParms = DumpDirOpenAdapterParms;
        break;

    case LLC_DIR_OPEN_DIRECT:
        cmdname = "DIR.OPEN.DIRECT";
        haveParms = TRUE;
        DumpParms = DumpDirOpenDirectParms;
        break;

    case LLC_DIR_READ_LOG:
        cmdname = "DIR.READ.LOG";
        haveParms = TRUE;
        DumpParms = DumpDirReadLogParms;
        break;

    case LLC_DIR_SET_EXCEPTION_FLAGS:
        cmdname = "DIR.SET.EXCEPTION.FLAGS";
        haveParms = TRUE;
        DumpParms = DumpDirSetExceptionFlagsParms;
        break;

    case LLC_DIR_SET_FUNCTIONAL_ADDRESS:
        cmdname = "DIR.SET.FUNCTIONAL.ADDRESS";
        haveParms = TRUE;
        DumpParms = DumpDirSetFunctionalAddressParms;
        parmsInCcb = TRUE;
        break;

    case LLC_DIR_SET_GROUP_ADDRESS:
        cmdname = "DIR.SET.GROUP.ADDRESS";
        haveParms = TRUE;
        DumpParms = DumpDirSetGroupAddressParms;
        parmsInCcb = TRUE;
        break;

    case LLC_DIR_STATUS:
        cmdname = "DIR.STATUS";
        haveParms = TRUE;
        DumpParms = DumpDirStatusParms;
        break;

    case LLC_DIR_TIMER_CANCEL:
        cmdname = "DIR.TIMER.CANCEL";
        haveParms = TRUE;
        DumpParms = DumpDirTimerCancelParms;
        parmsInCcb = TRUE;
        break;

    case LLC_DIR_TIMER_CANCEL_GROUP:
        cmdname = "DIR.TIMER.CANCEL.GROUP";
        haveParms = TRUE;
        DumpParms = DumpDirTimerCancelGroupParms;
        parmsInCcb = TRUE;
        break;

    case LLC_DIR_TIMER_SET:
        cmdname = "DIR.TIMER.SET";
        haveParms = TRUE;
        DumpParms = DumpDirTimerSetParms;
        parmsInCcb = TRUE;
        break;

    case LLC_DLC_CLOSE_SAP:
        cmdname = "DLC.CLOSE.SAP";
        haveParms = TRUE;
        DumpParms = DumpDlcCloseSapParms;
        parmsInCcb = TRUE;
        break;

    case LLC_DLC_CLOSE_STATION:
        cmdname = "DLC.CLOSE.STATION";
        haveParms = TRUE;
        DumpParms = DumpDlcCloseStationParms;
        parmsInCcb = TRUE;
        break;

    case LLC_DLC_CONNECT_STATION:
        cmdname = "DLC.CONNECT.STATION";
        haveParms = TRUE;
        DumpParms = DumpDlcConnectStationParms;
        break;

    case LLC_DLC_FLOW_CONTROL:
        cmdname = "DLC.FLOW.CONTROL";
        haveParms = TRUE;
        DumpParms = DumpDlcFlowControlParms;
        parmsInCcb = TRUE;
        break;

    case LLC_DLC_MODIFY:
        cmdname = "DLC.MODIFY";
        haveParms = TRUE;
        DumpParms = DumpDlcModifyParms;
        break;

    case LLC_DLC_OPEN_SAP:
        cmdname = "DLC.OPEN.SAP";
        haveParms = TRUE;
        DumpParms = DumpDlcOpenSapParms;
        break;

    case LLC_DLC_OPEN_STATION:
        cmdname = "DLC.OPEN.STATION";
        haveParms = TRUE;
        DumpParms = DumpDlcOpenStationParms;
        break;

    case LLC_DLC_REALLOCATE_STATIONS:
        cmdname = "DLC.REALLOCATE";
        haveParms = TRUE;
        DumpParms = DumpDlcReallocateParms;
        break;

    case LLC_DLC_RESET:
        cmdname = "DLC.RESET";
        break;

    case LLC_DLC_SET_THRESHOLD:
        cmdname = "DLC.SET.THRESHOLD";
        haveParms = TRUE;
        break;

    case LLC_DLC_STATISTICS:
        cmdname = "DLC.STATISTICS";
        haveParms = TRUE;
        DumpParms = DumpDlcStatisticsParms;
        break;

    case 0x25:

        //
        // not supported !
        //

        cmdname = "PDT.TRACE.OFF";
        break;

    case 0x24:

        //
        // not supported !
        //

        cmdname = "PDT.TRACE.ON";
        break;

    case LLC_READ:
        cmdname = "READ";
        haveParms = !CcbIsInput;
        DumpParms = DumpReadParms;
        dumpInputParms = FALSE;
        dumpOutputParms = !CcbIsInput && Ccb->uchDlcStatus == LLC_STATUS_SUCCESS;
        break;

    case LLC_READ_CANCEL:
        cmdname = "READ.CANCEL";
        break;

    case LLC_RECEIVE:
        cmdname = "RECEIVE";
        haveParms = TRUE;
        DumpParms = DumpReceiveParms;
        break;

    case LLC_RECEIVE_CANCEL:
        cmdname = "RECEIVE.CANCEL";
        haveParms = TRUE;
        DumpParms = DumpReceiveCancelParms;
        parmsInCcb = TRUE;
        break;

    case LLC_RECEIVE_MODIFY:
        cmdname = "RECEIVE.MODIFY";
        haveParms = TRUE;
        break;

    case LLC_TRANSMIT_FRAMES:
        cmdname = "TRANSMIT.FRAMES";
        haveParms = TRUE;
        DumpParms = DumpTransmitFramesParms;
        dumpOutputParms = FALSE;
        break;

    case LLC_TRANSMIT_DIR_FRAME:
        cmdname = "TRANSMIT.DIR.FRAME";
        haveParms = TRUE;
        DumpParms = DumpTransmitDirFrameParms;
        dumpOutputParms = FALSE;
        break;

    case LLC_TRANSMIT_I_FRAME:
        cmdname = "TRANSMIT.I.FRAME";
        haveParms = TRUE;
        DumpParms = DumpTransmitIFrameParms;
        dumpOutputParms = FALSE;
        break;

    case LLC_TRANSMIT_TEST_CMD:
        cmdname = "TRANSMIT.TEST.CMD";
        haveParms = TRUE;
        DumpParms = DumpTransmitTestCmdParms;
        dumpOutputParms = FALSE;
        break;

    case LLC_TRANSMIT_UI_FRAME:
        cmdname = "TRANSMIT.UI.FRAME";
        haveParms = TRUE;
        DumpParms = DumpTransmitUiFrameParms;
        break;

    case LLC_TRANSMIT_XID_CMD:
        cmdname = "TRANSMIT.XID.CMD";
        haveParms = TRUE;
        DumpParms = DumpTransmitXidCmdParms;
        dumpOutputParms = FALSE;
        break;

    case LLC_TRANSMIT_XID_RESP_FINAL:
        cmdname = "TRANSMIT.XID.RESP.FINAL";
        haveParms = TRUE;
        DumpParms = DumpTransmitXidRespFinalParms;
        dumpOutputParms = FALSE;

break;

    case LLC_TRANSMIT_XID_RESP_NOT_FINAL:
        cmdname = "TRANSMIT.XID.RESP.NOT.FINAL";
        haveParms = TRUE;
        DumpParms = DumpTransmitXidRespNotFinalParms;
        dumpOutputParms = FALSE;
        break;

    }

    if (HaveCcbFilter) {

        BYTE filter = AcslanDumpCcb[command];

#if DBGDBG
        PUT(("filter = %02x\n", filter));
#endif

        if (filter == 0xff) {

            //
            // do nothing - 0xff means use default in ACSLAN_DEBUG_FLAGS
            //

        } else {
            if (CcbIsInput) {
                if (!(filter & CF_DUMP_CCB_IN)) {

                    //
                    // not interested in this input CCB
                    //

                    return;
                }
                DumpAll = filter & CF_DUMP_PARMS_IN;
            } else {
                if (!(filter & CF_DUMP_CCB_OUT)) {

                    //
                    // not interested in this output CCB
                    //

                    return;
                }
                DumpAll = filter & CF_DUMP_PARMS_OUT;
            }
        }
    }

    PUT(("\n==============================================================================\n"));

    IF_DEBUG(DUMP_TIME) {
        PUT(("%sPUT CCB @ 0x%08x +%d mSec\n",
            CcbIsInput ? "IN" : "OUT",
            Ccb,
            numberOfTicks
            ));
    } else {
        PUT(("%sPUT CCB @ 0x%08x\n",
            CcbIsInput ? "IN" : "OUT",
            Ccb
            ));
    }

    PUT(("Adapter . . . . %02x\n"
        "Command . . . . %02x [%s]\n"
        "Status. . . . . %02x [%s]\n"
        "Reserved. . . . %02x\n"
        "Next. . . . . . %08x\n"
        "CompletionFlag. %08x\n",
        Ccb->uchAdapterNumber,
        Ccb->uchDlcCommand,
        cmdname,
        Ccb->uchDlcStatus,
        CcbIsInput ? "" : MapCcbRetcode(Ccb->uchDlcStatus),
        Ccb->uchReserved1,
        Ccb->pNext,
        Ccb->ulCompletionFlag
        ));

    if (haveParms) {
        if (parmsInCcb) {
            DumpParms(Ccb->u.pParameterTable);
        } else {
            parameterTable = Ccb->u.pParameterTable;
            PUT(("Parameters. . . %08x\n", parameterTable));
        }
    } else {
        PUT(("Parameters. . . %08x\n", Ccb->u.pParameterTable));
    }

    PUT(("CompletionEvent %08x\n"
        "Reserved. . . . %02x\n"
        "ReadFlag. . . . %02x\n"
        "Reserved. . . . %04x\n",
        Ccb->hCompletionEvent,
        Ccb->uchReserved2,
        Ccb->uchReadFlag,
        Ccb->usReserved3
        ));

    if (parameterTable && DumpAll) {
        if ((CcbIsInput && dumpInputParms) || (!CcbIsInput && dumpOutputParms)) {
            DumpParms(parameterTable);
        }
    }
}

LPSTR
MapCcbRetcode(
    IN  BYTE    Retcode
    )

/*++

Routine Description:

    Returns string describing error code

Arguments:

    Retcode - CCB_RETCODE

Return Value:

    LPSTR

--*/

{
    static char errbuf[128];

    if (Retcode == LLC_STATUS_PENDING) {
        return "Command in progress";
    } else if (Retcode > NUMBER_OF_ERROR_MESSAGES) {
        sprintf(errbuf, "*** Invalid error code 0x%2x ***", Retcode);
        return errbuf;
    }
    return CcbRetcodeExplanations[Retcode];
}

VOID
DumpData(
    IN LPSTR Title,
    IN PBYTE Address,
    IN DWORD Length,
    IN DWORD Options,
    IN DWORD Indent
    )
{
    char dumpBuf[80];
    char* bufptr;
    int i, n, iterations;
    char* hexptr;

    //
    // the usual dump style: 16 columns of hex bytes, followed by 16 columns
    // of corresponding ASCII characters, or '.' where the character is < 0x20
    // (space) or > 0x7f (del?)
    //

    if (Options & DD_LINE_BEFORE) {
        AcslanDebugPrintString("\n");
    }

    try {
        iterations = 0;
        while (Length) {
            bufptr = dumpBuf;
            if (Title && !iterations) {
                strcpy(bufptr, Title);
                bufptr = strchr(bufptr, 0);
            }

            if (Indent && ((Options & DD_INDENT_ALL) || iterations)) {

                int indentLen = (!iterations && Title)
                                    ? (Indent - strlen(Title) < 0)
                                        ? 1
                                        : Indent - strlen(Title)
                                    : Indent;

                memset(bufptr, ' ', indentLen);
                bufptr += indentLen;
            }

            if (!(Options & DD_NO_ADDRESS)) {
                bufptr += sprintf(bufptr, "%08x: ", Address);
            }

            n = (Length < 16) ? Length : 16;
            hexptr = bufptr;
            for (i = 0; i < n; ++i) {
                bufptr += sprintf(bufptr, "%02x", Address[i]);
                *bufptr++ = (i == 7) ? '-' : ' ';
            }

            if (Options & DD_UPPER_CASE) {
                strupr(hexptr);
            }

            if (!(Options & DD_NO_ASCII)) {
                if (n < 16) {
                    for (i = 0; i < 16-n; ++i) {
                        bufptr += sprintf(bufptr, Options & DD_DOT_DOT_SPACE ? ".. " : "   ");
                    }
                }
                bufptr += sprintf(bufptr, "  ");
                for (i = 0; i < n; ++i) {
                    *bufptr++ = (Address[i] < 0x20 || Address[i] > 0x7f) ? '.' : Address[i];
                }
            }

            *bufptr++ = '\n';
            *bufptr = 0;
            AcslanDebugPrintString(dumpBuf);
            Length -= n;
            Address += n;
            ++iterations;
        }

        if (Options & DD_LINE_AFTER) {
            AcslanDebugPrintString("\n");
        }
    } except(1) {
        PUT(("*** Error: Bad Data @ %x, length %d ***\n", Address, Length));
    }
}

PRIVATE
VOID
DefaultParameterTableDump(
    IN  PVOID   Parameters
    )

/*++

Routine Description:

    Displays default message for CCBs which have parameter tables that don't
    have a dump routine yet

Arguments:

    Parameters  - pointer to parameter table

Return Value:

    None.

--*/

{
    PUT(("Parameter table dump not implemented for this CCB\n"));
}

PRIVATE
VOID
DumpParameterTableHeader(
    IN  LPSTR   CommandName,
    IN  PVOID   Table
    )

/*++

Routine Description:

    Displays header for parameter table dump

Arguments:

    CommandName - name of command which owns parameter table
    Table       - flat 32-bit address of parameter table

Return Value:

    None.

--*/

{
    PUT(("\n%s parameter table @ 0x%08x\n", CommandName, Table));
}

PRIVATE
VOID
DumpBufferCreateParms(
    IN PVOID Parameters
    )
{
    PLLC_BUFFER_CREATE_PARMS parms = (PLLC_BUFFER_CREATE_PARMS)Parameters;

    DumpParameterTableHeader("BUFFER.CREATE", Parameters);

    PUT(("Buf Pool Handle %08x\n"
        "Buf Pool Addr . %08x\n"
        "Buffer Size . . %08x\n"
        "Minimum Size. . %08x\n",
        parms->hBufferPool,
        parms->pBuffer,
        parms->cbBufferSize,
        parms->cbMinimumSizeThreshold
        ));
}

PRIVATE
VOID
DumpBufferFreeParms(
    IN  PVOID   Parameters
    )
{
    PLLC_BUFFER_FREE_PARMS parms = (PLLC_BUFFER_FREE_PARMS)Parameters;

    DumpParameterTableHeader("BUFFER.FREE", Parameters);

    PUT(("reserved. . . . %04x\n"
        "buffers left. . %04x\n"
        "reserved. . . . %02x %02x %02x %02x\n"
        "first buffer. . %08x\n",
        parms->usReserved1,
        parms->cBuffersLeft,
        ((PBYTE)&(parms->ulReserved))[0],
        ((PBYTE)&(parms->ulReserved))[1],
        ((PBYTE)&(parms->ulReserved))[2],
        ((PBYTE)&(parms->ulReserved))[3],
        parms->pFirstBuffer
        ));
}

PRIVATE
VOID
DumpBufferGetParms(
    IN  PVOID   Parameters
    )
{
    PLLC_BUFFER_GET_PARMS parms = (PLLC_BUFFER_GET_PARMS)Parameters;

    DumpParameterTableHeader("BUFFER.GET", Parameters);

    PUT(("reserved. . . . %04x\n"
        "buffers left. . %04x\n"
        "buffers to get. %04x\n"
        "buffer size . . %04x\n"
        "first buffer. . %08x\n",
        parms->usReserved1,
        parms->cBuffersLeft,
        parms->cBuffersToGet,
        parms->cbBufferSize,
        parms->pFirstBuffer
        ));
}

PRIVATE
VOID
DumpDirInitializeParms(
    IN  PVOID   Parameters
    )
{
    PLLC_DIR_INITIALIZE_PARMS parms = (PLLC_DIR_INITIALIZE_PARMS)Parameters;

    DumpParameterTableHeader("DIR.INITIALIZE", Parameters);

    PUT(("Bring Ups . . . %04x\n"
        "Reserved. . . . %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n"
        "                %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n"
        "                %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
        parms->usBringUps,
        parms->Reserved[ 0],
        parms->Reserved[ 1],
        parms->Reserved[ 2],
        parms->Reserved[ 3],
        parms->Reserved[ 4],
        parms->Reserved[ 5],
        parms->Reserved[ 6],
        parms->Reserved[ 7],
        parms->Reserved[ 8],
        parms->Reserved[ 9],
        parms->Reserved[10],
        parms->Reserved[11],
        parms->Reserved[12],
        parms->Reserved[13],
        parms->Reserved[14],
        parms->Reserved[15],
        parms->Reserved[16],
        parms->Reserved[17],
        parms->Reserved[18],
        parms->Reserved[19],
        parms->Reserved[20],
        parms->Reserved[21],
        parms->Reserved[22],
        parms->Reserved[23],
        parms->Reserved[24],
        parms->Reserved[25],
        parms->Reserved[26],
        parms->Reserved[27],
        parms->Reserved[28],
        parms->Reserved[29]
        ));
}

PRIVATE
VOID
DumpDirOpenAdapterParms(
    IN  PVOID   Parameters
    )
{
    PLLC_DIR_OPEN_ADAPTER_PARMS parms = (PLLC_DIR_OPEN_ADAPTER_PARMS)Parameters;
    PLLC_ADAPTER_OPEN_PARMS pAdapterParms = parms->pAdapterParms;
    PLLC_EXTENDED_ADAPTER_PARMS pExtendedParms = parms->pExtendedParms;
    PLLC_DLC_PARMS pDlcParms = parms->pDlcParms;

    DumpParameterTableHeader("DIR.OPEN.ADAPTER", Parameters);

    PUT(("adapter parms . %08x\n"
        "extended parms. %08x\n"
        "DLC parms . . . %08x\n"
        "reserved. . . . %08x\n",
        pAdapterParms,
        pExtendedParms,
        pDlcParms,
        parms->pReserved1
        ));

    if (pAdapterParms) {
        PUT(("\n"
            "Adapter Parms @ %08x\n"
            "open error. . . %04x\n"
            "open options. . %04x\n"
            "node address. . %02x-%02x-%02x-%02x-%02x-%02x\n"
            "group address . %08x\n"
            "func. address . %08x\n"
            "Reserved 1. . . %04x\n"
            "Reserved 2. . . %04x\n"
            "max frame size  %04x\n"
            "Reserved 3. . . %04x %04x %04x %04x\n"
            "bring ups . . . %04x\n"
            "init warnings . %04x\n"
            "Reserved 4. . . %04x %04x %04x\n",
            pAdapterParms,
            pAdapterParms->usOpenErrorCode,
            pAdapterParms->usOpenOptions,
            pAdapterParms->auchNodeAddress[0],
            pAdapterParms->auchNodeAddress[1],
            pAdapterParms->auchNodeAddress[2],
            pAdapterParms->auchNodeAddress[3],
            pAdapterParms->auchNodeAddress[4],
            pAdapterParms->auchNodeAddress[5],
            *(LPDWORD)(&pAdapterParms->auchGroupAddress),
            *(LPDWORD)(&pAdapterParms->auchFunctionalAddress),
            pAdapterParms->usReserved1,
            pAdapterParms->usReserved2,
            pAdapterParms->usMaxFrameSize,
            pAdapterParms->usReserved3[0],
            pAdapterParms->usReserved3[1],
            pAdapterParms->usReserved3[2],
            pAdapterParms->usReserved3[3],
            pAdapterParms->usBringUps,  // blooargh
            pAdapterParms->InitWarnings,
            pAdapterParms->usReserved4[0],
            pAdapterParms->usReserved4[1],
            pAdapterParms->usReserved4[2]
            ));
    }

    if (pExtendedParms) {
        PUT(("\n"
            "Extended Parms @ %08x\n"
            "hBufferPool . . %08x\n"
            "pSecurityDesc . %08x\n"
            "EthernetType. . %08x [%s]\n",
            pExtendedParms,
            pExtendedParms->hBufferPool,
            pExtendedParms->pSecurityDescriptor,
            pExtendedParms->LlcEthernetType,
            MapEthernetType(pExtendedParms->LlcEthernetType)
            ));
    }

    if (pDlcParms) {
        PUT(("\n"
            "DLC Parms @ %08x\n"
            "max SAPs. . . . %02x\n"
            "max links . . . %02x\n"
            "max group SAPs. %02x\n"
            "max group membs %02x\n"
            "T1 Tick 1 . . . %02x\n"
            "T2 Tick 1 . . . %02x\n"
            "Ti Tick 1 . . . %02x\n"
            "T1 Tick 2 . . . %02x\n"
            "T2 Tick 2 . . . %02x\n"
            "Ti Tick 2 . . . %02x\n",
            pDlcParms,
            pDlcParms->uchDlcMaxSaps,
            pDlcParms->uchDlcMaxStations,
            pDlcParms->uchDlcMaxGroupSaps,
            pDlcParms->uchDlcMaxGroupMembers,
            pDlcParms->uchT1_TickOne,
            pDlcParms->uchT2_TickOne,
            pDlcParms->uchTi_TickOne,
            pDlcParms->uchT1_TickTwo,
            pDlcParms->uchT2_TickTwo,
            pDlcParms->uchTi_TickTwo
            ));
    }
}

PRIVATE
LPSTR
MapEthernetType(
    IN LLC_ETHERNET_TYPE EthernetType
    )
{
    switch (EthernetType) {
    case LLC_ETHERNET_TYPE_DEFAULT:
        return "DEFAULT";

    case LLC_ETHERNET_TYPE_AUTO:
        return "AUTO";

    case LLC_ETHERNET_TYPE_802_3:
        return "802.3";

    case LLC_ETHERNET_TYPE_DIX:
        return "DIX";
    }
    return "*** Unknown Ethernet Type ***";
}

PRIVATE
VOID
DumpDirOpenDirectParms(
    IN PVOID Parameters
    )
{
    PLLC_DIR_OPEN_DIRECT_PARMS parms = (PLLC_DIR_OPEN_DIRECT_PARMS)Parameters;

    DumpParameterTableHeader("DIR.OPEN.DIRECT", Parameters);

    PUT(("reserved. . . . %04x %04x %04x %04x\n"
        "open options. . %04x\n"
        "ethernet type . %04x\n"
        "protocol mask . %08x\n"
        "protocol match. %08x\n"
        "protocol offset %04x\n",
        parms->Reserved[0],
        parms->Reserved[1],
        parms->Reserved[2],
        parms->Reserved[3],
        parms->usOpenOptions,
        parms->usEthernetType,
        parms->ulProtocolTypeMask,
        parms->ulProtocolTypeMatch,
        parms->usProtocolTypeOffset
        ));
}

PRIVATE
VOID
DumpDirReadLogParms(
    IN PVOID Parameters
    )
{
    PLLC_DIR_READ_LOG_PARMS parms = (PLLC_DIR_READ_LOG_PARMS)Parameters;

    DumpParameterTableHeader("DIR.READ.LOG", Parameters);

    PUT(("type id . . . . %04x [%s]\n"
        "log buf len . . %04x\n"
        "log buf ptr . . %08x\n"
        "act. log. len.. %04x\n",
        parms->usTypeId,
        MapLogType(parms->usTypeId),
        parms->cbLogBuffer,
        parms->pLogBuffer,
        parms->cbActualLength
        ));
}

PRIVATE
LPSTR
MapLogType(
    IN USHORT Type
    )
{
    switch (Type) {
    case 0:
        return "Adapter Error Log";

    case 1:
        return "Direct Interface Error Log";

    case 2:
        return "Adapter & Direct Interface Error Logs";
    }
    return "*** Unknown Log Type ***";
}

PRIVATE
VOID
DumpDirSetExceptionFlagsParms(
    IN PVOID Parameters
    )
{
    PLLC_DIR_SET_EFLAG_PARMS parms = (PLLC_DIR_SET_EFLAG_PARMS)Parameters;

    DumpParameterTableHeader("DIR.SET.EXCEPTION.FLAGS", Parameters);

    PUT(("Adapter Check Flag. %08x\n"
        "Network Status Flag %08x\n"
        "PC Error Flag . . . %08x\n"
        "System Action Flag. %08x\n",
        parms->ulAdapterCheckFlag,
        parms->ulNetworkStatusFlag,
        parms->ulPcErrorFlag,
        parms->ulSystemActionFlag
        ));
}

PRIVATE
VOID
DumpDirSetFunctionalAddressParms(
    IN PVOID Parameters
    )
{
    PUT(("Functional addr %08x\n", Parameters));
}

PRIVATE
VOID
DumpDirSetGroupAddressParms(
    IN PVOID Parameters
    )
{
    PUT(("Group addr. . . %08x\n", Parameters));
}

PRIVATE
VOID
DumpDirStatusParms(
    IN  PVOID   Parameters
    )
{
    PLLC_DIR_STATUS_PARMS parms = (PLLC_DIR_STATUS_PARMS)Parameters;

    DumpParameterTableHeader("DIR.STATUS", Parameters);

    PUT(("perm node addr. %02x-%02x-%02x-%02x-%02x-%02x\n"
        "local node addr %02x-%02x-%02x-%02x-%02x-%02x\n"
        "group addr. . . %08lx\n"
        "functional addr %08lx\n"
        "max SAPs. . . . %02x\n"
        "open SAPs . . . %02x\n"
        "max stations. . %02x\n"
        "open stations . %02x\n"
        "avail stations. %02x\n"
        "adapter config. %02x\n"
        "reserved 1. . . %02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x\n"
        "reserved 2. . . %08x\n"
        "reserved 3. . . %08x\n"
        "max frame len . %08x\n"
        "last NW status. %04x\n"
        "adapter type. . %04x [%s]\n",
        parms->auchPermanentAddress[0],
        parms->auchPermanentAddress[1],
        parms->auchPermanentAddress[2],
        parms->auchPermanentAddress[3],
        parms->auchPermanentAddress[4],
        parms->auchPermanentAddress[5],
        parms->auchNodeAddress[0],
        parms->auchNodeAddress[1],
        parms->auchNodeAddress[2],
        parms->auchNodeAddress[3],
        parms->auchNodeAddress[4],
        parms->auchNodeAddress[5],
        *(LPDWORD)(&parms->auchGroupAddress),
        *(LPDWORD)(&parms->auchFunctAddr),
        parms->uchMaxSap,
        parms->uchOpenSaps,
        parms->uchMaxStations,
        parms->uchOpenStation,
        parms->uchAvailStations,
        parms->uchAdapterConfig,
        parms->auchReserved1[0],
        parms->auchReserved1[1],
        parms->auchReserved1[2],
        parms->auchReserved1[3],
        parms->auchReserved1[4],
        parms->auchReserved1[5],
        parms->auchReserved1[6],
        parms->auchReserved1[7],
        parms->auchReserved1[8],
        parms->auchReserved1[9],
        parms->ulReserved1,
        parms->ulReserved2,
        parms->ulMaxFrameLength,
        parms->usLastNetworkStatus,
        parms->usAdapterType,
        MapAdapterType(parms->usAdapterType)
        ));
}

PRIVATE
LPSTR
MapAdapterType(
    IN USHORT AdapterType
    )
{
    switch (AdapterType) {
    case 0x0001:
        return "Token Ring Network PC Adapter";

    case 0x0002:
        return "Token Ring Network PC Adapter II";

    case 0x0004:
        return "Token Ring Network Adapter/A";

    case 0x0008:
        return "Token Ring Network PC Adapter II";

    case 0x0020:
        return "Token Ring Network 16/4 Adapter";

    case 0x0040:
        return "Token Ring Network 16/4 Adapter/A";

    case 0x0080:
        return "Token Ring Network Adapter/A";

    case 0x0100:
        return "Ethernet Adapter";

    case 0x4000:
        return "PC Network Adapter";

    case 0x8000:
        return "PC Network Adapter/A";
    }
    return "*** Unknown Adapter Type ***";
}

PRIVATE
VOID
DumpDirTimerCancelParms(
    IN PVOID Parameters
    )
{
    PUT(("timer addr. . . %08x\n", Parameters));
}

PRIVATE
VOID
DumpDirTimerCancelGroupParms(
    IN PVOID Parameters
    )
{
    PUT(("timer cmpl flag %08x\n", Parameters));
}

PRIVATE
VOID
DumpDirTimerSetParms(
    IN PVOID Parameters
    )
{
    PUT(("time value. . . %04x\n", HIWORD(Parameters)));
}

PRIVATE
VOID
DumpDlcCloseSapParms(
    IN PVOID Parameters
    )
{
    PUT(("station id      %04x\n"
        "reserved. . . . %02x %02x\n",
        HIWORD(Parameters),
        HIBYTE(LOWORD(Parameters)),
        LOBYTE(LOWORD(Parameters))
        ));
}

PRIVATE
VOID
DumpDlcCloseStationParms(
    IN PVOID Parameters
    )
{
    PUT(("station id. . . %04x\n"
        "reserved. . . . %02x %02x\n",
        HIWORD(Parameters),
        HIBYTE(LOWORD(Parameters)),
        LOBYTE(LOWORD(Parameters))
        ));
}

PRIVATE
VOID
DumpDlcConnectStationParms(
    IN  PVOID   Parameters
    )
{
    PLLC_DLC_CONNECT_PARMS parms = (PLLC_DLC_CONNECT_PARMS)Parameters;
    LPBYTE routing = parms->pRoutingInfo;
    int i, n;

    DumpParameterTableHeader("DLC.CONNECT.STATION", Parameters);

    PUT(("station id. . . %04x\n"
        "reserved. . . . %04x\n"
        "routing addr. . %08x [",
        parms->usStationId,
        parms->usReserved,
        routing
        ));
    if (routing) {
        n = (int)(routing[0] & 0x1f);
        for (i=0; i<n-1; ++i) {
            PUT(("%02x ", routing[i]));
        }
        PUT(("%02x", routing[i]));
    }
    PUT(("]\n"));
    if (routing) {
        char broadcastIndicators = routing[0] & 0xe0;
        char length = routing[0] & 0x1f;
        char direction = routing[1] & 0x80;
        char largestFrame = routing[1] & 0x70;
        PUT(("Routing Info Description: "));
        if (broadcastIndicators & 0x80 == 0) {
            PUT(("Non Broadcast "));
        } else if (broadcastIndicators & 0xc0 == 0x80) {
            PUT(("All Routes Broadcast "));
        } else if (broadcastIndicators & 0xc0 == 0xc0) {
            PUT(("Single Route Broadcast "));
        }
        PUT(("Length = %d ", length));
        if (direction) {
            PUT(("interpret right-to-left "));
        } else {
            PUT(("interpret left-to-right "));
        }
        switch (largestFrame) {
        case 0x00:
            PUT(("<= 516 bytes in I-field "));
            break;

        case 0x10:
            PUT(("<= 1500 bytes in I-field "));
            break;

        case 0x20:
            PUT(("<= 2052 bytes in I-field "));
            break;

        case 0x30:
            PUT(("<= 4472 bytes in I-field "));
            break;

        case 0x40:
            PUT(("<= 8144 bytes in I-field "));
            break;

        case 0x50:
            PUT(("<= 11407 bytes in I-field "));
            break;

        case 0x60:
            PUT(("<= 17800 bytes in I-field "));
            break;

        case 0x70:
            PUT(("All-Routes Broadcast Frame "));
            break;

        }
        PUT(("\n"));
    }
}

PRIVATE
VOID
DumpDlcFlowControlParms(
    IN PVOID Parameters
    )
{
    PUT(("station id. . . %04x\n"
        "flow control. . %02x [%s]\n",
        HIWORD(Parameters),
        HIBYTE(LOWORD(Parameters)),
        MapFlowControl(HIBYTE(LOWORD(Parameters)))
        ));
}

PRIVATE
LPSTR
MapFlowControl(
    IN BYTE FlowControl
    )
{
    if (FlowControl & 0x80) {
        if (FlowControl & 0x40) {
            return "RESET LOCAL BUSY - BUFFER";
        } else {
            return "RESET LOCAL BUSY - USER";
        }
    } else {
        return "Enter LOCAL BUSY state";
    }
}

PRIVATE
VOID
DumpDlcModifyParms(
    IN PVOID Parameters
    )
{
    PLLC_DLC_MODIFY_PARMS parms = (PLLC_DLC_MODIFY_PARMS)Parameters;

    DumpParameterTableHeader("DLC.MODIFY", Parameters);

    PUT(("reserved. . . . %04x\n"
        "station id. . . %04x\n"
        "timer T1. . . . %02x\n"
        "timer T2. . . . %02x\n"
        "timer Ti. . . . %02x\n"
        "maxout. . . . . %02x\n"
        "maxin . . . . . %02x\n"
        "maxout incr . . %02x\n"
        "max retry count %02x\n"
        "reserved. . . . %02x\n"
        "max info field. %02x\n"
        "access priority %02x\n"
        "reserved. . . . %02x %02x %02x %02x\n"
        "group count . . %02x\n"
        "group list. . . %08x ",
        parms->usRes,
        parms->usStationId,
        parms->uchT1,
        parms->uchT2,
        parms->uchTi,
        parms->uchMaxOut,
        parms->uchMaxIn,
        parms->uchMaxOutIncr,
        parms->uchMaxRetryCnt,
        parms->uchReserved1,
        parms->usMaxInfoFieldLength,
        parms->uchAccessPriority,
        parms->auchReserved3[0],
        parms->auchReserved3[1],
        parms->auchReserved3[2],
        parms->auchReserved3[3],
        parms->cGroupCount,
        parms->pGroupList
        ));

    if (parms->pGroupList) {
        DumpData(NULL,
                 parms->pGroupList,
                 parms->cGroupCount,
                 DD_NO_ADDRESS | DD_NO_ASCII | DD_UPPER_CASE,
                 DEFAULT_FIELD_WIDTH + 8 + 1
                 );
    }

    PUT(("\n"));
}

PRIVATE
VOID
DumpDlcOpenSapParms(
    IN  PVOID   Parameters
    )
{
    PLLC_DLC_OPEN_SAP_PARMS parms = (PLLC_DLC_OPEN_SAP_PARMS)Parameters;

    DumpParameterTableHeader("DLC.OPEN.SAP", Parameters);

    PUT(("station id. . . %04x\n"
        "user stat . . . %04x\n"
        "T1. . . . . . . %02x\n"
        "T2. . . . . . . %02x\n"
        "Ti. . . . . . . %02x\n"
        "max out . . . . %02x\n"
        "max in  . . . . %02x\n"
        "max out incr. . %02x\n"
        "max retry count %02x\n"
        "max members . . %02x\n"
        "max I field . . %04x\n"
        "SAP value . . . %02x\n"
        "options/pri . . %02x\n"
        "link count. . . %02x\n"
        "reserved. . . . %02x %02x\n"
        "group count . . %02x\n"
        "group list. . . %08x\n"
        "status flags. . %08x\n"
        "reserved. . . . %02x %02x %02x %02x %02x %02x %02x %02x\n"
        "links available %02x\n",
        parms->usStationId,
        parms->usUserStatValue,
        parms->uchT1,
        parms->uchT2,
        parms->uchTi,
        parms->uchMaxOut,
        parms->uchMaxIn,
        parms->uchMaxOutIncr,
        parms->uchMaxRetryCnt,
        parms->uchMaxMembers,
        parms->usMaxI_Field,
        parms->uchSapValue,
        parms->uchOptionsPriority,
        parms->uchcStationCount,
        parms->uchReserved2[0],
        parms->uchReserved2[1],
        parms->cGroupCount,
        parms->pGroupList,
        parms->DlcStatusFlags,
        parms->uchReserved3[0],
        parms->uchReserved3[1],
        parms->uchReserved3[2],
        parms->uchReserved3[3],
        parms->uchReserved3[4],
        parms->uchReserved3[5],
        parms->uchReserved3[6],
        parms->uchReserved3[7],
        parms->cLinkStationsAvail
        ));
}

PRIVATE
VOID
DumpDlcOpenStationParms(
    IN  PVOID   Parameters
    )
{
    PLLC_DLC_OPEN_STATION_PARMS parms = (PLLC_DLC_OPEN_STATION_PARMS)Parameters;
    LPBYTE dest = parms->pRemoteNodeAddress;
    char destAddr[19];

    DumpParameterTableHeader("DLC.OPEN.STATION", Parameters);

    destAddr[0] = 0;
    if (dest) {
        sprintf(destAddr, "%02x-%02x-%02x-%02x-%02x-%02x",
                dest[0],
                dest[1],
                dest[2],
                dest[3],
                dest[4],
                dest[5]
                );
    }
    PUT(("SAP station . . %04x\n"
        "link station. . %04x\n"
        "T1. . . . . . . %02x\n"
        "T2. . . . . . . %02x\n"
        "Ti. . . . . . . %02x\n"
        "max out . . . . %02x\n"
        "max in. . . . . %02x\n"
        "max out incr. . %02x\n"
        "max retry count %02x\n"
        "remote SAP. . . %02x\n"
        "max I field . . %04x\n"
        "access priority %02x\n"
        "remote node . . %08x [%s]\n",
        parms->usSapStationId,
        parms->usLinkStationId,
        parms->uchT1,
        parms->uchT2,
        parms->uchTi,
        parms->uchMaxOut,
        parms->uchMaxIn,
        parms->uchMaxOutIncr,
        parms->uchMaxRetryCnt,
        parms->uchRemoteSap,
        parms->usMaxI_Field,
        parms->uchAccessPriority,
        dest,
        destAddr
        ));
}

PRIVATE
VOID
DumpDlcReallocateParms(
    IN PVOID Parameters
    )
{
    PLLC_DLC_REALLOCATE_PARMS parms = (PLLC_DLC_REALLOCATE_PARMS)Parameters;

    DumpParameterTableHeader("DLC.REALLOCATE", Parameters);

    PUT(("station id. . . %04x\n"
        "option. . . . . %02x [%screase link stations]\n"
        "station count . %02x\n"
        "adapter stns. . %02x\n"

"SAP stations. . %02x\n"
        "adapter total . %02x\n"
        "SAP total . . . %02x\n",
        parms->usStationId,
        parms->uchOption,
        parms->uchOption & 0x80 ? "De" : "In",
        parms->uchStationCount,
        parms->uchStationsAvailOnAdapter,
        parms->uchStationsAvailOnSap,
        parms->uchTotalStationsOnAdapter,
        parms->uchTotalStationsOnSap
        ));
}

PRIVATE
VOID
DumpDlcResetParms(
    IN PVOID Parameters
    )
{
    PUT(("station id. . . %04x\n", HIWORD(Parameters)));
}

PRIVATE
VOID
DumpDlcSetThresholdParms(
    IN PVOID Parameters
    )
{
    PLLC_DLC_SET_THRESHOLD_PARMS parms = (PLLC_DLC_SET_THRESHOLD_PARMS)Parameters;

    DumpParameterTableHeader("DLC.SET.THRESHOLD", Parameters);

    PUT(("station id. . . %04x\n"
        "buf threshold . %04x\n"
        "event . . . . . %08x\n",
        parms->usStationId,
        parms->cBufferThreshold,
        parms->AlertEvent
        ));
}

PRIVATE
VOID
DumpDlcStatisticsParms(
    IN PVOID Parameters
    )
{
    PLLC_DLC_STATISTICS_PARMS parms = (PLLC_DLC_STATISTICS_PARMS)Parameters;

    DumpParameterTableHeader("DLC.STATISTICS", Parameters);

    PUT(("station id. . . %04x\n"
        "log buf size. . %04x\n"
        "log buffer. . . %08x\n"
        "actual log len. %04x\n"
        "options . . . . %02x [%s]\n",
        parms->usStationId,
        parms->cbLogBufSize,
        parms->pLogBuf,
        parms->usActLogLength,
        parms->uchOptions,
        parms->uchOptions & 0x80 ? "Reset counters" : ""
        ));
}

PRIVATE
VOID
DumpReadParms(
    IN PVOID Parameters
    )
{
    PLLC_READ_PARMS parms = (PLLC_READ_PARMS)Parameters;

    DumpParameterTableHeader("READ", Parameters);

    try {
        PUT(("station id. . . %04x\n"
            "option ind. . . %02x [%s]\n"
            "event set . . . %02x\n"
            "event . . . . . %02x [%s]\n"
            "crit. subset. . %02x\n"
            "notify flag . . %08x\n",
            parms->usStationId,
            parms->uchOptionIndicator,
            MapOptionIndicator(parms->uchOptionIndicator),
            parms->uchEventSet,
            parms->uchEvent,
            MapReadEvent(parms->uchEvent),
            parms->uchCriticalSubset,
            parms->ulNotificationFlag
            ));

        //
        // rest of table interpreted differently depending on whether status change
        //

        if (parms->uchEvent & 0x38) {
            PUT(("station id. . . %04x\n"
                "status code . . %04x [%s]\n"
                "FRMR data . . . %02x %02x %02x %02x %02x\n"
                "access pri. . . %02x\n"
                "remote addr . . %02x-%02x-%02x-%02x-%02x-%02x\n"
                "remote SAP. . . %02x\n"
                "reserved. . . . %02x\n"
                "user stat . . . %04x\n",
                parms->Type.Status.usStationId,
                parms->Type.Status.usDlcStatusCode,
                MapDlcStatus(parms->Type.Status.usDlcStatusCode),
                parms->Type.Status.uchFrmrData[0],
                parms->Type.Status.uchFrmrData[1],
                parms->Type.Status.uchFrmrData[2],
                parms->Type.Status.uchFrmrData[3],
                parms->Type.Status.uchFrmrData[4],
                parms->Type.Status.uchAccessPritority,
                parms->Type.Status.uchRemoteNodeAddress[0],
                parms->Type.Status.uchRemoteNodeAddress[1],
                parms->Type.Status.uchRemoteNodeAddress[2],
                parms->Type.Status.uchRemoteNodeAddress[3],
                parms->Type.Status.uchRemoteNodeAddress[4],
                parms->Type.Status.uchRemoteNodeAddress[5],
                parms->Type.Status.uchRemoteSap,
                parms->Type.Status.uchReserved,
                parms->Type.Status.usUserStatusValue
                ));
        } else {
            PUT(("CCB count . . . %04x\n"
                "CCB list. . . . %08x\n"
                "buffer count. . %04x\n"
                "buffer list . . %08x\n"
                "frame count . . %04x\n"
                "frame list. . . %08x\n"
                "error code. . . %04x\n"
                "error data. . . %04x %04x %04x\n",
                parms->Type.Event.usCcbCount,
                parms->Type.Event.pCcbCompletionList,
                parms->Type.Event.usBufferCount,
                parms->Type.Event.pFirstBuffer,
                parms->Type.Event.usReceivedFrameCount,
                parms->Type.Event.pReceivedFrame,
                parms->Type.Event.usEventErrorCode,
                parms->Type.Event.usEventErrorData[0],
                parms->Type.Event.usEventErrorData[1],
                parms->Type.Event.usEventErrorData[2]
                ));

            IF_DEBUG(DUMP_ASYNC_CCBS) {
                if (parms->Type.Event.usCcbCount) {
                    DumpCcb(parms->Type.Event.pCcbCompletionList,
                            TRUE,   // DumpAll
                            FALSE   // CcbIsInput
                            );
                }
            }

            IF_DEBUG(DUMP_RX_INFO) {
                if (parms->Type.Event.usReceivedFrameCount) {
                    DumpReceiveDataBuffer(parms->Type.Event.pReceivedFrame);
                }
            }
        }
    } except(1) {
        PUT(("*** Error: Bad READ Parameter Table @ %x ***\n", parms));
    }
}

PRIVATE
LPSTR
MapOptionIndicator(
    IN UCHAR OptionIndicator
    )
{
    switch (OptionIndicator) {
    case 0:
        return "Match READ command using station id nnss";

    case 1:
        return "Match READ command using SAP number nn00";

    case 2:
        return "Match READ command using all events";
    }
    return "*** Unknown READ Option Indicator ***";
}

PRIVATE LPSTR MapReadEvent(UCHAR Event) {
    switch (Event) {
    case 0x80:
        return "Reserved Event!";

    case 0x40:
        return "System Action (non-critical)";

    case 0x20:
        return "Network Status (non-critical)";

    case 0x10:
        return "Critical Exception";

    case 0x8:
        return "DLC Status Change";

    case 0x4:
        return "Receive Data";

    case 0x2:
        return "Transmit Completion";

    case 0x1:
        return "Command Completion";
    }
    return "*** Unknown READ Event ***";
}

PRIVATE LPSTR MapDlcStatus(WORD Status) {
    if (Status & 0x8000) {
        return "Link lost";
    } else if (Status & 0x4000) {
        return "DM/DISC Received -or- DISC ack'd";
    } else if (Status & 0x2000) {
        return "FRMR Received";
    } else if (Status & 0x1000) {
        return "FRMR Sent";
    } else if (Status & 0x0800) {
        return "SABME Received for open link station";
    } else if (Status & 0x0400) {
        return "SABME Received - link station opened";
    } else if (Status & 0x0200) {
        return "REMOTE Busy Entered";
    } else if (Status & 0x0100) {
        return "REMOTE Busy Left";
    } else if (Status & 0x0080) {
        return "Ti EXPIRED";
    } else if (Status & 0x0040) {
        return "DLC counter overflow - issue DLC.STATISTICS";
    } else if (Status & 0x0020) {
        return "Access Priority lowered";
    } else if (Status & 0x001e) {
        return "*** ERROR - INVALID STATUS ***";
    } else if (Status & 0x0001) {
        return "Entered LOCAL Busy";
    }
    return "*** Unknown DLC Status ***";
}

PRIVATE
VOID
DumpReadCancelParms(
    IN PVOID Parameters
    )
{
}

PRIVATE
VOID
DumpReceiveParms(
    IN  PVOID   Parameters
    )
{
    PLLC_RECEIVE_PARMS parms = (PLLC_RECEIVE_PARMS)Parameters;

    DumpParameterTableHeader("RECEIVE", Parameters);

    PUT(("station id. . . %04x\n"
        "user length . . %04x\n"
        "receive flag. . %08x\n"
        "first buffer. . %08x\n"
        "options . . . . %02x [%s]\n"
        "reserved. . . . %02x %02x %02x\n"
        "rcv read optns. %02x [%s]\n",
        parms->usStationId,
        parms->usUserLength,
        parms->ulReceiveFlag,
        parms->pFirstBuffer,
        parms->uchOptions,
        MapReceiveOptions(parms->uchOptions),
        parms->auchReserved1[0],
        parms->auchReserved1[1],
        parms->auchReserved1[2],
        parms->uchRcvReadOption,
        MapRcvReadOption(parms->uchRcvReadOption)
        ));
}

PRIVATE
LPSTR
MapReceiveOptions(
    IN UCHAR Options
    )
{
    static char buf[80];
    BOOL space = FALSE;

    buf[0] = 0;
    if (Options & 0x80) {
        strcat(buf, "Contiguous MAC");
        Options &= 0x7f;
        space = TRUE;
    }
    if (Options & 0x40) {
        if (space) {
            strcat(buf, " ");
        }
        strcat(buf, "Contiguous DATA");
        Options &= 0xbf;
        space = TRUE;
    }
    if (Options & 0x20) {
        if (space) {
            strcat(buf, " ");
        }
        strcat(buf, "Break");
        Options &= 0xdf;
        space = TRUE;
    }
    if (Options) {
        if (space) {
            strcat(buf, " ");
        }
        strcat(buf, "*** Invalid options ***");
    }
    return buf;
}

PRIVATE
LPSTR
MapRcvReadOption(
    IN UCHAR Option
    )
{
    switch (Option) {
    case 0:
        return "Receive frames not chained";

    case 1:
        return "Chain receive frames for link station";

    case 2:
        return "Chain receive frames for SAP";
    }
    return "*** Unknown option ***";
}

PRIVATE
VOID
DumpReceiveCancelParms(
    IN PVOID Parameters
    )
{
    PUT(("station id. . . %04x\n", HIWORD(Parameters)));
}

PRIVATE
VOID
DumpReceiveModifyParms(
    IN PVOID Parameters
    )
{
}

PRIVATE
VOID
DumpTransmitDirFrameParms(
    IN  PVOID   Parameters
    )
{
    DumpParameterTableHeader("TRANSMIT.DIR.FRAME", Parameters);
    DumpTransmitParms(Parameters);
}

PRIVATE
VOID
DumpTransmitIFrameParms(
    IN PVOID Parameters
    )
{
    DumpParameterTableHeader("TRANSMIT.I.FRAME", Parameters);
    DumpTransmitParms(Parameters);
}

PRIVATE
VOID
DumpTransmitTestCmdParms(
    IN  PVOID   Parameters
    )
{
    DumpParameterTableHeader("TRANSMIT.TEST.CMD", Parameters);
    DumpTransmitParms(Parameters);
}

PRIVATE
VOID
DumpTransmitUiFrameParms(
    IN  PVOID   Parameters
    )
{
    DumpParameterTableHeader("TRANSMIT.UI.FRAME", Parameters);
    DumpTransmitParms(Parameters);
}

PRIVATE
VOID
DumpTransmitXidCmdParms(
    IN  PVOID   Parameters
    )
{
    DumpParameterTableHeader("TRANSMIT.XID.CMD", Parameters);
    DumpTransmitParms(Parameters);
}

PRIVATE
VOID
DumpTransmitXidRespFinalParms(
    IN  PVOID   Parameters
    )
{
    DumpParameterTableHeader("TRANSMIT.XID.RESP.FINAL", Parameters);
    DumpTransmitParms(Parameters);
}

PRIVATE
VOID
DumpTransmitXidRespNotFinalParms(
    IN  PVOID   Parameters
    )
{
    DumpParameterTableHeader("TRANSMIT.XID.RESP.NOT.FINAL", Parameters);
    DumpTransmitParms(Parameters);
}

PRIVATE
VOID
DumpTransmitFramesParms(
    IN PVOID Parameters
    )
{
    DumpParameterTableHeader("TRANSMIT.FRAMES", Parameters);
//    DumpTransmitParms(Parameters);
}

PRIVATE
VOID
DumpTransmitParms(
    IN PVOID Parameters
    )
{
    PLLC_TRANSMIT_PARMS parms = (PLLC_TRANSMIT_PARMS)Parameters;

    try {
        PUT(("StationId . . . %04x\n"
            "Transmit FS . . %02x\n"
            "Remote SAP. . . %02x\n"
            "Xmit Queue 1. . %08x\n"
            "Xmit Queue 2. . %08x\n"
            "Buffer Length 1 %02x\n"
            "Buffer Length 2 %02x\n"
            "Buffer 1. . . . %08x\n"
            "Buffer 2. . . . %08x\n"
            "Xmt Read Option %02x [%s]\n"
            "\n",
            parms->usStationId,
            parms->uchTransmitFs,
            parms->uchRemoteSap,
            parms->pXmitQueue1,
            parms->pXmitQueue2,
            parms->cbBuffer1,
            parms->cbBuffer2,
            parms->pBuffer1,
            parms->pBuffer2,
            parms->uchXmitReadOption,
            MapXmitReadOption(parms->uchXmitReadOption)
            ));

        IF_DEBUG(DUMP_TX_INFO) {
            if (parms->pXmitQueue1) {
                PUT(("XMIT_QUEUE_ONE:\n"));
                DumpTransmitQueue(parms->pXmitQueue1);
            }

            if (parms->pXmitQueue2) {
                PUT(("XMIT_QUEUE_TWO:\n"));
                DumpTransmitQueue(parms->pXmitQueue2);
            }
        }

        IF_DEBUG(DUMP_TX_DATA) {
            if (parms->cbBuffer1) {
                DumpData("BUFFER_ONE. . . ",
                        (PBYTE)parms->pBuffer1,
                        (DWORD)parms->cbBuffer1,
                        DD_NO_ADDRESS
                        | DD_LINE_AFTER
                        | DD_INDENT_ALL
                        | DD_UPPER_CASE
                        | DD_DOT_DOT_SPACE,
                        DEFAULT_FIELD_WIDTH
                        );
            }

            if (parms->cbBuffer2) {
                DumpData("BUFFER_TWO. . . ",
                        (PBYTE)parms->pBuffer2,
                        (DWORD)parms->cbBuffer2,
                        DD_NO_ADDRESS
                        | DD_LINE_AFTER
                        | DD_INDENT_ALL
                        | DD_UPPER_CASE
                        | DD_DOT_DOT_SPACE,
                        DEFAULT_FIELD_WIDTH
                        );
            }
        }
    } except(1) {
        PUT(("*** Error: Bad Transmit Parameter Table @ %x ***\n", parms));
    }
}

PRIVATE
VOID
DumpTransmitQueue(
    IN PLLC_XMIT_BUFFER Buffer
    )
{
    try {
        while (Buffer) {
            PUT(("Next. . . . . . %08x\n"
                "Reserved. . . . %04x\n"
                "Data In Buffer. %04x\n"
                "User Data . . . %04x\n"
                "User Length . . %04x\n"
                "\n",
                Buffer->pNext,
                Buffer->usReserved1,
                Buffer->cbBuffer,
                Buffer->usReserved2,
                Buffer->cbUserData
                ));

            IF_DEBUG(DUMP_TX_DATA) {
                DumpData(NULL,
                        (PBYTE)Buffer->auchData,
                        (DWORD)Buffer->cbBuffer + (DWORD)Buffer->cbUserData,
                        DD_DEFAULT_OPTIONS,
                        DEFAULT_FIELD_WIDTH
                        );
            }
            Buffer = Buffer->pNext;
        }
    } except(1) {
        PUT(("*** Error: Bad Transmit Queue/Buffer @ %x ***\n", Buffer));
    }
}

PRIVATE
LPSTR
MapXmitReadOption(
    IN UCHAR Option
    )
{
    switch (Option) {
    case 0:
        return "Chain this Transmit on Link station basis";

    case 1:
        return "Do not chain this Transmit completion";

    case 2:
        return "Chain this Transmit on SAP station basis";
    }
    return "*** Unknown XMIT_READ_OPTION ***";
}

VOID
DumpReceiveDataBuffer(
    IN PVOID Buffer
    )
{
    PLLC_BUFFER pBuf = (PLLC_BUFFER)Buffer;
    BOOL contiguous;
    WORD userLength;
    WORD dataLength;
    WORD userOffset;

    try {
        contiguous = pBuf->Contiguous.uchOptions & 0xc0;
        userLength = pBuf->Next.cbUserData;
        dataLength = pBuf->Next.cbBuffer;
        userOffset = pBuf->Next.offUserData;
    } except(1) {
        PUT(("*** Error: Bad received data buffer address %x ***\n", pBuf));
        return;
    }

    //
    // Buffer 1: [not] contiguous MAC/DATA
    //

    try {
        PUT(("\n"
            "%sContiguous MAC/DATA frame @%08x\n"
            "next buffer . . %08x\n"
            "frame length. . %04x\n"
            "data length . . %04x\n"
            "user offset . . %04x\n"
            "user length . . %04x\n"
            "station id. . . %04x\n"
            "options . . . . %02x\n"
            "message type. . %02x [%s]\n"
            "buffers left. . %04x\n"
            "rcv FS. . . . . %02x\n"
            "adapter num . . %02x\n"
            "next frame. . . %08x\n",
            contiguous ? "" : "Not",
            pBuf,
            pBuf->Contiguous.pNextBuffer,
            pBuf->Contiguous.cbFrame,
            pBuf->Contiguous.cbBuffer,
            pBuf->Contiguous.offUserData,
            pBuf->Contiguous.cbUserData,
            pBuf->Contiguous.usStationId,
            pBuf->Contiguous.uchOptions,
            pBuf->Contiguous.uchMsgType,
            MapMessageType(pBuf->Contiguous.uchMsgType),
            pBuf->Contiguous.cBuffersLeft,
            pBuf->Contiguous.uchRcvFS,
            pBuf->Contiguous.uchAdapterNumber,
            pBuf->Contiguous.pNextFrame
            ));

        if (!contiguous) {

            //
            // dump NotContiguous header
            //

            DWORD cbLanHeader = (DWORD)pBuf->NotContiguous.cbLanHeader;
            DWORD cbDlcHeader = (DWORD)pBuf->NotContiguous.cbDlcHeader;

            PUT(("LAN hdr len . . %02x\n"
                "DLC hdr len . . %02x\n",
                cbLanHeader,
                cbDlcHeader
                ));
            DumpData("LAN header. . . ",
                    pBuf->NotContiguous.auchLanHeader,
                    cbLanHeader,
                    DD_NO_ADDRESS | DD_NO_ASCII | DD_UPPER_CASE | DD_INDENT_ALL,
                    DEFAULT_FIELD_WIDTH
                    );
            DumpData("DLC header. . . ",
                    pBuf->NotContiguous.auchDlcHeader,
                    cbDlcHeader,
                    DD_NO_ADDRESS | DD_NO_ASCII | DD_UPPER_CASE | DD_INDENT_ALL,
                    DEFAULT_FIELD_WIDTH
                    );

            if (userLength) {
                DumpData("user space. . . ",
                        (PBYTE)pBuf + userOffset,
                        userLength,
                        DD_NO_ADDRESS | DD_UPPER_CASE | DD_INDENT_ALL,
                        DEFAULT_FIELD_WIDTH
                        );
            } else {
                PUT(("user space. . . \n"));
            }

            IF_DEBUG(DUMP_RX_DATA) {

                if (dataLength) {
                    DumpData("rcvd data . . . ",
                            (PBYTE)pBuf + userOffset + userLength,
                            dataLength,
                            DD_NO_ADDRESS | DD_UPPER_CASE | DD_INDENT_ALL | DD_DOT_DOT_SPACE,
                            DEFAULT_FIELD_WIDTH
                            );
                } else {
                    PUT(("rcvd data . . . \n"));
                }
            }
        } else {

            //
            // dump Contiguous header
            //
            // data length is size of frame in contiguous buffer?
            //

            if (userLength) {
                DumpData("user space. . . ",
                        (PBYTE)pBuf + userOffset,
                        userLength,
                        DD_NO_ADDRESS | DD_UPPER_CASE | DD_INDENT_ALL | DD_DOT_DOT_SPACE,
                        DEFAULT_FIELD_WIDTH
                        );
            } else {
                PUT(("user space. . . \n"));
            }

            IF_DEBUG(DUMP_RX_DATA) {

                dataLength = pBuf->Contiguous.cbFrame;

                if (dataLength) {
                    DumpData("rcvd data . . . ",
                            (PBYTE)pBuf + userOffset + userLength,
                            dataLength,
                            DD_NO_ADDRESS | DD_UPPER_CASE | DD_INDENT_ALL | DD_DOT_DOT_SPACE,
                            DEFAULT_FIELD_WIDTH
                            );
                } else {
                    PUT(("rcvd data . . . \n"));
                }
            }
        }

        //
        // dump second & subsequent buffers
        //

        IF_DEBUG(DUMP_DATA_CHAIN) {

            for (pBuf = pBuf->pNext; pBuf; pBuf = pBuf->pNext) {

                userLength = pBuf->Next.cbUserData;
                dataLength = pBuf->Next.cbBuffer;

                PUT(("\n"
                    "Buffer 2/Subsequent @%08x\n"
                    "next buffer . . %08x\n"
                    "frame length. . %04x\n"
                    "data length . . %04x\n"
                    "user offset . . %04x\n"
                    "user length . . %04x\n",
                    pBuf,
                    pBuf->pNext,
                    pBuf->Next.cbFrame,
                    dataLength,
                    pBuf->Next.offUserData,
                    userLength
                    ));

                if (userLength) {
                    DumpData("user space. . . ",
                            (PBYTE)&pBuf + pBuf->Next.offUserData,
                            userLength,
                            DD_NO_ADDRESS | DD_UPPER_CASE | DD_INDENT_ALL | DD_DOT_DOT_SPACE,
                            DEFAULT_FIELD_WIDTH
                            );
                } else {
                    PUT(("user space. . . \n"));
                }

                IF_DEBUG(DUMP_RX_DATA) {

                    //
                    // there must be received data
                    //

                    DumpData("rcvd data . . . ",
                            (PBYTE)pBuf + pBuf->Next.offUserData + userLength,
                            dataLength,
                            DD_NO_ADDRESS | DD_UPPER_CASE | DD_INDENT_ALL | DD_DOT_DOT_SPACE,
                            DEFAULT_FIELD_WIDTH
                            );
                }
            }
        }

        if (((PLLC_BUFFER)Buffer)->Contiguous.pNextFrame) {
            DumpReceiveDataBuffer(((PLLC_BUFFER)Buffer)->Contiguous.pNextFrame);
        }

    } except(1) {
        PUT(("*** Error: Bad Receive Data Buffer @ %x ***\n", Buffer));
    }
}

PRIVATE
LPSTR
MapMessageType(
    IN UCHAR MessageType
    )
{
    switch (MessageType) {
    case 0x00:
        return "Direct Transmit Frame";

    case 0x02:
        return "MAC Frame (Direct Station on Token Ring only)";

    case 0x04:
        return "I-Frame";

    case 0x06:
        return "UI-Frame";

    case 0x08:
        return "XID Command (POLL)";

    case 0x0a:
        return "XID Command (not POLL)";

    case 0x0c:
        return "XID Response (FINAL)";

    case 0x0e:
        return "XID Response (not FINAL)";

    case 0x10:
        return "TEST Response (FINAL)";

    case 0x12:
        return "TEST Response (not FINAL)";

    case 0x14:
        return "Direct 802.2/OTHER - non-MAC frame (Direct Station only)";

    case 0x16:
        return "TEST Command (POLL)";

    case 0x18:
        return "Direct Ethernet Frame";

    case 0x1a:
        return "Last Frame Type";

//    case 0x5dd:
//        return "First Ethernet Frame Type";

    default:
        return "*** BAD FRAME TYPE ***";
    }
}

//PRIVATE
//VOID
//DumpData(
//    IN PBYTE Address,
//    IN DWORD Length
//    )
//{
//    char dumpBuf[80];
//    char* bufptr;
//    int i, n;
//
//    //
//    // the usual dump style: 16 columns of hex bytes, followed by 16 columns
//    // of corresponding ASCII characters, or '.' where the character is < 0x20
//    // (space) or > 0x7f (del?)
//    //
//
//    while (Length) {
//        bufptr = dumpBuf;
//        bufptr += sprintf(bufptr, "%08x: ", Address);
//        if (Length < 16) {
//            n = Length;
//        } else {
//            n = 16;
//        }
//        for (i = 0; i < n; ++i) {
//            bufptr += sprintf(bufptr, "%02x", Address[i]);
//            if (i == 7) {
//                *bufptr = '-';
//            } else {
//                *bufptr = ' ';
//            }
//            ++bufptr;
//        }
//        if (n < 16) {
//            for (i = 0; i < 16-n; ++i) {
//                bufptr += sprintf(bufptr, "   ");
//            }
//        }
//        bufptr += sprintf(bufptr, "  ");
//        for (i = 0; i < n; ++i) {
//            if (Address[i] < 0x20 || Address[i] > 0x7f) {
//                *bufptr++ = '.';
//            } else {
//                *bufptr++ = Address[i];
//            }
//        }
//        *bufptr++ = '\n';
//        *bufptr = 0;
//        PUT((dumpBuf));
//        Length -= n;
//        Address += n;
//    }
//    PUT(("\n"));
//}
#endif


SIMPLEX.C

/*++

Copyright 1994 - 1998 Microsoft Corporation

Module Name:

    simplex.c

Abstract:

    Simple single-threaded DLC test/example program. Need 2 instances of this
    app - 1 to send and 1 to receive (i.e. the typical DLC situation hence
    simplex, or half-duplex in old money). By default, both sides use SAP 4

    Receiver is started:

        simplex

    Transmitter is started e.g.

        simplex /t02608c4c970e

    in this example the node address is in canonical form (ethernet format) as
    displayed by "net config wksta", e.g., not the non-canonical (token-ring
    format) that the DLC API expects. If this test app is being run over
    token ring then you would supply the non-canonical address, as used by
    token ring, e.g.

        simplex /t10005a7b08b4

    Command line options are:

        /a# - use adapter #
        /b# - change the buffer pool size from the default 20K to #
        /o  - options:
            /or# - set receive READ option
            /ot# - set transmit READ option
        /r# - send to remote SAP # (transmitter only)
        /s# - open local SAP #
        /t# - send to station address # (transmitter only)
        /z# - transmit packets of size #, else send random sized packets
              (transmitter only)

    Contents:
        main
        usage
        get_funky_number
        is_radical_digit
        char_to_number
        handle_ctrl_c
        terminate
        xtou
        open_adapter
        adapter_status
        close_adapter
        create_buffer
        open_sap
        open_station
        connect_station
        flow_control
        get_buffer
        free_buffer
        post_receive
        post_read
        tx_i_frame
        slush
        do_transmit
        do_receive
        check_keyboard
        dispatch_read_events
        handle_status_change
        handle_receive_data
        handle_transmit_complete
        handle_command_complete
        twiddle_bits
        swap_bits
        my_malloc
        my_calloc
        my_free
        nice_num

Author:

    Richard L Firth (rfirth) 6-Mar-1994

Environment:

    Win32 app (console)

Revision History:

John Lee (johnlee) 22-Feb-1996

--*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <memory.h>
#include <conio.h>
#include <signal.h>
#undef tolower
#include <windows.h>
#include <dlcapi.h>

#include "dlcdebug.h"

#ifndef _CRTAPI1
#define _CRTAPI1
#endif

#define SIMPLEX_VERSION "1.11"

#define RECEIVE_MODE    0
#define TRANSMIT_MODE   1

#define DLCBUFSIZE  20000
#define SAP_NUMBER  4
#define RECEIVE_COMPLETE_FLAG   0x50204030
#define RECEIVE_DATA_FLAG       0x50204040
#define TRANSMIT_COMPLETE_FLAG  0x50404030

#define TX_STATE_OPENING    1
#define TX_STATE_OPENED     2
#define TX_STATE_TRANSMITTING   3
#define TX_STATE_BUSY       4

#define RX_STATE_LISTENING  1
#define RX_STATE_RECEIVING  2
#define RX_STATE_BLOCKED    3

#define MAX_OUTSTANDING_TRANSMIT_THRESHOLD  100
#define MIN_OUTSTANDING_TRANSMIT_THRESHOLD  10

#define IS_ARG(c)   (((c) == '-') || ((c) == '/'))
#define ZAP(thing)  memset(&thing, 0, sizeof(thing))
#define MALLOC      my_malloc
#define CALLOC      my_calloc
#define FREE        my_free

typedef struct {
    DWORD sequence;
    DWORD size;
    DWORD signature;
    DWORD checksum;
    char data[];
} TEST_PACKET, *PTEST_PACKET;

void _CRTAPI1 main(int, char**);
void usage(void);
DWORD get_funky_number(char**);
BOOL is_radical_digit(char, DWORD);
DWORD char_to_number(char);
void _CRTAPI1 handle_ctrl_c(int);
void terminate(int);
unsigned char xtou(char);
void open_adapter(void);
unsigned short adapter_status(void);
void close_adapter(void);
void create_buffer(int);
void open_sap(int);
void open_station(void);
void connect_station(unsigned short);
void flow_control(int);
PLLC_BUFFER get_buffer(void);
int free_buffer(PLLC_BUFFER);
void post_receive(void);
PLLC_CCB post_read(void);
void tx_i_frame(void);
DWORD slush(char*, int);
void do_transmit(void);
void do_receive(void);
void check_keyboard(void);
void dispatch_read_events(PLLC_CCB);
void handle_status_change(PLLC_CCB);
void handle_receive_data(PLLC_CCB);
void handle_transmit_complete(PLLC_CCB);
void handle_command_complete(PLLC_CCB);
void twiddle_bits(LPBYTE, DWORD);
unsigned char swap_bits(unsigned char);
void* my_malloc(int);
void* my_calloc(int, int);
void my_free(void*);
char* nice_num(unsigned long);

BYTE Adapter = 0;
DWORD BufferPoolSize = DLCBUFSIZE;
BYTE RemoteNode[6];
WORD LocalSap = SAP_NUMBER;
WORD RemoteSap = SAP_NUMBER;
DWORD Mode = RECEIVE_MODE;
BOOL SwapAddressBits = 0;
HANDLE TheMainEvent;
DWORD MaxFrameSize;
DWORD TransmitDataLength = 0;
LPBYTE BufferPool;
HANDLE BufferHandle;
USHORT StationId;
DWORD RxState = 0;
DWORD TxState = 0;
DWORD LocalBusy = 0;
DWORD RemoteBusy = 0;
DWORD Verbose = 0;
DWORD JustBufferInfo = 0;
LONG AllocatedBytesOutstanding = 0;
DWORD TotalBytesAllocated = 0;
DWORD TotalBytesFreed = 0;
LONG OutstandingTransmits = 0;
DWORD DisplayBufferFreeInfo = 1;
DWORD DisplayFrameReceivedInfo = 1;
DWORD DisplayTransmitInfo = 0;
DWORD DisplayCcb = 1;
DWORD TotalTransmits = 0;
DWORD TotalTransmitCompletions = 0;
DWORD TransmitCompleteEvents = 0;
DWORD CommandCompleteEvents = 0;
DWORD StatusChangeEvents = 0;
DWORD ReceiveDataEvents = 0;
DWORD DataFramesReceived = 0;
DWORD DlcBuffersReceived = 0;
DWORD DlcBuffersFreed = 0;
DWORD TotalBytesTransmitted = 0;
DWORD TotalTxBytesCompleted = 0;
DWORD TotalPacketBytesReceived = 0;
DWORD TotalDlcBytesReceived = 0;
DWORD TotalReadsChecked = 0;
DWORD TotalReadEvents = 0;
DWORD MaxChainedReceives = 0;
DWORD MaxChainedTransmits = 0;
DWORD MinBuffersAvailable = 0;
DWORD MaxBuffersAvailable = 0;

DWORD LinkLostEvents = 0;
DWORD DiscEvents = 0;
DWORD FrmrReceivedEvents = 0;
DWORD FrmrSentEvents = 0;
DWORD SabmeResetEvents = 0;
DWORD SabmeOpenEvents = 0;
DWORD RemoteBusyEnteredEvents = 0;
DWORD RemoteBusyLeftEvents = 0;
DWORD TiExpiredEvents = 0;
DWORD DlcCounterOverflowEvents = 0;
DWORD AccessPriorityLoweredEvents = 0;
DWORD InvalidStatusChangeEvents = 0;
DWORD LocalBusyEvents = 0;

BYTE OptionChainReceiveData = 1;
BYTE OptionChainTransmits = 0;

// The application ID is used on Windows 95, but not on Windows NT.

BYTE bApplId = 0;

void _CRTAPI1 main(int argc, char** argv) {

    printf("\nDLC simplex test. Version " SIMPLEX_VERSION " " __DATE__ " " __TIME__ "\n\n");

    for (--argc, ++argv; argc; --argc, ++argv) {
        if (IS_ARG(**argv)) {
            switch (tolower(*++*argv)) {
            case 'a':
                Adapter = atoi(++*argv);
                break;

            case 'b':
                ++*argv;
                BufferPoolSize = get_funky_number(argv);
                break;

            case 'h':
            case '?':
                usage();

            case 'o':
                ++*argv;
                while (**argv) {
                    switch (tolower(**argv)) {
                    case 'r':
                        ++*argv;
                        OptionChainReceiveData = (BYTE)get_funky_number(argv);
                        break;

                    case 't':
                        ++*argv;
                        OptionChainTransmits = (BYTE)get_funky_number(argv);
                        break;

                    default:
                        printf("error: unrecognized option '%c'\n", **argv);
                        usage();
                    }
                }
            case 'r':
                ++*argv;
                RemoteSap = (WORD)get_funky_number(argv);
                break;

            case 's':
                ++*argv;
                LocalSap = (WORD)get_funky_number(argv);
                break;

            case 't': {

                int i;
                LPSTR p = ++*argv;

                Mode = TRANSMIT_MODE;
                if (strlen(p) != 12) {
                    printf("incorrect remote node format (12 hex digits required)\n");
                    usage();
                }
                for (i = 0; i < 6; ++i) {
                    RemoteNode[i] = (xtou(*p++) << 4) + xtou(*p++);
                }
                break;
            }

            case 'v':
                Verbose = 1;
                break;

            case 'z':
                ++*argv;
                TransmitDataLength = get_funky_number(argv);
                break;
            }
        }
    }

    if ((TheMainEvent = CreateEvent(NULL, TRUE, FALSE, NULL)) == NULL) {
        printf("CreateEvent returns %d\n", GetLastError());
        exit(1);
    }

    printf("Running in %s mode.\n\n",
            (Mode == TRANSMIT_MODE) ? "Transmit" : "Receive"
            );

    if (Mode == TRANSMIT_MODE) {
        printf("remote node = %02x-%02x-%02x-%02x-%02x-%02x\n",
            RemoteNode[0] & 0xff,
            RemoteNode[1] & 0xff,
            RemoteNode[2] & 0xff,
            RemoteNode[3] & 0xff,
            RemoteNode[4] & 0xff,
            RemoteNode[5] & 0xff
            );
        DisplayTransmitInfo = 1;
    }
    open_adapter();
    MaxFrameSize = min((1500 - (14 + 4)), MaxFrameSize);
    if (TransmitDataLength && (TransmitDataLength > MaxFrameSize)) {
        TransmitDataLength = MaxFrameSize;
    }
    printf("opened adapter %d. maximum frame size = %d\n", Adapter, MaxFrameSize);
    switch (adapter_status()) {
    case 0:
        printf("type of adapter %d is token ring: not flipping hamburgers (nor address bits)\n", Adapter);
        SwapAddressBits = 0;
        break;

    case 1:
        printf("type of adapter %d is ethernet: will flip address bits\n", Adapter);
        SwapAddressBits = 1;
        break;

    case 2:
        printf("type of adapter %d is PC/Network card: don't know how to handle\n", Adapter);
        terminate(1);

    case 3:
        printf("adapter %d is >> UNKNOWN <<. Will assume FDDI and flip bits.\n"
               "If not correct, please fix\n", Adapter);
        printf("hit a key to continue... "); getch(); putchar('\n');
        break;
    }
    create_buffer(BufferPoolSize);
    MinBuffersAvailable = free_buffer(get_buffer());
    printf("created %d byte buffer pool @%x. Handle = %x. Initial buffers = %d\n",
        BufferPoolSize, BufferPool, BufferHandle, MinBuffersAvailable);
    open_sap(LocalSap);
    if (Verbose) {
        printf("opened SAP %d: StationId = %04x\n", LocalSap, StationId);
    }
    signal(SIGINT, handle_ctrl_c);
    if (Mode == TRANSMIT_MODE) {
        if (SwapAddressBits) {
            twiddle_bits(RemoteNode, 6);
        }
        do_transmit();
    } else {
        do_receive();
    }
    terminate(0);
}

void usage() {
    printf("usage: simplex [/a#] [/b#] [/h] [/o<option>] [/r#] [/s#] [/t<node>] [/v] [/z#]\n"
           "\n"
           "       /a = adapter number. Default = 0\n"
           "       /b = buffer pool size\n"
           "       /h = this help\n"
           "       /o = options:\n"
           "       /or[#] = chain receive data\n"
           "           0 = do NOT chain\n"
           "           1 = chain on link station basis (DEFAULT)\n"
           "           2 = chain on SAP basis\n"
           "       /ot[#] = chain transmit completions\n"
           "           0 = chain on link station basis (DEFAULT)\n"
           "           1 = do NOT chain\n"
           "           2 = chain on SAP basis\n"
           "       /r = remote SAP number (transmitter)\n"
           "       /s = local SAP number to use\n"
           "       /t = transmit mode\n"
           "       /v = verbose\n"
           "       /z = transmit data length. If omitted, packet size is random\n"
           "\n"
           "<node> is remote node id in correct form for medium\n"
           "default mode is receiver\n"
           "The buffer pool minimum threshold is 25%% of the buffer pool size\n"
           "\n"
           );
    exit(1);
}

DWORD get_funky_number(char** string) {

    DWORD radix = 10;
    char* p = *string;
    DWORD num = 0, sign=1;

    if (!*p) {
        return 0;
    }

    if (*p=='-') {
        sign = (DWORD)-1;
        p++;
    }

    if (!strnicmp(p, "0x", 2)) {
        p += 2;
        radix = 16;
    }

    while (is_radical_digit(*p, radix)) {
        num = num * radix + char_to_number(*p);
        ++p;
    }

    if (toupper(*p) == 'K') {
        if (radix == 10) {
            ++p;
            num *= 1024;
        } else {
            *string = NULL;
            return 0;
        }
    }
    *string = p;
    return sign * num;
}

BOOL is_radical_digit(char possible_digit, DWORD radix) {
    return (radix == 16) ? isxdigit(possible_digit) : isdigit(possible_digit);
}

DWORD char_to_number(char character) {
    if (isdigit(character)) {
        return (DWORD)(character - '0');
    } else {
        return (DWORD)(toupper(character) - 'A') + 10;
    }
}

void _CRTAPI1 handle_ctrl_c(int sig) {

    char ch;

    printf("\a\n"
           "Interrupted from console (control-c detected)\n"
           "Quit program? [Y/N] "
           );
    do {
        ch = tolower(getch());
        if (ch != 'y' && ch != 'n') {
            putchar('\a');
        }
    } while ( ch != 'y' && ch != 'n' );
    putchar(ch);
    putchar('\n');
    if (ch == 'y') {
        terminate(1);
    }
    signal(SIGINT, handle_ctrl_c);
}

void terminate(int exit_code) {

    close_adapter();

    printf("\nterminating %s mode\n", (Mode == TRANSMIT_MODE) ? "transmit" : "receive");

    printf("\n"
           "Memory statistics:\n");
    printf("\tAllocatedBytesOutstanding    = %s\n", nice_num(AllocatedBytesOutstanding));
    printf("\tTotalBytesAllocated          = %s\n", nice_num(TotalBytesAllocated));
    printf("\tTotalBytesFreed              = %s\n", nice_num(TotalBytesFreed));

    printf("\n"
           "Buffer statistics:\n");
    printf("\tMinBuffersAvailable          = %s\n", nice_num(MinBuffersAvailable));
    printf("\tMaxBuffersAvailable          = %s\n", nice_num(MaxBuffersAvailable));
    printf("\tDlcBuffersFreed              = %s\n", nice_num(DlcBuffersFreed));

    printf("\n"
           "READ statistics:\n");
    printf("\tTotalReadsChecked            = %s\n", nice_num(TotalReadsChecked));
    printf("\tTotalReadEvents              = %s\n", nice_num(TotalReadEvents));
    printf("\tCommandCompleteEvents        = %s\n", nice_num(CommandCompleteEvents));
    printf("\tTransmitCompleteEvents       = %s\n", nice_num(TransmitCompleteEvents));
    printf("\tReceiveDataEvents            = %s\n", nice_num(ReceiveDataEvents));
    printf("\tStatusChangeEvents           = %s\n", nice_num(StatusChangeEvents));

    if (Mode == TRANSMIT_MODE) {
        printf("\n"
               "Transmit statistics:\n");
        printf("\tTotalTransmits               = %s\n", nice_num(TotalTransmits));
        printf("\tTotalTransmitCompletions     = %s\n", nice_num(TotalTransmitCompletions));
        printf("\tOutstandingTransmits         = %s\n", nice_num(OutstandingTransmits));
        printf("\tTotalBytesTransmitted        = %s\n", nice_num(TotalBytesTransmitted));
        printf("\tTotalTxBytesCompleted        = %s\n", nice_num(TotalTxBytesCompleted));
        printf("\tMaxChainedTransmits          = %s\n", nice_num(MaxChainedTransmits));
    } else {
        printf("\n"
               "Receive statistics:\n");
        printf("\tDataFramesReceived           = %s\n", nice_num(DataFramesReceived));
        printf("\tDlcBuffersReceived           = %s\n", nice_num(DlcBuffersReceived));
        printf("\tTotalPacketBytesReceived     = %s\n", nice_num(TotalPacketBytesReceived));
        printf("\tTotalDlcBytesReceived        = %s\n", nice_num(TotalDlcBytesReceived));
        printf("\tMaxChainedReceives           = %s\n", nice_num(MaxChainedReceives));
    }

    printf("\n"
           "Status change statistics:\n");
    printf("\tLinkLostEvents               = %s\n", nice_num(LinkLostEvents));
    printf("\tDiscEvents                   = %s\n", nice_num(DiscEvents));
    printf("\tFrmrReceivedEvents           = %s\n", nice_num(FrmrReceivedEvents));
    printf("\tFrmrSentEvents               = %s\n", nice_num(FrmrSentEvents));
    printf("\tSabmeResetEvents             = %s\n", nice_num(SabmeResetEvents));
    printf("\tSabmeOpenEvents              = %s\n", nice_num(SabmeOpenEvents));
    printf("\tRemoteBusyEnteredEvents      = %s\n", nice_num(RemoteBusyEnteredEvents));
    printf("\tRemoteBusyLeftEvents         = %s\n", nice_num(RemoteBusyLeftEvents));
    printf("\tTiExpiredEvents              = %s\n", nice_num(TiExpiredEvents));
    printf("\tDlcCounterOverflowEvents     = %s\n", nice_num(DlcCounterOverflowEvents));
    printf("\tAccessPriorityLoweredEvents  = %s\n", nice_num(AccessPriorityLoweredEvents));
    printf("\tInvalidStatusChangeEvents    = %s\n", nice_num(InvalidStatusChangeEvents));
    printf("\tLocalBusyEvents              = %s\n", nice_num(LocalBusyEvents));

    exit(exit_code);
}

unsigned char xtou(char ch) {
    return ((ch >= '0') && (ch <= '9'))
        ? (unsigned char)(ch - '0')
        : ((ch >= 'A') && (ch <= 'F'))
            ? (unsigned char)((ch - 'A') + 10)
            : (unsigned char)((ch - 'a') + 10);
}

void open_adapter() {

    LLC_CCB ccb;
    LLC_DIR_OPEN_ADAPTER_PARMS parms;
    LLC_ADAPTER_OPEN_PARMS adapterParms;
    LLC_DLC_PARMS dlcParms;
    LLC_EXTENDED_ADAPTER_PARMS extendedParms;
    LLC_STATUS status;

    ZAP(ccb);
    ZAP(adapterParms);
    ZAP(dlcParms);
    ZAP(extendedParms);

    parms.pAdapterParms = &adapterParms;
    parms.pExtendedParms = &extendedParms;
    parms.pDlcParms = &dlcParms;

    ccb.uchAdapterNumber = Adapter;
    ccb.uchDlcCommand = LLC_DIR_OPEN_ADAPTER;
    ccb.u.pParameterTable = (PLLC_PARMS)&parms;
    ccb.hCompletionEvent = TheMainEvent;
    ResetEvent(TheMainEvent);

    status = AcsLan(&ccb, NULL);
    if (status != ACSLAN_STATUS_COMMAND_ACCEPTED) {
        printf("open_adapter: AcsLan returns %d [%#.2x]\n",
            status, ccb.uchDlcStatus);
        puts(MapCcbRetcode(ccb.uchDlcStatus));
        terminate(1);
    }
    status = WaitForSingleObject(TheMainEvent, INFINITE);
    if (status != WAIT_OBJECT_0) {
        printf("open_adapter: WaitForSingleObject returns %d [%d]\n",
            status, GetLastError());
        terminate(1);
    }
    if (ccb.uchDlcStatus) {
        printf("open_adapter: DLC returns %#.2x\n", ccb.uchDlcStatus);
        puts(MapCcbRetcode(ccb.uchDlcStatus));
        terminate(1);
    }
bApplId = ccb.uchReserved2;
    MaxFrameSize = adapterParms.usMaxFrameSize;
}

unsigned short adapter_status() {

    LLC_CCB ccb;
    LLC_DIR_STATUS_PARMS parms;
    ACSLAN_STATUS status;

    ZAP(ccb);
    ZAP(parms);

    ccb.uchAdapterNumber = Adapter;
    ccb.uchDlcCommand = LLC_DIR_STATUS;
    ccb.u.pParameterTable = (PLLC_PARMS)&parms;
    ccb.hCompletionEvent = TheMainEvent;
    ResetEvent(TheMainEvent);
ccb.uchReserved2 = bApplId;

    status = AcsLan(&ccb, NULL);
    if (status != ACSLAN_STATUS_COMMAND_ACCEPTED) {
        printf("adapter_status: AcsLan returns %d [%#.2x]\n",
            status, ccb.uchDlcStatus);
        puts(MapCcbRetcode(ccb.uchDlcStatus));
        terminate(1);
    }
    status = WaitForSingleObject(TheMainEvent, INFINITE);
    if (status != WAIT_OBJECT_0) {
        printf("adapter_status: WaitForSingleObject returns %d [%d]\n",
            status, GetLastError());
        terminate(1);
    }
    if (ccb.uchDlcStatus) {
        printf("adapter_status: DLC returns %#.2x\n", ccb.uchDlcStatus);
        puts(MapCcbRetcode(ccb.uchDlcStatus));
        terminate(1);
    }
    switch (parms.usAdapterType) {
    case 0x0001:    // Token Ring Network PC Adapter
    case 0x0002:    // Token Ring Network PC Adapter II
    case 0x0004:    // Token Ring Network Adapter/A
    case 0x0008:    // Token Ring Network PC Adapter II
    case 0x0020:    // Token Ring Network 16/4 Adapter
    case 0x0040:    // Token Ring Network 16/4 Adapter/A
    case 0x0080:    // Token Ring Network Adapter/A
        return 0;

    case 0x0100:    // Ethernet Adapter
        return 1;

    case 0x4000:    // PC Network Adapter
    case 0x8000:    // PC Network Adapter/A
        return 2;
    }
    return 3;   // unknown
}

void close_adapter() {

    LLC_CCB ccb;
    ACSLAN_STATUS status;

    ZAP(ccb);

    ccb.uchAdapterNumber = Adapter;
    ccb.uchDlcCommand = LLC_DIR_CLOSE_ADAPTER;
    ccb.hCompletionEvent = TheMainEvent;
    ResetEvent(TheMainEvent);
ccb.uchReserved2 = bApplId;

    status = AcsLan(&ccb, NULL);
    if (status != ACSLAN_STATUS_COMMAND_ACCEPTED) {
        printf("close_adapter: AcsLan returns %d [%#.2x]\n",
            status, ccb.uchDlcStatus);
        terminate(1);
    }
    status = WaitForSingleObject(TheMainEvent, INFINITE);
    if (status != WAIT_OBJECT_0) {
        printf("close_adapter: WaitForSingleObject returns %d [%d]\n",
            status, GetLastError());
        terminate(1);
    }
    if (ccb.uchDlcStatus) {
        printf("close_adapter: DLC returns %#.2x\n", ccb.uchDlcStatus);
        puts(MapCcbRetcode(ccb.uchDlcStatus));
        terminate(1);
    }
}

void create_buffer(int buflen) {

    LLC_CCB ccb;
    LLC_BUFFER_CREATE_PARMS parms;
    LPBYTE buffer;
    LLC_STATUS status;

    ZAP(ccb);
    ZAP(parms);

    buffer = MALLOC(buflen);

    parms.pBuffer = buffer;
    parms.cbBufferSize = buflen;
    parms.cbMinimumSizeThreshold = buflen / 4;

    ccb.uchAdapterNumber = Adapter;
    ccb.uchDlcCommand = LLC_BUFFER_CREATE;
    ccb.u.pParameterTable = (PLLC_PARMS)&parms;
    ccb.hCompletionEvent = TheMainEvent;
    ResetEvent(TheMainEvent);
ccb.uchReserved2 = bApplId;

    status = AcsLan(&ccb, NULL);
    if (status != ACSLAN_STATUS_COMMAND_ACCEPTED || ccb.uchDlcStatus) {
        printf("create_buffer: AcsLan returns %d [%#.2x]\n",
            status, ccb.uchDlcStatus);
        puts(MapCcbRetcode(ccb.uchDlcStatus));
        terminate(1);
    }
    status = WaitForSingleObject(TheMainEvent, INFINITE);
    if (status != WAIT_OBJECT_0) {
        printf("create_buffer: WaitForSingleObject returns %d [%d]\n",
            status, GetLastError());
        terminate(1);
    }
    if (ccb.uchDlcStatus) {
        printf("create_buffer: DLC returns %#.2x\n", ccb.uchDlcStatus);
        puts(MapCcbRetcode(ccb.uchDlcStatus));
        terminate(1);
    }
    BufferHandle = parms.hBufferPool;
    BufferPool = buffer;
}

void open_sap(int sap) {

    LLC_CCB ccb;
    LLC_DLC_OPEN_SAP_PARMS parms;
    LLC_STATUS status;

    ZAP(ccb);
    ZAP(parms);

    ccb.uchAdapterNumber = Adapter;
    ccb.uchDlcCommand = LLC_DLC_OPEN_SAP;
    ccb.u.pParameterTable = (PLLC_PARMS)&parms;
    ccb.hCompletionEvent = TheMainEvent;
    ResetEvent(TheMainEvent);
ccb.uchReserved2 = bApplId;

    parms.uchSapValue = (UCHAR)sap;
    parms.uchOptionsPriority = LLC_INDIVIDUAL_SAP;
    parms.uchcStationCount = 1;
// Set this to non-0 so event will be signalled.
// The ibm specs mandate that.
parms.DlcStatusFlags = 1; // non-0 to get signalled - johnlee

    status = AcsLan(&ccb, NULL);
    if (status != ACSLAN_STATUS_COMMAND_ACCEPTED || ccb.uchDlcStatus) {
        printf("open_sap: AcsLan returns %d [%#.2x]\n",
            status, ccb.uchDlcStatus);
        terminate(1);
    }
    status = WaitForSingleObject(TheMainEvent, INFINITE);
    if (status != WAIT_OBJECT_0) {
        printf("open_sap: WaitForSingleObject returns %d [%d]\n",
            status, GetLastError());
        terminate(1);
    }
    if (ccb.uchDlcStatus) {
        printf("open_sap: DLC returns %#.2x\n", ccb.uchDlcStatus);
        puts(MapCcbRetcode(ccb.uchDlcStatus));
        terminate(1);
    }
    StationId = parms.usStationId;
}

void open_station() {

    LLC_CCB ccb;
    LLC_DLC_OPEN_STATION_PARMS parms;
    LLC_STATUS status;

    ZAP(ccb);
    ZAP(parms);

// This should not be LocalSap. Read the specs. -- johnlee
    parms.usSapStationId = StationId; //(USHORT)LocalSap << 8;
    parms.uchRemoteSap = (UCHAR)RemoteSap;
    parms.pRemoteNodeAddress = (PVOID)RemoteNode;

    ccb.uchAdapterNumber = Adapter;
    ccb.uchDlcCommand = LLC_DLC_OPEN_STATION;
    ccb.u.pParameterTable = (PLLC_PARMS)&parms;
    ccb.hCompletionEvent = TheMainEvent;
    ResetEvent(TheMainEvent);
ccb.uchReserved2 = bApplId;

    status = AcsLan(&ccb, NULL);
    if (status != ACSLAN_STATUS_COMMAND_ACCEPTED || ccb.uchDlcStatus) {
        printf("open_station: AcsLan returns %d [%#.2x]\n",
            status, ccb.uchDlcStatus);
        terminate(1);
    }
    status = WaitForSingleObject(TheMainEvent, INFINITE);
    if (status != WAIT_OBJECT_0) {
        printf("open_station: WaitForSingleObject returns %d [%d]\n",
            status, GetLastError());
        terminate(1);
    }
    if (ccb.uchDlcStatus) {
        printf("open_station: DLC returns %#.2x\n", ccb.uchDlcStatus);
        puts(MapCcbRetcode(ccb.uchDlcStatus));
        terminate(1);
    }
    StationId = parms.usLinkStationId;
}

void connect_station(unsigned short station) {

    LLC_CCB ccb;
    LLC_DLC_CONNECT_PARMS parms;
    ACSLAN_STATUS status;

    ZAP(ccb);
    ZAP(parms);

    parms.usStationId = station;

    ccb.uchAdapterNumber = Adapter;
    ccb.uchDlcCommand = LLC_DLC_CONNECT_STATION;
    ccb.u.pParameterTable = (PLLC_PARMS)&parms;
    ccb.hCompletionEvent = TheMainEvent;
    ResetEvent(TheMainEvent);
ccb.uchReserved2 = bApplId;

    status = AcsLan(&ccb, NULL);
    if (status != ACSLAN_STATUS_COMMAND_ACCEPTED) {
        printf("connect_station: AcsLan returns %d [%#.2x]\n",
            status, ccb.uchDlcStatus);
        terminate(1);
    }
    status = WaitForSingleObject(TheMainEvent, INFINITE);
    if (status != WAIT_OBJECT_0) {
        printf("connect_station: WaitForSingleObject returns %d [%d]\n",
            status, GetLastError());
        terminate(1);
    }
    if (ccb.uchDlcStatus) {
        printf("connect_station: DLC returns %#.2x\n", ccb.uchDlcStatus);
        puts(MapCcbRetcode(ccb.uchDlcStatus));
        terminate(1);
    }
    if (Verbose) {
        printf("connect_station: OK\n");
    }
}

void flow_control(int station) {

    LLC_CCB ccb;
    ACSLAN_STATUS status;

    ZAP(ccb);

    ccb.uchAdapterNumber = Adapter;
    ccb.uchDlcCommand = LLC_DLC_FLOW_CONTROL;
    ccb.u.dlc.usStationId = station;
    ccb.u.dlc.usParameter = 0xc0;
    ccb.hCompletionEvent = TheMainEvent;
    ResetEvent(TheMainEvent);
ccb.uchReserved2 = bApplId;

    if (Verbose) {
        printf("flow_control(%04x, c0)\n", station);
    }
    status = AcsLan(&ccb, NULL);
    if (status != ACSLAN_STATUS_COMMAND_ACCEPTED) {
        printf("flow_control: AcsLan returns %d [%#.2x]\n",
            status, ccb.uchDlcStatus);
        terminate(1);
    }
    status = WaitForSingleObject(TheMainEvent, INFINITE);
    if (status != WAIT_OBJECT_0) {
        printf("flow_control: WaitForSingleObject returns %d [%d]\n",
            status, GetLastError());
        terminate(1);
    }
    if (ccb.uchDlcStatus) {
        printf("flow_control: DLC returns %#.2x\n", ccb.uchDlcStatus);
        puts(MapCcbRetcode(ccb.uchDlcStatus));
        terminate(1);
    }
    if (Verbose) {
        printf("flow_control: OK\n");
    }
    LocalBusy = 0;
}

PLLC_BUFFER get_buffer() {

    LLC_CCB ccb;
    LLC_BUFFER_GET_PARMS parms;
    ACSLAN_STATUS status;

    ZAP(ccb);
    ZAP(parms);

    parms.cBuffersToGet = 1;
    parms.cbBufferSize = 256;

    ccb.uchAdapterNumber = Adapter;
    ccb.uchDlcCommand = LLC_BUFFER_GET;
    ccb.u.pParameterTable = (PLLC_PARMS)&parms;
    ccb.hCompletionEvent = TheMainEvent;
    ResetEvent(TheMainEvent);
ccb.uchReserved2 = bApplId;

    status = AcsLan(&ccb, NULL);
    if (status != ACSLAN_STATUS_COMMAND_ACCEPTED) {
        printf("get_buffer(): AcsLan returns %d [%#.2x]\n", status, ccb.uchDlcStatus);
        //terminate(1);
        return NULL;
    }
    if (WaitForSingleObject(TheMainEvent, INFINITE) != WAIT_OBJECT_0) {
        printf("get_buffer: WaitForSingleObject returns %d\n", GetLastError());
        terminate(1);
    }

if (ccb.uchDlcStatus) {
        printf("get_buffer: DLC returns %#.2x\n", ccb.uchDlcStatus);
        puts(MapCcbRetcode(ccb.uchDlcStatus));
        terminate(1);
    }
    if (parms.cBuffersLeft < MinBuffersAvailable) {
        MinBuffersAvailable = parms.cBuffersLeft;
    }
    if (parms.cBuffersLeft > MaxBuffersAvailable) {
        MaxBuffersAvailable = parms.cBuffersLeft;
    }
    return (PLLC_BUFFER)parms.pFirstBuffer;
}

int free_buffer(PLLC_BUFFER buffer) {

    LLC_CCB ccb;
    LLC_BUFFER_FREE_PARMS parms;
    ACSLAN_STATUS status;

    if (!buffer) {

        //
        // microhackette in case get_buffer failed
        //

        return 0;
    }

    ZAP(ccb);
    ZAP(parms);

    parms.pFirstBuffer = (PLLC_XMIT_BUFFER)buffer;

    ccb.uchAdapterNumber = Adapter;
    ccb.uchDlcCommand = LLC_BUFFER_FREE;
    ccb.u.pParameterTable = (PLLC_PARMS)&parms;
    ccb.hCompletionEvent = TheMainEvent;
    ResetEvent(TheMainEvent);
ccb.uchReserved2 = bApplId;

    status = AcsLan(&ccb, NULL);
    if (status != ACSLAN_STATUS_COMMAND_ACCEPTED) {
        printf("free_buffer(%x): AcsLan returns %d [%#.2x]\n", buffer, status, ccb.uchDlcStatus);
        terminate(1);
    }
    if (WaitForSingleObject(TheMainEvent, INFINITE) != WAIT_OBJECT_0) {
        printf("free_buffer: WaitForSingleObject returns %d\n", GetLastError());
        terminate(1);
    }
    if (ccb.uchDlcStatus) {
        printf("free_buffer(%#x): DLC returns %#.2x\n", buffer, ccb.uchDlcStatus);
        puts(MapCcbRetcode(ccb.uchDlcStatus));
        terminate(1);
    }
    if (DisplayBufferFreeInfo) {
        printf("free_buffer(%x): %u buffers left\n", buffer, parms.cBuffersLeft);
    }
    if (parms.cBuffersLeft < MinBuffersAvailable) {
        MinBuffersAvailable = parms.cBuffersLeft;
    }
    if (parms.cBuffersLeft > MaxBuffersAvailable) {
        MaxBuffersAvailable = parms.cBuffersLeft;
    }
    return parms.cBuffersLeft;
}

void post_receive() {

    PLLC_CCB pccb;
    PLLC_RECEIVE_PARMS pparms;
    ACSLAN_STATUS status;

    pccb = CALLOC(1, sizeof(*pccb));
    pparms = CALLOC(1, sizeof(*pparms));

    pparms->usStationId = StationId;
    pparms->ulReceiveFlag = RECEIVE_DATA_FLAG;
    pparms->uchRcvReadOption = OptionChainReceiveData;

    pccb->uchAdapterNumber = Adapter;
    pccb->uchDlcCommand = LLC_RECEIVE;
    pccb->ulCompletionFlag = RECEIVE_COMPLETE_FLAG;
    pccb->u.pParameterTable = (PLLC_PARMS)pparms;
pccb->uchReserved2 = bApplId;

    status = AcsLan(pccb, NULL);
    if (status != ACSLAN_STATUS_COMMAND_ACCEPTED) {
        printf("post_receive: AcsLan returns %d [%#.2x]\n", status, pccb->uchDlcStatus);
        terminate(1);
    }
    if (pccb->uchDlcStatus != 0xFF) {
        printf("post_receive: CCB.RETCODE = %#.2x\n", pccb->uchDlcStatus);
        puts(MapCcbRetcode(pccb->uchDlcStatus));
        terminate(1);
    }
    if (Verbose) {
        printf("receive posted on station %04x\n", StationId);
    }
}

PLLC_CCB post_read() {

    PLLC_CCB pccb;
    PLLC_READ_PARMS pparms;
    ACSLAN_STATUS status;

    pccb = CALLOC(1, sizeof(*pccb));
    pparms = CALLOC(1, sizeof(*pparms));

    pparms->usStationId = StationId;
    pparms->uchOptionIndicator = 2; // retrieve ALL events for this app
    pparms->uchEventSet = 0x7f;     // interested in ALL possible events

 pccb->uchAdapterNumber = Adapter;
    pccb->uchDlcCommand = LLC_READ;
    pccb->u.pParameterTable = (PLLC_PARMS)pparms;
    pccb->hCompletionEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (!pccb->hCompletionEvent) {
        printf("post_read: CreateEvent returns %d\n", GetLastError());
        terminate(1);
    }

pccb->uchReserved2 = bApplId;

    status = AcsLan(pccb, NULL);
    if (status != ACSLAN_STATUS_COMMAND_ACCEPTED) {
        printf("post_read: AcsLan returns %d [%#.2x]\n", status, pccb->uchDlcStatus);
        terminate(1);
    }
    return pccb;
}

void tx_i_frame() {

    PLLC_CCB pccb;
    PLLC_TRANSMIT_PARMS pparms;
    ACSLAN_STATUS status;
    int data_size;
    PTEST_PACKET packet;

    static DWORD PacketSequenceNumber = 0;

    pccb = CALLOC(1, sizeof(*pccb));
    pparms = CALLOC(1, sizeof(*pparms));

    data_size = TransmitDataLength
              ? TransmitDataLength
              : (rand() * 600) / RAND_MAX;
  // since this app does not specify max i-frame
  // 600 will be used by windows dlc32 as specified by
  // the ibm dlc specs -- johnlee
              //: (rand() * MaxFrameSize) / RAND_MAX;
    data_size = max(data_size, sizeof(TEST_PACKET));

    packet = (PTEST_PACKET)MALLOC(data_size);
    packet->sequence = PacketSequenceNumber++;
    packet->size = data_size;
    packet->signature = 0x11191962;

    packet->checksum = slush(packet->data, data_size - sizeof(TEST_PACKET));

    pparms->usStationId = StationId;
    pparms->cbBuffer1 = data_size;
    pparms->pBuffer1 = packet;
    pparms->uchXmitReadOption = OptionChainTransmits;

    pccb->uchAdapterNumber = Adapter;
    pccb->uchDlcCommand = LLC_TRANSMIT_I_FRAME;
    pccb->ulCompletionFlag = TRANSMIT_COMPLETE_FLAG;
    pccb->u.pParameterTable = (PLLC_PARMS)pparms;

pccb->uchReserved2 = bApplId;

    status = AcsLan(pccb, NULL);

    //
    // this may fail with 0x69 (or maybe 0xa1) if the system is out of MDL
    // resources. This can happen if we have a lot of completed transmits
    // waiting to be deallocated. In this case just deallocate resources and
    // return - do_transmit should spin removing completed transmits until we
    // can continue
    //

    if (status != ACSLAN_STATUS_COMMAND_ACCEPTED) {
        printf("tx_i_frame: AcsLan returns %d [%#.2x]\n",
               status,
               pccb->uchDlcStatus
               );
        puts(MapCcbRetcode(pccb->uchDlcStatus));
        printf("AllocatedBytesOutstanding = %s [%#x]\n",
                nice_num(AllocatedBytesOutstanding),
                AllocatedBytesOutstanding
                );
        printf("TotalBytesAllocated       = %s [%#x]\n",
                nice_num(TotalBytesAllocated)
                , TotalBytesAllocated
                );
        printf("OutstandingTransmits      = %s\n",
                nice_num(OutstandingTransmits)
                );
        printf("TotalTransmits            = %s\n",
                nice_num(TotalTransmits)
                );
        printf("TotalTransmitCompletions  = %s\n",
                nice_num(TotalTransmitCompletions)
                );
        printf("TransmitCompleteEvents    = %s\n",
                nice_num(TransmitCompleteEvents)
                );
        if (DisplayCcb) {
            DUMPCCB(pccb, TRUE, FALSE);
        }
        FREE(packet);
        FREE(pparms);
        FREE(pccb);
        --PacketSequenceNumber; // didn't tx this sequence #
    } else {
        if (Verbose) {
            printf("tx_i_frame: transmitted %4d bytes\n", data_size);
        }
        ++OutstandingTransmits;
        ++TotalTransmits;
        TotalBytesTransmitted += (DWORD)data_size;
    }
}

DWORD slush(char* buffer, int length) {

    DWORD cs = 0;
    unsigned char ch;

    while (length--) {
        ch = (unsigned char)(rand() & 0xff);
        *buffer++ = ch;
        cs += (DWORD)ch;
    }
    return cs;
}

void do_transmit() {

    PLLC_CCB read_ccb;
    BOOL need_read = TRUE;
    BOOL wait_min = FALSE;

    TxState = TX_STATE_OPENING;
    open_station();
    if (Verbose) {
        printf("opened link station. StationId = %04x\n", StationId);
    }
    connect_station(StationId);
    TxState = TX_STATE_OPENED;
    if (Verbose) {
        printf("connected to remote\n");
    }

    while (1) {
        if (need_read) {
            read_ccb = post_read();
            need_read = FALSE;
        }
        if (WaitForSingleObject(read_ccb->hCompletionEvent, 0) == WAIT_OBJECT_0) {
            dispatch_read_events(read_ccb);
            need_read = TRUE;
        }
        if (wait_min) {
            if (OutstandingTransmits <= MIN_OUTSTANDING_TRANSMIT_THRESHOLD) {
                wait_min = FALSE;
            }
        } else if (OutstandingTransmits > MAX_OUTSTANDING_TRANSMIT_THRESHOLD) {
            if (Verbose) {
                printf("do_transmit: not transmitting: outstanding transmits (%d) > threshold (%d)\n",
                    OutstandingTransmits, MAX_OUTSTANDING_TRANSMIT_THRESHOLD);
            }
            wait_min = TRUE;
        } else if (RemoteBusy) {
            if (Verbose) {
                printf("do_transmit: not transmitting: remote busy\n");
            }
            Sleep(100);
        } else {
            tx_i_frame();
        }
        check_keyboard();
    }
}

void do_receive() {

    PLLC_CCB read_ccb;
    DWORD waitStatus;

    post_receive();

    RxState = RX_STATE_LISTENING;
    while (1) {
        read_ccb = post_read();
        waitStatus = WaitForSingleObject(read_ccb->hCompletionEvent, INFINITE);
        if (waitStatus != WAIT_OBJECT_0) {
            printf("do_receive: WaitForSingleObject returns %d??\n", GetLastError());
            continue;
        }
        dispatch_read_events(read_ccb);
        check_keyboard();
    }
}

void check_keyboard() {
    if (kbhit()) {
        switch (tolower(getch())) {
        case 'b':   // just the facts ma'am
            JustBufferInfo = !JustBufferInfo;
            if (JustBufferInfo) {
                Verbose = 0;
            }
            break;

        case 'd':
            DisplayCcb = !DisplayCcb;
            break;

        case 'f':
            DisplayFrameReceivedInfo = !DisplayFrameReceivedInfo;
            break;

        case 'i':
            DisplayBufferFreeInfo = !DisplayBufferFreeInfo;
            break;

        case 's':   // Stop & Go
            while (tolower(getch()) != 'g') {
                putchar('\a');
            }
            break;

        case 't':
            DisplayTransmitInfo = !DisplayTransmitInfo;
            break;

        case 'v':   // toggle Verbose
            Verbose = !Verbose;
            break;

        case 'x':   // eXit
            terminate(0);

        default:
            putchar('\a');
        }
    }
}

void dispatch_read_events(PLLC_CCB read_ccb) {

    BYTE event;
    BYTE comb;

    ++TotalReadsChecked;

    if (read_ccb->uchDlcStatus == LLC_STATUS_SUCCESS) {
        event = ((PLLC_READ_PARMS)read_ccb->u.pParameterTable)->uchEvent;
        if (Verbose) {
            printf("dispatch_read_events: event %02x occurred: ", event);
        }
        for (comb = 0x80; comb; comb >>= 1) {
            if (event & comb) {
                ++TotalReadEvents;
            }
            switch (event & comb) {
            case 0x80:
                if (Verbose) {
                    printf("RESERVED - shouldn't happen??!!\n");
                }
                break;

            case 0x40:
                if (Verbose) {
                    printf("SYSTEM ACTION (non-critical)?\n");
                }
                break;

            case 0x20:
                if (Verbose) {
                    printf("NETWORK STATUS (non-critical)?\n");
                }
                break;

            case 0x10:
                if (Verbose) {
                    printf("CRITICAL EXCEPTION?\n");
                }
                break;

            case 0x08:
                if (Verbose) {
                    printf("DLC STATUS CHANGE\n");
                }
                handle_status_change(read_ccb);
                break;

            case 0x04:
                if (Verbose) {
                    printf("RECEIVED DATA\n");
                }
                handle_receive_data(read_ccb);
                break;

            case 0x02:
                if (Verbose) {
                    printf("TRANSMIT COMPLETION\n");
                }
                handle_transmit_complete(read_ccb);
                break;

            case 0x01:
                if (Verbose) {
                    printf("COMMAND COMPLETION\n");
                }
                handle_command_complete(read_ccb);
                break;
            }
        }
    } else {
        printf("dispatch_read_events: read error %#.2x\n", read_ccb->uchDlcStatus);
    }
    FREE(read_ccb->u.pParameterTable);
    CloseHandle(read_ccb->hCompletionEvent);
    FREE(read_ccb);
}

void handle_status_change(PLLC_CCB read_ccb) {

    PLLC_READ_PARMS parms = (PLLC_READ_PARMS)read_ccb->u.pParameterTable;
    USHORT status = parms->Type.Status.usDlcStatusCode;
    USHORT comb;
    BOOL lost_it = FALSE;

    for (comb = 0x8000; comb; comb >>= 1) {
        if (status & comb) {
            ++StatusChangeEvents;
        }
        switch (status & comb) {
        case 0x8000:
            printf("LINK LOST\n");
            lost_it = TRUE;
            ++LinkLostEvents;
            break;

        case 0x4000:
            printf("DM/DISC received or DISC acked\n");
            lost_it = TRUE;
            ++DiscEvents;
            break;

        case 0x2000:
            printf("FRMR received\n");
            lost_it = TRUE;
            ++FrmrReceivedEvents;
            break;

        case 0x1000:
            printf("FRMR sent\n");
            ++FrmrSentEvents;
            break;

        case 0x0800:
            printf("SABME received on open LINK station\n");
            ++SabmeResetEvents;
            break;

        case 0x0400:
            memcpy(RemoteNode, parms->Type.Status.uchRemoteNodeAddress, 6);
            if (SwapAddressBits) {
                twiddle_bits(RemoteNode, 6);
            }
            printf("SABME received - new link %04x. RemoteNode = %02x-%02x-%02x-%02x-%02x-%02x\n",
                    parms->Type.Status.usStationId,
                    RemoteNode[0] & 0xff,
                    RemoteNode[1] & 0xff,
                    RemoteNode[2] & 0xff,
                    RemoteNode[3] & 0xff,
                    RemoteNode[4] & 0xff,
                    RemoteNode[5] & 0xff
                    );
            if (Mode == RECEIVE_MODE) {
                connect_station(parms->Type.Status.usStationId);
                RxState = RX_STATE_RECEIVING;
            } else {
                printf(" - ON TRANSMITTING SIDE????\n");
            }
            ++SabmeOpenEvents;
            break;

        case 0x0200:
            printf("REMOTE BUSY\n");
            RemoteBusy = 1;
            ++RemoteBusyEnteredEvents;
            break;

        case 0x0100:
            printf("REMOTE BUSY CLEARED\n");
            RemoteBusy = 0;
            ++RemoteBusyLeftEvents;
            break;

        case 0x0080:
            printf("Ti EXPIRED\n");
            ++TiExpiredEvents;
            break;

        case 0x0040:
            printf("DLC counter overflow\n");
            ++DlcCounterOverflowEvents;
            break;

        case 0x0020:
            printf("Access priority lowered (on ethernet????!)\n");
            ++AccessPriorityLoweredEvents;
            break;

        case 0x0010:
        case 0x0008:
        case 0x0004:
        case 0x0002:
            printf("\aThis status code (%04x) should be reserved!\n", status & comb);
            ++InvalidStatusChangeEvents;
            break;

        case 0x0001: {

            int bufs_avail;

            LocalBusy = 1;
            flow_control(parms->Type.Status.usStationId);
            ++LocalBusyEvents;
            bufs_avail = free_buffer(get_buffer());
            printf("LOCAL BUSY: %d buffers left\n", bufs_avail);
            if (bufs_avail) {
                LocalBusy = 0;
                printf("LOCAL BUSY CLEARED\n");
            }
            break;
        }
        }
    }
    if (lost_it) {
        printf("lost it - quitting\n");
        terminate(1);
    }
}

void handle_receive_data(PLLC_CCB read_ccb) {

    PLLC_READ_PARMS parms = (PLLC_READ_PARMS)read_ccb->u.pParameterTable;
    DWORD i;
    PLLC_BUFFER rx_frame;
    PLLC_BUFFER next_frame;
    DWORD nframes;
    DWORD bufs_left;
    PLLC_BUFFER pbuf;

    ++ReceiveDataEvents;

    nframes = parms->Type.Event.usReceivedFrameCount;
    if (nframes > MaxChainedReceives) {
        MaxChainedReceives = nframes;
    }
    rx_frame = parms->Type.Event.pReceivedFrame;
    bufs_left = rx_frame->NotContiguous.cBuffersLeft;
    if (bufs_left < MinBuffersAvailable) {
        MinBuffersAvailable = bufs_left;
    }
    if (bufs_left > MaxBuffersAvailable) {
        MaxBuffersAvailable = bufs_left;
    }
    if (DisplayFrameReceivedInfo) {
        printf("handle_receive_data: %d frames received, %d buffers left\n",
            nframes,
            bufs_left
            );
    }
    for (i = 0; i < nframes; ++i) {
        ++DataFramesReceived;
        next_frame = rx_frame->NotContiguous.pNextFrame;
        if (!JustBufferInfo) {
            printf("Packet Sequence %08x  # bytes = %4d  %d buffers left\n",
                ((PTEST_PACKET)&rx_frame->NotCont.auchData)->sequence,
                ((PTEST_PACKET)&rx_frame->NotCont.auchData)->size,
                rx_frame->NotContiguous.cBuffersLeft
                );
        }
        TotalDlcBytesReceived += rx_frame->Next.cbFrame;
        TotalPacketBytesReceived += ((PTEST_PACKET)&rx_frame->NotCont.auchData)->size;
        for (pbuf = rx_frame; pbuf; pbuf = pbuf->pNext) {
            ++DlcBuffersReceived;
        }
        free_buffer(rx_frame);
        ++DlcBuffersFreed;
        if (!next_frame && i != nframes - 1) {
            printf("handle_receive_data: unexpected NULL pointer terminates list @ %d\n", i);
            break;
        }
        rx_frame = next_frame;
    }
}

void handle_transmit_complete(PLLC_CCB read_ccb) {

    PLLC_READ_PARMS parms = (PLLC_READ_PARMS)read_ccb->u.pParameterTable;
    DWORD i;
    PLLC_CCB tx_ccb;
    PLLC_CCB next_ccb;
    DWORD nframes;
    DWORD txlen;

    ++TransmitCompleteEvents;

    nframes = parms->Type.Event.usCcbCount;
    if (nframes > MaxChainedTransmits) {
        MaxChainedTransmits = nframes;
    }
    if (Verbose || DisplayTransmitInfo) {
        printf("handle_transmit_complete: %d transmits completed\n", nframes);
    }
    tx_ccb = parms->Type.Event.pCcbCompletionList;
    for (i = 0; i < nframes; ++i) {
        next_ccb = tx_ccb->pNext;
        txlen = ((PLLC_TRANSMIT_PARMS)tx_ccb->u.pParameterTable)->cbBuffer1;
        TotalTxBytesCompleted += txlen;
        if (tx_ccb->uchDlcStatus) {
            printf("\ahandle_transmit_complete: TX CCB %08x error %#.2x\n",
                tx_ccb, tx_ccb->uchDlcStatus);
            if (tx_ccb->uchDlcStatus == LLC_STATUS_INVALID_FRAME_LENGTH) {
                printf("data length = %d\n", txlen);
            }
        }
        FREE(((PLLC_TRANSMIT_PARMS)tx_ccb->u.pParameterTable)->pBuffer1);
        FREE(tx_ccb->u.pParameterTable);
        FREE(tx_ccb);
        --OutstandingTransmits;
        if (OutstandingTransmits < 0) {
            printf("handle_transmit_complete: more transmit completions than transmits (%d)\n",
                OutstandingTransmits);
        }
        ++TotalTransmitCompletions;
        tx_ccb = next_ccb;
        if (!next_ccb && i != nframes - 1) {
            printf("handle_transmit_complete: unexpected NULL pointer terminates list @ %d\n", i);
            break;
        }
    }
}

void handle_command_complete(PLLC_CCB read_ccb) {

    PLLC_READ_PARMS parms = (PLLC_READ_PARMS)read_ccb->u.pParameterTable;

    ++CommandCompleteEvents;

    printf("handle_command_complete: %d CCBs, %d buffers, %d received frames\n",
        parms->Type.Event.usCcbCount,
        parms->Type.Event.usBufferCount,
        parms->Type.Event.usReceivedFrameCount
        );
}

void twiddle_bits(LPBYTE buffer, DWORD length) {

    while (length--) {
        *buffer = swap_bits(*buffer);
        ++buffer;
    }
}

unsigned char swap_bits(unsigned char b) {

    unsigned char bb = 0;
    unsigned char mask;

    for (mask = 1; mask; mask <<= 1) {
        bb <<= 1;
        bb |= ((b & mask) ? 1 : 0);
    }
    return bb;
}

void* my_malloc(int size) {

    void* ptr;

    size += 2 * sizeof(DWORD);
    ptr = malloc(size);
    if (ptr) {
        *((LPDWORD)ptr)++ = (DWORD)size;
        *((LPDWORD)ptr)++ = 0xcec171a2;
        AllocatedBytesOutstanding += (LONG)size;
        if (AllocatedBytesOutstanding < 0) {
            printf("my_malloc: alloc overflow? AllocatedBytesOutstanding=%x\n", AllocatedBytesOutstanding);
        }
        TotalBytesAllocated += (DWORD)size;
    }
    return ptr;
}

void* my_calloc(int num, int size) {

    void* ptr;

    size = (num * size) + 2 * sizeof(DWORD);
    ptr = calloc(1, size);
    if (ptr) {
        *((LPDWORD)ptr)++ = (DWORD)size;
        *((LPDWORD)ptr)++ = 0xcec171a2;
        AllocatedBytesOutstanding += (LONG)size;
        if (AllocatedBytesOutstanding < 0) {
            printf("my_calloc: alloc overflow? AllocatedBytesOutstanding=%x\n", AllocatedBytesOutstanding);
        }
        TotalBytesAllocated += (DWORD)size;
    }
    return ptr;
}

void my_free(void* ptr) {

    DWORD size;

    ((LPDWORD)ptr) -= 2;
    size = ((LPDWORD)ptr)[0];

    if (((LPDWORD)ptr)[1] != 0xcec171a2) {
        printf("\amy_free: bad block %x?\n", ptr);
    } else {
        AllocatedBytesOutstanding -= size;
        if (AllocatedBytesOutstanding < 0) {
            printf("my_free: free underflow? AllocatedBytesOutstanding=%x\n", AllocatedBytesOutstanding);
        }
        free(ptr);
        TotalBytesFreed += (DWORD)size;
    }
}

char* nice_num(unsigned long number) {

    int fwidth = 0;
    int i;
    static char buffer[32];
    char* buf = buffer;

    if (!number) {
        if (!fwidth) {
            buf[0] = '0';
            buf[1] = 0;
        } else {
            memset(buf, ' ', fwidth);
            buf[fwidth-1] = '0';
            buf[fwidth] = 0;
        }
    } else {
        if (!fwidth) {

            ULONG n = number;

            ++fwidth;
            for (i = 10; i <= 1000000000; i *= 10) {
                if (n/i) {
                    ++fwidth;
                } else {
                    break;
                }
            }
            fwidth += (fwidth / 3) - (((fwidth % 3) == 0) ? 1 : 0);
        }
        buf[fwidth] = 0;
        buf += fwidth;
        i=0;
        while (number && fwidth) {
            *--buf = (char)((number%10)+'0');
            --fwidth;
            number /= 10;
            if (++i == 3 && fwidth) {
                if (number) {
                    *--buf = ',';
                    --fwidth;
                    i=0;
                }
            }
        }
        while (fwidth--) *--buf = ' ';
    }
    return buf;
}


DISDRAW.CPP

// disdraw.cpp : Defines the class behaviors for the application.
//

#include "stdafx.h"
#include "disdraw.h"
#include "drawdlg.h"
#include "logindlg.h"

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

#define MAX_USERNAME_LEN 32

/////////////////////////////////////////////////////////////////////////////
// CDisdrawApp

BEGIN_MESSAGE_MAP(CDisdrawApp, CWinApp)
//{{AFX_MSG_MAP(CDisdrawApp)
// 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()

/////////////////////////////////////////////////////////////////////////////
// CDisdrawApp construction

CDisdrawApp::CDisdrawApp()
{
// TODO: add construction code here,
// Place all significant initialization in InitInstance
}

/////////////////////////////////////////////////////////////////////////////
// The one and only CDisdrawApp object

CDisdrawApp theApp;

/////////////////////////////////////////////////////////////////////////////
// CDisdrawApp initialization

BOOL CDisdrawApp::InitInstance()
{
// Standard initialization
// If you are not using these features and wish to reduce the size
//  of your final executable, you should remove from the following
//  the specific initialization routines 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

//
// Prompt for the user's name
//
char mbsUserName[MAX_USERNAME_LEN];
DWORD dwNumBytes = MAX_USERNAME_LEN;
GetUserName(mbsUserName, &dwNumBytes);
CLoginDlg dlgLogin;
dlgLogin.m_strLogin = mbsUserName;
if (dlgLogin.DoModal() == IDCANCEL)
{
return FALSE;
}
dlgLogin.m_strLogin.MakeUpper();

CDisdrawDlg dlg;
m_pMainWnd = &dlg;
dlg.m_strLogin = dlgLogin.m_strLogin;
int nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
// TODO: Place code here to handle when the dialog is
//  dismissed with OK
}
else if (nResponse == IDCANCEL)
{
// TODO: Place code here to handle when the dialog is
//  dismissed with Cancel
}

// Since the dialog has been closed, return FALSE so that we exit the
//  application, rather than start the application's message pump.
return FALSE;
}


DISDRAW.H

// disdraw.h : main header file for the DISDRAW application
//

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

#include "resource.h"// main symbols

/////////////////////////////////////////////////////////////////////////////
// CDisdrawApp:
// See disdraw.cpp for the implementation of this class
//

class CDisdrawApp : public CWinApp
{
public:
CDisdrawApp();

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CDisdrawApp)
public:
virtual BOOL InitInstance();
//}}AFX_VIRTUAL

// Implementation

//{{AFX_MSG(CDisdrawApp)
// 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()
};

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


DRAWAREA.CPP

// drawarea.cpp : implementation file
//

#include "stdafx.h"
#include "disdraw.h"
#include "drawdlg.h"

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

/////////////////////////////////////////////////////////////////////////////
// CDrawArea

CDrawArea::CDrawArea()
{
}

CDrawArea::~CDrawArea()
{
}

BEGIN_MESSAGE_MAP(CDrawArea, CEdit)
//{{AFX_MSG_MAP(CDrawArea)
ON_WM_LBUTTONDOWN()
ON_WM_MOUSEMOVE()
ON_WM_PAINT()
ON_WM_RBUTTONUP()
ON_WM_CTLCOLOR_REFLECT()
ON_WM_KEYDOWN()
ON_WM_CHAR()
ON_WM_SETFOCUS()
ON_WM_SETCURSOR()
ON_WM_LBUTTONDBLCLK()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CDrawArea message handlers

void CDrawArea::AddKeystroke(char *mbsKey)
{
//
// The backspace keystroke should erase the last character
//
if (mbsKey[0] == VK_BACK)
{
int nStart, nEnd;
GetSel(nStart, nEnd);
SetSel(nEnd-1, nEnd);
mbsKey[0] = '\0';

        //
        // Erasing a line feed should erase the preceding carriage return
        //
        CString strText;
        GetWindowText(strText);
        int iLength = strText.GetLength();
        if (iLength > 0 && strText.GetAt(strText.GetLength() - 1) == '\n')
            SetSel(nEnd-2, nEnd);
}

//
// Append a line feed to a carriage return
//
else if (mbsKey[0] == VK_RETURN)
strcat(mbsKey, "\xa");

//
// Store the new keystroke
//
ReplaceSel(mbsKey);
}

void CDrawArea::AddLine(LINE line)
{
//
// Set the clipping region for the drawing
//
CClientDC dc(this);
RECT rect;
GetClientRect(&rect);
CRgn rgn;
rgn.CreateRectRgnIndirect(&rect);
dc.SelectClipRgn(&rgn);

//
// Draw the line and save it for redrawing
//
dc.MoveTo(line.ptStart);
dc.LineTo(line.ptEnd);
    m_listLines.AddTail(line);
}

void CDrawArea::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default

m_ptLast = point;
SetFocus();
}

void CDrawArea::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default

if (nFlags & MK_LBUTTON)
{
//
// Add the drawn line to the drawing
//
LINE line;
line.ptStart = m_ptLast;
line.ptEnd = point;
        AddLine(line);

//
// Send the line to the friend
//
((CDisdrawDlg *)GetParent())->SendMouseMovement(line);

//
// Remember the end point of the line
//
m_ptLast = point;
}
}

void CDrawArea::OnPaint()
{
CPaintDC dc(this); // device context for painting

// TODO: Add your message handler code here

//
// Set the clipping region for the drawing
//
RECT rect;
GetClientRect(&rect);
CRgn rgn;
rgn.CreateRectRgnIndirect(&rect);
dc.SelectClipRgn(&rgn);

//
// Erase the drawing area
//
dc.FillSolidRect(&rect, GetSysColor(COLOR_MENU));

//
// Redisplay the text
//
    CString strText;
    GetWindowText(strText);
    dc.SelectStockObject(ANSI_VAR_FONT);
    rect.top++;
    rect.left++;
    dc.DrawText(strText, &rect, DT_TOP | DT_WORDBREAK);

//
// Redraw each line
//
LINE line;
POSITION posLine = m_listLines.GetHeadPosition();
while (posLine != NULL)
{
line = m_listLines.GetNext(posLine);
dc.MoveTo(line.ptStart);
dc.LineTo(line.ptEnd);
}

// Do not call CEdit::OnPaint() for painting messages
}

void CDrawArea::OnRButtonUp(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default

//
// Clear the drawing area
//
SetSel(0, -1);
Clear();
m_listLines.RemoveAll();
RedrawWindow();
}

BOOL CDrawArea::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
// TODO: Add your message handler code here and/or call default

return 0;
}

void CDrawArea::OnSetFocus(CWnd* pOldWnd)
{
CEdit::OnSetFocus(pOldWnd);

// TODO: Add your message handler code here

HideCaret();
}

HBRUSH CDrawArea::CtlColor(CDC* pDC, UINT nCtlColor)
{
// TODO: Change any attributes of the DC here
pDC->SetBkColor(GetSysColor(COLOR_MENU));

// TODO: Return a non-NULL brush if the parent's handler should not be called
return HBRUSH((CBrush *)pDC->SelectStockObject(LTGRAY_BRUSH));
}

void CDrawArea::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
// TODO: Add your message handler code here and/or call default

switch (nChar)
{
case VK_LEFT:
case VK_RIGHT:
case VK_UP:
case VK_DOWN:
case VK_HOME:break;
default:CEdit::OnKeyDown(nChar, nRepCnt, nFlags);
}
}

void CDrawArea::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
// TODO: Add your message handler code here and/or call default
((CDisdrawDlg *)GetParent())->SendKeystroke(nChar);

CEdit::OnChar(nChar, nRepCnt, nFlags);

RedrawWindow();
}

void CDrawArea::OnLButtonDblClk(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
}


DRAWAREA.H

// drawarea.h : header file
//

#define MAX_MSG_BODY_LEN 32

typedef struct tagLINE
{
CPoint ptStart;
CPoint ptEnd;
} LINE;

/////////////////////////////////////////////////////////////////////////////
// CDrawArea window

class CDrawArea : public CEdit
{
// Construction
public:
CDrawArea();

// Attributes
public:

protected:
CPoint m_ptLast;
CList<LINE, LINE &> m_listLines;

// Operations
public:
void AddLine(LINE line);
void AddKeystroke(char *mbsKey);

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CDrawArea)
//}}AFX_VIRTUAL

// Implementation
public:
virtual ~CDrawArea();

// Generated message map functions
protected:
//{{AFX_MSG(CDrawArea)
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
afx_msg void OnPaint();
afx_msg void OnRButtonUp(UINT nFlags, CPoint point);
afx_msg HBRUSH CtlColor(CDC* pDC, UINT nCtlColor);
afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
afx_msg void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags);
afx_msg void OnSetFocus(CWnd* pOldWnd);
afx_msg BOOL OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message);
afx_msg void OnLButtonDblClk(UINT nFlags, CPoint point);
//}}AFX_MSG

DECLARE_MESSAGE_MAP()
};

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


DRAWDLG.CPP

// drawdlg.cpp : implementation file
//

#include "stdafx.h"
#include "disdraw.h"
#include "drawdlg.h"

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

#define MAX_FORMAT_NAME_LEN 256

//
// Distributed draw queue type
//
CLSID guidDrawType =
{ 0x151ceac0, 0xacb5, 0x11cf, { 0x8b, 0x51, 0x00, 0x20, 0xaf, 0x92, 0x95, 0x46 } };

/////////////////////////////////////////////////////////////////////////////
// CDisdrawDlg dialog

CDisdrawDlg::CDisdrawDlg(CWnd* pParent /*=NULL*/)
: CDialog(CDisdrawDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CDisdrawDlg)
m_strFriend = _T("");
m_iDelivery = 0;
//}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CDisdrawDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CDisdrawDlg)
DDX_Control(pDX, IDC_DRAWAREA_SCRIBLLE, m_drawScribble);
DDX_Control(pDX, IDC_BUTTON_ATTACH, m_btnAttach);
DDX_Text(pDX, IDC_EDIT_FRIEND, m_strFriend);
DDX_Radio(pDX, IDC_RADIO_EXPRESS, m_iDelivery);
//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CDisdrawDlg, CDialog)
//{{AFX_MSG_MAP(CDisdrawDlg)
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_BUTTON_ATTACH, OnButtonAttach)
ON_EN_CHANGE(IDC_EDIT_FRIEND, OnChangeEditFriend)
ON_WM_CLOSE()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CDisdrawDlg message handlers

BOOL LocateQueue(CString m_strLabel, WCHAR *wcsFormatName, DWORD dwNumChars)
{
//
// Set restrictions to locate the draw queue with the specified label
//
DWORD cProps = 0;
MQPROPERTYRESTRICTION aPropRestriction[2];
MQRESTRICTION Restriction;

aPropRestriction[cProps].rel           = PREQ;
aPropRestriction[cProps].prop          = PROPID_Q_TYPE;
aPropRestriction[cProps].prval.vt      = VT_CLSID;
aPropRestriction[cProps].prval.puuid   = &guidDrawType;
cProps++;

WCHAR wcsLabel[MQ_MAX_Q_LABEL_LEN];
mbstowcs(wcsLabel, m_strLabel, MQ_MAX_Q_LABEL_LEN);
aPropRestriction[cProps].rel           = PREQ;
aPropRestriction[cProps].prop          = PROPID_Q_LABEL;
aPropRestriction[cProps].prval.vt      = VT_LPWSTR;
aPropRestriction[cProps].prval.pwszVal = wcsLabel;
cProps++;

Restriction.cRes = cProps;
Restriction.paPropRes = aPropRestriction;

//
// Request the queue instance for the specified queue
//
cProps = 0;
QUEUEPROPID aPropId[1];
MQCOLUMNSET Column;

aPropId[cProps] = PROPID_Q_INSTANCE;
cProps++;

Column.cCol = cProps;
Column.aCol = aPropId;

//
// Locate the specified queue
//
HANDLE hEnum;
BOOL fFound = FALSE;
HRESULT hr = MQLocateBegin(NULL, &Restriction, &Column, NULL, &hEnum);
if (!FAILED(hr))
{
    MQPROPVARIANT aPropVar[1];
DWORD cQueue = 1;
hr = MQLocateNext(hEnum, &cQueue, aPropVar);
if (!FAILED(hr) && cQueue > 0)
{
//
// Obtain the format name for the located queue
//
hr = MQInstanceToFormatName(aPropVar[0].puuid, wcsFormatName, &dwNumChars);
            MQFreeMemory(aPropVar[0].puuid);
if (!FAILED(hr))
fFound = TRUE;
}

MQLocateEnd(hEnum);
}

    return fFound;
}

BOOL CDisdrawDlg::OpenReceiveQueue()
{
    //
    // Do not create the receiving queue if it already exists in the enterprise
    //
    HRESULT hr;
WCHAR wcsFormatName[MAX_FORMAT_NAME_LEN];
    if (!LocateQueue(m_strLogin, wcsFormatName, MAX_FORMAT_NAME_LEN))
    {
    //
    // Form the pathname to the receiving queue
    //
    char mbsPathName[MQ_MAX_Q_NAME_LEN];
    DWORD dwNumChars = MQ_MAX_Q_NAME_LEN;
    GetComputerName(mbsPathName, &dwNumChars);
strcat(mbsPathName, "\\");
strcat(mbsPathName, m_strLogin);

    //
    // Prepare the receiving queue properties
    //
    DWORD cProps = 0;
    QUEUEPROPID  aPropId[3];
    MQPROPVARIANT aPropVar[3];
    MQQUEUEPROPS propsQueue;

WCHAR wcsPathName[MQ_MAX_Q_NAME_LEN];
mbstowcs(wcsPathName, mbsPathName, MQ_MAX_Q_NAME_LEN);
    aPropId[cProps] = PROPID_Q_PATHNAME;
    aPropVar[cProps].vt = VT_LPWSTR;
    aPropVar[cProps].pwszVal = wcsPathName;
    cProps++;

    aPropId[cProps] = PROPID_Q_TYPE;
    aPropVar[cProps].vt = VT_CLSID;
    aPropVar[cProps].puuid = &guidDrawType;
    cProps++;

WCHAR wcsLabel[MQ_MAX_Q_LABEL_LEN];
mbstowcs(wcsLabel, m_strLogin, MQ_MAX_Q_LABEL_LEN);
    aPropId[cProps] = PROPID_Q_LABEL;
    aPropVar[cProps].vt = VT_LPWSTR;
    aPropVar[cProps].pwszVal = wcsLabel;
    cProps++;

    propsQueue.cProp= cProps;
    propsQueue.aPropID= aPropId;
    propsQueue.aPropVar = aPropVar;
    propsQueue.aStatus= NULL;

    //
    // Create the receiving queue
    //
    dwNumChars = MAX_FORMAT_NAME_LEN;
    hr = MQCreateQueue(NULL, &propsQueue, wcsFormatName, &dwNumChars);

    //
    // If the receiving queue already exists, obtain its format name
    //
    if (hr == MQ_ERROR_QUEUE_EXISTS)
    hr = MQPathNameToFormatName(wcsPathName, wcsFormatName, &dwNumChars);

    if (FAILED(hr))
    return FALSE;
    }

//
// Open the receiving queue (may need to retry due to replication latency)
//
while ((hr = MQOpenQueue(wcsFormatName,
                             MQ_RECEIVE_ACCESS,
                             0, &m_hqIncoming)) == MQ_ERROR_QUEUE_NOT_FOUND)
Sleep (500);

if (FAILED(hr))
return FALSE;

return TRUE;
}

DWORD ReceiveUpdates(CDisdrawDlg *pDrawDlg)
{
   //
// Prepare the message properties to receive
//
DWORD cProps = 0;
MQMSGPROPS    propsMessage;
MQPROPVARIANT aPropVar[2];
MSGPROPID     aPropId[2];

WCHAR wcsBody[MAX_MSG_BODY_LEN];
aPropId[cProps]= PROPID_M_BODY;
aPropVar[cProps].vt= VT_UI1 | VT_VECTOR;
aPropVar[cProps].caub.cElems = sizeof(wcsBody);
aPropVar[cProps].caub.pElems = (UCHAR *)wcsBody;
cProps++;

    aPropId[cProps]= PROPID_M_BODY_SIZE;
aPropVar[cProps].vt= VT_UI4;
cProps++;

propsMessage.cProp    = cProps;
propsMessage.aPropID  = aPropId;
propsMessage.aPropVar = aPropVar;
propsMessage.aStatus  = NULL;

    //
    // Keep receiving updates sent to the incoming queue
    //
    HRESULT hr;
    LINE line;
char mbsBody[MAX_MSG_BODY_LEN + 1];

    while (TRUE)
    {
        //
        // Synchronously receive a message from the incoming queue
        //
        hr = MQReceiveMessage(pDrawDlg->m_hqIncoming, INFINITE,
                              MQ_ACTION_RECEIVE, &propsMessage,
  NULL, NULL, NULL, NULL);

//
// Determine if the message contains a keystroke or a line
//
        if (!FAILED(hr))
        {
//
// Convert the body to a multi-byte null-terminated string
//
            UINT uNumChars = aPropVar[1].ulVal/sizeof(WCHAR);
wcstombs(mbsBody, wcsBody, uNumChars);
mbsBody[uNumChars] = '\0';

//
// Add the keystroke to the drawing
//
if (uNumChars == 1)
            {
pDrawDlg->m_drawScribble.AddKeystroke(mbsBody);
            }

//
// Add the received line to the drawing
//
else
{
        sscanf(mbsBody, "%07ld%07ld%07ld%07ld",
   &line.ptStart.x, &line.ptStart.y,
   &line.ptEnd.x, &line.ptEnd.y);
pDrawDlg->m_drawScribble.AddLine(line);
}
        }
    }

    return 0;
}

BOOL CDisdrawDlg::OnInitDialog()
{
CDialog::OnInitDialog();

// 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

// TODO: Add extra initialization here

//
// Display the login name in the window title
//
SetWindowText(m_strLogin);

//
// No queues are open yet
//
m_hqIncoming = NULL;
m_hqOutgoing = NULL;

//
// Open the receiving queue and receive incoming messages
//
    DWORD dwThreadID;
    if (OpenReceiveQueue())
        CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ReceiveUpdates, this, 0,
                     &dwThreadID);
else
AfxMessageBox("Cannot open receiving queue.");

return TRUE;  // return TRUE  unless you set the focus to a control
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CDisdrawDlg::OnPaint()
{
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();
}
}

// The system calls this to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CDisdrawDlg::OnQueryDragIcon()
{
return (HCURSOR) m_hIcon;
}

void CDisdrawDlg::SendKeystroke(UINT uChar)
{
//
// Send the keystroke to the friend, if any
//
if (m_hqOutgoing != NULL)
{
//
// Prepare the message properties to send
//
DWORD cProps = 0;
MQMSGPROPS    propsMessage;
MQPROPVARIANT aPropVar[5];
MSGPROPID     aPropId[5];

WCHAR wcsBody[1];
swprintf(wcsBody, L"%c", uChar);
aPropId[cProps]= PROPID_M_BODY;
aPropVar[cProps].vt= VT_UI1 | VT_VECTOR;
aPropVar[cProps].caub.cElems = sizeof(wcsBody);
aPropVar[cProps].caub.pElems = (UCHAR *)wcsBody;
cProps++;

WCHAR wcsLabel[MQ_MAX_MSG_LABEL_LEN];
swprintf(wcsLabel, L"Key: %c", uChar);
aPropId[cProps]= PROPID_M_LABEL;
aPropVar[cProps].vt= VT_LPWSTR;
aPropVar[cProps].pwszVal= wcsLabel;
cProps++;

        UpdateData(TRUE);
aPropId[cProps]= PROPID_M_DELIVERY;
aPropVar[cProps].vt= VT_UI1;
aPropVar[cProps].bVal= m_iDelivery;
cProps++;

aPropId[cProps]= PROPID_M_PRIORITY;
aPropVar[cProps].vt= VT_UI1;
aPropVar[cProps].bVal= 4;
cProps++;

aPropId[cProps]= PROPID_M_BODY_TYPE;
aPropVar[cProps].vt= VT_UI4;
aPropVar[cProps].lVal= (long)VT_BSTR;
cProps++;

propsMessage.cProp    = cProps;
propsMessage.aPropID  = aPropId;
propsMessage.aPropVar = aPropVar;
propsMessage.aStatus  = NULL;

//
// Send the message to the outgoing queue
//
MQSendMessage(m_hqOutgoing, &propsMessage, NULL);
}
}

void CDisdrawDlg::SendMouseMovement(LINE line)
{
//
// Send the line to the friend, if any
//
if (m_hqOutgoing != NULL)
{
//
// Prepare the message properties to send
//
DWORD cProps = 0;
MQMSGPROPS    propsMessage;
MQPROPVARIANT aPropVar[5];
MSGPROPID     aPropId[5];

WCHAR wcsBody[MAX_MSG_BODY_LEN];
swprintf(wcsBody, L"%07ld%07ld%07ld%07ld",
 line.ptStart.x, line.ptStart.y, line.ptEnd.x, line.ptEnd.y);
aPropId[cProps]= PROPID_M_BODY;
aPropVar[cProps].vt= VT_UI1 | VT_VECTOR;
aPropVar[cProps].caub.cElems = sizeof(wcsBody);
aPropVar[cProps].caub.pElems = (UCHAR *)wcsBody;
cProps++;

WCHAR wcsLabel[MQ_MAX_MSG_LABEL_LEN];
swprintf(wcsLabel, L"%ld,%ld To %ld,%ld",
 line.ptStart.x, line.ptStart.y, line.ptEnd.x, line.ptEnd.y);
aPropId[cProps]= PROPID_M_LABEL;
aPropVar[cProps].vt= VT_LPWSTR;
aPropVar[cProps].pwszVal= wcsLabel;
cProps++;

        UpdateData(TRUE);
aPropId[cProps]= PROPID_M_DELIVERY;
aPropVar[cProps].vt= VT_UI1;
aPropVar[cProps].bVal= m_iDelivery;
cProps++;

aPropId[cProps]= PROPID_M_PRIORITY;
aPropVar[cProps].vt= VT_UI1;
aPropVar[cProps].bVal= 3;
cProps++;

aPropId[cProps]= PROPID_M_BODY_TYPE;
aPropVar[cProps].vt= VT_UI4;
aPropVar[cProps].lVal= (long)VT_BSTR;
cProps++;

propsMessage.cProp    = cProps;
propsMessage.aPropID  = aPropId;
propsMessage.aPropVar = aPropVar;
propsMessage.aStatus  = NULL;

//
// Send the message to the outgoing queue
//
MQSendMessage(m_hqOutgoing, &propsMessage, NULL);
}
}

void CDisdrawDlg::OnButtonAttach()
{
// TODO: Add your control notification handler code here

//
// Obtain the name of the friend
//
UpdateData(TRUE);
m_strFriend.MakeUpper();

    //
    // Make sure the friend queue exists
    //
    WCHAR wcsFormatName[MAX_FORMAT_NAME_LEN];
if (!LocateQueue(m_strFriend, wcsFormatName, MAX_FORMAT_NAME_LEN))
{
AfxMessageBox("No such friend, sorry...");
return;
}

//
// Open the friend queue for sending
//
HANDLE hqNewFriend;
HRESULT hr = MQOpenQueue(wcsFormatName, MQ_SEND_ACCESS, 0, &hqNewFriend);
if (FAILED(hr))
AfxMessageBox("Cannot open friend queue.");

else
{
//
// Close the previous friend and update the window title
//
if (m_hqOutgoing != NULL)
MQCloseQueue(m_hqOutgoing);
m_hqOutgoing = hqNewFriend;
CString strTitle = m_strLogin + " connected to " + m_strFriend;
SetWindowText(strTitle);
m_btnAttach.EnableWindow(FALSE);
}
}

void CDisdrawDlg::OnChangeEditFriend()
{
// TODO: Add your control notification handler code here

m_btnAttach.EnableWindow(TRUE);
}

void CDisdrawDlg::OnClose()
{
// TODO: Add your message handler code here and/or call default

//
// Close the open queues, if any
//
if (m_hqIncoming != NULL)
MQCloseQueue(m_hqIncoming);

if (m_hqOutgoing != NULL)
MQCloseQueue(m_hqOutgoing);

CDialog::OnClose();
}


DRAWDLG.H

// drawdlg.h : header file
//

#include "drawarea.h"

/////////////////////////////////////////////////////////////////////////////
// CDisdrawDlg dialog

class CDisdrawDlg : public CDialog
{
// Construction
public:
CDisdrawDlg(CWnd* pParent = NULL);// standard constructor

void SendMouseMovement(LINE lineNew);
void SendKeystroke(UINT uChar);

// Data
CString m_strLogin;
HANDLE m_hqIncoming;
HANDLE m_hqOutgoing;

// Dialog Data
//{{AFX_DATA(CDisdrawDlg)
enum { IDD = IDD_DISDRAW_DIALOG };
CDrawAream_drawScribble;
CButtonm_btnAttach;
CStringm_strFriend;
intm_iDelivery;
//}}AFX_DATA

// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CDisdrawDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX);// DDX/DDV support
//}}AFX_VIRTUAL

// Implementation
protected:
HICON m_hIcon;

BOOL OpenReceiveQueue();

// Generated message map functions
//{{AFX_MSG(CDisdrawDlg)
virtual BOOL OnInitDialog();
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
afx_msg void OnButtonAttach();
afx_msg void OnChangeEditFriend();
afx_msg void OnClose();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};


LOGINDLG.CPP

// logindlg.cpp : implementation file
//

#include "stdafx.h"
#include "disdraw.h"
#include "logindlg.h"

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

/////////////////////////////////////////////////////////////////////////////
// CLoginDlg dialog

CLoginDlg::CLoginDlg(CWnd* pParent /*=NULL*/)
: CDialog(CLoginDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CLoginDlg)
m_strLogin = _T("");
//}}AFX_DATA_INIT
}

void CLoginDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CLoginDlg)
DDX_Text(pDX, IDC_EDIT_LOGIN, m_strLogin);
//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CLoginDlg, CDialog)
//{{AFX_MSG_MAP(CLoginDlg)
// NOTE: the ClassWizard will add message map macros here
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CLoginDlg message handlers


LOGINDLG.H

// logindlg.h : header file
//

/////////////////////////////////////////////////////////////////////////////
// CLoginDlg dialog

class CLoginDlg : public CDialog
{
// Construction
public:
CLoginDlg(CWnd* pParent = NULL);   // standard constructor

// Dialog Data
//{{AFX_DATA(CLoginDlg)
enum { IDD = IDD_LOGIN };
CStringm_strLogin;
//}}AFX_DATA

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CLoginDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
//}}AFX_VIRTUAL

// Implementation
protected:

// Generated message map functions
//{{AFX_MSG(CLoginDlg)
// NOTE: the ClassWizard will add member functions here
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by disdraw.rc
//
#define IDD_DISDRAW_DIALOG              102
#define IDR_MAINFRAME                   128
#define IDD_LOGIN                       129
#define IDC_EDIT_FRIEND                 1000
#define IDC_BUTTON_ATTACH               1001
#define IDC_RADIO_EXPRESS               1002
#define IDC_RADIO_RECOVERABLE           1003
#define IDC_DRAWAREA_SCRIBLLE           1004
#define IDC_EDIT_LOGIN                  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


STDAFX.CPP

// stdafx.cpp : source file that includes just the standard includes
//disdraw.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

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

#include <afxtempl.h>// MFC collection classes
#include <afxole.h>// MFC OLE support

#include "mq.h"// MSMQ


README.TXT

This sample demonstrates how to use the MSMQ ActiveX components with the #import
compiler directive new to Visual C++ 5.0. This program also demonstrates how to p
erform asynchronous receive using MSMQ ActiveX Events in C++.

This sample program functions identically to the VB_Draw and C_Draw samples included
with the MSMQ product and will inter-operate with those two samples.

In order to handle ActiveX Event notification a new class, CMSMQEventHandler, has been
defined.  This class can be used in other projects by deriving a new class from
CMSMQEventHandler and overriding the Arrived and ArrivedError methods.

Aside from this new class the majority of code in this project was taken or adapted
from the existing C_Draw and VB_Draw samples.


DRAWAREA.H

// drawarea.h : header file
//
#include <afxtempl.h>

#define MAX_MSG_BODY_LEN 32

typedef struct tagLINE
{
CPoint ptStart;
CPoint ptEnd;
} LINE;

/////////////////////////////////////////////////////////////////////////////
// CDrawArea window

class CDrawArea : public CEdit
{
// Construction
public:
CDrawArea();

// Attributes
public:

protected:
CPoint m_ptLast;
CList<LINE, LINE &> m_listLines;

// Operations
public:
void AddLine(LINE line);
void AddKeystroke(char *mbsKey);

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CDrawArea)
//}}AFX_VIRTUAL

// Implementation
public:
virtual ~CDrawArea();

// Generated message map functions
protected:
//{{AFX_MSG(CDrawArea)
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
afx_msg void OnPaint();
afx_msg void OnRButtonUp(UINT nFlags, CPoint point);
afx_msg HBRUSH CtlColor(CDC* pDC, UINT nCtlColor);
afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
afx_msg void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags);
afx_msg void OnSetFocus(CWnd* pOldWnd);
afx_msg BOOL OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message);
afx_msg void OnLButtonDblClk(UINT nFlags, CPoint point);
//}}AFX_MSG

DECLARE_MESSAGE_MAP()
};

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


IMP_DRAW.CPP

// Imp_Draw.cpp : Defines the class behaviors for the application.
//

#include "stdafx.h"
#include "Imp_Draw.h"
#include "Imp_DrawDlg.h"

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

/////////////////////////////////////////////////////////////////////////////
// CImp_DrawApp

BEGIN_MESSAGE_MAP(CImp_DrawApp, CWinApp)
//{{AFX_MSG_MAP(CImp_DrawApp)
// 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()

/////////////////////////////////////////////////////////////////////////////
// CImp_DrawApp construction

CImp_DrawApp::CImp_DrawApp()
{
// TODO: add construction code here,
// Place all significant initialization in InitInstance
}

/////////////////////////////////////////////////////////////////////////////
// The one and only CImp_DrawApp object

CImp_DrawApp theApp;

/////////////////////////////////////////////////////////////////////////////
// CImp_DrawApp initialization

BOOL CImp_DrawApp::InitInstance()
{
// Standard initialization
// If you are not using these features and wish to reduce the size
//  of your final executable, you should remove from the following
//  the specific initialization routines 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

CImp_DrawDlg dlg;
m_pMainWnd = &dlg;
int nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
// TODO: Place code here to handle when the dialog is
//  dismissed with OK
}
else if (nResponse == IDCANCEL)
{
// TODO: Place code here to handle when the dialog is
//  dismissed with Cancel
}

// Since the dialog has been closed, return FALSE so that we exit the
//  application, rather than start the application's message pump.
return FALSE;
}


IMP_DRAW.H

// Imp_Draw.h : main header file for the IMP_DRAW application
//

#if !defined(AFX_IMP_DRAW_H__02EE0036_FC6D_11D0_A14D_00C04FB9D5DF__INCLUDED_)
#define AFX_IMP_DRAW_H__02EE0036_FC6D_11D0_A14D_00C04FB9D5DF__INCLUDED_

#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000

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

#include "resource.h"// main symbols

/////////////////////////////////////////////////////////////////////////////
// CImp_DrawApp:
// See Imp_Draw.cpp for the implementation of this class
//

class CImp_DrawApp : public CWinApp
{
public:
CImp_DrawApp();

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CImp_DrawApp)
public:
virtual BOOL InitInstance();
//}}AFX_VIRTUAL

// Implementation

//{{AFX_MSG(CImp_DrawApp)
// 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()
};

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

//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_IMP_DRAW_H__02EE0036_FC6D_11D0_A14D_00C04FB9D5DF__INCLUDED_)


IMP_DRAWDLG.CPP

// Imp_DrawDlg.cpp : implementation file
//

#include "stdafx.h"
#include <afxdisp.h>
#include "Imp_Draw.h"
#include "Imp_DrawDlg.h"
#include "logindlg.h"

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

/////////////////////////////////////////////////////////////////////////////
// CImp_DrawDlg dialog

CImp_DrawDlg::CImp_DrawDlg(CWnd* pParent /*=NULL*/)
: CDialog(CImp_DrawDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CImp_DrawDlg)
m_csFriendName = _T("");
m_iRadio = -1;
//}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
m_vtguidDraw = "{151ceac0-acb5-11cf-8b51-0020af929546}";
m_vtFriendName = "";
m_pHandler = NULL;
}

CImp_DrawDlg::~CImp_DrawDlg()
{
if (m_pHandler)
delete m_pHandler;// Handler's destructor will release event object
};

void CImp_DrawDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CImp_DrawDlg)
DDX_Control(pDX, IDC_DRAWAREA_SCRIBLLE, m_drawScribble);
DDX_Text(pDX, IDC_EDIT_FRIEND, m_csFriendName);
DDX_Radio(pDX, IDC_RADIO_EXPRESS, m_iRadio);
//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CImp_DrawDlg, CDialog)
//{{AFX_MSG_MAP(CImp_DrawDlg)
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_BUTTON_ATTACH, OnButtonAttach)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CImp_DrawDlg message handlers
CString CImp_DrawDlg::GetUserName()
{
DWORD dwsize = 255;
char username[255];
if (::GetUserName(&username[0], &dwsize))
return username;
return "";
};

CString CImp_DrawDlg::GetComputerName()
{
DWORD dwsize = 255;
char username[255];
if (::GetComputerName(&username[0], &dwsize))
return username;
return "";
}

CString CImp_DrawDlg::LoginPrompt(CString DefaultName)
{
CLoginDlgdialog;
dialog.m_strLogin = DefaultName;
dialog.DoModal();
return dialog.m_strLogin;
};

void CImp_DrawDlg::SendMouseMovement(LINE line)
{
try
{
if (m_qFriend != NULL && m_qFriend->IsOpen)
{
m_msgOut->Priority = 3;

/* Pack the data into one line (twice) */
WCHAR wcsBody[MAX_MSG_BODY_LEN];
swprintf(wcsBody, L"%07ld%07ld%07ld%07ld",
 line.ptStart.x, line.ptStart.y, line.ptEnd.x, line.ptEnd.y);

WCHAR wcsLabel[MQ_MAX_MSG_LABEL_LEN];
swprintf(wcsLabel, L"%ld,%ld To %ld,%ld",
 line.ptStart.x, line.ptStart.y, line.ptEnd.x, line.ptEnd.y);

/* Initialize a variant with the wcsbody data */
_variant_tvtBody(wcsBody);

m_msgOut->Body = vtBody;
m_msgOut->Label = wcsLabel;
m_msgOut->Delivery = m_iRadio;
m_msgOut->Send(m_qFriend);
};
}
catch(_com_error comerr)
{
HandleErrors(comerr);
};
};

void CImp_DrawDlg::SendKeystroke(UINT uChar)
{
try
{
if (m_qFriend != NULL && m_qFriend->IsOpen)
{
_variant_t vtChar((char*)&uChar);
m_msgOut->Priority = 4;
m_msgOut->Body = &vtChar;
m_msgOut->Label = "Key: " + uChar;
m_msgOut->Delivery = m_iRadio;
m_msgOut->Send(m_qFriend);
};
}
catch(_com_error comerr)
{
HandleErrors(comerr);
};
};

void CImp_DrawDlg::Arrived(IDispatch* pdispQueue, long lErrorCode, long lCursor)
{
if (lErrorCode != S_OK)
{
// This code is what will run if ArrivedError is called
char* szErr = "";
itoa(lErrorCode, szErr, 16);
CString tempString = "Method returned HRESULT: ";
tempString += szErr;
MessageBox(tempString, NULL, MB_ICONSTOP | MB_OK);
return;
};

IMSMQMessagePtr msgIn;
LINEline;
char*strTextIn = "";
_variant_tvtTimeout((short) 100);
_bstr_tbtBody;

try
{
IMSMQQueuePtrq(pdispQueue);// Creates a Queue smart pointer pointing to pdispQueue
msgIn = q->Receive(&vtMissing, &vtMissing, &vtMissing, &vtTimeout);// Any messages?
btBody = msgIn->Body;// convert to char*
strTextIn = (char*) btBody;
if (strlen(strTextIn) == 1)
m_drawScribble.AddKeystroke(strTextIn);
else
{
       sscanf(strTextIn, "%07ld%07ld%07ld%07ld",
&line.ptStart.x, &line.ptStart.y,
&line.ptEnd.x, &line.ptEnd.y);
m_drawScribble.AddLine(line);
};
}
catch(_com_error comerr)
{
HandleErrors(comerr);
};

m_queue->EnableNotification(m_qevent);// IMPORTANT: Re-enable notification
};

void CImp_DrawDlg::HandleErrors(_com_error comerr)
{
HRESULT hr = comerr.Error();
char* szErr = "";
itoa(hr, szErr, 16);
CString tempString = "Method returned HRESULT: ";
tempString += szErr;
tempString += ".  Exiting.";
MessageBox(tempString, NULL, MB_ICONSTOP | MB_OK);
AfxAbort();
};

BOOL CImp_DrawDlg::OnInitDialog()
{
CDialog::OnInitDialog();

// 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

// TODO: Add extra initialization here
HRESULT hr = AfxOleInit();

try
{
CStringcsDefaultQueueName;
CStringcsComputerName;

IMSMQQueryPtrquery("MSMQ.MSMQQuery");
IMSMQQueueInfoPtrqinfo;
IMSMQQueueInfosPtrqinfos;

m_msgOut.CreateInstance("MSMQ.MSMQMessage");
csDefaultQueueName = GetUserName();
m_csLogin = LoginPrompt(csDefaultQueueName);// Present login dialog
if (m_csLogin == "")
return FALSE;
m_csLogin.MakeUpper();
SetWindowText(m_csLogin);

m_vtLogin = m_csLogin;
qinfos = query->LookupQueue(&vtMissing,&m_vtguidDraw, &m_vtLogin);  // Look for user's queue
qinfos->Reset();
qinfo = qinfos->Next();

if (qinfo == NULL)// if it doesn't exist
{
qinfo.CreateInstance("MSMQ.MSMQQueueInfo");
csComputerName = GetComputerName();
if (csComputerName == "")
csComputerName = ".";
qinfo->PathName = (_bstr_t) LPCTSTR(csComputerName + "\\" + m_csLogin);
qinfo->Label = (_bstr_t) LPCTSTR(m_csLogin);
qinfo->ServiceTypeGuid = (_bstr_t) m_vtguidDraw;
qinfo->Create();// We'll make one
};
for (int i = 0; i < 5; i++)
{
// We'll try to open it five times in case the creation takes time to replicate
try
{
m_queue = NULL;
m_queue = qinfo->Open(MQ_RECEIVE_ACCESS, MQ_DENY_NONE);
if (m_queue != NULL)
i=5;
}
catch (_com_error comerr)
{
hr = comerr.Error();
if (hr != MQ_ERROR_QUEUE_NOT_FOUND)
throw comerr;
};
};

if (m_queue == NULL)
{
MessageBox("Could not open your queue after five tries", MB_OK);
return FALSE;
};

// Now we'll link up with the event source
// the first five lines below imitate
// Dim WithEvents m_qevent as MSMQ.MSMQEvent

m_qevent.CreateInstance("MSMQ.MSMQEvent");
if (m_pHandler)
delete m_pHandler;
m_pHandler = new CMSMQEventHandler();
hr = m_pHandler->AdviseSource(m_qevent);

hr = m_queue->EnableNotification(m_qevent);

}
catch (_com_error comerr)
{
HandleErrors(comerr);
};

m_iRadio = 1;
UpdateData(FALSE);

return TRUE;  // return TRUE  unless you set the focus to a control
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CImp_DrawDlg::OnPaint()
{
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();
}
}

// The system calls this to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CImp_DrawDlg::OnQueryDragIcon()
{
return (HCURSOR) m_hIcon;
}

void CImp_DrawDlg::OnButtonAttach()
{

try
{
IMSMQQueryPtrqueryFriend("MSMQ.MSMQQuery");// Creates a Query object
IMSMQQueueInfoPtrqinfoFriend;
IMSMQQueueInfosPtrqinfos;
UpdateData(TRUE);
m_csFriendName.MakeUpper();
m_vtFriendName = m_csFriendName;
qinfos = queryFriend->LookupQueue(&vtMissing,&m_vtguidDraw, &m_vtFriendName);
qinfos->Reset();
qinfoFriend = qinfos->Next();
if (qinfoFriend == NULL)
{
MessageBox("No such friend, sorry...");
}
else
{
if (m_qFriend != NULL && m_qFriend->IsOpen)
m_qFriend->Close();

m_qFriend = qinfoFriend->Open(MQ_SEND_ACCESS, MQ_DENY_NONE);
SetWindowText(m_csLogin + " - Connected to " + m_csFriendName);
};
}
catch (_com_error comerr)
{
HandleErrors(comerr);
};
}


IMP_DRAWDLG.H

// Imp_DrawDlg.h : header file
//

#import <mqoa.dll> no_namespace
#include "MQEventHandler.h"
#include "drawarea.h"

#if !defined(AFX_IMP_DRAWDLG_H__02EE0038_FC6D_11D0_A14D_00C04FB9D5DF__INCLUDED_)
#define AFX_IMP_DRAWDLG_H__02EE0038_FC6D_11D0_A14D_00C04FB9D5DF__INCLUDED_

#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000

#define MAXNUMLEN 7

/////////////////////////////////////////////////////////////////////////////
// CImp_DrawDlg dialog

class CImp_DrawDlg : public CDialog
{
// Construction
public:
CImp_DrawDlg(CWnd* pParent = NULL);// standard constructor
~CImp_DrawDlg();

// Dialog Data
//{{AFX_DATA(CImp_DrawDlg)
enum { IDD = IDD_IMP_DRAW_DIALOG };
CDrawAream_drawScribble;
CStringm_csFriendName;
intm_iRadio;
//}}AFX_DATA

struct Line
{
long X1;
long Y1;
long X2;
long Y2;
};

_variant_tm_vtLogin;
_variant_tm_vtFriendName;
_variant_tm_vtguidDraw;

longm_lLastX;
longm_lLastY;

intm_cLines;
intm_iArraySize;
CStringm_csScreenText;
intm_iWasText;
CStringm_csLogin;

IMSMQQueuePtrm_queue;
IMSMQEventPtrm_qevent;
IMSMQQueuePtrm_qFriend;
IMSMQMessagePtrm_msgOut;

void SendMouseMovement(LINE line);
void SendKeystroke(UINT uChar);

void Arrived(/*[in]*/ IDispatch* pdispQueue,
/*[in]*/ long lErrorCode,
/*[in]*/ long lCursor);

void HandleErrors(_com_error comerr);
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CImp_DrawDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX);// DDX/DDV support
//}}AFX_VIRTUAL

// Implementation
protected:
HICON m_hIcon;

CString GetUserName();
CString GetComputerName();
CStringLoginPrompt(CString DefaultName);

CMSMQEventHandler*m_pHandler;
// Generated message map functions
//{{AFX_MSG(CImp_DrawDlg)
virtual BOOL OnInitDialog();
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
afx_msg void OnButtonAttach();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()

};

//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_IMP_DRAWDLG_H__02EE0038_FC6D_11D0_A14D_00C04FB9D5DF__INCLUDED_)


LOGINDLG.CPP

// logindlg.cpp : implementation file
//

#include "stdafx.h"
#include "Imp_Draw.h"
#include "logindlg.h"

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

/////////////////////////////////////////////////////////////////////////////
// CLoginDlg dialog

CLoginDlg::CLoginDlg(CWnd* pParent /*=NULL*/)
: CDialog(CLoginDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CLoginDlg)
m_strLogin = _T("");
//}}AFX_DATA_INIT
}

void CLoginDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CLoginDlg)
DDX_Text(pDX, IDC_EDIT_LOGIN, m_strLogin);
//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CLoginDlg, CDialog)
//{{AFX_MSG_MAP(CLoginDlg)
// NOTE: the ClassWizard will add message map macros here
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CLoginDlg message handlers


MQEVENTHANDLER.CPP

#include "stdafx.h"
#include <afxdisp.h>
#include "Imp_Draw.h"
#include "Imp_DrawDlg.h"
#import <mqoa.dll> no_namespace

//#include "MQEventHandler.h"

BEGIN_DISPATCH_MAP(CMSMQEventHandler, CCmdTarget)
DISP_FUNCTION_ID(CMSMQEventHandler, "Arrived", 0, Arrived, VT_EMPTY, VTS_DISPATCH VTS_I4)
DISP_FUNCTION_ID(CMSMQEventHandler, "ArrivedError", 1, ArrivedError, VT_EMPTY, VTS_DISPATCH VTS_I4 VTS_I4)
END_DISPATCH_MAP()

CMSMQEventHandler::CMSMQEventHandler()
{
EnableAutomation();
dwMyCookie = 0;
pMyCP = NULL;
}

CMSMQEventHandler::~CMSMQEventHandler()
{
if (pMyCP)
{
pMyCP->Unadvise(dwMyCookie);
pMyCP->Release();
};

}

void CMSMQEventHandler::Arrived(IDispatch* pdispQueue, long lCursor)
{
CImp_DrawDlg* pDlg = (CImp_DrawDlg*) AfxGetApp()->m_pMainWnd;
pDlg->Arrived(pdispQueue, S_OK, lCursor);
}

void CMSMQEventHandler::ArrivedError(IDispatch* pdispQueue, long lErrorCode, long lCursor)
{
CImp_DrawDlg* pDlg = (CImp_DrawDlg*) AfxGetApp()->m_pMainWnd;
pDlg->Arrived(pdispQueue, lErrorCode, lCursor);
}

HRESULT CMSMQEventHandler::AdviseSource(IDispatch* pDispatch)
{
LPCONNECTIONPOINTCONTAINERpCPCont = NULL;
HRESULThr;

hr = pDispatch->QueryInterface(IID_IConnectionPointContainer, (void**) &pCPCont);
if (FAILED(hr))
return hr;

hr = pCPCont->FindConnectionPoint(__uuidof(_DMSMQEventEvents), &pMyCP);
pCPCont->Release();
if (FAILED(hr))
return hr;

return pMyCP->Advise(GetIDispatch(TRUE), &dwMyCookie);
};


MQEVENTHANDLER.H

class CMSMQEventHandler : public CCmdTarget
{
public:

CMSMQEventHandler();
~CMSMQEventHandler();

virtual void Arrived(/*[in]*/ IDispatch* pdispQueue,
/*[in]*/ long lCursor);
virtual void ArrivedError(/*[in]*/ IDispatch* pdispQueue,
/*[in]*/ long lErrorCode,
/*[in]*/ long lCursor);

HRESULT AdviseSource(IDispatch* pDispatch);

DECLARE_DISPATCH_MAP()

private:
DWORDdwMyCookie;
LPCONNECTIONPOINTpMyCP;

};


DRAWAREA.CPP

// drawarea.cpp : implementation file
//

#include "stdafx.h"
#include "Imp_draw.h"
#include "Imp_drawdlg.h"

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

/////////////////////////////////////////////////////////////////////////////
// CDrawArea

CDrawArea::CDrawArea()
{
}

CDrawArea::~CDrawArea()
{
}

BEGIN_MESSAGE_MAP(CDrawArea, CEdit)
//{{AFX_MSG_MAP(CDrawArea)
ON_WM_LBUTTONDOWN()
ON_WM_MOUSEMOVE()
ON_WM_PAINT()
ON_WM_RBUTTONUP()
ON_WM_CTLCOLOR_REFLECT()
ON_WM_KEYDOWN()
ON_WM_CHAR()
ON_WM_SETFOCUS()
ON_WM_SETCURSOR()
ON_WM_LBUTTONDBLCLK()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CDrawArea message handlers

void CDrawArea::AddKeystroke(char *mbsKey)
{
//
// The backspace keystroke should erase the last character
//
if (mbsKey[0] == VK_BACK)
{
int nStart, nEnd;
GetSel(nStart, nEnd);
SetSel(nEnd-1, nEnd);
mbsKey[0] = '\0';

        //
        // Erasing a line feed should erase the preceding carriage return
        //
        CString strText;
        GetWindowText(strText);
        int iLength = strText.GetLength();
        if (iLength > 0 && strText.GetAt(strText.GetLength() - 1) == '\n')
            SetSel(nEnd-2, nEnd);
}

//
// Append a line feed to a carriage return
//
else if (mbsKey[0] == VK_RETURN)
strcat(mbsKey, "\xa");

//
// Store the new keystroke
//
ReplaceSel(mbsKey);
}

void CDrawArea::AddLine(LINE line)
{
//
// Set the clipping region for the drawing
//
CClientDC dc(this);
RECT rect;
GetClientRect(&rect);
CRgn rgn;
rgn.CreateRectRgnIndirect(&rect);
dc.SelectClipRgn(&rgn);

//
// Draw the line and save it for redrawing
//
dc.MoveTo(line.ptStart);
dc.LineTo(line.ptEnd);
    m_listLines.AddTail(line);
}

void CDrawArea::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default

m_ptLast = point;
SetFocus();
}

void CDrawArea::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default

if (nFlags & MK_LBUTTON)
{
//
// Add the drawn line to the drawing
//
LINE line;
line.ptStart = m_ptLast;
line.ptEnd = point;
        AddLine(line);

//
// Send the line to the friend
//
((CImp_DrawDlg *)GetParent())->SendMouseMovement(line);

//
// Remember the end point of the line
//
m_ptLast = point;
}
}

void CDrawArea::OnPaint()
{
CPaintDC dc(this); // device context for painting

// TODO: Add your message handler code here

//
// Set the clipping region for the drawing
//
RECT rect;
GetClientRect(&rect);
CRgn rgn;
rgn.CreateRectRgnIndirect(&rect);
dc.SelectClipRgn(&rgn);

//
// Erase the drawing area
//
dc.FillSolidRect(&rect, GetSysColor(COLOR_MENU));

//
// Redisplay the text
//
    CString strText;
    GetWindowText(strText);
    dc.SelectStockObject(ANSI_VAR_FONT);
    rect.top++;
    rect.left++;
    dc.DrawText(strText, &rect, DT_TOP | DT_WORDBREAK);

//
// Redraw each line
//
LINE line;
POSITION posLine = m_listLines.GetHeadPosition();
while (posLine != NULL)
{
line = m_listLines.GetNext(posLine);
dc.MoveTo(line.ptStart);
dc.LineTo(line.ptEnd);
}

// Do not call CEdit::OnPaint() for painting messages
}

void CDrawArea::OnRButtonUp(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default

//
// Clear the drawing area
//
SetSel(0, -1);
Clear();
m_listLines.RemoveAll();
RedrawWindow();
}

BOOL CDrawArea::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
// TODO: Add your message handler code here and/or call default

return 0;
}

void CDrawArea::OnSetFocus(CWnd* pOldWnd)
{
CEdit::OnSetFocus(pOldWnd);

// TODO: Add your message handler code here

HideCaret();
}

HBRUSH CDrawArea::CtlColor(CDC* pDC, UINT nCtlColor)
{
// TODO: Change any attributes of the DC here
pDC->SetBkColor(GetSysColor(COLOR_MENU));

// TODO: Return a non-NULL brush if the parent's handler should not be called
return HBRUSH((CBrush *)pDC->SelectStockObject(LTGRAY_BRUSH));
}

void CDrawArea::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
// TODO: Add your message handler code here and/or call default

switch (nChar)
{
case VK_LEFT:
case VK_RIGHT:
case VK_UP:
case VK_DOWN:
case VK_HOME:break;
default:CEdit::OnKeyDown(nChar, nRepCnt, nFlags);
}
}

void CDrawArea::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
// TODO: Add your message handler code here and/or call default
((CImp_DrawDlg *)GetParent())->SendKeystroke(nChar);

CEdit::OnChar(nChar, nRepCnt, nFlags);

RedrawWindow();
}

void CDrawArea::OnLButtonDblClk(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
}


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by Imp_Draw.rc
//
#define IDD_IMP_DRAW_DIALOG             102
#define IDR_MAINFRAME                   128
#define IDD_LOGIN                       130
#define IDC_EDIT_FRIEND                 1000
#define IDC_BUTTON_ATTACH               1001
#define IDC_RADIO_EXPRESS               1002
#define IDC_RADIO_RECOVERABLE           1003
#define IDC_DRAWAREA_SCRIBLLE           1004
#define IDC_EDIT_LOGIN                  1005

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


STDAFX.CPP

// stdafx.cpp : source file that includes just the standard includes
//Imp_Draw.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
//

#if !defined(AFX_STDAFX_H__02EE003A_FC6D_11D0_A14D_00C04FB9D5DF__INCLUDED_)
#define AFX_STDAFX_H__02EE003A_FC6D_11D0_A14D_00C04FB9D5DF__INCLUDED_

#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000

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

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

//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_STDAFX_H__02EE003A_FC6D_11D0_A14D_00C04FB9D5DF__INCLUDED_)


README.TXT

The JavaDraw sample demonstrates how to implement a distributed drawing application
using Java SDK 2.0 and MSMQ ActiveX components.   It sends and receives MSMQ messages
which store drawings using safearrays, charater data as strings, as well as a
"Clear screen" message to both the sender and the receiver.  The sample also demonstrates
how to handle MSMQ ActiveX Events in Java.

The sample prepares and sends one message for the entire stroke ( drawing between
mouse-down and mouse-up ).  It demonstrates an object oriented approach to send and
receive different data types using Java.

VB and C-based versions of disdraw samples on MSMQ SDK will not
interoperate with this sample since they send one message per pixel of drawing.  A
compatible version of JavaDraw can be made by reducing the functionality and removing
the safe-array part with simple text

In order to compile the sample code, you MUST use the latest Java compiler included
in SDK 2.0.   Before building the sample, run the JActiveX tool on MSMQ type library
( MQOA.dll ) to generate java wrapper classes for MSMQ ActiveX components.   For a
description of JActiveX tool, please refer to the Microsoft SDK for Java 2.0
documentation available at http://www.microsoft.com/java/sdk

SafeArrays and Variants are described in the Microsoft SDK for Java 2.0 documentation
which is available from http://www.microsoft.com/java/sdk.


CLOSQDLG.CPP

// ClosQDlg.cpp : implementation file
//

#include "stdafx.h"
#include "MQApitst.h"
#include "ClosQDlg.h"

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

/////////////////////////////////////////////////////////////////////////////
// CCloseQueueDialog dialog

CCloseQueueDialog::CCloseQueueDialog(CArray <ARRAYQ*, ARRAYQ*>*  pStrArray, CWnd* pParent /*=NULL*/)
: CDialog(CCloseQueueDialog::IDD, pParent)
{
m_pStrArray = pStrArray;

//{{AFX_DATA_INIT(CCloseQueueDialog)
m_szPathName = _T("");
//}}AFX_DATA_INIT
}

void CCloseQueueDialog::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CCloseQueueDialog)
DDX_Control(pDX, IDC_COMBO, m_PathNameCB);
DDX_CBString(pDX, IDC_COMBO, m_szPathName);
DDV_MaxChars(pDX, m_szPathName, 128);
//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CCloseQueueDialog, CDialog)
//{{AFX_MSG_MAP(CCloseQueueDialog)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CCloseQueueDialog message handlers

BOOL CCloseQueueDialog::OnInitDialog()
{
CDialog::OnInitDialog();

int i;

// TODO: Add extra initialization here
for  (i=0 ; i<m_pStrArray->GetSize() ; i++)
VERIFY (m_PathNameCB.AddString((*m_pStrArray)[i]->szPathName) != CB_ERR);

    if (m_PathNameCB.GetCount() > 0) m_PathNameCB.SetCurSel(0);

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


CLOSQDLG.H

// ClosQDlg.h : header file
//

/////////////////////////////////////////////////////////////////////////////
// CCloseQueueDialog dialog

class CCloseQueueDialog : public CDialog
{
// Construction
public:
CCloseQueueDialog(CArray <ARRAYQ*, ARRAYQ*>*, CWnd* pParent = NULL);   // standard constructor

// Dialog Data
//{{AFX_DATA(CCloseQueueDialog)
enum { IDD = IDD_CLOSE_QUEUE_DIALOG };
CComboBoxm_PathNameCB;
CStringm_szPathName;
//}}AFX_DATA

/* pointer to the array with the strings for the combo box (Queues PathName). */
CArray <ARRAYQ*, ARRAYQ*>* m_pStrArray ;

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CCloseQueueDialog)
protected:
virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
//}}AFX_VIRTUAL

// Implementation
protected:

// Generated message map functions
//{{AFX_MSG(CCloseQueueDialog)
virtual BOOL OnInitDialog();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()

public:
// BUGBUG - set the 256 to BUFFERSIZE definition
void GetPathName(TCHAR szPathName[256])
{
_tcscpy (szPathName, m_szPathName);
}

};


CRQDLG.CPP

// CrQDlg.cpp : implementation file
//

#include "stdafx.h"
#include "MQApitst.h"
#include "CrQDlg.h"

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

/////////////////////////////////////////////////////////////////////////////
// CCreateQueueDialog dialog

CCreateQueueDialog::CCreateQueueDialog(CWnd* pParent /*=NULL*/)
   : CDialog(CCreateQueueDialog::IDD, pParent)
{
   TCHAR szTmpBuffer[BUFFERSIZE];
   DWORD dwTmpBufferSize = BUFFERSIZE;

   GetComputerName(szTmpBuffer, &dwTmpBufferSize);

   _tcscat(szTmpBuffer, TEXT("\\"));

   //{{AFX_DATA_INIT(CCreateQueueDialog)
   m_strLabel = TEXT("MQ API test");
   m_strPathName = szTmpBuffer;
   //}}AFX_DATA_INIT
}

void CCreateQueueDialog::DoDataExchange(CDataExchange* pDX)
{
   CDialog::DoDataExchange(pDX);
   //{{AFX_DATA_MAP(CCreateQueueDialog)
   DDX_Text(pDX, IDC_QUEUE_LABEL, m_strLabel);
   DDV_MaxChars(pDX, m_strLabel, 128);
   DDX_Text(pDX, IDC_QUEUE_PATHNAME, m_strPathName);
   DDV_MaxChars(pDX, m_strPathName, 128);
   //}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CCreateQueueDialog, CDialog)
   //{{AFX_MSG_MAP(CCreateQueueDialog)
   //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CCreateQueueDialog message handlers


CRQDLG.H

// CrQDlg.h : header file
//

/////////////////////////////////////////////////////////////////////////////
// CCreateQueueDialog dialog

class CCreateQueueDialog : public CDialog
{
// Construction
public:
CCreateQueueDialog(CWnd* pParent = NULL);   // standard constructor

// Dialog Data
//{{AFX_DATA(CCreateQueueDialog)
enum { IDD = IDD_CREATE_QUEUE_DIALOG };
CStringm_strLabel;
CStringm_strPathName;
//}}AFX_DATA

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CCreateQueueDialog)
protected:
virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
//}}AFX_VIRTUAL

// Implementation
protected:

// Generated message map functions
//{{AFX_MSG(CCreateQueueDialog)
//}}AFX_MSG
DECLARE_MESSAGE_MAP()

public:
// BUGBUG - set the 256 to BUFFERSIZE definition
void GetPathName(TCHAR szPathNameBuffer[256])
{
_tcscpy (szPathNameBuffer, LPCTSTR(m_strPathName));
}

// BUGBUG - set the 256 to BUFFERSIZE definition
void GetLabel(TCHAR szLabelBuffer[256])
{
_tcscpy (szLabelBuffer, LPCTSTR(m_strLabel));
}

};


DELQDLG.CPP

// DelQDlg.cpp : implementation file
//

#include "stdafx.h"
#include "MQApitst.h"
#include "DelQDlg.h"

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

/////////////////////////////////////////////////////////////////////////////
// CDeleteQueueDialog dialog

CDeleteQueueDialog::CDeleteQueueDialog(CArray<ARRAYQ*, ARRAYQ*>* pStrArray, CWnd* pParent /*=NULL*/)
: CDialog(CDeleteQueueDialog::IDD, pParent)
{
m_pStrArray = pStrArray;

//{{AFX_DATA_INIT(CDeleteQueueDialog)
m_szPathName = _T("");
//}}AFX_DATA_INIT
}

void CDeleteQueueDialog::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CDeleteQueueDialog)
DDX_Control(pDX, IDC_DELETE_QUEUE_COMBO, m_PathNameCB);
DDX_CBString(pDX, IDC_DELETE_QUEUE_COMBO, m_szPathName);
DDV_MaxChars(pDX, m_szPathName, 128);
//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CDeleteQueueDialog, CDialog)
//{{AFX_MSG_MAP(CDeleteQueueDialog)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CDeleteQueueDialog message handlers

BOOL CDeleteQueueDialog::OnInitDialog()
{
CDialog::OnInitDialog();

int i;

// TODO: Add extra initialization here
for  (i=0 ; i<m_pStrArray->GetSize() ; i++)
VERIFY (m_PathNameCB.AddString((*m_pStrArray)[i]->szPathName) != CB_ERR);

    if (m_PathNameCB.GetCount() > 0) m_PathNameCB.SetCurSel(0);

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


DELQDLG.H

// DelQDlg.h : header file
//

/////////////////////////////////////////////////////////////////////////////
// CDeleteQueueDialog dialog

class CDeleteQueueDialog : public CDialog
{
// Construction
public:
CDeleteQueueDialog(CArray <ARRAYQ*, ARRAYQ*>*, CWnd* pParent = NULL);   // standard constructor

// Dialog Data
//{{AFX_DATA(CDeleteQueueDialog)
enum { IDD = IDD_DELETE_QUEUE_DIALOG };
CComboBoxm_PathNameCB;
CStringm_szPathName;
//}}AFX_DATA

/* pointer to the array with the strings for the combo box (Queues PathName). */
CArray <ARRAYQ*, ARRAYQ*>* m_pStrArray ;

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CDeleteQueueDialog)
protected:
virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
//}}AFX_VIRTUAL

// Implementation
protected:

// Generated message map functions
//{{AFX_MSG(CDeleteQueueDialog)
virtual BOOL OnInitDialog();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()

public:
// BUGBUG - set the 256 to BUFFERSIZE definition
void GetPathName(TCHAR szPathName[256])
{
_tcscpy (szPathName, m_szPathName);
}

};


LOCATDLG.CPP

// LocatDlg.cpp : implementation file
//

#include "stdafx.h"
#include "MQApitst.h"
#include "LocatDlg.h"

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

/////////////////////////////////////////////////////////////////////////////
// CLocateDialog dialog

CLocateDialog::CLocateDialog(CWnd* pParent /*=NULL*/)
: CDialog(CLocateDialog::IDD, pParent)
{
//{{AFX_DATA_INIT(CLocateDialog)
m_szLabel = TEXT("MQ API test");
//}}AFX_DATA_INIT
}

void CLocateDialog::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CLocateDialog)
DDX_Text(pDX, IDC_LOCATE_LABEL, m_szLabel);
DDV_MaxChars(pDX, m_szLabel, 128);
//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CLocateDialog, CDialog)
//{{AFX_MSG_MAP(CLocateDialog)
// NOTE: the ClassWizard will add message map macros here
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CLocateDialog message handlers


LOCATDLG.H

// LocatDlg.h : header file
//

/////////////////////////////////////////////////////////////////////////////
// CLocateDialog dialog

class CLocateDialog : public CDialog
{
// Construction
public:
CLocateDialog(CWnd* pParent = NULL);   // standard constructor

// Dialog Data
//{{AFX_DATA(CLocateDialog)
enum { IDD = IDD_LOCATE_DIALOG };
CStringm_szLabel;
//}}AFX_DATA

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CLocateDialog)
protected:
virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
//}}AFX_VIRTUAL

// Implementation
protected:

// Generated message map functions
//{{AFX_MSG(CLocateDialog)
// NOTE: the ClassWizard will add member functions here
//}}AFX_MSG
DECLARE_MESSAGE_MAP()

public:
// BUGBUG - set the 256 to BUFFERSIZE definition
void GetLabel(TCHAR szLabelBuffer[256])
{
_tcscpy (szLabelBuffer, m_szLabel);
}

};


MAINFRM.CPP

/* ************************************************************************ */
/*                                                                          */
/* Main file of the application MQ API test.                                */
/*                                                                          */
/* ************************************************************************ */

//
// MainFrm.cpp : implementation of the CMainFrame class
//

#include "stdafx.h"
#include "MQApitst.h"
#include <afxtempl.h>

#include "MainFrm.h"
#include "CrQDlg.h"
#include "DelQDlg.h"
#include "OpenQDlg.h"
#include "ClosQDlg.h"
#include "SendMDlg.h"
#include "RecvMDlg.h"
#include "RecWDlg.h"
#include "LocatDlg.h"

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

LPSTR UnicodeStringToAnsiString(LPCWSTR lpcsUnicode)
{
    LPSTR lpAnsiString = NULL;

    if (lpcsUnicode)
    {
        DWORD dwSize = wcstombs(NULL, lpcsUnicode, 0);
        lpAnsiString = new char[dwSize+1];
        size_t rc = wcstombs(lpAnsiString, lpcsUnicode, dwSize);
        ASSERT(rc != (size_t)(-1));
        lpAnsiString[dwSize] = '\0';
    }

    return lpAnsiString;
}

void AnsiStringToUnicode(LPWSTR lpsUnicode, LPSTR  lpsAnsi, DWORD  nSize)
{
    if (lpsUnicode == NULL)
    {
        return;
    }

    ASSERT(lpsAnsi != NULL);

    size_t rc = mbstowcs(lpsUnicode, lpsAnsi, nSize);
    ASSERT(rc != (size_t)(-1));
    if (lpsUnicode[nSize-1] != L'\0')
        lpsUnicode[nSize] = L'\0';
}

#ifdef UNICODE
#define _mqscpy(dest, src)  wcscpy(dest, src)
#else
#define _mqscpy(dest, src)  AnsiStringToUnicode(dest, src, _tcslen(src)+1)
#endif

BOOL GetTextualSid(
    PSID pSid,          // binary SID
    LPTSTR TextualSID,   // buffer for textual representation of SID
    LPDWORD dwBufferLen // required/provided TextualSid buffersize
    )
{
    PSID_IDENTIFIER_AUTHORITY psia;
    DWORD dwSubAuthorities;
    DWORD dwSidRev=SID_REVISION;
    DWORD dwCounter;
    DWORD dwSidSize;

    // obtain SidIdentifierAuthority
    psia=&((SID *)pSid)->IdentifierAuthority;

    // obtain sidsubauthority count
    dwSubAuthorities=(DWORD)((SID *)pSid)->SubAuthorityCount;

    //
    // compute buffer length
    // S-SID_REVISION- + identifierauthority- + subauthorities- + NULL
    //
    dwSidSize = (15 + 12 + (12 * dwSubAuthorities) + 1) * sizeof(TCHAR);

    //
    // check provided buffer length.
    // If not large enough, indicate proper size and setlasterror
    //
    if (*dwBufferLen < dwSidSize)
    {
        *dwBufferLen = dwSidSize;
        SetLastError(ERROR_INSUFFICIENT_BUFFER);
        return FALSE;
    }

    //
    // prepare S-SID_REVISION-
    //
    TextualSID += _stprintf(TextualSID, TEXT("S-%lu-"), dwSidRev );

    //
    // prepare SidIdentifierAuthority
    //
    if ( (psia->Value[0] != 0) || (psia->Value[1] != 0) )
    {
        TextualSID += _stprintf(TextualSID,
                    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
    {
        TextualSID += _stprintf(TextualSID, 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++)
    {
        TextualSID += _stprintf(TextualSID, TEXT("-%lu"),
                    ((SID *)pSid)->SubAuthority[ dwCounter] );
    }

    return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
// CMainFrame

IMPLEMENT_DYNCREATE(CMainFrame, CFrameWnd)

BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
   //{{AFX_MSG_MAP(CMainFrame)
   ON_WM_CREATE()
   ON_COMMAND(ID_API_CREATE_QUEUE, OnApiCreateQueue)
   ON_COMMAND(ID_API_DELETE_QUEUE, OnApiDeleteQueue)
   ON_COMMAND(ID_API_OPEN_QUEUE, OnApiOpenQueue)
   ON_COMMAND(ID_API_CLOSE_QUEUE, OnApiCloseQueue)
   ON_COMMAND(ID_API_SEND_MESSAGE, OnApiSendMessage)
   ON_COMMAND(ID_API_RECEIVE_MESSAGE, OnApiReceiveMessage)
   ON_COMMAND(ID_API_LOCATE, OnApiLocate)
   //}}AFX_MSG_MAP
END_MESSAGE_MAP()

static UINT indicators[] =
{
   ID_SEPARATOR,           // status line indicator
   ID_INDICATOR_CAPS,
   ID_INDICATOR_NUM,
   ID_INDICATOR_SCRL,
};

/////////////////////////////////////////////////////////////////////////////
// CMainFrame construction/destruction

CMainFrame::CMainFrame()
{
   // TODO: add member initialization code here.
}

CMainFrame::~CMainFrame()
{
}

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
        return -1;

    if (!m_wndToolBar.Create(this) ||
        !m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
    {
        TRACE0("Failed to create toolbar\n");
        return -1;      // fail to create
    }

    if (!m_wndStatusBar.Create(this) ||
        !m_wndStatusBar.SetIndicators(indicators,
        sizeof(indicators)/sizeof(UINT)))
    {
        TRACE0("Failed to create status bar\n");
        return -1;      // fail to create
    }

    // TODO: Remove this if you don't want tool tips or a resizeable toolbar
    m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle() |
        CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);

    // TODO: Delete these three lines if you don't want the toolbar to
    //  be dockable
    m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
    EnableDocking(CBRS_ALIGN_ANY);
    DockControlBar(&m_wndToolBar);

    return 0;
}

BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
    // TODO: Modify the Window class or styles here by modifying
    //  the CREATESTRUCT cs

    return CFrameWnd::PreCreateWindow(cs);
}

/////////////////////////////////////////////////////////////////////////////
// CMainFrame diagnostics

#ifdef _DEBUG
void CMainFrame::AssertValid() const
{
    CFrameWnd::AssertValid();
}

void CMainFrame::Dump(CDumpContext& dc) const
{
    CFrameWnd::Dump(dc);
}

#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CMainFrame message handlers

#define MAXINDEX 31

/* ************************************************************************ */
/*                        RemoveFromPathNameArray                           */
/* ************************************************************************ */
/* This function goes through the PathName Array and compares the given     */
/* PathName to the PathName's of the items in the array.                    */
/* If a match is found the item is removed from the array and the function  */
/* returns a pointer to the item, otherwise a NULL pointer is returned.     */
/* ************************************************************************ */
ARRAYQ* CMainFrame::RemoveFromPathNameArray(TCHAR szPathName[MAX_Q_PATHNAME_LEN])
{
    int Index;
    int MaxIndex = m_PathNameArray.GetSize();
    ARRAYQ* pQueue;

    //
    // Loop through the PathName array.
    //
    for (Index=0; Index<MaxIndex; Index++)
    {
        if (_tcscmp(szPathName, m_PathNameArray[Index]->szPathName) == 0)
        {
            //
            // Found a match.
            //
            pQueue = m_PathNameArray[Index];
            m_PathNameArray.RemoveAt(Index);
            return pQueue;
        }
    }
    return NULL; // ERROR - no match was found.
}

/* ************************************************************************ */
/*                             CleanPathNameArray                           */
/* ************************************************************************ */
/* This function goes through the PathName array and deletes all the items  */
/* in it. the function frees the allocated memory.                          */
/* ************************************************************************ */
void CMainFrame::CleanPathNameArray()
{
    ARRAYQ* pQueue;

    while (m_PathNameArray.GetSize() > 0)
    {
        pQueue = m_PathNameArray[0];
        m_PathNameArray.RemoveAt(0);
        delete pQueue;
    }
}

/* ************************************************************************ */
/*                        RemoveFromOpenedQueuePathNameArray                */
/* ************************************************************************ */
/* This function goes through the OpenedPathName Array and compares the     */
/* given PathName to the PathName's of the items in the array.              */
/* If a match is found the item is removed from the array and the function  */
/* returns a pointer to the item, otherwise a NULL pointer is returned.     */
/* ************************************************************************ */
ARRAYQ* CMainFrame::RemoveFromOpenedQueuePathNameArray(TCHAR szPathName[MAX_Q_PATHNAME_LEN])
{
    int Index;
    int MaxIndex = m_OpenedQueuePathNameArray.GetSize();
    ARRAYQ* pQueue;

    //
    // Loop through the OpenedPathName array.
    //
    for (Index=0; Index<MaxIndex; Index++)
    {
        if (_tcscmp(szPathName, m_OpenedQueuePathNameArray[Index]->szPathName) == 0)
        {
            //
            // Found a match.
            //
            pQueue = m_OpenedQueuePathNameArray[Index];
            m_OpenedQueuePathNameArray.RemoveAt(Index);
            return pQueue;
        }
    }
    return NULL; // ERROR - no match was found.
}

/* ************************************************************************ */
/*                           IsOpenedQueueArrayEmpty                        */
/* ************************************************************************ */
/* This function checks if the size of the OpenedPathName array is zero or  */
/* less and if so it returns TRUE otherwise it returns FALSE.               */
/* ************************************************************************ */
BOOL CMainFrame::IsOpenedQueueArrayEmpty()
{
    if (m_OpenedQueuePathNameArray.GetSize() <= 0)
    {
        return TRUE;
    }
    else
    {
        return FALSE;
    }
}

/* ************************************************************************ */
/*                        MoveToOpenedQueuePathNameArray                    */
/* ************************************************************************ */
/* This function moves an item from the PathName array to the               */
/* OpenedPathName array. also it updates the hadle and the access rights    */
/* to the moved queue.                                                      */
/* ************************************************************************ */
void CMainFrame::MoveToOpenedQueuePathNameArray(TCHAR szPathName[MAX_Q_PATHNAME_LEN],
                                                QUEUEHANDLE hQueue, DWORD dwAccess)
{
    ARRAYQ* pQueue;

    pQueue = RemoveFromPathNameArray(szPathName);
    pQueue->hHandle = hQueue;                     // add Queue Handle.
    pQueue->dwAccess = dwAccess;                  // add Queue Access rights.
    Add2OpenedQueuePathNameArray(pQueue);
}

/* ************************************************************************ */
/*                              MoveToPathNameArray                         */
/* ************************************************************************ */
/* This function moves an item from the OpenedPathName array to the         */
/* PathName array.                                                          */
/* ************************************************************************ */
void CMainFrame::MoveToPathNameArray(TCHAR szPathName[MAX_Q_PATHNAME_LEN])
{
    ARRAYQ* pQueue;

    pQueue = RemoveFromOpenedQueuePathNameArray(szPathName);
    Add2PathNameArray(pQueue);
}

/* ************************************************************************ */
/*                             UpdatePathNameArrays                         */
/* ************************************************************************ */
/* This function goes through the Opened Queue PathName array and for every */
/* item in it, it checkes if the item is found in the PathName array as     */
/* well, if so the function removes the item from the PathName array.       */
/* ************************************************************************ */
void CMainFrame::UpdatePathNameArrays()
{
    int PathNameIndex;
    int OpenedPathNameIndex;
    int MaxPathNameIndex = m_PathNameArray.GetSize();
    int MaxOpenedPathNameIndex = m_OpenedQueuePathNameArray.GetSize();
    ARRAYQ* pQueue;

    //
    // Loop through the OpenedPathName array.
    //
    for (OpenedPathNameIndex=0; OpenedPathNameIndex<MaxOpenedPathNameIndex; OpenedPathNameIndex++)
    {
        for (PathNameIndex=0; PathNameIndex<MaxPathNameIndex; PathNameIndex++)
        {
            if (_tcscmp(m_OpenedQueuePathNameArray[OpenedPathNameIndex]->szPathName,
                m_PathNameArray[PathNameIndex]->szPathName) == 0)
            {
                //
                // Found a match, remove it from PathName Array.
                //
                pQueue = m_PathNameArray[PathNameIndex];
                m_PathNameArray.RemoveAt(PathNameIndex);
                delete pQueue;
                //
                // get out of inner for loop.
                //
                break;
            }
        }
    }
}

/* ************************************************************************ */
/*                              GetQueueHandle                              */
/* ************************************************************************ */
/* This function goes through the OpenedPathName array and retrieve the     */
/* Handle to the queue which matches the given PathName. If no match was    */
/* found the function returns FALSE.                                        */
/* ************************************************************************ */
BOOL CMainFrame::GetQueueHandle(TCHAR szPathName[MAX_Q_PATHNAME_LEN],
                                QUEUEHANDLE* phClosedQueueHandle)
{
    int Index;
    int MaxIndex = m_OpenedQueuePathNameArray.GetSize();
    ARRAYQ* pQueue;

    //
    // Loop through the OpenedPathName array.
    //
    for (Index=0; Index<MaxIndex; Index++)
    {
        if (_tcscmp(szPathName, m_OpenedQueuePathNameArray[Index]->szPathName) == 0)
        {
            //
            // Found a match.
            //
            pQueue = m_OpenedQueuePathNameArray[Index];
            *phClosedQueueHandle = pQueue->hHandle;
            return TRUE;
        }
    }
    return FALSE; // ERROR - no match was found.
}

/* ************************************************************************ */
/*                       TranslatePathNameToFormatName                      */
/* ************************************************************************ */
/* This function goes through the PathName array and retrieve the           */
/* FormatName to the queue which matches the given PathName. If no match    */
/* was found the function returns FALSE.                                    */
/* ************************************************************************ */
BOOL CMainFrame::TranslatePathNameToFormatName(TCHAR szPathName[MAX_Q_PATHNAME_LEN],
                                               TCHAR szFormatName[MAX_Q_FORMATNAME_LEN])
{
    int Index;
    int MaxIndex = m_PathNameArray.GetSize();
    ARRAYQ* pQueue;

    //
    // Loop through the PathName array.
    //
    for (Index=0; Index<MaxIndex; Index++)
    {
        if (_tcscmp(szPathName, m_PathNameArray[Index]->szPathName) == 0)
        {
            //
            // Found a match.
            //
            pQueue = m_PathNameArray[Index];
            _tcsncpy (szFormatName, pQueue->szFormatName, MAX_Q_FORMATNAME_LEN);
            return TRUE;
        }
    }
    return FALSE; // ERROR - no match was found.
}

/* ************************************************************************ */
/*                  TranslateOpenedQueuePathNameToFormatName                */
/* ************************************************************************ */
/* This function goes through the OpenedPathName array and retrieve the     */
/* FormatName to the queue which matches the given PathName. If no match    */
/* was found the function returns FALSE.                                    */
/* ************************************************************************ */
BOOL CMainFrame::TranslateOpenedQueuePathNameToFormatName(
    TCHAR szPathName[MAX_Q_PATHNAME_LEN],
    TCHAR szFormatName[MAX_Q_FORMATNAME_LEN]
    )
{
    int Index;
    int MaxIndex = m_OpenedQueuePathNameArray.GetSize();
    ARRAYQ* pQueue;

    //
    // Loop through the OpenedPathName array.
    //
    for (Index=0; Index<MaxIndex; Index++)
    {
        if (_tcscmp(szPathName, m_OpenedQueuePathNameArray[Index]->szPathName) == 0)
        {
            //
            // Found a match.
            //
            pQueue = m_OpenedQueuePathNameArray[Index];
            _tcsncpy (szFormatName, pQueue->szFormatName, MAX_Q_FORMATNAME_LEN);
            return TRUE;
        }
    }
    return FALSE; // ERROR - no match was found.
}

/* ************************************************************************ */
/*                         DisplayPathNameArray                             */
/* ************************************************************************ */
/* This function goes through the PathName Array and prints it to screen.   */
/* ************************************************************************ */
void CMainFrame::DisplayPathNameArray()
{
    int Index;
    int MaxIndex = m_PathNameArray.GetSize();
    TCHAR szMsgBuffer[BUFFERSIZE];

    _stprintf(szMsgBuffer, TEXT("   Located Queues Path Name :"));
    PrintToScreen(szMsgBuffer);
    //
    // Loop through the PathName array.
    //
    for (Index=0; Index<MaxIndex; Index++)
    {
        //
        // Print the PathNames.
        //
        _stprintf(szMsgBuffer, TEXT("\t%d. %s"),Index+1, m_PathNameArray[Index]->szPathName);
        PrintToScreen(szMsgBuffer);
    }
}

/* ************************************************************************ */
/*                    DisplayOpenedQueuePathNameArray                       */
/* ************************************************************************ */
/* This function goes through the Opened Queues PathName Array and          */
/* prints it to screen.                                                     */
/* ************************************************************************ */
void CMainFrame::DisplayOpenedQueuePathNameArray()
{
    int Index;
    int MaxIndex = m_OpenedQueuePathNameArray.GetSize();
    TCHAR szMsgBuffer[BUFFERSIZE];

    _stprintf(szMsgBuffer, TEXT("   Currently Opened Queues Path Names:"));
    PrintToScreen(szMsgBuffer);
    //
    // Loop through the OpenedQueuePathName array.
    //
    for (Index=0; Index<MaxIndex; Index++)
    {
        //
        // Print the PathNames.
        //
        _stprintf(szMsgBuffer, TEXT("\t%d. %s"),Index+1, m_OpenedQueuePathNameArray[Index]->szPathName);
        PrintToScreen(szMsgBuffer);
    }
}

/* ************************************************************************ */
/*                          GetMsgClassStatus                               */
/* ************************************************************************ */
/* This function sets proper status string based on a given MQMSG class.    */
/* ************************************************************************ */

struct
{
unsigned shortmclass;
LPTSTR          pszDescription;
} StringClass[] =
  {
{ MQMSG_CLASS_NORMAL, TEXT("The Message was received successfully.")},
{ MQMSG_CLASS_ACK_REACH_QUEUE, TEXT("The REACH QUEUE ACK Message was read successfully.")},
{ MQMSG_CLASS_ACK_RECEIVE, TEXT("The RECEIVE ACK Message was read successfully.")},
{ MQMSG_CLASS_NACK_BAD_DST_Q, TEXT("The DESTINATION QUEUE HANDLE INVALID Nack Message was read successfully.")},
{ MQMSG_CLASS_NACK_RECEIVE_TIMEOUT, TEXT("The TIME TO RECEIVE EXPIRED Nack Message was read successfully.")},
{ MQMSG_CLASS_NACK_REACH_QUEUE_TIMEOUT, TEXT("The TIME TO REACH QUEUE EXPIRED Nack Message was read successfully.")},
{ MQMSG_CLASS_NACK_Q_EXCEED_QUOTA, TEXT("The QUEUE IS FULL Nack Message was read successfully.")},
{ MQMSG_CLASS_NACK_ACCESS_DENIED, TEXT("The SENDER HAVE NO SEND ACCESS RIGHTS ON QUEUE Nack Message was read successfully.")},
{ MQMSG_CLASS_NACK_HOP_COUNT_EXCEEDED, TEXT("The HOP COUNT EXCEEDED Nack Message was read successfully.")},
{ MQMSG_CLASS_NACK_BAD_SIGNATURE, TEXT("The MESSAGE RECEIVED WITH BAD SIGNATURE Nack Message was read successfully.")},
{ MQMSG_CLASS_NACK_BAD_ENCRYPTION, TEXT("The MSG COULD NOT DECRYPTED Nack Message was read successfully.")},
    { MQMSG_CLASS_NACK_COULD_NOT_ENCRYPT, TEXT("The SOURCE QM COULD NOT ENCRYPT MSG FOR DEST QM Nack Message was read successfully.")},
{ 0, NULL}
  };

void CMainFrame::ClassToString(unsigned short MsgClass,LPTSTR pszStatus)
{
//
// loop the StringClass array to find MsgClass
//
DWORD dwIndex = 0;
while (StringClass[dwIndex].pszDescription != NULL)
{
if (StringClass[dwIndex].mclass == MsgClass)
{
_stprintf(pszStatus,StringClass[dwIndex].pszDescription);
return;
}
dwIndex++;
}

//
// MsgClass not found - print default error
//
_stprintf(pszStatus,TEXT("The NACK (0x%X) Message was read successfully."),MsgClass);
}

/* ************************************************************************ */
/*                            OnApiCreateQueue                              */
/* ************************************************************************ */
/* This function opens a dialog box and asks the user for the queue's       */
/* PathName and Label. Then it creates the specified queue.                 */
/*                                                                          */
/* Uses: MQCreateQueue.                                                     */
/* ************************************************************************ */
void CMainFrame::OnApiCreateQueue()
{
    // TODO: Add your command handler code here

    TCHAR szMsgBuffer[BUFFERSIZE];

    MQQUEUEPROPS QueueProps;
    MQPROPVARIANT aVariant[MAXINDEX];
    QUEUEPROPID aPropId[MAXINDEX];
    DWORD PropIdCount = 0;
    HRESULT hr;

    PSECURITY_DESCRIPTOR pSecurityDescriptor;

    TCHAR szPathNameBuffer[MAX_Q_PATHNAME_LEN];
    TCHAR szFormatNameBuffer[MAX_Q_FORMATNAME_LEN];
    TCHAR szLabelBuffer[MAX_Q_PATHNAME_LEN];
    DWORD dwFormatNameBufferLength = MAX_Q_FORMATNAME_LEN;

    //
    // Display CreateQueue Dialog.
    //
    CCreateQueueDialog CreateQueueDialog;

    if(CreateQueueDialog.DoModal() == IDCANCEL)
    {
        return;
    }
    CreateQueueDialog.GetPathName(szPathNameBuffer);
    CreateQueueDialog.GetLabel(szLabelBuffer);

    //
    // Get the input fields from the dialog box
    // and prepare the property array PROPVARIANT
    //

    //
    // Set the PROPID_Q_PATHNAME property
    //
    aPropId[PropIdCount] = PROPID_Q_PATHNAME;    //PropId
    aVariant[PropIdCount].vt = VT_LPWSTR;        //Type
    aVariant[PropIdCount].pwszVal = new WCHAR[MAX_Q_PATHNAME_LEN];
    _mqscpy(aVariant[PropIdCount].pwszVal, szPathNameBuffer); //Value

    PropIdCount++;

    //
    // Set the PROPID_Q_LABEL property
    //
    aPropId[PropIdCount] = PROPID_Q_LABEL;    //PropId
    aVariant[PropIdCount].vt = VT_LPWSTR;     //Type
    aVariant[PropIdCount].pwszVal = new WCHAR[MAX_Q_PATHNAME_LEN];
    _mqscpy(aVariant[PropIdCount].pwszVal, szLabelBuffer); //Value

    PropIdCount++;

    //
    // Set the MQEUEUPROPS structure
    //
    QueueProps.cProp = PropIdCount;           //No of properties
    QueueProps.aPropID = aPropId;             //Id of properties
    QueueProps.aPropVar = aVariant;           //Value of properties
    QueueProps.aStatus = NULL;                //No error reports

    //
    // No security (default)
    //
    pSecurityDescriptor = NULL;

    //
    // Create the queue
    //
#ifdef UNICODE
    hr = MQCreateQueue(
            pSecurityDescriptor,            //Security
            &QueueProps,                    //Queue properties
            szFormatNameBuffer,             //Output: Format Name
            &dwFormatNameBufferLength       //Output: Format Name len
            );
#else
    WCHAR szwFormatNameBuffer[MAX_Q_FORMATNAME_LEN];
    hr = MQCreateQueue(
            pSecurityDescriptor,            //Security
            &QueueProps,                    //Queue properties
            szwFormatNameBuffer,            //Output: Format Name
            &dwFormatNameBufferLength       //Output: Format Name len
            );

    if (SUCCEEDED(hr))
    {
        size_t rc =wcstombs(szFormatNameBuffer, szwFormatNameBuffer, dwFormatNameBufferLength);
        ASSERT(rc != (size_t)(-1));
    }
#endif

    if (FAILED(hr))
    {
        //
        // Error - display message
        //
        _stprintf(szMsgBuffer, TEXT("MQCreateQueue failed, Error code = 0x%x."),hr);
        MessageBox(szMsgBuffer, TEXT("ERROR"), MB_OK);
    }
    else
    {
        //
        // Success - write in edit control.
        //
        _stprintf(szMsgBuffer, TEXT("The queue %s was created successfully. ( FormatName: %s )"),
            szPathNameBuffer, szFormatNameBuffer);
        PrintToScreen(szMsgBuffer);

        //
        // Add the new queue to the PathName Array.
        //
        ARRAYQ* pNewQueue = new ARRAYQ;
        //
        // Save PathName and FormatName in the ARRAYQ structure.
        //
        _tcsncpy (pNewQueue->szPathName, szPathNameBuffer, MAX_Q_PATHNAME_LEN);
        _tcsncpy (pNewQueue->szFormatName, szFormatNameBuffer, MAX_Q_FORMATNAME_LEN);
        Add2PathNameArray(pNewQueue);
    }

    //
    // Free allocated memory
    //
    delete   aVariant[0].pwszVal;
    delete   aVariant[1].pwszVal;
}

/* ************************************************************************ */
/*                            OnApiDeleteQueue                              */
/* ************************************************************************ */
/* This function opens a dialog box and asks the user for the queue's       */
/* PathName. then it deletes the specified queue.                           */
/*                                                                          */
/* Uses: MQDeleteQueue.                                                     */
/* ************************************************************************ */
void CMainFrame::OnApiDeleteQueue()
{
    // TODO: Add your command handler code here
    TCHAR szPathNameBuffer[MAX_Q_PATHNAME_LEN];
    TCHAR szFormatNameBuffer[MAX_Q_PATHNAME_LEN];
    TCHAR szMsgBuffer[BUFFERSIZE];

    HRESULT hr;

    DWORD dwFormatNameBufferLength = MAX_Q_PATHNAME_LEN;

    CDeleteQueueDialog DeleteQueueDialog(&m_PathNameArray);

    //
    // Display DeleteQueue Dialog.
    //
    if (DeleteQueueDialog.DoModal() == IDCANCEL)
    {
        return;
    }

    DeleteQueueDialog.GetPathName(szPathNameBuffer);

    //
    // Translate the path name to format name using the ARRAYQ arrays.
    //
    if (TranslatePathNameToFormatName(szPathNameBuffer, szFormatNameBuffer) == FALSE)
    {
        //
        // Error - display message
        //
        _stprintf(szMsgBuffer, TEXT("Queue wasn't found"));
        MessageBox(szMsgBuffer, TEXT("ERROR"), MB_OK);
        return;
    }

    //
    // Delete the queue.
    //
#ifdef UNICODE
    hr = MQDeleteQueue(szFormatNameBuffer);  // FormatName of the Queue to be deleted.
#else
    WCHAR szwFormatNameBuffer[MAX_Q_FORMATNAME_LEN];
    size_t rc = mbstowcs(szwFormatNameBuffer, szFormatNameBuffer, _tcslen(szFormatNameBuffer)+1);
    ASSERT(rc != (size_t)(-1));
    hr = MQDeleteQueue(szwFormatNameBuffer);  // FormatName of the Queue to be deleted.
#endif

    if (FAILED(hr))
    {
        //
        // Error - display message
        //
        _stprintf(szMsgBuffer, TEXT("MQDeleteQueue failed, Error code = 0x%x."),hr);
        MessageBox(szMsgBuffer, TEXT("ERROR"), MB_OK);
    }
    else
    {
        //
        // Success - write in edit control
        //
        _stprintf(szMsgBuffer, TEXT("The queue %s was deleted successfully."), szPathNameBuffer);
        PrintToScreen(szMsgBuffer);

//
        // Delete the name from the Path Names array.
        //
        ARRAYQ* DeletedQueue = RemoveFromPathNameArray(szPathNameBuffer);
        if (DeletedQueue != NULL)
        {
            delete DeletedQueue;
        }
    }
}

/* ************************************************************************ */
/*                             OnApiOpenQueue                               */
/* ************************************************************************ */
/* This function opens a dialog box and asks the user for the queue's       */
/* PathName. then it opens the specified queue.                             */
/*                                                                          */
/* Uses: MQOpenQueue.                                                       */
/* ************************************************************************ */
void CMainFrame::OnApiOpenQueue()
{
    // TODO: Add your command handler code here
    TCHAR szPathNameBuffer[MAX_Q_PATHNAME_LEN];
    TCHAR szFormatNameBuffer[MAX_Q_PATHNAME_LEN];
    TCHAR szAccessBuffer[50]; // BUGBUG - maybe set with a define
    TCHAR szMsgBuffer[BUFFERSIZE];

    HRESULT hr;

    DWORD dwFormatNameBufferLength = MAX_Q_PATHNAME_LEN;
    DWORD dwAccess;

    QUEUEHANDLE hQueue;

    COpenQueueDialog OpenQueueDialog(&m_PathNameArray);

    //
    // Display the OpenQueue dialog.
    //
    if (OpenQueueDialog.DoModal() == IDCANCEL)
    {
        return;
    }

    OpenQueueDialog.GetPathName(szPathNameBuffer);
    dwAccess = OpenQueueDialog.GetAccess();
    //
    // Set the access buffer string.
    //
    switch (dwAccess)
    {
    case (MQ_RECEIVE_ACCESS | MQ_SEND_ACCESS):

        _tcscpy(szAccessBuffer, TEXT("MQ_RECEIVE_ACCESS, MQ_SEND_ACCESS."));
        break;

    case MQ_RECEIVE_ACCESS:

        _tcscpy(szAccessBuffer, TEXT("MQ_RECEIVE_ACCESS."));
        break;

    case MQ_SEND_ACCESS:

        _tcscpy(szAccessBuffer, TEXT("MQ_SEND_ACCESS."));
        break;

    default:

        _tcscpy(szAccessBuffer, TEXT("NONE."));
        break;
    }

    //
    // Translate the path name to format name using the ARRAYQ arrays.
    //
    if (TranslatePathNameToFormatName(szPathNameBuffer, szFormatNameBuffer) == FALSE)
    {
        //
        // Error - display message
        //
        _stprintf(szMsgBuffer, TEXT("Queue wasn't found"));
        MessageBox(szMsgBuffer, TEXT("ERROR"), MB_OK);
        return;
    }

    //
    // Open the queue. (no sharing)
    //
#ifdef UNICODE
    hr = MQOpenQueue(
            szFormatNameBuffer,     // Format Name of the queue to be opened.
            dwAccess,               // Access rights to the Queue.
            0,                      // No receive Exclusive.
            &hQueue                 // OUT: handle to the opened Queue.
            );
#else
    WCHAR szwFormatNameBuffer[MAX_Q_FORMATNAME_LEN];
    size_t rc = mbstowcs(szwFormatNameBuffer, szFormatNameBuffer, _tcslen(szFormatNameBuffer)+1);
    ASSERT(rc != (size_t)(-1));

    hr = MQOpenQueue(
            szwFormatNameBuffer,    // Format Name of the queue to be opened.
            dwAccess,               // Access rights to the Queue.
            0,                      // No receive Exclusive.
            &hQueue                 // OUT: handle to the opened Queue.
            );
#endif

    if (FAILED(hr))
    {
        //
        // Error - display message
        //
        _stprintf(szMsgBuffer, TEXT("MQOpenQueue failed, Error code = 0x%x."),hr);
        MessageBox(szMsgBuffer, TEXT("ERROR"), MB_OK);
    }
    else
    {
        //
        // Success - write in edit control
        //
        _stprintf(szMsgBuffer,
            TEXT("The queue %s was opened successfully.\r\n\tQueueHandle: 0x%x\r\n\tQueue Access : %s"),
            szPathNameBuffer,
            hQueue,
            szAccessBuffer);
        PrintToScreen(szMsgBuffer);

        //
        // move the queue to the opened queues array.
        //
        MoveToOpenedQueuePathNameArray(szPathNameBuffer, hQueue, dwAccess);
    }
}

/* ************************************************************************ */
/*                            OnApiCloseQueue                               */
/* ************************************************************************ */
/* This function opens a dialog box and asks the user for the queue's       */
/* PathName. then it closes the specified queue.                            */
/*                                                                          */
/* Uses: MQCloseQueue.                                                      */
/* ************************************************************************ */
void CMainFrame::OnApiCloseQueue()
{
    // TODO: Add your command handler code here

    TCHAR szPathNameBuffer[MAX_Q_PATHNAME_LEN];
    TCHAR szMsgBuffer[BUFFERSIZE];

    HRESULT hr;

    DWORD dwFormatNameBufferLength = MAX_Q_PATHNAME_LEN;

    QUEUEHANDLE hClosedQueueHandle;

    //
    // Display CloseQueue Dialog.
    //
    CCloseQueueDialog CloseQueueDialog(&m_OpenedQueuePathNameArray);

    if (CloseQueueDialog.DoModal() == IDCANCEL)
    {
        return;
    }

    CloseQueueDialog.GetPathName(szPathNameBuffer);

    //
    // Get the closed queue handle.
    //
    if (GetQueueHandle(szPathNameBuffer, &hClosedQueueHandle) == FALSE)
    {
        //
        // Error - display message
        //
        _stprintf(szMsgBuffer, TEXT("The Queue couldn't be closed since it was not opened before."));
        MessageBox(szMsgBuffer, TEXT("ERROR"), MB_OK);
        return;
    }

    //
    // Close the queue.
    //
    hr = MQCloseQueue(hClosedQueueHandle);   // the handle of the Queue to be closed.
    if (FAILED(hr))
    {
        //
        // Error - display message
        //
        _stprintf(szMsgBuffer, TEXT("MQCloseQueue failed, Error code = 0x%x."),hr);
        MessageBox(szMsgBuffer, TEXT("ERROR"), MB_OK);
    }
    else
    {
        //
        // Success - write in edit control
        //
        _stprintf(szMsgBuffer, TEXT("The queue %s was closed successfully."), szPathNameBuffer);
        PrintToScreen(szMsgBuffer);
        //
        // Move the queue form the opened queues array to the path name array.
        //
        MoveToPathNameArray(szPathNameBuffer);
    }
}

/* ************************************************************************ */
/*                            OnApiSendMessage                              */
/* ************************************************************************ */
/* This function opens a dialog box and asks the user for the queue's       */
/* PathName and some message properties. Then it sends the message to the   */
/* specified queue.                                                         */
/*                                                                          */
/* Uses: MQSendMessage.                                                     */
/* ************************************************************************ */

//
// two static buffers to hold the last message body and label for the next time.
//
TCHAR szLastMessageBody[BUFFERSIZE];
TCHAR szLastMessageLabel[BUFFERSIZE];

void CMainFrame::OnApiSendMessage()
{
    // TODO: Add your command handler code here

    TCHAR szPathNameBuffer[MAX_Q_PATHNAME_LEN];
    TCHAR szAdminPathNameBuffer[MAX_Q_PATHNAME_LEN];
    TCHAR szAdminFormatNameBuffer[MAX_Q_FORMATNAME_LEN];
    TCHAR szMsgBuffer[BUFFERSIZE];

    MQMSGPROPS MsgProps;
    MQPROPVARIANT aVariant[MAXINDEX];
    MSGPROPID aPropId[MAXINDEX];
    DWORD PropIdCount = 0;

    HRESULT hr;

    unsigned char bPriority;
    unsigned char bDelivery;
    unsigned char bJournal;
    unsigned char bDeadLetter;
unsigned char bAuthenticated;
unsigned char bEncrypted;
    unsigned char bAcknowledge;

    WCHAR szMessageBodyBuffer [BUFFERSIZE];
    WCHAR szMessageLabelBuffer[BUFFERSIZE];
    DWORD dwTimeToReachQueue;
DWORD dwTimeToBeReceived;

    QUEUEHANDLE hQueue;

    CSendMessageDialog SendMessageDialog(&m_OpenedQueuePathNameArray);

    //
    // Display the SendMessage dialog.
    //
    if (SendMessageDialog.DoModal() == IDCANCEL)
    {
        return;
    }

    //
    // Retrieve the properties from the dialog box.
    //
    SendMessageDialog.GetPathName(szPathNameBuffer);
    SendMessageDialog.GetAdminPathName(szAdminPathNameBuffer);
    bPriority = SendMessageDialog.GetPriority();
    bDelivery = SendMessageDialog.GetDelivery();
    bJournal = SendMessageDialog.GetJournal();
    bDeadLetter = SendMessageDialog.GetDeadLetter();
bAuthenticated = SendMessageDialog.GetAuthenticated();
bEncrypted = SendMessageDialog.GetEncrypted();
    bAcknowledge = SendMessageDialog.GetAcknowledge();
    SendMessageDialog.GetMessageBody(szLastMessageBody);
    SendMessageDialog.GetMessageLabel(szLastMessageLabel);
    dwTimeToReachQueue = SendMessageDialog.GetTimeToReachQueue();
dwTimeToBeReceived = SendMessageDialog.GetTimeToBeReceived();

    //
    // Update the Last message properties.
    //
    _mqscpy(szMessageBodyBuffer, szLastMessageBody);
    _mqscpy(szMessageLabelBuffer, szLastMessageLabel);

    //
    // Get the target queue handle.
    //
    if (GetQueueHandle(szPathNameBuffer, &hQueue) == FALSE)
    {
        //
        // Error - display message
        //
        _stprintf(szMsgBuffer, TEXT("GetQueueHandle failed. Queue not opened yet."));
        MessageBox(szMsgBuffer, TEXT("ERROR"), MB_OK);
        return;
    }

    //
    // Get the admin queue FormatName.
    //
    if (TranslateOpenedQueuePathNameToFormatName(szAdminPathNameBuffer, szAdminFormatNameBuffer) == FALSE)
    {
        //
        // Error - display message
        //
        _stprintf(szMsgBuffer, TEXT("TranslatePathNameToFormatName failed, Queue has not been opened yet."));
        MessageBox(szMsgBuffer, TEXT("ERROR"), MB_OK);
        return;
    }

    //
    // prepare the property array PROPVARIANT.
    //

    //
    // Set the PROPID_M_PRIORITY property.
    //
    aPropId[PropIdCount] = PROPID_M_PRIORITY;    //PropId
    aVariant[PropIdCount].vt = VT_UI1;           //Type
    aVariant[PropIdCount].bVal = bPriority;      //Value

    PropIdCount++;

    //
    // Set the PROPID_M_DELIVERY property.
    //
    aPropId[PropIdCount] = PROPID_M_DELIVERY;    //PropId
    aVariant[PropIdCount].vt = VT_UI1;           //Type
    aVariant[PropIdCount].bVal = bDelivery;      //Value

    PropIdCount++;

    //
    // Set the PROPID_M_ACKNOWLEDGE property.
    //
    aPropId[PropIdCount] = PROPID_M_ACKNOWLEDGE; //PropId
    aVariant[PropIdCount].vt = VT_UI1;           //Type
    aVariant[PropIdCount].bVal = bAcknowledge;   //Value

    PropIdCount++;

    //
    // Set the PROPID_M_BODY property.
    //
    aPropId[PropIdCount] = PROPID_M_BODY;                  //PropId
    aVariant[PropIdCount].vt = VT_VECTOR|VT_UI1;           //Type
    aVariant[PropIdCount].caub.cElems =
        (wcslen(szMessageBodyBuffer) + 1) * sizeof(WCHAR); //Value
    aVariant[PropIdCount].caub.pElems = (unsigned char *)szMessageBodyBuffer;

    PropIdCount++;

    //
    // Set the PROPID_M_LABEL property.
    //
    aPropId[PropIdCount] = PROPID_M_LABEL;                  //PropId
    aVariant[PropIdCount].vt = VT_LPWSTR;                   //Type
    aVariant[PropIdCount].pwszVal = szMessageLabelBuffer;     //Value

    PropIdCount++;

    //
    // Set the PROPID_M_TIME_TO_REACH_QUEUE property.
    //
    aPropId[PropIdCount] = PROPID_M_TIME_TO_REACH_QUEUE;    //PropId
    aVariant[PropIdCount].vt = VT_UI4;                      //Type
    aVariant[PropIdCount].ulVal = dwTimeToReachQueue;       //Value

    PropIdCount++;

    //
    // Set the PROPID_M_TIME_TO_BE_RECEIVED property.
    //
    aPropId[PropIdCount] = PROPID_M_TIME_TO_BE_RECEIVED;    //PropId
    aVariant[PropIdCount].vt = VT_UI4;                      //Type
    aVariant[PropIdCount].ulVal = dwTimeToBeReceived;       //Value

    PropIdCount++;

    if (bJournal || bDeadLetter)
    {
        //
        // Set the PROPID_M_JOURNAL property.
        //
        aPropId[PropIdCount] = PROPID_M_JOURNAL;            //PropId
        aVariant[PropIdCount].vt = VT_UI1;                  //Type

        if (bJournal)
            aVariant[PropIdCount].bVal = MQMSG_JOURNAL;
        else
            aVariant[PropIdCount].bVal = 0;
        if (bDeadLetter)
            aVariant[PropIdCount].bVal |= MQMSG_DEADLETTER;

        PropIdCount++;
    }

if (bAuthenticated)
{
//
// Set the PROPID_M_AUTH_LEVEL property.
//
aPropId[PropIdCount] = PROPID_M_AUTH_LEVEL;            //PropId
aVariant[PropIdCount].vt = VT_UI4;                     //Type
aVariant[PropIdCount].ulVal = MQMSG_AUTH_LEVEL_ALWAYS; //Value

PropIdCount++;
}

if (bEncrypted)
{
//
// Set the PROPID_M_ENCRYPTION_ALG property.
//
aPropId[PropIdCount] = PROPID_M_PRIV_LEVEL;            //PropId
aVariant[PropIdCount].vt = VT_UI4;                     //Type
aVariant[PropIdCount].ulVal = MQMSG_PRIV_LEVEL_BODY;   //Value

PropIdCount++;
}

    //
    // Set the PROPID_M_ADMIN_QUEUE property.
    //
    aPropId[PropIdCount] = PROPID_M_ADMIN_QUEUE;               //PropId
    aVariant[PropIdCount].vt = VT_LPWSTR;                      //Type
#ifdef UNICODE
    aVariant[PropIdCount].pwszVal = szAdminFormatNameBuffer;   //Value
#else
    WCHAR szwAdminFormatNameBuffer[MAX_Q_FORMATNAME_LEN];
    size_t rc = mbstowcs(szwAdminFormatNameBuffer,
                    szAdminFormatNameBuffer,
                    _tcslen(szAdminFormatNameBuffer)+1);
    ASSERT(rc != (size_t)(-1));
    aVariant[PropIdCount].pwszVal = szwAdminFormatNameBuffer;  //Value
#endif

    PropIdCount++;

    //
    // Set the MQMSGPROPS structure
    //
    MsgProps.cProp = PropIdCount;       //Number of properties.
    MsgProps.aPropID = aPropId;         //Id of properties.
    MsgProps.aPropVar = aVariant;       //Value of properties.
    MsgProps.aStatus  = NULL;           //No Error report.

    //
    // Send the message.
    //
    hr = MQSendMessage(
            hQueue,                     // handle to the Queue.
            &MsgProps,                  // Message properties to be sent.
            NULL                        // No transaction
            );

    if (FAILED(hr))
    {
        //
        // Error - display message
        //
        _stprintf(szMsgBuffer, TEXT("MQSendMessage failed, Error code = 0x%x."),hr);
        MessageBox(szMsgBuffer, TEXT("ERROR"), MB_OK);
    }
    else
    {
        //
        // Success - write in edit control
        //
        _stprintf(szMsgBuffer, TEXT("The Message \"%s\" was sent successfully."), szLastMessageLabel);
        PrintToScreen(szMsgBuffer);
    }
}

/* ************************************************************************ */
/*                           OnApiReceiveMessage                            */
/* ************************************************************************ */
/* This function opens a dialog box and asks the user for the queue's       */
/* PathName and the Time to wait for the message. Then it tries to get a    */
/* message from the specified queue at the given time.                      */
/*                                                                          */
/* Uses: MQReceiveMessage, MQFreeMemory.                                    */
/* ************************************************************************ */
void CMainFrame::OnApiReceiveMessage()
{
    TCHAR szPathNameBuffer[MAX_Q_PATHNAME_LEN];
    TCHAR szMsgBuffer[2*BUFFERSIZE];
    TCHAR szDomainName[BUFFERSIZE];
 TCHAR szAccountName[BUFFERSIZE];
    DWORD dwActNameSize = sizeof(szAccountName);
    DWORD dwDomNameSize = sizeof(szDomainName);
    TCHAR szTextSid[BUFFERSIZE];
    DWORD dwTextSidSize = sizeof(szTextSid);
    BYTE  blobBuffer[BUFFERSIZE];

    MQMSGPROPS MsgProps;
    MQPROPVARIANT aVariant[MAXINDEX];
    MSGPROPID aPropId[MAXINDEX];
    DWORD PropIdCount = 0;

    HRESULT hr;

    WCHAR szMessageLabelBuffer[BUFFERSIZE];
    DWORD dwTimeout;

    QUEUEHANDLE hQueue;

    CReceiveWaitDialog    WaitDialog;
    CReceiveMessageDialog ReceiveMessageDialog(&m_OpenedQueuePathNameArray);

    //
    // Display the ReceiveMessage dialog.
    //
    if (ReceiveMessageDialog.DoModal() == IDCANCEL)
    {
        return;
    }

    ReceiveMessageDialog.DestroyWindow();
    ReceiveMessageDialog.GetPathName(szPathNameBuffer);

    //
    // Get the queue handle.
    //
    if (GetQueueHandle(szPathNameBuffer, &hQueue) == FALSE)
    {
        //
        // Error - display message
        //
        _stprintf(szMsgBuffer, TEXT("GetQueueHandle failed. Queue was not found in Opened Queue Array"));
        MessageBox(szMsgBuffer, TEXT("ERROR"), MB_OK);
        return;
    }

    //
    // Retrieve the properties form the dialog box.
    //
    dwTimeout = ReceiveMessageDialog.GetTimeout();

    //
    // prepare the property array PROPVARIANT of
    // message properties that we want to receive
    //

    //
    // Set the PROPID_M_BODY property.
    //
    aPropId[PropIdCount] = PROPID_M_BODY;                                //PropId
    aVariant[PropIdCount].vt = VT_VECTOR|VT_UI1;                         //Type
    aVariant[PropIdCount].caub.cElems = ReceiveMessageDialog.GetBodySize() ;
    aVariant[PropIdCount].caub.pElems = (unsigned char *) new
                               char [ aVariant[PropIdCount].caub.cElems ] ;

    int iBodyIndex = PropIdCount ;
    PropIdCount++;

    //
    // Set the PROPID_M_LABEL property.
    //
    aPropId[PropIdCount] = PROPID_M_LABEL;                   //PropId
    aVariant[PropIdCount].vt = VT_LPWSTR;                    //Type
    aVariant[PropIdCount].pwszVal = szMessageLabelBuffer;

    PropIdCount++;

    //
    // Set the PROPID_M_PRIORITY property.
    //
    aPropId[PropIdCount] = PROPID_M_PRIORITY;               //PropId
    aVariant[PropIdCount].vt = VT_UI1;                      //Type

    PropIdCount++;

    //
    // Set the PROPID_M_CLASS property.
    //
    aPropId[PropIdCount] = PROPID_M_CLASS;                  //PropId
    aVariant[PropIdCount].vt = VT_UI2;                      //Type

    PropIdCount++;

    //
    // Set the PROPID_M_AUTHENTICATED property.
    //
    aPropId[PropIdCount] = PROPID_M_AUTHENTICATED;          //PropId
    aVariant[PropIdCount].vt = VT_UI1;                      //Type

    PropIdCount++;

//
// Set the PROPID_M_SENDERID property
//
aPropId[PropIdCount] = PROPID_M_SENDERID;               //PropId
aVariant[PropIdCount].vt = VT_UI1|VT_VECTOR;            //Type
aVariant[PropIdCount].blob.pBlobData = blobBuffer;
aVariant[PropIdCount].blob.cbSize = sizeof(blobBuffer);

PropIdCount++;

//
// Set the PROPID_M_PRIV_LEVEL property
//
aPropId[PropIdCount] = PROPID_M_PRIV_LEVEL;             //PropId
aVariant[PropIdCount].vt = VT_UI4          ;            //Type

PropIdCount++;

    //
    // Set the PROPID_M_LABEL_LEN property.
    //
    aPropId[PropIdCount] = PROPID_M_LABEL_LEN;              //PropId
    aVariant[PropIdCount].vt = VT_UI4;                      //Type
    aVariant[PropIdCount].ulVal = BUFFERSIZE;               //Value

    PropIdCount++;

    //
    // Set the MQMSGPROPS structure
    //
    MsgProps.cProp = PropIdCount;       //Number of properties.
    MsgProps.aPropID = aPropId;         //Id of properties.
    MsgProps.aPropVar = aVariant;       //Value of properties.
    MsgProps.aStatus  = NULL;           //No Error report.

    //
    // Display a message window until the message from the queue will be received.
    //
    WaitDialog.Create(IDD_WAIT_DIALOG,pMainView);
    WaitDialog.ShowWindow(SW_SHOWNORMAL);
    WaitDialog.UpdateWindow();
    WaitDialog.CenterWindow();
    pMainView->RedrawWindow();

    //
    // Receive the message.
    //
    hr = MQReceiveMessage(
               hQueue,               // handle to the Queue.
               dwTimeout,            // Max time (msec) to wait for the message.
               MQ_ACTION_RECEIVE,    // Action.
               &MsgProps,            // properties to retrieve.
               NULL,                 // No overlaped structure.
               NULL,                 // No callback function.
               NULL,                 // No Cursor.
               NULL                  // No transaction
               );

    WaitDialog.ShowWindow(SW_HIDE);

if(hr == MQ_ERROR_IO_TIMEOUT)
{
        _stprintf(szMsgBuffer, TEXT("MQReceiveMessage failed, Timeout expired."),hr);
        MessageBox(szMsgBuffer, TEXT("ERROR"), MB_OK);
}
else if(hr != MQ_OK)
{
        //
        // Error - display message
        //
        _stprintf(szMsgBuffer, TEXT("MQReceiveMessage failed, Error code = 0x%x."),hr);
        MessageBox(szMsgBuffer, TEXT("ERROR"), MB_OK);
}
else
{
        //
        // Success - write in edit control
        //
ClassToString(aVariant[3].uiVal,szMsgBuffer);
        PrintToScreen(szMsgBuffer);

        //
        // Print some of the Message properties.
        //
#ifdef UNICODE
        _stprintf(szMsgBuffer, TEXT("\tLabel: %s"), (WCHAR *)(aVariant[1].pwszVal));
#else
        {
            PCHAR lpLable = UnicodeStringToAnsiString((WCHAR *)(aVariant[1].pwszVal));
            _stprintf(szMsgBuffer, TEXT("\tLabel: %s"), lpLable);
            delete [] lpLable;
        }
#endif
        PrintToScreen(szMsgBuffer);

        //
        // Only if the message is not a falcon message print the body.
        // (this is done since in ACK messages there is no message body).
        //
        if (aVariant[3].bVal == MQMSG_CLASS_NORMAL)
        {
#ifdef UNICODE
            _stprintf(szMsgBuffer, TEXT("\tBody : %s"), (WCHAR *)(aVariant[0].caub.pElems));
#else
            {
                PCHAR pBody = UnicodeStringToAnsiString((WCHAR *)(aVariant[0].caub.pElems));
                _stprintf(szMsgBuffer, TEXT("\tBody : %s"), pBody);
                delete [] pBody;
            }
#endif
            PrintToScreen(szMsgBuffer);
        }

        _stprintf(szMsgBuffer, TEXT("\tPriority : %d"), aVariant[2].bVal);
        PrintToScreen(szMsgBuffer);

//
// Print Sender ID
//
        //
        // See if we're running on NT or Win95.
        //
        OSVERSIONINFO OsVerInfo;

        OsVerInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
        GetVersionEx(&OsVerInfo);

        if (OsVerInfo.dwPlatformId == VER_PLATFORM_WIN32_NT)
        {
            //
            //  On NT
            //
    SID_NAME_USE peUse;
    if (LookupAccountSid(NULL,
     blobBuffer,
     szAccountName,
     &dwActNameSize,
     szDomainName,
     &dwDomNameSize,
     &peUse) )
            {
                _stprintf(szMsgBuffer, TEXT("\tUser: %s\\%s"),
        (WCHAR *)(szDomainName),(WCHAR *)(szAccountName));
        PrintToScreen(szMsgBuffer);
            }
        }
        else
        {
            //
            // LookupAccountSid is not implemented on Win95,
            // instead print textual sid
            //
            if ( GetTextualSid((PSID)blobBuffer, szTextSid, &dwTextSidSize))
            {
                _stprintf(szMsgBuffer, TEXT("\tUser SID : %s"), szTextSid);
        PrintToScreen(szMsgBuffer);
            }
        }
//
// Print "Authenticated" or "Non Authenticated"
//
if (aVariant[4].bVal)
PrintToScreen(TEXT("\tMessage is Authenticated."));
else
PrintToScreen(TEXT("\tMessage is Not Authenticated."));

//
// Print "Encrypted" or "Non Encrypted"
//
if (aVariant[6].ulVal)
PrintToScreen(TEXT("\tMessage is Encrypted."));
else
PrintToScreen(TEXT("\tMessage is Not Encrypted."));
   }

   delete aVariant[ iBodyIndex ].caub.pElems ;
}

/* ************************************************************************ */
/*                               OnApiLocate                                */
/* ************************************************************************ */
/* This function opens a dialog box and ask the user to give a Label. Then  */
/* it locates all the Queues in the DS with a matching label.               */
/* The function updates the PathName Array with those queues.               */
/*                                                                          */
/* Uses: MQLocateBegin, MQLocateNext, MQLocateEnd,                          */
/*       MQInstanceToFormatName, MQFreeMemory.                              */
/* ************************************************************************ */
void CMainFrame::OnApiLocate()
{
    // TODO: Add your command handler code here
    TCHAR szMsgBuffer[BUFFERSIZE];
    TCHAR szLabelBuffer[BUFFERSIZE];

    HRESULT hr;

    MQPROPERTYRESTRICTION PropertyRestriction;
    MQRESTRICTION  Restriction;
    MQCOLUMNSET    Column;
    QUEUEPROPID    aPropId[2]; // only two properties to retrieve.
    HANDLE         hEnum;
    DWORD       cQueue;
    MQPROPVARIANT   aPropVar[MAX_VAR] = {0};
    ARRAYQ*         pArrayQ;
    DWORD       i;
    DWORD           dwColumnCount = 0;
    DWORD dwFormatNameLength = MAX_Q_FORMATNAME_LEN;

    CLocateDialog LocateDialog;

    //
    // Display the ReceiveMessage dialog.
    //
    if (LocateDialog.DoModal() == IDCANCEL)
    {
        return;
    }

    //
    // Retrieve the label from the dialog box.
    //
    LocateDialog.GetLabel(szLabelBuffer);

    //
    // Clean the PathNameArray before locate.
    //
    CleanPathNameArray();

    //
    // Prepare Parameters to locate a queue.
    //

    //
    // Prepare property restriction.
    // Restriction = All queue with PROPID_Q_LABEL equal to "MQ API test".
    //
    PropertyRestriction.rel = PREQ;
    PropertyRestriction.prop = PROPID_Q_LABEL;
    PropertyRestriction.prval.vt = VT_LPWSTR;
#ifdef UNICODE
    PropertyRestriction.prval.pwszVal = szLabelBuffer;
#else
    DWORD size = _tcslen(szLabelBuffer) +1;
    PropertyRestriction.prval.pwszVal = new WCHAR[size];
    AnsiStringToUnicode(PropertyRestriction.prval.pwszVal, szLabelBuffer,size);
#endif

    //
    // prepare a restriction with one property restriction.
    //
    Restriction.cRes = 1;
    Restriction.paPropRes = &PropertyRestriction;

    //
    // Columset (In other words what property I want to retrieve).
    // Only the PathName is important.
    //
    aPropId[dwColumnCount] = PROPID_Q_PATHNAME;
    dwColumnCount++;

    aPropId[dwColumnCount] = PROPID_Q_INSTANCE;
    dwColumnCount++;

    Column.cCol = dwColumnCount;
    Column.aCol = aPropId;

    //
    // Locate the queues. Issue the query
    //
    hr = MQLocateBegin(
            NULL,           //start search at the top.
            &Restriction,   //Restriction
            &Column,        //ColumnSet
            NULL,           //No sort order
            &hEnum          //Enumeration Handle
            );

    if(FAILED(hr))
    {
        //
        // Error - display message
        //
        _stprintf(szMsgBuffer,
            TEXT("MQLocateBegin failed, Error code = 0x%x."),hr);
        MessageBox(szMsgBuffer, TEXT("ERROR"), MB_OK);
        return;
    }

    //
    // Get the results.
    //
    cQueue = MAX_VAR;

    //
    // If cQueue == 0 it means that no Variants were retrieved in the last MQLocateNext.
    //
    while (cQueue != 0)
    {
        hr = MQLocateNext(
                hEnum,      // handle returned by MQLocateBegin.
                &cQueue,    // size of aPropVar array.
                aPropVar    // OUT: an array of MQPROPVARIANT to get the results in.
                );

        if(FAILED(hr))
        {
            //
            // Error - display message
            //
            _stprintf(szMsgBuffer,
                TEXT("MQLocateNext failed, Error code = 0x%x."),hr);
            MessageBox(szMsgBuffer, TEXT("ERROR"), MB_OK);
            return;
        }

        for (i=0; i<cQueue; i++)
        {
            //
            // add the new path names to the path name array.
            //
            pArrayQ = new ARRAYQ;
#ifdef UNICODE
            wcsncpy (pArrayQ->szPathName, aPropVar[i].pwszVal, MAX_Q_PATHNAME_LEN);
#else
            size_t rc = wcstombs(pArrayQ->szPathName, aPropVar[i].pwszVal, MAX_Q_PATHNAME_LEN);
            ASSERT(rc != (size_t)(-1));

#endif
            //
            // move to the next property.
            //
            i = i + 1;

            //
            // Get the FormatName of the queue and set it in the PathName array.
            //
#ifdef UNICODE
            hr = MQInstanceToFormatName(aPropVar[i].puuid, pArrayQ->szFormatName, &dwFormatNameLength);
#else
            WCHAR szwFormatNameBuffer[MAX_Q_FORMATNAME_LEN];
            hr = MQInstanceToFormatName(aPropVar[i].puuid, szwFormatNameBuffer, &dwFormatNameLength);
            if (SUCCEEDED(hr))
            {
                size_t rwc =wcstombs(pArrayQ->szFormatName, szwFormatNameBuffer, dwFormatNameLength);
                ASSERT(rwc != (size_t)(-1));
            }
#endif

            if(FAILED(hr))
            {
                //
                // Error - display message
                //
                _stprintf (szMsgBuffer,
                    TEXT("MQGUIDToFormatName failed, Error code = 0x%x."),hr);
                MessageBox(szMsgBuffer, TEXT("ERROR"), MB_OK);
            }

            //
            // Free the memory allocated by MSMQ
            //
            MQFreeMemory(aPropVar[i].pwszVal);

            //
            // Add the new Queue to the PathNameArray.
            //
            Add2PathNameArray(pArrayQ);
        }
    }

    //
    // End the locate operation.
    //
    hr = MQLocateEnd(hEnum);   // handle returned by MQLocateBegin.
    if(FAILED(hr))
    {
        //
        // Error - display message
        //
        _stprintf (szMsgBuffer,
            TEXT("MQLocateEnd failed, Error code = 0x%x."),hr);
        MessageBox(szMsgBuffer, TEXT("ERROR"), MB_OK);
        return;
    }

    //
    // Display the Queues found on the locate.
    //
    _stprintf (szMsgBuffer, TEXT("Locate Operation completed successfully"));
    PrintToScreen(szMsgBuffer);
    UpdatePathNameArrays();
    DisplayPathNameArray();
    DisplayOpenedQueuePathNameArray();
}

/* ************************************************************************ */
/*                           OnUpdateFrameTitle                             */
/* ************************************************************************ */
void CMainFrame::OnUpdateFrameTitle(BOOL bAddToTitle)
{
    SetWindowText (TEXT("MQ API test"));
}


MAINFRM.H

// MainFrm.h : interface of the CMainFrame class
//
/////////////////////////////////////////////////////////////////////////////

class CMainFrame : public CFrameWnd
{
protected: // create from serialization only
CMainFrame();
DECLARE_DYNCREATE(CMainFrame)

// Attributes
public:

// Operations
public:

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CMainFrame)
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
//}}AFX_VIRTUAL

// Implementation
public:
virtual ~CMainFrame();
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif

protected:  // control bar embedded members
CStatusBar  m_wndStatusBar;
CToolBar    m_wndToolBar;

// Generated message map functions
protected:
//{{AFX_MSG(CMainFrame)
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnApiCreateQueue();
afx_msg void OnApiDeleteQueue();
afx_msg void OnApiOpenQueue();
afx_msg void OnApiCloseQueue();
afx_msg void OnApiSendMessage();
afx_msg void OnApiReceiveMessage();
afx_msg void OnApiLocate();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()

protected:

    void OnUpdateFrameTitle(BOOL bAddToTitle);

protected:
//
// An array to hold the pathnames of the queues that were created so far.
//

    //
    // Holds information about all "located" queues
    //
CArray<ARRAYQ*, ARRAYQ*> m_PathNameArray;

    //
    // Holds information about all "opened" queues
    //
CArray<ARRAYQ*, ARRAYQ*> m_OpenedQueuePathNameArray;

public:

//
// Utility function to display status string based on received msg Class property
//
void ClassToString(unsigned short MsgClass,LPTSTR pszStatus);

//
// functions to manipulate the path names array.
//

    //
    // Adds a given ARRAYQ pointer to the PathName array.
    //
void Add2PathNameArray(ARRAYQ* NewPath) {m_PathNameArray.Add(NewPath);}
    //
    // Removes an ARRAYQ pointer whose pathname matches
    // the given pathname form the PathName array.
    //
ARRAYQ* RemoveFromPathNameArray(TCHAR szPathName[MAX_Q_PATHNAME_LEN]);
    //
    // Empties the PathName array and frees all allocated memory.
    //
void CleanPathNameArray();
    //
    // Looks for an ARRAYQ pointer whose pathname matches the given one,
    // and returns the formatname of that ARRAYQ.
    //
    BOOL TranslatePathNameToFormatName(TCHAR szPathName[MAX_Q_PATHNAME_LEN],
                                       TCHAR szFormatName[MAX_Q_FORMATNAME_LEN]);
    //
    // Prints to screen the pathname of all ARRAYQ's in the PathName array.
    //
    void DisplayPathNameArray();

//
// functions to manipulate the opened queues path name array.
//

    //
    // Adds a given ARRAYQ pointer to the Opened Queue PathName array.
    //
    void Add2OpenedQueuePathNameArray(ARRAYQ* NewPath)
{m_OpenedQueuePathNameArray.Add(NewPath);}
    //
    // Removes an ARRAYQ pointer whose pathname matches
    // the given pathname form the Opened Queue PathName array.
    //
ARRAYQ* RemoveFromOpenedQueuePathNameArray(TCHAR szPathName[MAX_Q_PATHNAME_LEN]);
    //
    // Checks if the Opened Queue PathName array is Empty.
    //
BOOL IsOpenedQueueArrayEmpty();
    //
    // Looks for an ARRAYQ pointer whose pathname matches the given one,
    // and returns the formatname of that ARRAYQ.
    //
    BOOL TranslateOpenedQueuePathNameToFormatName(
                                           TCHAR szPathName[MAX_Q_PATHNAME_LEN],
                                           TCHAR szFormatName[MAX_Q_FORMATNAME_LEN]);
    //
    // Looks for an ARRAYQ pointer whose pathname matches the given one,
    // and returns the Queue Handle of that ARRAYQ.
    //
BOOL GetQueueHandle(TCHAR szPathName[MAX_Q_PATHNAME_LEN],
 QUEUEHANDLE* phClosedQueueHandle);
    //
    // Prints to screen the pathname of all ARRAYQ's in the PathName array.
    //
    void DisplayOpenedQueuePathNameArray();

//
// functions to manipulate both of the queues.
//

    //
    // Moves an ARRAYQ pointer from the PathName array to the Opened Queue PathName array.
    //
    void MoveToOpenedQueuePathNameArray(TCHAR szPathName[MAX_Q_PATHNAME_LEN],
                                           QUEUEHANDLE hQueue, DWORD dwAccess);
    //
    // Moves an ARRAYQ pointer from the Opened Queue PathName array array to the PathName.
    //
void MoveToPathNameArray(TCHAR szPathName[MAX_Q_PATHNAME_LEN]);
    //
    // Looks for ARRAYQ's that are found in both arrays
    // and removes them from the PathName array.
    //
    void UpdatePathNameArrays();

};

extern "C" void  PrintToScreen(const TCHAR * Format, ...);
extern CWnd* pMainView;

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


MQAPITST.CPP

// test.cpp : Defines the class behaviors for the application.
//

#include "stdafx.h"
#include "MQApitst.h"

#include "MainFrm.h"
#include "testDoc.h"
#include "testView.h"

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

/////////////////////////////////////////////////////////////////////////////
// CTestApp

BEGIN_MESSAGE_MAP(CTestApp, CWinApp)
//{{AFX_MSG_MAP(CTestApp)
ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
// NOTE - the ClassWizard will add and remove mapping macros here.
//    DO NOT EDIT what you see in these blocks of generated code!
//}}AFX_MSG_MAP
// Standard file based document commands
ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew)
ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CTestApp construction

CTestApp::CTestApp()
{
// TODO: add construction code here,
// Place all significant initialization in InitInstance
}

/////////////////////////////////////////////////////////////////////////////
// The one and only CTestApp object

CTestApp theApp;

/////////////////////////////////////////////////////////////////////////////
// CTestApp initialization

BOOL CTestApp::InitInstance()
{
// Standard initialization
// If you are not using these features and wish to reduce the size
//  of your final executable, you should remove from the following
//  the specific initialization routines 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

LoadStdProfileSettings();  // Load standard INI file options (including MRU)

// Register the application's document templates.  Document templates
//  serve as the connection between documents, frame windows and views.

CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CTestDoc),
RUNTIME_CLASS(CMainFrame),       // main SDI frame window
RUNTIME_CLASS(CTestView));
AddDocTemplate(pDocTemplate);

// Parse command line for standard shell commands, DDE, file open
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);

// Dispatch commands specified on the command line
if (!ProcessShellCommand(cmdInfo))
return FALSE;

return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About

class CAboutDlg : public CDialog
{
public:
CAboutDlg();

// Dialog Data
//{{AFX_DATA(CAboutDlg)
enum { IDD = IDD_ABOUTBOX };
//}}AFX_DATA

// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CAboutDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
//}}AFX_VIRTUAL

// Implementation
protected:
//{{AFX_MSG(CAboutDlg)
// No message handlers
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
//{{AFX_DATA_INIT(CAboutDlg)
//}}AFX_DATA_INIT
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CAboutDlg)
//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
//{{AFX_MSG_MAP(CAboutDlg)
// No message handlers
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

// App command to run the dialog
void CTestApp::OnAppAbout()
{
CAboutDlg aboutDlg;
aboutDlg.DoModal();
}

/////////////////////////////////////////////////////////////////////////////
// CTestApp commands

//
// Pointer to hold the main window pointer.
//
CWnd* pMainView;

extern "C" void  PrintToScreen(const TCHAR * Format, ...)
{

   TCHAR szText[500];
   va_list l;
   static len = 0;

    //Format the string
    va_start(l, Format);
#ifdef UNICODE
    vswprintf(szText, Format, l);
#else
    vsprintf(szText, Format, l);
#endif

   CEdit& rfMainEdit = ((CTestView*)pMainView)->GetEditCtrl();

int i = rfMainEdit.GetLineCount();

((CTestView*)pMainView)->GetEditCtrl().SetSel(INT_MAX,INT_MAX);
((CTestView*)pMainView)->GetEditCtrl().ReplaceSel(szText);
((CTestView*)pMainView)->GetEditCtrl().ReplaceSel(TEXT("\r\n"));
len += lstrlen(szText) + 2;
((CTestView*)pMainView)->GetDocument()->SetModifiedFlag(FALSE);
}


MQAPITST.H

// test.h : main header file for the TEST application
//

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

#include "resource.h"       // main symbols

#include "mq.h"

/////////////////////////////////////////////////////////////////////////////
// CTestApp:
// See test.cpp for the implementation of this class
//

class CTestApp : public CWinApp
{
public:
CTestApp();

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CTestApp)
public:
virtual BOOL InitInstance();
//}}AFX_VIRTUAL

// Implementation

//{{AFX_MSG(CTestApp)
afx_msg void OnAppAbout();
// 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()
};

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

#define BUFFERSIZE 256
#define MAX_Q_PATHNAME_LEN 256
#define MAX_Q_FORMATNAME_LEN 256
#define DEFAULT_M_TIMETOREACHQUEUE -1
#define DEFAULT_M_TIMETOBERECEIVED -1
#define MAX_VAR 20

//
// A structure for the array of queues that the application handles.
//
typedef struct {
TCHAR szPathName[MAX_Q_PATHNAME_LEN];     // holds the Queue path name.
TCHAR szFormatName[MAX_Q_FORMATNAME_LEN]; // holds the Queue format name.
QUEUEHANDLE hHandle;                         // a handle for an open Queue.
DWORD dwAccess;                              // access for the queue.
} ARRAYQ;


OPENQDLG.CPP

// OpenQDlg.cpp : implementation file
//

#include "stdafx.h"
#include "MQApitst.h"
#include "OpenQDlg.h"

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

/////////////////////////////////////////////////////////////////////////////
// COpenQueueDialog dialog

COpenQueueDialog::COpenQueueDialog(CArray <ARRAYQ*, ARRAYQ*>* pStrArray, CWnd* pParent /*=NULL*/)
: CDialog(COpenQueueDialog::IDD, pParent)
{
m_pStrArray = pStrArray;

//{{AFX_DATA_INIT(COpenQueueDialog)
m_bReceiveAccessFlag = FALSE;
m_bPeekAccessFlag = FALSE;
m_SendAccessFlag = FALSE;
m_szPathName = _T("");
//}}AFX_DATA_INIT
}

void COpenQueueDialog::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(COpenQueueDialog)
DDX_Control(pDX, IDC_PATHNAME_COMBO, m_PathNameCB);
DDX_Check(pDX, IDC_IDC_MQ_RECEIVE_ACCESS, m_bReceiveAccessFlag);
DDX_Check(pDX, IDC_MQ_PEEK_ACCESS, m_bPeekAccessFlag);
DDX_Check(pDX, IDC_MQ_SEND_ACCESS, m_SendAccessFlag);
DDX_CBString(pDX, IDC_PATHNAME_COMBO, m_szPathName);
DDV_MaxChars(pDX, m_szPathName, 128);
//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(COpenQueueDialog, CDialog)
//{{AFX_MSG_MAP(COpenQueueDialog)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// COpenQueueDialog message handlers

BOOL COpenQueueDialog::OnInitDialog()
{
CDialog::OnInitDialog();

// TODO: Add extra initialization here

int i;

for  (i=0 ; i<m_pStrArray->GetSize() ; i++)
VERIFY (m_PathNameCB.AddString((*m_pStrArray)[i]->szPathName) != CB_ERR);

    if (m_PathNameCB.GetCount() > 0) m_PathNameCB.SetCurSel(0);

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


OPENQDLG.H

// OpenQDlg.h : header file
//

/////////////////////////////////////////////////////////////////////////////
// COpenQueueDialog dialog

class COpenQueueDialog : public CDialog
{
// Construction
public:
COpenQueueDialog(CArray <ARRAYQ*, ARRAYQ*>*, CWnd* pParent = NULL);   // standard constructor

// Dialog Data
//{{AFX_DATA(COpenQueueDialog)
enum { IDD = IDD_OPEN_QUEUE_DIALOG };
CComboBoxm_PathNameCB;
BOOLm_bReceiveAccessFlag;
BOOLm_bPeekAccessFlag;
BOOLm_SendAccessFlag;
CStringm_szPathName;
//}}AFX_DATA

/* pointer to the array with the strings for the combo box (Queues PathName). */
CArray <ARRAYQ*, ARRAYQ*>* m_pStrArray ;

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(COpenQueueDialog)
protected:
virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
//}}AFX_VIRTUAL

// Implementation
protected:

// Generated message map functions
//{{AFX_MSG(COpenQueueDialog)
virtual BOOL OnInitDialog();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()

protected:

public:
// BUGBUG - set the 256 to BUFFERSIZE definition
void GetPathName(TCHAR szPathName[256])
{
_tcscpy (szPathName, m_szPathName);
}

DWORD GetAccess()
{
return (
((m_bReceiveAccessFlag) ? MQ_RECEIVE_ACCESS : 0) |
((m_bPeekAccessFlag)? MQ_PEEK_ACCESS: 0) |
((m_SendAccessFlag)? MQ_SEND_ACCESS: 0)
);
}
};


RECVMDLG.CPP

// RecvMDlg.cpp : implementation file
//

#include "stdafx.h"
#include "MQApitst.h"
#include "RecvMDlg.h"

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

/////////////////////////////////////////////////////////////////////////////
// CReceiveMessageDialog dialog

CReceiveMessageDialog::CReceiveMessageDialog(CArray <ARRAYQ*, ARRAYQ*>* pStrArray, CWnd* pParent /*=NULL*/)
   : CDialog(CReceiveMessageDialog::IDD, pParent)
{
   m_pStrArray = pStrArray;

   //{{AFX_DATA_INIT(CReceiveMessageDialog)
   m_szPathName = _T("");
   m_iTimeout = 20000;
m_dwBodySize = BUFFERSIZE;
//}}AFX_DATA_INIT
}

void CReceiveMessageDialog::DoDataExchange(CDataExchange* pDX)
{
   CDialog::DoDataExchange(pDX);
   //{{AFX_DATA_MAP(CReceiveMessageDialog)
   DDX_Control(pDX, IDC_COMBO, m_PathNameCB);
   DDX_CBString(pDX, IDC_COMBO, m_szPathName);
   DDV_MaxChars(pDX, m_szPathName, 128);
   DDX_Text(pDX, IDC_TIMEOUT, m_iTimeout);
DDX_Text(pDX, IDC_EDT_BODY_SIZE, m_dwBodySize);
DDV_MinMaxDWord(pDX, m_dwBodySize, 1, 100000000);
//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CReceiveMessageDialog, CDialog)
   //{{AFX_MSG_MAP(CReceiveMessageDialog)
   //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CReceiveMessageDialog message handlers

BOOL CReceiveMessageDialog::OnInitDialog()
{
   CDialog::OnInitDialog();

   // TODO: Add extra initialization here

   int i;

   for  (i=0 ; i<m_pStrArray->GetSize() ; i++)
   {
      if (((*m_pStrArray)[i]->dwAccess & MQ_RECEIVE_ACCESS) != FALSE)
      {
         VERIFY (m_PathNameCB.AddString((*m_pStrArray)[i]->szPathName) != CB_ERR);
      }
   }

    if (m_PathNameCB.GetCount() > 0) m_PathNameCB.SetCurSel(0);

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


RECVMDLG.H

// RecvMDlg.h : header file
//

/////////////////////////////////////////////////////////////////////////////
// CReceiveMessageDialog dialog

class CReceiveMessageDialog : public CDialog
{
// Construction
public:
CReceiveMessageDialog(CArray <ARRAYQ*, ARRAYQ*>*, CWnd* pParent = NULL);   // standard constructor

// Dialog Data
//{{AFX_DATA(CReceiveMessageDialog)
enum { IDD = IDD_RECEIVE_MESSAGE_DIALOG };
CComboBoxm_PathNameCB;
CStringm_szPathName;
intm_iTimeout;
DWORDm_dwBodySize;
//}}AFX_DATA

/* pointer to the array with the strings for the combo box (Queues PathName). */
CArray <ARRAYQ*, ARRAYQ*>* m_pStrArray ;

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CReceiveMessageDialog)
protected:
virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
//}}AFX_VIRTUAL

// Implementation
protected:

// Generated message map functions
//{{AFX_MSG(CReceiveMessageDialog)
virtual BOOL OnInitDialog();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()

public:

// BUGBUG - set the 256 to BUFFERSIZE definition
void GetPathName(TCHAR szPathName[256])
{
_tcscpy (szPathName, m_szPathName);
}

DWORD GetTimeout()
{
      if (m_iTimeout < 0)
      {
         m_iTimeout = INFINITE;
      }
return (m_iTimeout);
}

DWORD GetBodySize()
{
      if (m_dwBodySize == 0)
      {
         m_dwBodySize = BUFFERSIZE ;
      }
return (m_dwBodySize) ;
}

};


RECWDLG.CPP

// RecWDlg.cpp : implementation file
//

#include "stdafx.h"
#include "MQApitst.h"
#include "RecWDlg.h"

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

/////////////////////////////////////////////////////////////////////////////
// CReceiveWaitDialog dialog

CReceiveWaitDialog::CReceiveWaitDialog(CWnd* pParent /*=NULL*/)
: CDialog(CReceiveWaitDialog::IDD, pParent)
{
//{{AFX_DATA_INIT(CReceiveWaitDialog)
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
}

void CReceiveWaitDialog::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CReceiveWaitDialog)
// NOTE: the ClassWizard will add DDX and DDV calls here
//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CReceiveWaitDialog, CDialog)
//{{AFX_MSG_MAP(CReceiveWaitDialog)
// NOTE: the ClassWizard will add message map macros here
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CReceiveWaitDialog message handlers

void CReceiveWaitDialog::CenterWindow()
{
    CWnd * pParent = GetParent();
    RECT parentRect;
    RECT childRect;

    pParent->GetWindowRect(&parentRect);
    int dxP = parentRect.right-parentRect.left;
    int dyP = parentRect.bottom - parentRect.top;

    GetWindowRect(&childRect);
    int dxC = childRect.right-childRect.left;
    int dyC = childRect.bottom - childRect.top;

    childRect.left = dxP/2-dxC/2;
    childRect.right = childRect.left+dxC;

    childRect.top = dyP/2-dyC/2;
    childRect.bottom = childRect.top+dyC;

    MoveWindow(&childRect);
};


RECWDLG.H

// RecWDlg.h : header file
//

/////////////////////////////////////////////////////////////////////////////
// CReceiveWaitDialog dialog

class CReceiveWaitDialog : public CDialog
{
// Construction
public:
CReceiveWaitDialog(CWnd* pParent = NULL);   // standard constructor
    void CenterWindow();

// Dialog Data
//{{AFX_DATA(CReceiveWaitDialog)
enum { IDD = IDD_WAIT_DIALOG };
// NOTE: the ClassWizard will add data members here
//}}AFX_DATA

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CReceiveWaitDialog)
protected:
virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
//}}AFX_VIRTUAL

// Implementation
protected:

// Generated message map functions
//{{AFX_MSG(CReceiveWaitDialog)
// NOTE: the ClassWizard will add member functions here
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by MQApitst.rc
//
#define IDD_ABOUTBOX                    100
#define IDR_MAINFRAME                   128
#define IDR_TESTTYPE                    129
#define IDD_CREATE_QUEUE_DIALOG         130
#define IDD_DELETE_QUEUE_DIALOG         131
#define IDD_OPEN_QUEUE_DIALOG           132
#define IDD_CLOSE_QUEUE_DIALOG          133
#define IDD_SEND_MESSAGE_DIALOG         134
#define IDD_RECEIVE_MESSAGE_DIALOG      135
#define IDD_WAIT_DIALOG                 136
#define IDD_LOCATE_DIALOG               137
#define IDC_QUEUE_PATHNAME              1000
#define IDC_QUEUE_LABEL                 1001
#define IDC_DELETE_QUEUE_COMBO          1003
#define IDC_MQ_PEEK_ACCESS              1007
#define IDC_MQ_SEND_ACCESS              1008
#define IDC_IDC_MQ_RECEIVE_ACCESS       1009
#define IDC_PATHNAME_COMBO              1010
#define IDC_COMBO                       1011
#define IDC_ADMIN_COMBO                 1012
#define IDC_MESSAGE_PRIORITY            1013
#define IDC_DELIVERY_EXPRESS_RADIO      1014
#define IDC_DELIVERY_RECOVERABLE_RADIO  1015
#define IDC_ACK_FULL_RADIO              1016
#define IDC_ACK_FULL_REACH_QUEUE_RADIO  1016
#define IDC_ACK_NEG_RADIO               1017
#define IDC_ACK_FULL_RECEIVE_RADIO      1017
#define IDC_ACK_NONE_RADIO              1018
#define IDC_LABEL                       1020
#define IDC_BODY                        1021
#define IDC_TIMEOUT                     1022
#define IDC_TARGET_COMBO                1024
#define IDC_LOCATE_LABEL                1025
#define IDC_JOURNAL                     1026
#define IDC_DEAD_LETTER                 1027
#define IDC_AUTHENTICATED               1029
#define IDC_TIME_TO_BE_RECEIVED         1030
#define IDC_NACK_REACH_QUEUE_RADIO      1031
#define IDC_NACK_RECEIVE_RADIO          1032
#define IDC_TIME_TO_REACH_QUEUE         1033
#define IDC_EDIT1                       1035
#define IDC_RADIO3                      1036
#define IDC_EDT_BODY_SIZE               1037
#define IDC_ENCRYPTED                   1038
#define ID_API_CREATE_QUEUE             32771
#define ID_API_DELETE_QUEUE             32772
#define ID_API_OPEN_QUEUE               32773
#define ID_API_CLOSE_QUEUE              32774
#define ID_API_SEND_MESSAGE             32775
#define ID_API_RECEIVE_MESSAGE          32776
#define ID_API_LOCATE                   32777

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_3D_CONTROLS                     1
#define _APS_NEXT_RESOURCE_VALUE        141
#define _APS_NEXT_COMMAND_VALUE         32785
#define _APS_NEXT_CONTROL_VALUE         1039
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif


SENDMDLG.CPP

// SendMDlg.cpp : implementation file
//

#include "stdafx.h"
#include "MQApitst.h"
#include "SendMDlg.h"

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

/////////////////////////////////////////////////////////////////////////////
// CSendMessageDialog dialog

CSendMessageDialog::CSendMessageDialog(CArray<ARRAYQ*, ARRAYQ*>* pStrArray, CWnd* pParent /*=NULL*/)
: CDialog(CSendMessageDialog::IDD, pParent)
{
m_pStrArray = pStrArray;

//{{AFX_DATA_INIT(CSendMessageDialog)
m_strBody = szLastMessageBody;
m_strLabel = szLastMessageLabel;
m_bPriority = (MQ_MAX_PRIORITY - MQ_MIN_PRIORITY)/2;
m_iAck = MQMSG_ACKNOWLEDGMENT_NONE;
m_iDelivery = MQMSG_DELIVERY_EXPRESS;
m_szPathName = _T("");
m_szAdminPathName = _T("");
m_Journal = FALSE;
m_DeadLetter = FALSE;
m_Authenticated = FALSE;
m_Encrypted = FALSE;
m_dwTimeToReachQueue = (DWORD)DEFAULT_M_TIMETOREACHQUEUE;
m_dwTimeToBeReceived = (DWORD)DEFAULT_M_TIMETOBERECEIVED;
//}}AFX_DATA_INIT
}

void CSendMessageDialog::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CSendMessageDialog)
DDX_Control(pDX, IDC_ADMIN_COMBO, m_AdminPathNameCB);
DDX_Control(pDX, IDC_TARGET_COMBO, m_PathNameCB);
DDX_Text(pDX, IDC_BODY, m_strBody);
DDV_MaxChars(pDX, m_strBody, 128);
DDX_Text(pDX, IDC_LABEL, m_strLabel);
DDV_MaxChars(pDX, m_strLabel, 128);
DDX_Text(pDX, IDC_MESSAGE_PRIORITY, m_bPriority);
DDV_MinMaxByte(pDX, m_bPriority, MQ_MIN_PRIORITY, MQ_MAX_PRIORITY);
DDX_Radio(pDX, IDC_ACK_NONE_RADIO, m_iAck);
DDX_Radio(pDX, IDC_DELIVERY_EXPRESS_RADIO, m_iDelivery);
DDX_CBString(pDX, IDC_TARGET_COMBO, m_szPathName);
DDV_MaxChars(pDX, m_szPathName, 128);
DDX_CBString(pDX, IDC_ADMIN_COMBO, m_szAdminPathName);
DDV_MaxChars(pDX, m_szAdminPathName, 128);
DDX_Check(pDX, IDC_JOURNAL, m_Journal);
DDX_Check(pDX, IDC_DEAD_LETTER, m_DeadLetter);
DDX_Check(pDX, IDC_AUTHENTICATED, m_Authenticated);
DDX_Check(pDX, IDC_ENCRYPTED, m_Encrypted);
DDX_Text(pDX, IDC_TIME_TO_REACH_QUEUE, m_dwTimeToReachQueue);
DDX_Text(pDX, IDC_TIME_TO_BE_RECEIVED, m_dwTimeToBeReceived);
//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CSendMessageDialog, CDialog)
//{{AFX_MSG_MAP(CSendMessageDialog)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CSendMessageDialog message handlers

BOOL CSendMessageDialog::OnInitDialog()
{
CDialog::OnInitDialog();

int i;

// TODO: Add extra initialization here
for  (i=0 ; i<m_pStrArray->GetSize() ; i++)
{
if (((*m_pStrArray)[i]->dwAccess & MQ_SEND_ACCESS) != FALSE)
{
VERIFY (m_PathNameCB.AddString((*m_pStrArray)[i]->szPathName) != CB_ERR);
}
        VERIFY (m_AdminPathNameCB.AddString((*m_pStrArray)[i]->szPathName) != CB_ERR);
}

    //
    // Set the first PathName as default selection.
    //
    if (m_PathNameCB.GetCount() > 0) m_PathNameCB.SetCurSel(0);
    if (m_AdminPathNameCB.GetCount() > 0) m_AdminPathNameCB.SetCurSel(0);

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


SENDMDLG.H

// SendMDlg.h : header file
//

/////////////////////////////////////////////////////////////////////////////
// CSendMessageDialog dialog

class CSendMessageDialog : public CDialog
{
// Construction
public:
CSendMessageDialog(CArray <ARRAYQ*, ARRAYQ*>*, CWnd* pParent = NULL);   // standard constructor

// Dialog Data
//{{AFX_DATA(CSendMessageDialog)
enum { IDD = IDD_SEND_MESSAGE_DIALOG };
CComboBoxm_AdminPathNameCB;
CComboBoxm_PathNameCB;
CStringm_strBody;
CStringm_strLabel;
BYTEm_bPriority;
intm_iAck;
intm_iDelivery;
CStringm_szPathName;
CStringm_szAdminPathName;
BOOLm_Journal;
BOOLm_DeadLetter;
BOOL    m_Authenticated;
BOOL    m_Encrypted;
longm_dwTimeToReachQueue;
long    m_dwTimeToBeReceived;
//}}AFX_DATA

/* pointer to the array with the strings for the combo box (Queues PathName). */
CArray <ARRAYQ*, ARRAYQ*>* m_pStrArray ;

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CSendMessageDialog)
protected:
virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
//}}AFX_VIRTUAL

// Implementation
protected:

// Generated message map functions
//{{AFX_MSG(CSendMessageDialog)
virtual BOOL OnInitDialog();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()

public:
// BUGBUG - set the 256 to BUFFERSIZE definition
void GetPathName(TCHAR szPathName[256])
{
_tcscpy (szPathName, m_szPathName);
}

// BUGBUG - set the 256 to BUFFERSIZE definition
void GetAdminPathName(TCHAR szAdminPathName[256])
{
_tcscpy (szAdminPathName, m_szAdminPathName);
}

unsigned char GetDelivery()
{
return (m_iDelivery);
}

    unsigned char GetJournal()
{
return (m_Journal);
}

    unsigned char GetDeadLetter()
{
return (m_DeadLetter);
}

unsigned char GetAuthenticated()
{
return (m_Authenticated);
}

unsigned char GetEncrypted()
{
return (m_Encrypted);
}

unsigned char GetPriority()
{
return (m_bPriority);
}

unsigned char GetAcknowledge()
{
switch (m_iAck)
{
case 1 : return MQMSG_ACKNOWLEDGMENT_FULL_REACH_QUEUE;
case 2 : return MQMSG_ACKNOWLEDGMENT_FULL_RECEIVE;
case 3 : return MQMSG_ACKNOWLEDGMENT_NACK_REACH_QUEUE;
case 4 : return MQMSG_ACKNOWLEDGMENT_NACK_RECEIVE;
default: return MQMSG_ACKNOWLEDGMENT_NONE;
}
}

void GetMessageBody(TCHAR* pszMessageBodyBuffer)
{
_tcsncpy (pszMessageBodyBuffer, m_strBody, BUFFERSIZE);
}

void GetMessageLabel(TCHAR szMessageLabelBuffer[BUFFERSIZE])
{
_tcsncpy (szMessageLabelBuffer, m_strLabel, BUFFERSIZE);
}

DWORD GetTimeToReachQueue()
{
return (m_dwTimeToReachQueue);
}

DWORD GetTimeToBeReceived()
{
return (m_dwTimeToBeReceived);
}

};

//
// Two buffers to hold the last message label and body.
//
extern TCHAR szLastMessageLabel[BUFFERSIZE];
extern TCHAR szLastMessageBody[BUFFERSIZE];


STDAFX.CPP

// stdafx.cpp : source file that includes just the standard includes
//test.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

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

#include <afxole.h>
#include <afxtempl.h>


TESTDOC.CPP

// testDoc.cpp : implementation of the CTestDoc class
//

#include "stdafx.h"
#include "MQApitst.h"

#include "testDoc.h"

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

/////////////////////////////////////////////////////////////////////////////
// CTestDoc

IMPLEMENT_DYNCREATE(CTestDoc, CDocument)

BEGIN_MESSAGE_MAP(CTestDoc, CDocument)
//{{AFX_MSG_MAP(CTestDoc)
// NOTE - the ClassWizard will add and remove mapping macros here.
//    DO NOT EDIT what you see in these blocks of generated code!
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CTestDoc construction/destruction

CTestDoc::CTestDoc()
{
// TODO: add one-time construction code here

}

CTestDoc::~CTestDoc()
{
}

BOOL CTestDoc::OnNewDocument()
{
if (!CDocument::OnNewDocument())
return FALSE;

((CEditView*)m_viewList.GetHead())->SetWindowText(NULL);

// TODO: add reinitialization code here
// (SDI documents will reuse this document)

return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
// CTestDoc serialization

void CTestDoc::Serialize(CArchive& ar)
{
// CEditView contains an edit control which handles all serialization
((CEditView*)m_viewList.GetHead())->SerializeRaw(ar);
}

/////////////////////////////////////////////////////////////////////////////
// CTestDoc diagnostics

#ifdef _DEBUG
void CTestDoc::AssertValid() const
{
CDocument::AssertValid();
}

void CTestDoc::Dump(CDumpContext& dc) const
{
CDocument::Dump(dc);
}
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CTestDoc commands


TESTDOC.H

// testDoc.h : interface of the CTestDoc class
//
/////////////////////////////////////////////////////////////////////////////

class CTestDoc : public CDocument
{
protected: // create from serialization only
CTestDoc();
DECLARE_DYNCREATE(CTestDoc)

// Attributes
public:

// Operations
public:

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CTestDoc)
public:
virtual BOOL OnNewDocument();
virtual void Serialize(CArchive& ar);
//}}AFX_VIRTUAL

// Implementation
public:
virtual ~CTestDoc();
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif

protected:

// Generated message map functions
protected:
//{{AFX_MSG(CTestDoc)
// 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()
};

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


TESTVIEW.CPP

// testView.cpp : implementation of the CTestView class
//

#include "stdafx.h"
#include "MQApitst.h"
#include "mainfrm.h"

#include "testDoc.h"
#include "testView.h"

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

/////////////////////////////////////////////////////////////////////////////
// CTestView

IMPLEMENT_DYNCREATE(CTestView, CEditView)

BEGIN_MESSAGE_MAP(CTestView, CEditView)
//{{AFX_MSG_MAP(CTestView)
ON_WM_CREATE()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CTestView construction/destruction

CTestView::CTestView()
{
// TODO: add construction code here

}

CTestView::~CTestView()
{
}

BOOL CTestView::PreCreateWindow(CREATESTRUCT& cs)
{
// TODO: Modify the Window class or styles here by modifying
//  the CREATESTRUCT cs

BOOL bPreCreated = CEditView::PreCreateWindow(cs);
cs.style &= ~(ES_AUTOHSCROLL|WS_HSCROLL);// Enable word-wrapping

return bPreCreated;
}

/////////////////////////////////////////////////////////////////////////////
// CTestView drawing

void CTestView::OnDraw(CDC* pDC)
{
CTestDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);

// TODO: add draw code for native data here
}

/////////////////////////////////////////////////////////////////////////////
// CTestView diagnostics

#ifdef _DEBUG
void CTestView::AssertValid() const
{
CEditView::AssertValid();
}

void CTestView::Dump(CDumpContext& dc) const
{
CEditView::Dump(dc);
}

CTestDoc* CTestView::GetDocument() // non-debug version is inline
{
ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CTestDoc)));
return (CTestDoc*)m_pDocument;
}
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CTestView message handlers

int CTestView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CEditView::OnCreate(lpCreateStruct) == -1)
return -1;

// TODO: Add your specialized creation code here

pMainView = this;
return 0;
}


TESTVIEW.H

// testView.h : interface of the CTestView class
//
/////////////////////////////////////////////////////////////////////////////

class CTestView : public CEditView
{
protected: // create from serialization only
CTestView();
DECLARE_DYNCREATE(CTestView)

// Attributes
public:
CTestDoc* GetDocument();

// Operations
public:

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CTestView)
public:
virtual void OnDraw(CDC* pDC);  // overridden to draw this view
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
protected:
//}}AFX_VIRTUAL

// Implementation
public:
virtual ~CTestView();
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif

protected:

// Generated message map functions
protected:
//{{AFX_MSG(CTestView)
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};

#ifndef _DEBUG  // debug version in testView.cpp
inline CTestDoc* CTestView::GetDocument()
   { return (CTestDoc*)m_pDocument; }
#endif

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


README.TXT

MQASP sample shows how to perform basic queue operations using MSMQ ActiveX components in IIS server
side scripting with ASP/VBS.

To run the sample:
1. Create a new folder 'msmq' in your InetPub\scripts directory on the machine where II is installed.
Copy the sample files to that folder. The IIS Server machine must have MSMQ client or server
installed in order to use MSMQ ActiveX components.

2. Use Internet Explorer from any client machine to point to http://<yourservername>/scripts/msmq/default.asp.
MSMQ does not need to be installed on the machines running the browser.

For additional information on using MSMQ ActiveX in ASP pages, please see the following article in the
Microsoft Knowledge Base:

   ARTICLE-ID: Q173339
   "TITLE     : FAQ: How to use MSMQ from an ASP Page"

For additional information on using ASP pages, refer to Active Server Pages documentation.


CLOSEQ2.ASP

<%@ LANGUAGE="VBSCRIPT" %>

<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual InterDev 1.0">
<META HTTP-EQUIV="Content-Type" content="text/html; charset=iso-8859-1">
<TITLE>Document Title</TITLE>
</HEAD>
<BODY>

<!-- Insert HTML here -->

<A HREF="Default.asp">[Home]</A>
</BODY>
</HTML>


CREATEQ.ASP

<%@ LANGUAGE="VBSCRIPT" %>

<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual InterDev 1.0">
<META HTTP-EQUIV="Content-Type" content="text/html; charset=iso-8859-1">
<TITLE>MSMQ ASP Sample Create Queue Page</TITLE>
</HEAD>
<BODY>
<H1>MSMQ ASP Sample Create Queue Page</H1>
<H2>Creating Queue : <%= Request.QueryString ( "MachineName" ) & "\" & Request.QueryString ( "QueueName" ) %></H2>
<% Set msmqinfo=CreateObject ( "MSMQ.MSMQQueueInfo" )
msmqinfo.PathName=Request.QueryString ( "MachineName" ) & "\" & Request.QueryString ( "QueueName" )
msmqinfo.Create
Set msmqinfo=Nothing
%>
<A HREF="Default.asp">[Home]</A>
</BODY>
</HTML>


CREATEQ2.ASP

<%@ LANGUAGE="VBSCRIPT" %>

<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual InterDev 1.0">
<META HTTP-EQUIV="Content-Type" content="text/html; charset=iso-8859-1">
<TITLE>MSMQ ASP Sample Create Queue Page - 2</TITLE>
</HEAD>
<BODY>
<H1>MSMQ ASP Sample Create Queue Page - 2</H1>
<H2>Creating Queue : <%= Request.QueryString ( "QueuePath" )%></H2>
<% Set msmqinfo=CreateObject ( "MSMQ.MSMQQueueInfo" )
msmqinfo.strPathName=Request.QueryString ( "QueuePath" )
msmqinfo.Create
Set msmqinfo=Nothing
%>
<A HREF="Default.asp">[Home]</A>
</BODY>
</HTML>


DEFAULT.ASP

<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Developer Studio">
<META HTTP-EQUIV="Content-Type" content="text/html; charset=iso-8859-1">
<TITLE>MSMQ ASP Lab Home Page</TITLE>
    <SCRIPT LANGUAGE="VBScript">

Sub CreateQBtn_OnClick()
Window.location.href = "createq.asp?MachineName=" & MACHNAME.Value & "&QueueName=" & QNAME.Value
End Sub
Sub OpenQBtn_OnClick()
Window.location.href = "openq.asp?MachineName=" & MACHNAME.Value & "&QueueName=" & QNAME.Value
End Sub
Sub SendMsgBtn_OnClick()
Window.location.href = "sendmsg.asp?MachineName=" & <%=Session ("mname")%> & "&QueueName=" & <%=Session ("qname")%> & "&MsgText=" & SendMsgText.Value
End Sub
Sub RecvMsgBtn_OnClick()
Window.location.href = "recvmsg.asp?MachineName=" & MACHNAME.Value & "&QueueName=" & QNAME.Value
End Sub
Sub CloseQBtn_OnClick()
Window.location.href = "closeq.asp?MachineName=" & MACHNAME.Value & "&QueueName=" & QNAME.Value
End Sub
Sub DeleteQBtn_OnClick()
Window.location.href = "deleteq.asp?MachineName=" & MACHNAME.Value & "&QueueName=" & QNAME.Value
End Sub
Sub ListQBtn_OnClick()
Window.location.href = "listq.asp"
End Sub

    </SCRIPT>
</HEAD>
<BODY>
<H1>MSMQ ASP Sample Home Page</H1>

<TABLE>
<TR>
<TD COLSPAN=3>
    <INPUT TYPE=Button VALUE="List Queues" NAME="ListQBtn">
</TD>
</TR>
<TR>
<TD>Queue Name</TD>
<TD>
    <INPUT TYPE=Text VALUE=<%=Session ("qname")%> NAME="QNAME">
</TD>
</TR>
<TR>
<TD>Machine Name</TD>
<TD>
    <INPUT TYPE=Text VALUE=<%=Session ("mname")%> NAME="MACHNAME">
</TD>
</TR>
<TR>
<TD>
    <INPUT TYPE=Button VALUE="Create Queue" NAME="CreateQBtn">
</TD>
<TD>
    <INPUT TYPE=Button VALUE="Delete Queue" NAME="DeleteQBtn">
</TD>
</TR>
<TR>
</TR>
<TR>
<TD>
    <INPUT TYPE=Button VALUE="Open Queue  " NAME="OpenQBtn">
</TD>
<TD>
    <INPUT TYPE=Button VALUE="Close Queue " NAME="CloseQBtn">
</TD>
</TR>
<TR>
</TR>
<TR>
<TD>
    <INPUT TYPE=Button VALUE="Send Message" NAME="SendMsgBtn">
</TD>
<TD><INPUT TYPE=Text VALUE="" NAME="SendMsgText"></TD>
</TR>
<TR>
<TD>
    <INPUT TYPE=Button VALUE="Recv Message" NAME="RecvMsgBtn">
</TD>
</TR>
</TABLE>
</BODY>
</HTML>


DELETEQ.ASP

<%@ LANGUAGE="VBSCRIPT" %>

<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual InterDev 1.0">
<META HTTP-EQUIV="Content-Type" content="text/html; charset=iso-8859-1">
<TITLE>MSMQ Asp Sample Delete Queue Page</TITLE>
</HEAD>
<BODY>
<H1>MSMQ Asp Sample Delete Queue Page</H1>
<H2>Deleting Queue : <%= Request.QueryString ( "MachineName" ) & "\" & Request.QueryString ( "QueueName" ) %></H2>
<% Set msmqinfo=CreateObject ( "MSMQ.MSMQQueueInfo" )
msmqinfo.PathName= Request.QueryString ( "MachineName" ) & "\" & Request.QueryString ( "QueueName" )
msmqinfo.Delete
Set msmqinfo=Nothing
%>
<A HREF="Default.asp">[Home]</A>
</BODY>
</HTML>


DELETEQ2.ASP

<%@ LANGUAGE="VBSCRIPT" %>

<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual InterDev 1.0">
<META HTTP-EQUIV="Content-Type" content="text/html; charset=iso-8859-1">
<TITLE>MSMQ Asp Sample Delete Queue Page - 2</TITLE>
</HEAD>
<BODY>
<H1>MSMQ Asp Sample Delete Queue Page - 2</H1>
<H2>Deleting Queue : <%= Request.QueryString ( "QueuePath" )%></H2>
<% Set msmqinfo=CreateObject ( "MSMQ.MSMQQueueInfo" )
msmqinfo.PathName=Request.QueryString ( "QueuePath" )
msmqinfo.Delete
Set msmqinfo=Nothing
%>
<A HREF="Default.asp">[Home]</A>
</BODY>
</HTML>


LISTQ.ASP

<%@ LANGUAGE="VBSCRIPT" %>

<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual InterDev 1.0">
<META HTTP-EQUIV="Content-Type" content="text/html; charset=iso-8859-1">
<TITLE>Queue List Page</TITLE>
</HEAD>
<BODY>

<A HREF="Default.asp">[Home]</A>
<H1>MSMQ ASP Sample Queue List</H1>
<TABLE BORDER = 2>
<TR>
<TD><B>Machine Name</B></TD>
<TD><B>Queue Name</B></TD>
<TD><B>Open</B></TD>
<TD><B>Delete</B></TD>

<% Set query=CreateObject ( "MSMQ.MSMQQuery" )
Set qinfos=query.LookupQueue
qinfos.reset
set qinfo=qinfos.next
while Not qinfo is Nothing
%>
<TR>
<TD><%= Left ( qinfo.PathName, Instr ( qinfo.PathName, "\" ) - 1) %></TD>
<TD><%= Right ( qinfo.PathName, Len ( qinfo.PathName ) - Instr ( qinfo.PathName, "\" )) %></TD>
<TD><A HREF="openq2.asp?QueuePath=<%= qinfo.PathName %>">Open</A></TD>

<TD><A HREF="deleteq2.asp?QueuePath=<%= qinfo.PathName %>">Delete</A></TD>

</TR>
<%set qinfo=qinfos.next
Wend %>

</TABLE>

</BODY>
</HTML>


OPENQ.ASP

<%@ LANGUAGE="VBSCRIPT" %>

<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual InterDev 1.0">
<META HTTP-EQUIV="Content-Type" content="text/html; charset=iso-8859-1">
<TITLE>MSMQ Asp Sample Open Queue Page</TITLE>
</HEAD>
<BODY>
<H1>MSMQ Asp Sample Open Queue Page</H1>
<H2>Opening Queue : <%= Request.QueryString ( "MachineName" ) & "\" & Request.QueryString ( "QueueName" ) %></H2>
<% Set msmqinfo=CreateObject ( "MSMQ.MSMQQueueInfo" )
msmqinfo.PathName=Request.QueryString ( "MachineName" ) & "\" & Request.QueryString ( "QueueName" )
Set Session ( "SendQueue" ) = msmqinfo.Open ( 2, 0 )
Set Session ( "RecvQueue" ) = msmqinfo.Open ( 1, 0 )
set msmqinfo=Nothing
Session ( "qname" ) =  Request.QueryString ( "QueueName" )
Session ( "mname" ) =  Request.QueryString ( "MachineName" )
%>
<A HREF="Default.asp">[Home]</A>
</BODY>
</HTML>


OPENQ2.ASP

<%@ LANGUAGE="VBSCRIPT" %>

<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual InterDev 1.0">
<META HTTP-EQUIV="Content-Type" content="text/html; charset=iso-8859-1">
<TITLE>MSMQ Asp Sample Open Queue Page - 2</TITLE>
</HEAD>
<BODY>
<H1>MSMQ Asp Sample Open Queue Page - 2</H1>
<A HREF="Default.asp">[Home]</A>
<H2>Opening Queue : <%= Request.QueryString ( "QueuePath" )%></H2>
<% Set msmqinfo=CreateObject ( "MSMQ.MSMQQueueInfo" )
msmqinfo.PathName=Request.QueryString ( "QueuePath" )
Set Session ( "SendQueue" ) = msmqinfo.Open ( 2, 0 )
Set Session ( "RecvQueue" ) = msmqinfo.Open ( 1, 0 )
dim var
var = Request.QueryString ( "QueuePath" )
Session ( "qname" ) =  Right ( var, Len   ( var ) - Instr ( var, "\" ))
Session ( "mname" ) =  Left  ( var, Instr ( var, "\" ) - 1)
set msmqinfo=Nothing
%>
</BODY>
</HTML>


CLOSEQ.ASP

<%@ LANGUAGE="VBSCRIPT" %>

<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual InterDev 1.0">
<META HTTP-EQUIV="Content-Type" content="text/html; charset=iso-8859-1">
<TITLE>MSMQ ASP Sample Close Queue Page</TITLE>
</HEAD>
<BODY>
<H1>MSMQ ASP Sample Close Queue Page</H1>
<H2>Closing open queue</H2>
<% Set qs=Session ( "SendQueue" )
Set qr=Session ( "RecvQueue" )
qs.Close
qr.Close
Set Session ( "SendQueue" )=Nothing
Set Session ( "RecvQueue" )=Nothing
%>
<A HREF="Default.asp">[Home]</A>
</BODY>
</HTML>


RECVMSG.ASP

<%@ LANGUAGE="VBSCRIPT" %>

<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual InterDev 1.0">
<META HTTP-EQUIV="Content-Type" content="text/html; charset=iso-8859-1">
<TITLE>MSMQ Asp Sample Send Message Page</TITLE>
</HEAD>
<BODY>

<H3>Recieved Message ( body ): <HR>
<%

set q=Session ( "RecvQueue" )

set msg=q.Receive (0, true, true, 100 )
if Not msg is Nothing then
Response.Write msg.body
else
%>
*** No message in queue ***
<% end if %>
<HR>
from Queue <%= Session ("mname") & "\" & Session ("qname")%></H1>

<A HREF="Default.asp">[Back]</A>
</BODY>
</HTML>


SENDMSG.ASP

<%@ LANGUAGE="VBSCRIPT" %>

<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual InterDev 1.0">
<META HTTP-EQUIV="Content-Type" content="text/html; charset=iso-8859-1">
<TITLE>MSMQ Asp Sample Send Message Page</TITLE>
</HEAD>
<BODY>
<A HREF="Default.asp">[Back]</A>
<H3>Sending Message ( body ):<HR> <%= Request.QueryString ( "MsgText" )%> <HR>to Queue <%= Session ("mname") & "\" & Session ("qname")%></H1>
<%Set msg=CreateObject ( "MSMQ.MSMQMessage" )

if Not msg Is Nothing Then
msg.Label = "ASP Test Message"

msg.body = CStr ( Request.QueryString ( "MsgText" ))
set q=Session ( "SendQueue" )
msg.Send q, false
else
%>
Error creating Message Object
<% End if %>

</BODY>
</HTML>


README.TXT

MQJava sample shows how to create, open and close MSMQ queues in Java and how to
send and receive messages.

In order to compile the sample code, you must use the latest Java compiler and VM
included in Microsoft Java SDK 2.0.   Before building the sample, run the JActiveX
tool on MSMQ type library ( MQOA.dll ) to generate java wrapper classes for MSMQ
ActiveX components.  To run the sample, open the file msmqjavapp.html in
Internet Explorer.

For a description of JActiveX tool, please refer to the Microsoft SDK for Java 2.0
documentation available at http://www.microsoft.com/java/sdk


README.TXT

With Microsoft Message Queue Server it's possible to send the state of
COM objects supporting the IPersistStream or IPersistStorage interface.
This sample shows how to take advantage of this feature using Visual C++ and ATL


QUEUEDEF.H

//
// Defines the queue label and path
//
#define STR_QUEUE_NAMEL".\\IStreamTest"
#define STR_QUEUE_LABEL L"Object Test Queue"

struct InitOle {
    InitOle() { CoInitialize(NULL); }   // Initialize Component Object Model(COM) library
    ~InitOle() { CoUninitialize(); }// Uninitialize Component Object Model(COM) library
};


DLLDATA.C

/*********************************************************
   DllData file -- generated by MIDL compiler

        DO NOT ALTER THIS FILE

   This file is regenerated by MIDL on every IDL file compile.

   To completely reconstruct this file, delete it and rerun MIDL
   on all the IDL files in this DLL, specifying this file for the
   /dlldata command line option

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

#define PROXY_DELEGATION

#include <rpcproxy.h>

#ifdef __cplusplus
extern "C"   {
#endif

EXTERN_PROXY_FILE( GraphObj )

PROXYFILE_LIST_START
/* Start of list */
  REFERENCE_PROXY_FILE( GraphObj ),
/* End of list */
PROXYFILE_LIST_END

DLLDATA_ROUTINES( aProxyFileList, GET_DLL_CLSID )

#ifdef __cplusplus
}  /*extern "C" */
#endif

/* end of generated dlldata file */


DLLDATAX.C

// wrapper for dlldata.c

#ifdef _MERGE_PROXYSTUB // merge proxy stub DLL

#define REGISTER_PROXY_DLL //DllRegisterServer, etc.

#define _WIN32_WINNT 0x0400//for WinNT 4.0 or Win95 with DCOM
#define USE_STUBLESS_PROXY//defined only with MIDL switch /Oicf

#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 "GraphObj_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

#if !defined(AFX_DLLDATAX_H__BEEDC087_EBD2_11D0_A78F_00805F0D2083__INCLUDED_)
#define AFX_DLLDATAX_H__BEEDC087_EBD2_11D0_A78F_00805F0D2083__INCLUDED_

#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000

#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

//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_DLLDATAX_H__BEEDC087_EBD2_11D0_A78F_00805F0D2083__INCLUDED_)


GRAPHOBJ.CPP

// GraphObj.cpp : Implementation of DLL Exports.

// Note: 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.
//
//If you are not running WinNT4.0 or Win95 with DCOM, then you
//need to remove the following define from dlldatax.c
//#define _WIN32_WINNT 0x0400
//
//Further, if you are running MIDL without /Oicf switch, you also
//need to remove the following define from dlldatax.c.
//#define USE_STUBLESS_PROXY
//
//Modify the custom build rule for GraphObj.idl by adding the following
//files to the Outputs.
//GraphObj_p.c
//dlldata.c
//To build a separate proxy/stub DLL,
//run nmake -f GraphObjps.mk in the project directory.

#include "stdafx.h"
#include "resource.h"
#include "initguid.h"
#include "GraphObj.h"
#include "dlldatax.h"

#include "GraphObj_i.c"
#include "Point.h"
#include "Line.h"

#ifdef _MERGE_PROXYSTUB
extern "C" HINSTANCE hProxyDll;
#endif

CComModule _Module;

BEGIN_OBJECT_MAP(ObjectMap)
OBJECT_ENTRY(CLSID_Point, CPoint)
OBJECT_ENTRY(CLSID_Line, CLine)
END_OBJECT_MAP()

/////////////////////////////////////////////////////////////////////////////
// DLL Entry Point

extern "C"
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
lpReserved;
#ifdef _MERGE_PROXYSTUB
if (!PrxDllMain(hInstance, dwReason, lpReserved))
return FALSE;
#endif
if (dwReason == DLL_PROCESS_ATTACH)
{
_Module.Init(ObjectMap, hInstance);
DisableThreadLibraryCalls(hInstance);
}
else if (dwReason == DLL_PROCESS_DETACH)
_Module.Term();
return TRUE;    // ok
}

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

STDAPI DllCanUnloadNow(void)
{
#ifdef _MERGE_PROXYSTUB
if (PrxDllCanUnloadNow() != S_OK)
return S_FALSE;
#endif
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)
{
#ifdef _MERGE_PROXYSTUB
if (PrxDllGetClassObject(rclsid, riid, ppv) == S_OK)
return S_OK;
#endif
return _Module.GetClassObject(rclsid, riid, ppv);
}

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

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 - Removes entries from the system registry

STDAPI DllUnregisterServer(void)
{
#ifdef _MERGE_PROXYSTUB
PrxDllUnregisterServer();
#endif
_Module.UnregisterServer();
return S_OK;
}


GRAPHOBJ.H

/* this ALWAYS GENERATED file contains the definitions for the interfaces */

/* File created by MIDL compiler version 3.01.75 */
/* at Thu Sep 25 08:51:38 1997
 */
/* Compiler settings for GraphObj.idl:
    Oicf (OptLev=i2), W1, Zp8, env=Win32, ms_ext, c_ext
    error checks: none
*/
//@@MIDL_FILE_HEADING(  )
#include "rpc.h"
#include "rpcndr.h"
#ifndef COM_NO_WINDOWS_H
#include "windows.h"
#include "ole2.h"
#endif /*COM_NO_WINDOWS_H*/

#ifndef __GraphObj_h__
#define __GraphObj_h__

#ifdef __cplusplus
extern "C"{
#endif

/* Forward Declarations */

#ifndef __IPoint_FWD_DEFINED__
#define __IPoint_FWD_DEFINED__
typedef interface IPoint IPoint;
#endif /* __IPoint_FWD_DEFINED__ */

#ifndef __ILine_FWD_DEFINED__
#define __ILine_FWD_DEFINED__
typedef interface ILine ILine;
#endif /* __ILine_FWD_DEFINED__ */

#ifndef __Point_FWD_DEFINED__
#define __Point_FWD_DEFINED__

#ifdef __cplusplus
typedef class Point Point;
#else
typedef struct Point Point;
#endif /* __cplusplus */

#endif /* __Point_FWD_DEFINED__ */

#ifndef __Line_FWD_DEFINED__
#define __Line_FWD_DEFINED__

#ifdef __cplusplus
typedef class Line Line;
#else
typedef struct Line Line;
#endif /* __cplusplus */

#endif /* __Line_FWD_DEFINED__ */

/* header files for imported files */
#include "oaidl.h"
#include "ocidl.h"

void __RPC_FAR * __RPC_USER MIDL_user_allocate(size_t);
void __RPC_USER MIDL_user_free( void __RPC_FAR * );

#ifndef __IPoint_INTERFACE_DEFINED__
#define __IPoint_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IPoint
 * at Thu Sep 25 08:51:38 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][helpstring][dual][uuid][object] */

EXTERN_C const IID IID_IPoint;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("BEEDC08A-EBD2-11D0-A78F-00805F0D2083")
    IPoint : public IDispatch
    {
    public:
        virtual /* [helpstring][id][propget] */ HRESULT STDMETHODCALLTYPE get_x(
            /* [retval][out] */ long __RPC_FAR *pVal) = 0;

        virtual /* [helpstring][id][propput] */ HRESULT STDMETHODCALLTYPE put_x(
            /* [in] */ long newVal) = 0;

        virtual /* [helpstring][id][propget] */ HRESULT STDMETHODCALLTYPE get_y(
            /* [retval][out] */ long __RPC_FAR *pVal) = 0;

        virtual /* [helpstring][id][propput] */ HRESULT STDMETHODCALLTYPE put_y(
            /* [in] */ long newVal) = 0;

    };

#else /* C style interface */

    typedef struct IPointVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IPoint __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IPoint __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IPoint __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeInfoCount )(
            IPoint __RPC_FAR * This,
            /* [out] */ UINT __RPC_FAR *pctinfo);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeInfo )(
            IPoint __RPC_FAR * This,
            /* [in] */ UINT iTInfo,
            /* [in] */ LCID lcid,
            /* [out] */ ITypeInfo __RPC_FAR *__RPC_FAR *ppTInfo);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetIDsOfNames )(
            IPoint __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [size_is][in] */ LPOLESTR __RPC_FAR *rgszNames,
            /* [in] */ UINT cNames,
            /* [in] */ LCID lcid,
            /* [size_is][out] */ DISPID __RPC_FAR *rgDispId);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Invoke )(
            IPoint __RPC_FAR * This,
            /* [in] */ DISPID dispIdMember,
            /* [in] */ REFIID riid,
            /* [in] */ LCID lcid,
            /* [in] */ WORD wFlags,
            /* [out][in] */ DISPPARAMS __RPC_FAR *pDispParams,
            /* [out] */ VARIANT __RPC_FAR *pVarResult,
            /* [out] */ EXCEPINFO __RPC_FAR *pExcepInfo,
            /* [out] */ UINT __RPC_FAR *puArgErr);

        /* [helpstring][id][propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_x )(
            IPoint __RPC_FAR * This,
            /* [retval][out] */ long __RPC_FAR *pVal);

        /* [helpstring][id][propput] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_x )(
            IPoint __RPC_FAR * This,
            /* [in] */ long newVal);

        /* [helpstring][id][propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_y )(
            IPoint __RPC_FAR * This,
            /* [retval][out] */ long __RPC_FAR *pVal);

        /* [helpstring][id][propput] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_y )(
            IPoint __RPC_FAR * This,
            /* [in] */ long newVal);

        END_INTERFACE
    } IPointVtbl;

    interface IPoint
    {
        CONST_VTBL struct IPointVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IPoint_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IPoint_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IPoint_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IPoint_GetTypeInfoCount(This,pctinfo)\
    (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo)

#define IPoint_GetTypeInfo(This,iTInfo,lcid,ppTInfo)\
    (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo)

#define IPoint_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)\
    (This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)

#define IPoint_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)\
    (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)

#define IPoint_get_x(This,pVal)\
    (This)->lpVtbl -> get_x(This,pVal)

#define IPoint_put_x(This,newVal)\
    (This)->lpVtbl -> put_x(This,newVal)

#define IPoint_get_y(This,pVal)\
    (This)->lpVtbl -> get_y(This,pVal)

#define IPoint_put_y(This,newVal)\
    (This)->lpVtbl -> put_y(This,newVal)

#endif /* COBJMACROS */

#endif /* C style interface */

/* [helpstring][id][propget] */ HRESULT STDMETHODCALLTYPE IPoint_get_x_Proxy(
    IPoint __RPC_FAR * This,
    /* [retval][out] */ long __RPC_FAR *pVal);

void __RPC_STUB IPoint_get_x_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

/* [helpstring][id][propput] */ HRESULT STDMETHODCALLTYPE IPoint_put_x_Proxy(
    IPoint __RPC_FAR * This,
    /* [in] */ long newVal);

void __RPC_STUB IPoint_put_x_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

/* [helpstring][id][propget] */ HRESULT STDMETHODCALLTYPE IPoint_get_y_Proxy(
    IPoint __RPC_FAR * This,
    /* [retval][out] */ long __RPC_FAR *pVal);

void __RPC_STUB IPoint_get_y_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

/* [helpstring][id][propput] */ HRESULT STDMETHODCALLTYPE IPoint_put_y_Proxy(
    IPoint __RPC_FAR * This,
    /* [in] */ long newVal);

void __RPC_STUB IPoint_put_y_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IPoint_INTERFACE_DEFINED__ */

#ifndef __ILine_INTERFACE_DEFINED__
#define __ILine_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: ILine
 * at Thu Sep 25 08:51:38 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][helpstring][dual][uuid][object] */

EXTERN_C const IID IID_ILine;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("BEEDC08B-EBD2-11D0-A78F-00805F0D2083")
    ILine : public IDispatch
    {
    public:
        virtual /* [helpstring][id][propget] */ HRESULT STDMETHODCALLTYPE get_x1(
            /* [retval][out] */ long __RPC_FAR *pVal) = 0;

        virtual /* [helpstring][id][propput] */ HRESULT STDMETHODCALLTYPE put_x1(
            /* [in] */ long newVal) = 0;

        virtual /* [helpstring][id][propget] */ HRESULT STDMETHODCALLTYPE get_y1(
            /* [retval][out] */ long __RPC_FAR *pVal) = 0;

        virtual /* [helpstring][id][propput] */ HRESULT STDMETHODCALLTYPE put_y1(
            /* [in] */ long newVal) = 0;

        virtual /* [helpstring][id][propget] */ HRESULT STDMETHODCALLTYPE get_x2(
            /* [retval][out] */ long __RPC_FAR *pVal) = 0;

        virtual /* [helpstring][id][propput] */ HRESULT STDMETHODCALLTYPE put_x2(
            /* [in] */ long newVal) = 0;

        virtual /* [helpstring][id][propget] */ HRESULT STDMETHODCALLTYPE get_y2(
            /* [retval][out] */ long __RPC_FAR *pVal) = 0;

        virtual /* [helpstring][id][propput] */ HRESULT STDMETHODCALLTYPE put_y2(
            /* [in] */ long newVal) = 0;

    };

#else /* C style interface */

    typedef struct ILineVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            ILine __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            ILine __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            ILine __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeInfoCount )(
            ILine __RPC_FAR * This,
            /* [out] */ UINT __RPC_FAR *pctinfo);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeInfo )(
            ILine __RPC_FAR * This,
            /* [in] */ UINT iTInfo,
            /* [in] */ LCID lcid,
            /* [out] */ ITypeInfo __RPC_FAR *__RPC_FAR *ppTInfo);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetIDsOfNames )(
            ILine __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [size_is][in] */ LPOLESTR __RPC_FAR *rgszNames,
            /* [in] */ UINT cNames,
            /* [in] */ LCID lcid,
            /* [size_is][out] */ DISPID __RPC_FAR *rgDispId);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Invoke )(
            ILine __RPC_FAR * This,
            /* [in] */ DISPID dispIdMember,
            /* [in] */ REFIID riid,
            /* [in] */ LCID lcid,
            /* [in] */ WORD wFlags,
            /* [out][in] */ DISPPARAMS __RPC_FAR *pDispParams,
            /* [out] */ VARIANT __RPC_FAR *pVarResult,
            /* [out] */ EXCEPINFO __RPC_FAR *pExcepInfo,
            /* [out] */ UINT __RPC_FAR *puArgErr);

        /* [helpstring][id][propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_x1 )(
            ILine __RPC_FAR * This,
            /* [retval][out] */ long __RPC_FAR *pVal);

        /* [helpstring][id][propput] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_x1 )(
            ILine __RPC_FAR * This,
            /* [in] */ long newVal);

        /* [helpstring][id][propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_y1 )(
            ILine __RPC_FAR * This,
            /* [retval][out] */ long __RPC_FAR *pVal);

        /* [helpstring][id][propput] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_y1 )(
            ILine __RPC_FAR * This,
            /* [in] */ long newVal);

        /* [helpstring][id][propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_x2 )(
            ILine __RPC_FAR * This,
            /* [retval][out] */ long __RPC_FAR *pVal);

        /* [helpstring][id][propput] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_x2 )(
            ILine __RPC_FAR * This,
            /* [in] */ long newVal);

        /* [helpstring][id][propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_y2 )(
            ILine __RPC_FAR * This,
            /* [retval][out] */ long __RPC_FAR *pVal);

        /* [helpstring][id][propput] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_y2 )(
            ILine __RPC_FAR * This,
            /* [in] */ long newVal);

        END_INTERFACE
    } ILineVtbl;

    interface ILine
    {
        CONST_VTBL struct ILineVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define ILine_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define ILine_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define ILine_Release(This)\
    (This)->lpVtbl -> Release(This)

#define ILine_GetTypeInfoCount(This,pctinfo)\
    (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo)

#define ILine_GetTypeInfo(This,iTInfo,lcid,ppTInfo)\
    (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo)

#define ILine_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)\
    (This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)

#define ILine_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)\
    (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)

#define ILine_get_x1(This,pVal)\
    (This)->lpVtbl -> get_x1(This,pVal)

#define ILine_put_x1(This,newVal)\
    (This)->lpVtbl -> put_x1(This,newVal)

#define ILine_get_y1(This,pVal)\
    (This)->lpVtbl -> get_y1(This,pVal)

#define ILine_put_y1(This,newVal)\
    (This)->lpVtbl -> put_y1(This,newVal)

#define ILine_get_x2(This,pVal)\
    (This)->lpVtbl -> get_x2(This,pVal)

#define ILine_put_x2(This,newVal)\
    (This)->lpVtbl -> put_x2(This,newVal)

#define ILine_get_y2(This,pVal)\
    (This)->lpVtbl -> get_y2(This,pVal)

#define ILine_put_y2(This,newVal)\
    (This)->lpVtbl -> put_y2(This,newVal)

#endif /* COBJMACROS */

#endif /* C style interface */

/* [helpstring][id][propget] */ HRESULT STDMETHODCALLTYPE ILine_get_x1_Proxy(
    ILine __RPC_FAR * This,
    /* [retval][out] */ long __RPC_FAR *pVal);

void __RPC_STUB ILine_get_x1_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

/* [helpstring][id][propput] */ HRESULT STDMETHODCALLTYPE ILine_put_x1_Proxy(
    ILine __RPC_FAR * This,
    /* [in] */ long newVal);

void __RPC_STUB ILine_put_x1_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

/* [helpstring][id][propget] */ HRESULT STDMETHODCALLTYPE ILine_get_y1_Proxy(
    ILine __RPC_FAR * This,
    /* [retval][out] */ long __RPC_FAR *pVal);

void __RPC_STUB ILine_get_y1_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

/* [helpstring][id][propput] */ HRESULT STDMETHODCALLTYPE ILine_put_y1_Proxy(
    ILine __RPC_FAR * This,
    /* [in] */ long newVal);

void __RPC_STUB ILine_put_y1_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

/* [helpstring][id][propget] */ HRESULT STDMETHODCALLTYPE ILine_get_x2_Proxy(
    ILine __RPC_FAR * This,
    /* [retval][out] */ long __RPC_FAR *pVal);

void __RPC_STUB ILine_get_x2_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

/* [helpstring][id][propput] */ HRESULT STDMETHODCALLTYPE ILine_put_x2_Proxy(
    ILine __RPC_FAR * This,
    /* [in] */ long newVal);

void __RPC_STUB ILine_put_x2_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

/* [helpstring][id][propget] */ HRESULT STDMETHODCALLTYPE ILine_get_y2_Proxy(
    ILine __RPC_FAR * This,
    /* [retval][out] */ long __RPC_FAR *pVal);

void __RPC_STUB ILine_get_y2_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

/* [helpstring][id][propput] */ HRESULT STDMETHODCALLTYPE ILine_put_y2_Proxy(
    ILine __RPC_FAR * This,
    /* [in] */ long newVal);

void __RPC_STUB ILine_put_y2_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __ILine_INTERFACE_DEFINED__ */

#ifndef __GRAPHOBJLib_LIBRARY_DEFINED__
#define __GRAPHOBJLib_LIBRARY_DEFINED__

/****************************************
 * Generated header for library: GRAPHOBJLib
 * at Thu Sep 25 08:51:38 1997
 * using MIDL 3.01.75
 ****************************************/
/* [helpstring][version][uuid] */

EXTERN_C const IID LIBID_GRAPHOBJLib;

#ifdef __cplusplus
EXTERN_C const CLSID CLSID_Point;

class DECLSPEC_UUID("F36B4ADE-E7EC-11D0-A78A-00805F0D2083")
Point;
#endif

#ifdef __cplusplus
EXTERN_C const CLSID CLSID_Line;

class DECLSPEC_UUID("8650EE03-E4C0-11D0-81C4-204C4F4F5020")
Line;
#endif
#endif /* __GRAPHOBJLib_LIBRARY_DEFINED__ */

/* Additional Prototypes for ALL interfaces */

/* end of Additional Prototypes */

#ifdef __cplusplus
}
#endif

#endif


GRAPHOBJ.IDL

// GraphObj.idl : IDL source for GraphObj.dll
//

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

import "oaidl.idl";
import "ocidl.idl";

[
object,
uuid(BEEDC08A-EBD2-11D0-A78F-00805F0D2083),
dual,
helpstring("IPoint Interface"),
pointer_default(unique)
]
interface IPoint : IDispatch
{
[propget, id(1), helpstring("property x")] HRESULT x([out, retval] long *pVal);
[propput, id(1), helpstring("property x")] HRESULT x([in] long newVal);
[propget, id(2), helpstring("property y")] HRESULT y([out, retval] long *pVal);
[propput, id(2), helpstring("property y")] HRESULT y([in] long newVal);
};
[
object,
uuid(BEEDC08B-EBD2-11D0-A78F-00805F0D2083),
dual,
helpstring("ILine Interface"),
pointer_default(unique)
]
interface ILine : IDispatch
{
[propget, id(1), helpstring("property x1")] HRESULT x1([out, retval] long *pVal);
[propput, id(1), helpstring("property x1")] HRESULT x1([in] long newVal);
[propget, id(2), helpstring("property y1")] HRESULT y1([out, retval] long *pVal);
[propput, id(2), helpstring("property y1")] HRESULT y1([in] long newVal);
[propget, id(3), helpstring("property x2")] HRESULT x2([out, retval] long *pVal);
[propput, id(3), helpstring("property x2")] HRESULT x2([in] long newVal);
[propget, id(4), helpstring("property y2")] HRESULT y2([out, retval] long *pVal);
[propput, id(4), helpstring("property y2")] HRESULT y2([in] long newVal);
};
[
uuid(BEEDC07B-EBD2-11D0-A78F-00805F0D2083),
version(1.0),
helpstring("GraphObj 1.0 Type Library")
]
library GRAPHOBJLib
{
importlib("stdole32.tlb");
importlib("stdole2.tlb");

[
uuid(F36B4ADE-E7EC-11D0-A78A-00805F0D2083),
helpstring("Point Class")
]
coclass Point
{
[default] interface IPoint;
};
[
uuid(8650EE03-E4C0-11D0-81C4-204C4F4F5020),
helpstring("Line Class")
]
coclass Line
{
[default] interface ILine;
};
};


GRAPHOBJ_I.C

/* this file contains the actual definitions of */
/* the IIDs and CLSIDs */

/* link this file in with the server and any clients */

/* File created by MIDL compiler version 3.01.75 */
/* at Thu Sep 25 08:51:38 1997
 */
/* Compiler settings for GraphObj.idl:
    Oicf (OptLev=i2), W1, Zp8, env=Win32, ms_ext, c_ext
    error checks: none
*/
//@@MIDL_FILE_HEADING(  )
#ifdef __cplusplus
extern "C"{
#endif

#ifndef __IID_DEFINED__
#define __IID_DEFINED__

typedef struct _IID
{
    unsigned long x;
    unsigned short s1;
    unsigned short s2;
    unsigned char  c[8];
} IID;

#endif // __IID_DEFINED__

#ifndef CLSID_DEFINED
#define CLSID_DEFINED
typedef IID CLSID;
#endif // CLSID_DEFINED

const IID IID_IPoint = {0xBEEDC08A,0xEBD2,0x11D0,{0xA7,0x8F,0x00,0x80,0x5F,0x0D,0x20,0x83}};

const IID IID_ILine = {0xBEEDC08B,0xEBD2,0x11D0,{0xA7,0x8F,0x00,0x80,0x5F,0x0D,0x20,0x83}};

const IID LIBID_GRAPHOBJLib = {0xBEEDC07B,0xEBD2,0x11D0,{0xA7,0x8F,0x00,0x80,0x5F,0x0D,0x20,0x83}};

const CLSID CLSID_Point = {0xF36B4ADE,0xE7EC,0x11D0,{0xA7,0x8A,0x00,0x80,0x5F,0x0D,0x20,0x83}};

const CLSID CLSID_Line = {0x8650EE03,0xE4C0,0x11D0,{0x81,0xC4,0x20,0x4C,0x4F,0x4F,0x50,0x20}};

#ifdef __cplusplus
}
#endif


GRAPHOBJ_P.C

/* this ALWAYS GENERATED file contains the proxy stub code */

/* File created by MIDL compiler version 3.01.75 */
/* at Thu Sep 25 08:51:38 1997
 */
/* Compiler settings for GraphObj.idl:
    Oicf (OptLev=i2), W1, Zp8, env=Win32, ms_ext, c_ext
    error checks: none
*/
//@@MIDL_FILE_HEADING(  )

#define USE_STUBLESS_PROXY

#include "rpcproxy.h"
#include "GraphObj.h"

#define TYPE_FORMAT_STRING_SIZE   5
#define PROC_FORMAT_STRING_SIZE   193

typedef struct _MIDL_TYPE_FORMAT_STRING
    {
    short          Pad;
    unsigned char  Format[ TYPE_FORMAT_STRING_SIZE ];
    } MIDL_TYPE_FORMAT_STRING;

typedef struct _MIDL_PROC_FORMAT_STRING
    {
    short          Pad;
    unsigned char  Format[ PROC_FORMAT_STRING_SIZE ];
    } MIDL_PROC_FORMAT_STRING;

extern const MIDL_TYPE_FORMAT_STRING __MIDL_TypeFormatString;
extern const MIDL_PROC_FORMAT_STRING __MIDL_ProcFormatString;

/* Object interface: IUnknown, ver. 0.0,
   GUID={0x00000000,0x0000,0x0000,{0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}} */

/* Object interface: IDispatch, ver. 0.0,
   GUID={0x00020400,0x0000,0x0000,{0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}} */

/* Object interface: IPoint, ver. 0.0,
   GUID={0xBEEDC08A,0xEBD2,0x11D0,{0xA7,0x8F,0x00,0x80,0x5F,0x0D,0x20,0x83}} */

extern const MIDL_STUB_DESC Object_StubDesc;

extern const MIDL_SERVER_INFO IPoint_ServerInfo;

#pragma code_seg(".orpc")
static const unsigned short IPoint_FormatStringOffsetTable[] =
    {
    (unsigned short) -1,
    (unsigned short) -1,
    (unsigned short) -1,
    (unsigned short) -1,
    0,
    24,
    48,
    72
    };

static const MIDL_SERVER_INFO IPoint_ServerInfo =
    {
    &Object_StubDesc,
    0,
    __MIDL_ProcFormatString.Format,
    &IPoint_FormatStringOffsetTable[-3],
    0,
    0,
    0,
    0
    };

static const MIDL_STUBLESS_PROXY_INFO IPoint_ProxyInfo =
    {
    &Object_StubDesc,
    __MIDL_ProcFormatString.Format,
    &IPoint_FormatStringOffsetTable[-3],
    0,
    0,
    0
    };

CINTERFACE_PROXY_VTABLE(11) _IPointProxyVtbl =
{
    &IPoint_ProxyInfo,
    &IID_IPoint,
    IUnknown_QueryInterface_Proxy,
    IUnknown_AddRef_Proxy,
    IUnknown_Release_Proxy ,
    0 /* (void *)-1 /* IDispatch::GetTypeInfoCount */ ,
    0 /* (void *)-1 /* IDispatch::GetTypeInfo */ ,
    0 /* (void *)-1 /* IDispatch::GetIDsOfNames */ ,
    0 /* IDispatch_Invoke_Proxy */ ,
    (void *)-1 /* IPoint::get_x */ ,
    (void *)-1 /* IPoint::put_x */ ,
    (void *)-1 /* IPoint::get_y */ ,
    (void *)-1 /* IPoint::put_y */
};

static const PRPC_STUB_FUNCTION IPoint_table[] =
{
    STUB_FORWARDING_FUNCTION,
    STUB_FORWARDING_FUNCTION,
    STUB_FORWARDING_FUNCTION,
    STUB_FORWARDING_FUNCTION,
    NdrStubCall2,
    NdrStubCall2,
    NdrStubCall2,
    NdrStubCall2
};

CInterfaceStubVtbl _IPointStubVtbl =
{
    &IID_IPoint,
    &IPoint_ServerInfo,
    11,
    &IPoint_table[-3],
    CStdStubBuffer_DELEGATING_METHODS
};

/* Object interface: ILine, ver. 0.0,
   GUID={0xBEEDC08B,0xEBD2,0x11D0,{0xA7,0x8F,0x00,0x80,0x5F,0x0D,0x20,0x83}} */

extern const MIDL_STUB_DESC Object_StubDesc;

extern const MIDL_SERVER_INFO ILine_ServerInfo;

#pragma code_seg(".orpc")

static const MIDL_STUB_DESC Object_StubDesc =
    {
    0,
    NdrOleAllocate,
    NdrOleFree,
    0,
    0,
    0,
    0,
    0,
    __MIDL_TypeFormatString.Format,
    0, /* -error bounds_check flag */
    0x20000, /* Ndr library version */
    0,
    0x301004b, /* MIDL Version 3.1.75 */
    0,
    0,
    0,  /* Reserved1 */
    0,  /* Reserved2 */
    0,  /* Reserved3 */
    0,  /* Reserved4 */
    0   /* Reserved5 */
    };

static const unsigned short ILine_FormatStringOffsetTable[] =
    {
    (unsigned short) -1,
    (unsigned short) -1,
    (unsigned short) -1,
    (unsigned short) -1,
    0,
    24,
    48,
    72,
    96,
    120,
    144,
    168
    };

static const MIDL_SERVER_INFO ILine_ServerInfo =
    {
    &Object_StubDesc,
    0,
    __MIDL_ProcFormatString.Format,
    &ILine_FormatStringOffsetTable[-3],
    0,
    0,
    0,
    0
    };

static const MIDL_STUBLESS_PROXY_INFO ILine_ProxyInfo =
    {
    &Object_StubDesc,
    __MIDL_ProcFormatString.Format,
    &ILine_FormatStringOffsetTable[-3],
    0,
    0,
    0
    };

CINTERFACE_PROXY_VTABLE(15) _ILineProxyVtbl =
{
    &ILine_ProxyInfo,
    &IID_ILine,
    IUnknown_QueryInterface_Proxy,
    IUnknown_AddRef_Proxy,
    IUnknown_Release_Proxy ,
    0 /* (void *)-1 /* IDispatch::GetTypeInfoCount */ ,
    0 /* (void *)-1 /* IDispatch::GetTypeInfo */ ,
    0 /* (void *)-1 /* IDispatch::GetIDsOfNames */ ,
    0 /* IDispatch_Invoke_Proxy */ ,
    (void *)-1 /* ILine::get_x1 */ ,
    (void *)-1 /* ILine::put_x1 */ ,
    (void *)-1 /* ILine::get_y1 */ ,
    (void *)-1 /* ILine::put_y1 */ ,
    (void *)-1 /* ILine::get_x2 */ ,
    (void *)-1 /* ILine::put_x2 */ ,
    (void *)-1 /* ILine::get_y2 */ ,
    (void *)-1 /* ILine::put_y2 */
};

static const PRPC_STUB_FUNCTION ILine_table[] =
{
    STUB_FORWARDING_FUNCTION,
    STUB_FORWARDING_FUNCTION,
    STUB_FORWARDING_FUNCTION,
    STUB_FORWARDING_FUNCTION,
    NdrStubCall2,
    NdrStubCall2,
    NdrStubCall2,
    NdrStubCall2,
    NdrStubCall2,
    NdrStubCall2,
    NdrStubCall2,
    NdrStubCall2
};

CInterfaceStubVtbl _ILineStubVtbl =
{
    &IID_ILine,
    &ILine_ServerInfo,
    15,
    &ILine_table[-3],
    CStdStubBuffer_DELEGATING_METHODS
};

#pragma data_seg(".rdata")

#if !defined(__RPC_WIN32__)
#error  Invalid build platform for this stub.
#endif

#if !(TARGET_IS_NT40_OR_LATER)
#error You need a Windows NT 4.0 or later to run this stub because it uses these features:
#error   -Oif or -Oicf.
#error However, your C/C++ compilation flags indicate you intend to run this app on earlier systems.
#error This app will die there with the RPC_X_WRONG_STUB_VERSION error.
#endif

static const MIDL_PROC_FORMAT_STRING __MIDL_ProcFormatString =
    {
        0,
        {

/* Procedure get_x1 */

/* Procedure get_x */

0x33,/* FC_AUTO_HANDLE */
0x64,/* 100 */
/*  2 */NdrFcShort( 0x7 ),/* 7 */
#ifndef _ALPHA_
/*  4 */NdrFcShort( 0xc ),/* x86, MIPS, PPC Stack size/offset = 12 */
#else
NdrFcShort( 0x18 ),/* Alpha Stack size/offset = 24 */
#endif
/*  6 */NdrFcShort( 0x0 ),/* 0 */
/*  8 */NdrFcShort( 0x10 ),/* 16 */
/* 10 */0x4,/* 4 */
0x2,/* 2 */

/* Parameter pVal */

/* Parameter pVal */

/* 12 */NdrFcShort( 0x2150 ),/* 8528 */
#ifndef _ALPHA_
/* 14 */NdrFcShort( 0x4 ),/* x86, MIPS, PPC Stack size/offset = 4 */
#else
NdrFcShort( 0x8 ),/* Alpha Stack size/offset = 8 */
#endif
/* 16 */0x8,/* FC_LONG */
0x0,/* 0 */

/* Return value */

/* Return value */

/* 18 */NdrFcShort( 0x70 ),/* 112 */
#ifndef _ALPHA_
/* 20 */NdrFcShort( 0x8 ),/* x86, MIPS, PPC Stack size/offset = 8 */
#else
NdrFcShort( 0x10 ),/* Alpha Stack size/offset = 16 */
#endif
/* 22 */0x8,/* FC_LONG */
0x0,/* 0 */

/* Procedure put_x1 */

/* Procedure put_x */

/* 24 */0x33,/* FC_AUTO_HANDLE */
0x64,/* 100 */
/* 26 */NdrFcShort( 0x8 ),/* 8 */
#ifndef _ALPHA_
/* 28 */NdrFcShort( 0xc ),/* x86, MIPS, PPC Stack size/offset = 12 */
#else
NdrFcShort( 0x18 ),/* Alpha Stack size/offset = 24 */
#endif
/* 30 */NdrFcShort( 0x8 ),/* 8 */
/* 32 */NdrFcShort( 0x8 ),/* 8 */
/* 34 */0x4,/* 4 */
0x2,/* 2 */

/* Parameter newVal */

/* Parameter newVal */

/* 36 */NdrFcShort( 0x48 ),/* 72 */
#ifndef _ALPHA_
/* 38 */NdrFcShort( 0x4 ),/* x86, MIPS, PPC Stack size/offset = 4 */
#else
NdrFcShort( 0x8 ),/* Alpha Stack size/offset = 8 */
#endif
/* 40 */0x8,/* FC_LONG */
0x0,/* 0 */

/* Return value */

/* Return value */

/* 42 */NdrFcShort( 0x70 ),/* 112 */
#ifndef _ALPHA_
/* 44 */NdrFcShort( 0x8 ),/* x86, MIPS, PPC Stack size/offset = 8 */
#else
NdrFcShort( 0x10 ),/* Alpha Stack size/offset = 16 */
#endif
/* 46 */0x8,/* FC_LONG */
0x0,/* 0 */

/* Procedure get_y1 */

/* Procedure get_y */

/* 48 */0x33,/* FC_AUTO_HANDLE */
0x64,/* 100 */
/* 50 */NdrFcShort( 0x9 ),/* 9 */
#ifndef _ALPHA_
/* 52 */NdrFcShort( 0xc ),/* x86, MIPS, PPC Stack size/offset = 12 */
#else
NdrFcShort( 0x18 ),/* Alpha Stack size/offset = 24 */
#endif
/* 54 */NdrFcShort( 0x0 ),/* 0 */
/* 56 */NdrFcShort( 0x10 ),/* 16 */
/* 58 */0x4,/* 4 */
0x2,/* 2 */

/* Parameter pVal */

/* Parameter pVal */

/* 60 */NdrFcShort( 0x2150 ),/* 8528 */
#ifndef _ALPHA_
/* 62 */NdrFcShort( 0x4 ),/* x86, MIPS, PPC Stack size/offset = 4 */
#else
NdrFcShort( 0x8 ),/* Alpha Stack size/offset = 8 */
#endif
/* 64 */0x8,/* FC_LONG */
0x0,/* 0 */

/* Return value */

/* Return value */

/* 66 */NdrFcShort( 0x70 ),/* 112 */
#ifndef _ALPHA_
/* 68 */NdrFcShort( 0x8 ),/* x86, MIPS, PPC Stack size/offset = 8 */
#else
NdrFcShort( 0x10 ),/* Alpha Stack size/offset = 16 */
#endif
/* 70 */0x8,/* FC_LONG */
0x0,/* 0 */

/* Procedure put_y1 */

/* Procedure put_y */

/* 72 */0x33,/* FC_AUTO_HANDLE */
0x64,/* 100 */
/* 74 */NdrFcShort( 0xa ),/* 10 */
#ifndef _ALPHA_
/* 76 */NdrFcShort( 0xc ),/* x86, MIPS, PPC Stack size/offset = 12 */
#else
NdrFcShort( 0x18 ),/* Alpha Stack size/offset = 24 */
#endif
/* 78 */NdrFcShort( 0x8 ),/* 8 */
/* 80 */NdrFcShort( 0x8 ),/* 8 */
/* 82 */0x4,/* 4 */
0x2,/* 2 */

/* Parameter newVal */

/* Parameter newVal */

/* 84 */NdrFcShort( 0x48 ),/* 72 */
#ifndef _ALPHA_
/* 86 */NdrFcShort( 0x4 ),/* x86, MIPS, PPC Stack size/offset = 4 */
#else
NdrFcShort( 0x8 ),/* Alpha Stack size/offset = 8 */
#endif
/* 88 */0x8,/* FC_LONG */
0x0,/* 0 */

/* Return value */

/* Return value */

/* 90 */NdrFcShort( 0x70 ),/* 112 */
#ifndef _ALPHA_
/* 92 */NdrFcShort( 0x8 ),/* x86, MIPS, PPC Stack size/offset = 8 */
#else
NdrFcShort( 0x10 ),/* Alpha Stack size/offset = 16 */
#endif
/* 94 */0x8,/* FC_LONG */
0x0,/* 0 */

/* Procedure get_x2 */

/* 96 */0x33,/* FC_AUTO_HANDLE */
0x64,/* 100 */
/* 98 */NdrFcShort( 0xb ),/* 11 */
#ifndef _ALPHA_
/* 100 */NdrFcShort( 0xc ),/* x86, MIPS, PPC Stack size/offset = 12 */
#else
NdrFcShort( 0x18 ),/* Alpha Stack size/offset = 24 */
#endif
/* 102 */NdrFcShort( 0x0 ),/* 0 */
/* 104 */NdrFcShort( 0x10 ),/* 16 */
/* 106 */0x4,/* 4 */
0x2,/* 2 */

/* Parameter pVal */

/* 108 */NdrFcShort( 0x2150 ),/* 8528 */
#ifndef _ALPHA_
/* 110 */NdrFcShort( 0x4 ),/* x86, MIPS, PPC Stack size/offset = 4 */
#else
NdrFcShort( 0x8 ),/* Alpha Stack size/offset = 8 */
#endif
/* 112 */0x8,/* FC_LONG */
0x0,/* 0 */

/* Return value */

/* 114 */NdrFcShort( 0x70 ),/* 112 */
#ifndef _ALPHA_
/* 116 */NdrFcShort( 0x8 ),/* x86, MIPS, PPC Stack size/offset = 8 */
#else
NdrFcShort( 0x10 ),/* Alpha Stack size/offset = 16 */
#endif
/* 118 */0x8,/* FC_LONG */
0x0,/* 0 */

/* Procedure put_x2 */

/* 120 */0x33,/* FC_AUTO_HANDLE */
0x64,/* 100 */
/* 122 */NdrFcShort( 0xc ),/* 12 */
#ifndef _ALPHA_
/* 124 */NdrFcShort( 0xc ),/* x86, MIPS, PPC Stack size/offset = 12 */
#else
NdrFcShort( 0x18 ),/* Alpha Stack size/offset = 24 */
#endif
/* 126 */NdrFcShort( 0x8 ),/* 8 */
/* 128 */NdrFcShort( 0x8 ),/* 8 */
/* 130 */0x4,/* 4 */
0x2,/* 2 */

/* Parameter newVal */

/* 132 */NdrFcShort( 0x48 ),/* 72 */
#ifndef _ALPHA_
/* 134 */NdrFcShort( 0x4 ),/* x86, MIPS, PPC Stack size/offset = 4 */
#else
NdrFcShort( 0x8 ),/* Alpha Stack size/offset = 8 */
#endif
/* 136 */0x8,/* FC_LONG */
0x0,/* 0 */

/* Return value */

/* 138 */NdrFcShort( 0x70 ),/* 112 */
#ifndef _ALPHA_
/* 140 */NdrFcShort( 0x8 ),/* x86, MIPS, PPC Stack size/offset = 8 */
#else
NdrFcShort( 0x10 ),/* Alpha Stack size/offset = 16 */
#endif
/* 142 */0x8,/* FC_LONG */
0x0,/* 0 */

/* Procedure get_y2 */

/* 144 */0x33,/* FC_AUTO_HANDLE */
0x64,/* 100 */
/* 146 */NdrFcShort( 0xd ),/* 13 */
#ifndef _ALPHA_
/* 148 */NdrFcShort( 0xc ),/* x86, MIPS, PPC Stack size/offset = 12 */
#else
NdrFcShort( 0x18 ),/* Alpha Stack size/offset = 24 */
#endif
/* 150 */NdrFcShort( 0x0 ),/* 0 */
/* 152 */NdrFcShort( 0x10 ),/* 16 */
/* 154 */0x4,/* 4 */
0x2,/* 2 */

/* Parameter pVal */

/* 156 */NdrFcShort( 0x2150 ),/* 8528 */
#ifndef _ALPHA_
/* 158 */NdrFcShort( 0x4 ),/* x86, MIPS, PPC Stack size/offset = 4 */
#else
NdrFcShort( 0x8 ),/* Alpha Stack size/offset = 8 */
#endif
/* 160 */0x8,/* FC_LONG */
0x0,/* 0 */

/* Return value */

/* 162 */NdrFcShort( 0x70 ),/* 112 */
#ifndef _ALPHA_
/* 164 */NdrFcShort( 0x8 ),/* x86, MIPS, PPC Stack size/offset = 8 */
#else
NdrFcShort( 0x10 ),/* Alpha Stack size/offset = 16 */
#endif
/* 166 */0x8,/* FC_LONG */
0x0,/* 0 */

/* Procedure put_y2 */

/* 168 */0x33,/* FC_AUTO_HANDLE */
0x64,/* 100 */
/* 170 */NdrFcShort( 0xe ),/* 14 */
#ifndef _ALPHA_
/* 172 */NdrFcShort( 0xc ),/* x86, MIPS, PPC Stack size/offset = 12 */
#else
NdrFcShort( 0x18 ),/* Alpha Stack size/offset = 24 */
#endif
/* 174 */NdrFcShort( 0x8 ),/* 8 */
/* 176 */NdrFcShort( 0x8 ),/* 8 */
/* 178 */0x4,/* 4 */
0x2,/* 2 */

/* Parameter newVal */

/* 180 */NdrFcShort( 0x48 ),/* 72 */
#ifndef _ALPHA_
/* 182 */NdrFcShort( 0x4 ),/* x86, MIPS, PPC Stack size/offset = 4 */
#else
NdrFcShort( 0x8 ),/* Alpha Stack size/offset = 8 */
#endif
/* 184 */0x8,/* FC_LONG */
0x0,/* 0 */

/* Return value */

/* 186 */NdrFcShort( 0x70 ),/* 112 */
#ifndef _ALPHA_
/* 188 */NdrFcShort( 0x8 ),/* x86, MIPS, PPC Stack size/offset = 8 */
#else
NdrFcShort( 0x10 ),/* Alpha Stack size/offset = 16 */
#endif
/* 190 */0x8,/* FC_LONG */
0x0,/* 0 */

0x0
        }
    };

static const MIDL_TYPE_FORMAT_STRING __MIDL_TypeFormatString =
    {
        0,
        {
0x11, 0xc,/* FC_RP [alloced_on_stack] [simple_pointer] */
/*  2 */0x8,/* FC_LONG */
0x5c,/* FC_PAD */

0x0
        }
    };

const CInterfaceProxyVtbl * _GraphObj_ProxyVtblList[] =
{
    ( CInterfaceProxyVtbl *) &_IPointProxyVtbl,
    ( CInterfaceProxyVtbl *) &_ILineProxyVtbl,
    0
};

const CInterfaceStubVtbl * _GraphObj_StubVtblList[] =
{
    ( CInterfaceStubVtbl *) &_IPointStubVtbl,
    ( CInterfaceStubVtbl *) &_ILineStubVtbl,
    0
};

PCInterfaceName const _GraphObj_InterfaceNamesList[] =
{
    "IPoint",
    "ILine",
    0
};

const IID *  _GraphObj_BaseIIDList[] =
{
    &IID_IDispatch,
    &IID_IDispatch,
    0
};

#define _GraphObj_CHECK_IID(n)IID_GENERIC_CHECK_IID( _GraphObj, pIID, n)

int __stdcall _GraphObj_IID_Lookup( const IID * pIID, int * pIndex )
{
    IID_BS_LOOKUP_SETUP

    IID_BS_LOOKUP_INITIAL_TEST( _GraphObj, 2, 1 )
    IID_BS_LOOKUP_RETURN_RESULT( _GraphObj, 2, *pIndex )

}

const ExtendedProxyFileInfo GraphObj_ProxyFileInfo =
{
    (PCInterfaceProxyVtblList *) & _GraphObj_ProxyVtblList,
    (PCInterfaceStubVtblList *) & _GraphObj_StubVtblList,
    (const PCInterfaceName * ) & _GraphObj_InterfaceNamesList,
    (const IID ** ) & _GraphObj_BaseIIDList,
    & _GraphObj_IID_Lookup,
    2,
    2
};


IPERSISTSTREAM.H

#ifndef _IPERSISTSTREAM_H
#define _IPERSISTSTREAM_H

//////////////////////////////////////////////////////////////////////////////
// IMyPersistStream
// Implements IPersistStream as a template class.
//
// ToDo: Derive your own class form this class and implement the methods
//IMyPersistStreamImpl_Load
//IMyPersistStreamImpl_Save
//IMyPersistStreamImpl_GetSizeMax
// and the member variable
//m_bRequiresSave
//////////////////////////////////////////////////////////////////////////////
template <class T>
class ATL_NO_VTABLE IMyPersistStreamImpl
{
public:
// IUnknown
//
STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObject) = 0;
_ATL_DEBUG_ADDREF_RELEASE_IMPL(IMyPersistStreamImpl)

// IPersist
STDMETHOD(GetClassID)(CLSID *pClassID)
{
ATLTRACE(_T("IMyPersistStreamImpl::GetClassID\n"));
T* pT = static_cast<T*>(this);
*pClassID = pT->GetObjectCLSID();
return S_OK;
}

// IPersistStream
STDMETHOD(IsDirty)()
{
ATLTRACE(_T("IMyPersistStreamImpl::IsDirty\n"));
T* pT = static_cast<T*>(this);
return (pT->m_bRequiresSave) ? S_OK : S_FALSE;
}
STDMETHOD(Load)(LPSTREAM pStm)
{
ATLTRACE(_T("IMyPersistStreamImpl::Load\n"));
T* pT = static_cast<T*>(this);
return pT->IMyPersistStreamImpl_Load(pStm);
}
STDMETHOD(Save)(LPSTREAM pStm, BOOL fClearDirty)
{
T* pT = static_cast<T*>(this);
ATLTRACE(_T("IMyPersistStreamImpl::Save\n"));
return pT->IMyPersistStreamImpl_Save(pStm, fClearDirty);
}
STDMETHOD(GetSizeMax)(ULARGE_INTEGER FAR* pcbSize )
{
T* pT = static_cast<T*>(this);
ATLTRACE(_T("IMyPersistStreamImpl::GetSizeMax\n"));
return pT->IMyPersistStreamImpl_GetSizeMax(pcbSize);
}

};

#endif


LINE.CPP

// Line.cpp : Implementation of CLine
#include "stdafx.h"
#include "GraphObj.h"
#include "Line.h"

/////////////////////////////////////////////////////////////////////////////
// CLine

STDMETHODIMP CLine::InterfaceSupportsErrorInfo(REFIID riid)
{
static const IID* arr[] =
{
&IID_ILine,
};
for (int i=0;i<sizeof(arr)/sizeof(arr[0]);i++)
{
if (InlineIsEqualGUID(*arr[i],riid))
return S_OK;
}
return S_FALSE;
}

STDMETHODIMP CLine::get_x1(long * pVal)
{
*pVal = m_Line.x1;
return S_OK;
}

STDMETHODIMP CLine::put_x1(long newVal)
{
m_Line.x1 = newVal;

return S_OK;
}

STDMETHODIMP CLine::get_y1(long * pVal)
{
*pVal = m_Line.y1;

return S_OK;
}

STDMETHODIMP CLine::put_y1(long newVal)
{
m_Line.y1 = newVal;

return S_OK;
}

STDMETHODIMP CLine::get_x2(long * pVal)
{
*pVal = m_Line.x2;

return S_OK;
}

STDMETHODIMP CLine::put_x2(long newVal)
{
m_Line.x2 = newVal;

return S_OK;
}

STDMETHODIMP CLine::get_y2(long * pVal)
{
*pVal = m_Line.y2;

return S_OK;
}

STDMETHODIMP CLine::put_y2(long newVal)
{
m_Line.y2 = newVal;

return S_OK;
}

STDMETHODIMP CLine::IMyPersistStreamImpl_Load(LPSTREAM pIStream)
{
    ULONG           cb;
    HRESULT         hr;

    if (NULL==pIStream)
        return ResultFromScode(E_POINTER);

    hr=pIStream->Read(&m_Line, sizeof(LINEDATA), &cb);
    if (FAILED(hr) || sizeof(LINEDATA) != cb) {
pIStream->Release();
        return hr;
    }

return NOERROR;
}

STDMETHODIMP CLine::IMyPersistStreamImpl_Save(LPSTREAM pIStream, BOOL /* fClearDirty */)
{
    ULONG           cb;
    HRESULT         hr;

    if (NULL==pIStream)
        return ResultFromScode(E_POINTER);

    hr=pIStream->Write(&m_Line, (ULONG)sizeof(LINEDATA), &cb);

    if (FAILED(hr) || sizeof(LINEDATA)!=cb)
       return ResultFromScode(STG_E_WRITEFAULT);

return NOERROR;
}

STDMETHODIMP CLine::IMyPersistStreamImpl_GetSizeMax(ULARGE_INTEGER FAR* pcbSize)
{
pcbSize->LowPart = (DWORD)(sizeof(LINEDATA));
pcbSize->HighPart = 0L;
return NOERROR;
}


LINE.H

// Line.h : Declaration of the CLine

#ifndef __LINE_H_
#define __LINE_H_

#include "resource.h"       // main symbols
#define _ATL_DEBUG_QI
#define _ATL_DEBUG_REFCOUNT
#include <atlcom.h>
#include <atlctl.h>
#include <olectl.h>
#include "IPersistStream.h"

/////////////////////////////////////////////////////////////////////////////
// CLine
class /* ATL_NO_VTABLE */ CLine :
public CComObjectRootEx<CComMultiThreadModel>,
public CComCoClass<CLine, &CLSID_Line>,
public IMyPersistStreamImpl<CLine>,
public ISupportErrorInfo,
public IDispatchImpl<ILine, &IID_ILine, &LIBID_GRAPHOBJLib>
{
private:
typedef struct {
long x1, y1;
long x2, y2;
} LINEDATA;

LINEDATA m_Line;

public:
CLine()
{
}

DECLARE_REGISTRY_RESOURCEID(IDR_LINE)
DECLARE_NOT_AGGREGATABLE(CLine)

BEGIN_COM_MAP(CLine)
COM_INTERFACE_ENTRY(ILine)
COM_INTERFACE_ENTRY(IDispatch)
COM_INTERFACE_ENTRY(ISupportErrorInfo)
COM_INTERFACE_ENTRY_IMPL_IID(IID_IPersistStream, IMyPersistStream)
// COM_INTERFACE_ENTRY_IMPL(IPersistStreamInit)
END_COM_MAP()

// IMyPersistStream
BOOLm_bRequiresSave;
STDMETHOD(IMyPersistStreamImpl_Load)(LPSTREAM pStm);
STDMETHOD(IMyPersistStreamImpl_Save)(LPSTREAM pStm, BOOL fClearDirty);
STDMETHOD(IMyPersistStreamImpl_GetSizeMax)(ULARGE_INTEGER FAR* pcbSize);

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

// ILine
public:
STDMETHOD(get_y2)(/*[out, retval]*/ long *pVal);
STDMETHOD(put_y2)(/*[in]*/ long newVal);
STDMETHOD(get_x2)(/*[out, retval]*/ long *pVal);
STDMETHOD(put_x2)(/*[in]*/ long newVal);
STDMETHOD(get_y1)(/*[out, retval]*/ long *pVal);
STDMETHOD(put_y1)(/*[in]*/ long newVal);
STDMETHOD(get_x1)(/*[out, retval]*/ long *pVal);
STDMETHOD(put_x1)(/*[in]*/ long newVal);
};

#endif //__LINE_H_


POINT.CPP

// Point.cpp : Implementation of CPoint
#include "stdafx.h"
#include "GraphObj.h"
#include "Point.h"

/////////////////////////////////////////////////////////////////////////////
// CPoint

STDMETHODIMP CPoint::IMyPersistStreamImpl_Load(LPSTREAM pIStream)
{
    ULONG           cb;
    HRESULT         hr;

    if (NULL==pIStream)
        return ResultFromScode(E_POINTER);

    hr=pIStream->Read(&m_Point, sizeof(POINT), &cb);
    if (FAILED(hr) || sizeof(POINT) != cb) {
pIStream->Release();
        return hr;
    }

return NOERROR;
}

STDMETHODIMP CPoint::IMyPersistStreamImpl_Save(LPSTREAM pIStream, BOOL /*fClearDirty */)
{
    ULONG           cb;
    HRESULT         hr;

    if (NULL==pIStream)
        return ResultFromScode(E_POINTER);

    hr=pIStream->Write(&m_Point, (ULONG)sizeof(POINT), &cb);

    if (FAILED(hr) || sizeof(POINT)!=cb)
       return ResultFromScode(STG_E_WRITEFAULT);

return NOERROR;
}

STDMETHODIMP CPoint::IMyPersistStreamImpl_GetSizeMax(ULARGE_INTEGER FAR* pcbSize)
{
pcbSize->LowPart = (DWORD)(sizeof(POINT));
pcbSize->HighPart = 0L;
return NOERROR;
}

STDMETHODIMP CPoint::get_x(long * pVal)
{
*pVal = m_Point.x;
return S_OK;
}

STDMETHODIMP CPoint::put_x(long newVal)
{
m_Point.x = newVal;
return S_OK;
}

STDMETHODIMP CPoint::get_y(long * pVal)
{
*pVal = m_Point.y;
return S_OK;
}

STDMETHODIMP CPoint::put_y(long newVal)
{
m_Point.y = newVal;
return S_OK;
}


POINT.H

// Point.h : Declaration of the CPoint

#ifndef __POINT_H_
#define __POINT_H_

#include "resource.h"       // main symbols
#include "IPersistStream.h"

/////////////////////////////////////////////////////////////////////////////
// CPoint
class ATL_NO_VTABLE CPoint :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CPoint, &CLSID_Point>,
public IMyPersistStreamImpl<CPoint>,
public IDispatchImpl<IPoint, &IID_IPoint, &LIBID_GRAPHOBJLib>
{
private:
typedef struct {
long x, y;
} POINT;

POINTm_Point;

public:
CPoint()
{
}

DECLARE_REGISTRY_RESOURCEID(IDR_POINT)
DECLARE_NOT_AGGREGATABLE(CPoint)

BEGIN_COM_MAP(CPoint)
COM_INTERFACE_ENTRY(IPoint)
COM_INTERFACE_ENTRY(IDispatch)
COM_INTERFACE_ENTRY_IMPL_IID(IID_IPersistStream, IMyPersistStream)
END_COM_MAP()

// IMyPersistStream
BOOLm_bRequiresSave;
STDMETHOD(IMyPersistStreamImpl_Load)(LPSTREAM pStm);
STDMETHOD(IMyPersistStreamImpl_Save)(LPSTREAM pStm, BOOL fClearDirty);
STDMETHOD(IMyPersistStreamImpl_GetSizeMax)(ULARGE_INTEGER FAR* pcbSize);

// IPoint
public:
STDMETHOD(get_y)(/*[out, retval]*/ long *pVal);
STDMETHOD(put_y)(/*[in]*/ long newVal);
STDMETHOD(get_x)(/*[out, retval]*/ long *pVal);
STDMETHOD(put_x)(/*[in]*/ long newVal);
};

#endif //__POINT_H_


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by GraphObj.rc
//
#define IDS_PROJNAME                    100
#define IDR_POINT                       101
#define IDR_LINE                        102

// 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           103
#endif
#endif


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__BEEDC07F_EBD2_11D0_A78F_00805F0D2083__INCLUDED_)
#define AFX_STDAFX_H__BEEDC07F_EBD2_11D0_A78F_00805F0D2083__INCLUDED_

#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000

#define STRICT

#define _WIN32_WINNT 0x0400
#define _ATL_APARTMENT_THREADED

#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__BEEDC07F_EBD2_11D0_A78F_00805F0D2083__INCLUDED)


MAIN.CPP

#include "stdafx.h"
#import "mqoa.dll"    no_namespace // MSMQ COM object

#import "..\bin\GraphObj.dll" no_namespace// Point and Line object

// COM exception handler
void dump_com_error(_com_error &e)
{
    _tprintf(_T("Oops - hit an error!\n"));
    _tprintf(_T("\a\tCode = %08lx\n"), e.Error());
    _tprintf(_T("\a\tCode meaning = %s\n"), e.ErrorMessage());
    _bstr_t bstrSource(e.Source());
    _bstr_t bstrDescription(e.Description());
    _tprintf(_T("\a\tSource = %s\n"), (LPCTSTR) bstrSource);
    _tprintf(_T("\a\tDescription = %s\n"), (LPCTSTR) bstrDescription);
}

// Initialize OLE library
struct InitOle  _init_InitOle_;

void main()
{
    try {
// For these ActiveX components we need only smart interface pointer
IMSMQQueuePtrpQueue;
IMSMQMessagePtrpMessage;

// Instanciate the follwing ActiveX components
IMSMQQueueInfoPtrpQueueInfo(__uuidof(MSMQQueueInfo));

intn = 1;

cout << "\n\nMSMQ IPersistStream Test Object Receiver\n";
cout << "==========================================\n\n";

// Open the queue
pQueueInfo->PathName = ".\\IStreamTest";
try {
pQueue = pQueueInfo->Open(MQ_RECEIVE_ACCESS, MQ_DENY_NONE);
} catch (_com_error& e) {
if (e.Error() == MQ_ERROR_QUEUE_NOT_FOUND) {
cout << "\n\nERROR queue does not exist\n";
exit(-1);
} else
dump_com_error(e);
}

// Get the next message
pMessage = pQueue->Receive();

IPointPtrpPoint((IUnknown*)pMessage->Body);

// Check if it's a Point object
if (pPoint != NULL) {

cout << "\nGot a Point Object: " << pPoint->x << "   " << pPoint->y << "\n";

}
else {
ILinePtrpLine((IUnknown*)pMessage->Body);

// Check if it's a Line object
if (pLine != NULL) {
cout << "\nGot a Line Object: " << pLine->x1 << " " << pLine->y1 << " " << pLine->x2 << " " << pLine->y2 << "\n";
}
else
cout << "\nUnknown Object !!!\n";
}
pQueue->Close();

    } catch (_com_error& e) {
dump_com_error(e);
    }
}


STDAFX.CPP

#include "stdafx.h"


STDAFX.H

#include <windows.h>
#include <tchar.h>
#include <comdef.h>
#include <objbase.h>
#include <stdio.h>
#include <iostream.h>
#include "../GraphObj/GraphObj_i.c" // For IID_IPoint and IID_ILine
#include "../QueueDef.h"


MAIN.CPP

#import "mqoa.dll"no_namespace// MSMQ COM object

#import "..\bin\GraphObj.dll" no_namespace// Point and Line object

#include <stdio.h>
#include <tchar.h>
#include <iostream.h>
#include <mq.h>
#include "../GraphObj/GraphObj_i.c" // For IID_IPoint and IID_ILine
#include "../QueueDef.h"

// COM exception handler
void dump_com_error(_com_error &e)
{
    _tprintf(_T("Oops - hit an error!\n"));
    _tprintf(_T("\a\tCode = %08lx\n"), e.Error());
    _tprintf(_T("\a\tCode meaning = %s\n"), e.ErrorMessage());
    _bstr_t bstrSource(e.Source());
    _bstr_t bstrDescription(e.Description());
    _tprintf(_T("\a\tSource = %s\n"), (LPCTSTR) bstrSource);
    _tprintf(_T("\a\tDescription = %s\n"), (LPCTSTR) bstrDescription);
}

// Initialize OLE library
struct InitOle  _init_InitOle_;

void main()
{
    try {

// For these ActiveX components we need only smart interface pointer
IMSMQQueueInfosPtrpQueueInfos;
IMSMQQueueInfoPtrpQueueInfo;
IMSMQQueuePtrpQueue;
IUnknownPtrpIUnknown;

// Instanciate the follwing ActiveX components
IMSMQQueryPtrpQuery(__uuidof(MSMQQuery));
IMSMQMessagePtrpMessage(__uuidof(MSMQMessage));
ILinePtrpLine(__uuidof(Line));
IPointPtrpPoint(__uuidof(Point));

intn = 1;
_variant_tvarQueueNameInit(STR_QUEUE_LABEL);
VARIANTvarQueueName;

cout << "\n\nMSMQ IPersistStream Test\n";
cout << "==========================\n\n";

varQueueName = varQueueNameInit.Detach();

// Find the queue
pQueueInfos = pQuery->LookupQueue(&vtMissing, &vtMissing, &varQueueName);
varQueueNameInit.Attach(varQueueName);
pQueueInfos->Reset();

if ((pQueueInfo = pQueueInfos->Next()) == NULL) {

//  The queue was not found create it on the local system
pQueueInfo = new IMSMQQueueInfoPtr( __uuidof(MSMQQueueInfo) );
pQueueInfo->PathName = STR_QUEUE_NAME;
pQueueInfo->Label = STR_QUEUE_LABEL;
pQueueInfo->Create();
cout << "\nNew Queue created\n";
}

cout << "Which object should be send ?\n";
cout << "1 - Point\n";
cout << "2 - Line\n";
cout << ":";
cin >> n;

// Open the queue
pQueue = pQueueInfo->Open(MQ_SEND_ACCESS, MQ_DENY_NONE);

if (n == 1) {
// Initialize point object
pPoint->x = 8;
pPoint->y = 9;
pMessage->Body = static_cast<IUnknown*>(pPoint); // The message body gets the IUnknown pointer

}
else {
// Initialize line object
pLine->x1 = 1;
pLine->y1 = 2;
pLine->x2 = 3;
pLine->y2 = 4;
pMessage->Body = static_cast<IUnknown*>(pLine); // The message body gets the IUnknown pointer
}
cout << "Sending Object\n";
pMessage->Send(pQueue);

// Close the Queue
pQueue->Close();

    } catch (_com_error& e) {
dump_com_error(e);
    }
}


STDAFX.H

#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <iostream.h>
#include <mq.h>
#include <comdef.h>
#include <objbase.h>


GUIDS.CPP

//=--------------------------------------------------------------------------=
// Guids.Cpp
//=--------------------------------------------------------------------------=
// Copyright  1995  Microsoft Corporation.  All Rights Reserved.
//
// 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.
//=--------------------------------------------------------------------------=
//
// contains the guids we will define ourselves.
//

#define INITGUID                   // define all the guids.
#include <windows.h>
#include "mqoai.H"


MQTESTOA.CPP

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright (C) 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.
\******************************************************************************/

//
// Includes
//
#include <stdio.h>
#include <windows.h>

//
// Unique include file for ActiveX MSMQ apps
//
#include "mqoai.h"

//
// Various defines
//
#define MAX_VAR       20
#define MAX_BUFFER   500

//
// GUID created with the tool "GUIDGEN"
//
static WCHAR strGuidMQTestType[] =
  L"{c30e0960-a2c0-11cf-9785-00608cb3e80c}";
//
// Prototypes
//
void PrintError(char *s, HRESULT hr);
HRESULT Syntax();

char mbsMachineName[MAX_COMPUTERNAME_LENGTH + 1];

// Some useful macros
#define RELEASE(punk) if (punk) { (punk)->Release(); (punk) = NULL; }
#define ADDREF(punk) ((punk) ? (punk)->AddRef() : 0)
#define PRINTERROR(s, hr) { PrintError(s, hr); goto Cleanup; }

//-----------------------------------------------------
//
// Receiver Mode
// -------------
// The receiver side does the following:
//    1. Creates a public queue on its own machine
//       of type "strGuidMQTestType"
//    2. Opens the queue
//    3. In a Loop
//          Receives messages
//          Prints message body and message label
//    4. Cleanup handles
//    5. Deletes the queue from the directory service
//
//-----------------------------------------------------
HRESULT Receiver()
{
    IMSMQMessage *pmessageReceive = NULL;
    IMSMQQueue *pqReceive = NULL;
    IMSMQQueueInfo  *pqinfo = NULL;
    BSTR bstrPathName = NULL;
    BSTR bstrServiceType = NULL;
    BSTR bstrLabel = NULL;
    BSTR bstrMsgLabel = NULL;
    VARIANT varIsTransactional, varIsWorldReadable, varBody, varBody2, varWantDestQueue, varWantBody, varReceiveTimeout;
    WCHAR wcsPathName[1000];
    BOOL fQuit = FALSE;
    HRESULT hresult = NOERROR;

    printf("\nReceiver Mode on Machine: %s\n\n", mbsMachineName);

    //
    // Create MSMQQueueInfo object
    //
    hresult = CoCreateInstance(
                   CLSID_MSMQQueueInfo,
                   NULL,      // punkOuter
                   CLSCTX_SERVER,
                   IID_IMSMQQueueInfo,
                   (LPVOID *)&pqinfo);
    if (FAILED(hresult)) {
      PRINTERROR("Cannot create queue", hresult);
    }

    //
    // Prepare properties to create a queue on local machine
    //

    // Set the PathName
    swprintf(wcsPathName, L"%S\\MSMQTest", mbsMachineName);
    bstrPathName = SysAllocString(wcsPathName);
    if (bstrPathName == NULL) {
      PRINTERROR("OOM: pathname", E_OUTOFMEMORY);
    }
    pqinfo->put_PathName(bstrPathName);

    //
    // Set the type of the queue
    // (Will be used to locate all the queues of this type)
    //
    bstrServiceType = SysAllocString(strGuidMQTestType);
    if (bstrServiceType == NULL) {
      PRINTERROR("OOM: ServiceType", E_OUTOFMEMORY);
    }
    pqinfo->put_ServiceTypeGuid(bstrServiceType);

    //
    // Put a description to the queue
    // (Useful for administration through the MSMQ admin tools)
    //
    bstrLabel =
      SysAllocString(L"Sample ActiveX application of MSMQ SDK");
    if (bstrLabel == NULL) {
      PRINTERROR("OOM: label ", E_OUTOFMEMORY);
    }
    pqinfo->put_Label(bstrLabel);

    //
    // specify if transactional
    //
    VariantInit(&varIsTransactional);
    varIsTransactional.vt = VT_BOOL;
    varIsTransactional.boolVal = MQ_TRANSACTIONAL_NONE;
    VariantInit(&varIsWorldReadable);
    varIsWorldReadable.vt = VT_BOOL;
    varIsWorldReadable.boolVal = FALSE;
    //
    // create the queue
    //
    hresult = pqinfo->Create(&varIsTransactional, &varIsWorldReadable);
    if (FAILED(hresult)) {
      //
      // API Fails, not because the queue exists
      //
      if (hresult != MQ_ERROR_QUEUE_EXISTS) {
        PRINTERROR("Cannot create queue", hresult);
      }
    }

    //
    // Open the queue for receive access
    //
    hresult = pqinfo->Open(MQ_RECEIVE_ACCESS,
                           MQ_DENY_NONE,
                           &pqReceive);

    //
    // Little bit tricky. MQCreateQueue succeeded but it does not mean
    // that MQOpenQueue will, because of replication delay. The queue is
    // registered in MQIS, but it might take a replication interval
    // until the replica reach the server I am connected to.
    // To overcome this, open the queue in a loop.
    //
    // (in this specific case, this can happen only if this
    //  program is run on a Backup Server Controller - BSC, or on
    //  a client connected to a BSC)
    // To be totally on the safe side, we should have put some code
    // to exit the loop after a few retries, but hey, this is just a sample.
    //
    while (hresult == MQ_ERROR_QUEUE_NOT_FOUND) {
      printf(".");

      // Wait a bit
      Sleep(500);

      // And retry
      hresult = pqinfo->Open(MQ_RECEIVE_ACCESS,
                             MQ_DENY_NONE,
                             &pqReceive);
    }
    if (FAILED(hresult)) {
      PRINTERROR("Cannot open queue", hresult);
    }

    //
    // Main receiver loop
    //
    printf("\nWaiting for messages...\n");
    while (!fQuit) {
      //
      // Receive the message
      //
      VariantInit(&varWantDestQueue);
      VariantInit(&varWantBody);
      VariantInit(&varReceiveTimeout);
      varWantDestQueue.vt = VT_BOOL;
      varWantDestQueue.boolVal = TRUE;    // yes we want the dest queue
      varWantBody.vt = VT_BOOL;
      varWantBody.boolVal = TRUE;         // yes we want the msg body
      varReceiveTimeout.vt = VT_I4;
      varReceiveTimeout.lVal = INFINITE;  // infinite timeout
      hresult = pqReceive->Receive(
                  NULL,
                  &varWantDestQueue,
                  &varWantBody,
                  &varReceiveTimeout,
                  &pmessageReceive);
      if (FAILED(hresult)) {
        PRINTERROR("Receive message", hresult);
      }

      //
      // Display the received message
      //
      pmessageReceive->get_Label(&bstrMsgLabel);
      VariantInit(&varBody);
      VariantInit(&varBody2);
      hresult = pmessageReceive->get_Body(&varBody);
      if (FAILED(hresult)) {
        PRINTERROR("can't get body", hresult);
      }
      hresult = VariantChangeType(&varBody2,
                                  &varBody,
                                  0,
                                  VT_BSTR);
      if (FAILED(hresult)) {
        PRINTERROR("can't convert message to string.", hresult);
      }
      printf("%S : %s\n", bstrMsgLabel, V_BSTR(&varBody2));
      //
      // Check for end of app
      //
      if (stricmp((char *)V_BSTR(&varBody2), "quit") == 0) {
        fQuit = TRUE;
      }

      VariantClear(&varBody);
      VariantClear(&varBody2);

      //
      // release the current message
      //
      RELEASE(pmessageReceive);
    } /* while (1) */

    //
    // Cleanup - Close handle to the queue
    //
    pqReceive->Close();
    if (FAILED(hresult)) {
      PRINTERROR("Cannot close queue", hresult);
    }

    //
    // Finish - Let's delete the queue from the directory service
    // (We don't need to do it. Leaving the queue in the DS, enables
    //  sender applications to send messages even if the receiver is not
    //  available.)
    //
    hresult = pqinfo->Delete();
    if (FAILED(hresult)) {
      PRINTERROR("Cannot delete queue", hresult);
    }
    // fall through...

Cleanup:
    SysFreeString(bstrPathName);
    SysFreeString(bstrMsgLabel);
    SysFreeString(bstrServiceType);
    SysFreeString(bstrLabel);
    RELEASE(pmessageReceive);
    RELEASE(pqReceive);
    RELEASE(pqinfo);
    return hresult;
}

//-----------------------------------------------------
//
// Sender Mode
// -----------
// The sender side does the following:
//    1. Locates all queues of type "guidMQTestType"
//    2. Opens handles to all the queues
//    3. In a loop
//          Sends messages to all those queues
//    4. Cleanup handles
//
//-----------------------------------------------------
HRESULT Sender()
{
    IMSMQQuery *pquery = NULL;
    IMSMQQueueInfo *rgpqinfo[MAX_VAR];
    IMSMQQueue *rgpqSend[MAX_VAR];
    IMSMQQueueInfo *pqinfo = NULL;
    IMSMQQueueInfos *pqinfos = NULL;
    IMSMQMessage *pmessage = NULL;
    char *szBuffer = new char[MAX_BUFFER];
    WCHAR wcsMsgLabel[MQ_MAX_MSG_LABEL_LEN];
    BSTR bstrServiceType = NULL;
    BSTR bstrLabel = NULL;
    BSTR bstrBody = NULL;
    VARIANT varBody;
    DWORD i;
    DWORD cQueue = 0;
    HRESULT hresult = NOERROR;

    printf("\nSender Mode on Machine: %s\n\n", mbsMachineName);

    //
    // create query object for lookup
    //
    hresult = CoCreateInstance(
                   CLSID_MSMQQuery,
                   NULL,      // punkOuter
                   CLSCTX_SERVER,
                   IID_IMSMQQuery,
                   (LPVOID *)&pquery);
    if (FAILED(hresult)) {
      PRINTERROR("Cannot create query", hresult);
    }

    //
    // Prepare parameters to locate a queue: all queues that
    //  match test guid type
    //
    VARIANT varGuidQueue;
    VARIANT varStrLabel;
    VARIANT varGuidServiceType;
    VARIANT varRelServiceType;
    VARIANT varRelLabel;
    VARIANT varCreateTime;
    VARIANT varModifyTime;
    VARIANT varRelCreateTime;
    VARIANT varRelModifyTime;

    VariantInit(&varGuidQueue);
    VariantInit(&varStrLabel);
    VariantInit(&varGuidServiceType);
    VariantInit(&varRelServiceType);
    VariantInit(&varRelLabel);
    VariantInit(&varCreateTime);
    VariantInit(&varModifyTime);
    VariantInit(&varRelCreateTime);
    VariantInit(&varRelModifyTime);

    //
    // We only want to specify service type so we set
    //  the other variant params to VT_ERROR to simulate
    //  "missing", i.e. optional, params.
    //
    V_VT(&varGuidQueue) = VT_ERROR;
    V_VT(&varStrLabel) = VT_ERROR;
    V_VT(&varRelServiceType) = VT_ERROR;
    V_VT(&varRelLabel) = VT_ERROR;
    V_VT(&varCreateTime) = VT_ERROR;
    V_VT(&varModifyTime) = VT_ERROR;
    V_VT(&varRelCreateTime) = VT_ERROR;
    V_VT(&varRelModifyTime) = VT_ERROR;
    bstrServiceType = SysAllocString(strGuidMQTestType);
    if (bstrServiceType == NULL) {
      PRINTERROR("OOM: Service Type", E_OUTOFMEMORY);
    }
    V_VT(&varGuidServiceType) = VT_BSTR;
    V_BSTR(&varGuidServiceType) = bstrServiceType;

    hresult = pquery->LookupQueue(&varGuidQueue,
                                  &varGuidServiceType,
                                  &varStrLabel,
                                  &varCreateTime,
                                  &varModifyTime,
                                  &varRelServiceType,
                                  &varRelLabel,
                                  &varRelCreateTime,
                                  &varRelModifyTime,
                                  &pqinfos);
    if (FAILED(hresult)) {
      PRINTERROR("LookupQueue failed", hresult);
    }

    //
    // reset the queue collection
    //
    hresult = pqinfos->Reset();
    if (FAILED(hresult)) {
      PRINTERROR("Reset failed", hresult);
    }

    //
    // Open each of the queues found
    //
    i = 0;
    hresult = pqinfos->Next(&rgpqinfo[i]);
    if (FAILED(hresult)) {
      PRINTERROR("Next failed", hresult);
    }
    pqinfo = rgpqinfo[i];
    while (pqinfo) {
      //
      // Open the queue for send access
      //
      hresult = pqinfo->Open(
                  MQ_SEND_ACCESS,
                  MQ_DENY_NONE,
                  &rgpqSend[i]);
      if (FAILED(hresult)) {
        PRINTERROR("Open failed", hresult);
      }
      i++;
      hresult = pqinfos->Next(&rgpqinfo[i]);
      if (FAILED(hresult)) {
        PRINTERROR("Next failed", hresult);
      }
      pqinfo = rgpqinfo[i];
    }
    cQueue = i;
    if (cQueue == 0) {
      //
      // Could Not find any queue, so exit
      //
      PRINTERROR("No queue registered", hresult = E_INVALIDARG);
    }
    printf("\t%d queue(s) found.\n", cQueue);
    printf("\nEnter \"quit\" to exit\n");

    //
    // Build the message label property
    //
    swprintf(wcsMsgLabel, L"Message from %S", mbsMachineName);

    //
    // Main sender loop
    //
    while (1) {
      //
      // Get a string from the console
      //
      printf("Enter a string: ");
      if (gets(szBuffer) == NULL)
        break;

      //
      // create a message object
      //
      hresult = CoCreateInstance(
                     CLSID_MSMQMessage,
                     NULL,      // punkOuter
                     CLSCTX_SERVER,
                     IID_IMSMQMessage,
                     (LPVOID *)&pmessage);
      //
      // Send the message to all the queues
      //
      for (i = 0; i < cQueue; i++) {
        bstrLabel = SysAllocString(wcsMsgLabel);
        if (bstrLabel == NULL) {
          PRINTERROR("OOM: label", E_OUTOFMEMORY);
        }
        hresult = pmessage->put_Label(bstrLabel);

        //
        // This isn't a "true" unicode string of course...
        //
        bstrBody = SysAllocStringByteLen(szBuffer, strlen(szBuffer)+1);
        if (bstrBody == NULL) {
          PRINTERROR("OOM: body", E_OUTOFMEMORY);
        }
        VariantInit(&varBody);
        V_VT(&varBody) = VT_BSTR;
        V_BSTR(&varBody) = bstrBody;
        hresult = pmessage->put_Body(varBody);
        if (FAILED(hresult)) {
          PRINTERROR("put_body failed", hresult);
        }
        hresult = pmessage->Send(rgpqSend[i], NULL);
        if (FAILED(hresult)) {
          PRINTERROR("Send failed", hresult);
        }
        VariantClear(&varBody);
        bstrBody = NULL;
      }
      RELEASE(pmessage);

      //
      // Check for end of app
      //
      if (stricmp(szBuffer, "quit") == 0)
        break;
    } /* while (1) */

Cleanup:
    //
    // Close and release all the queues
    //
    for (i = 0; i < cQueue; i++) {
      rgpqSend[i]->Close();
      rgpqSend[i]->Release();
      rgpqinfo[i]->Release();
    }
    RELEASE(pqinfos);
    RELEASE(pquery);
    RELEASE(pmessage);
    SysFreeString(bstrLabel);
    SysFreeString(bstrBody);
    SysFreeString(bstrServiceType);
    delete [] szBuffer;
    return hresult;
}

//-----------------------------------------------------
//
//  MAIN
//
//-----------------------------------------------------
int main(int argc, char * * argv)
{
    DWORD dwNumChars;
    HRESULT hresult = NOERROR;

    if (argc != 2)
      return Syntax();

    hresult = OleInitialize(NULL);
    if (FAILED(hresult)) {
      PRINTERROR("Cannot init OLE", hresult);
    }

    //
    // Retrieve machine name
    //
    dwNumChars = MAX_COMPUTERNAME_LENGTH + 1;
    GetComputerNameA(mbsMachineName, &dwNumChars);

    if (strcmp(argv[1], "-s") == 0)
      hresult = Sender();

    else if (strcmp(argv[1], "-r") == 0)
      hresult = Receiver();

    else
      hresult = Syntax();

    printf("\nOK\n");

    // fall through...

Cleanup:
    return (int)hresult;
}

void PrintError(char *s, HRESULT hr)
{
    printf("Cleanup: %s (0x%X)\n", s, hr);
}

HRESULT Syntax()
{
    printf("\n");
    printf("Syntax: mqtestoa -s | -r\n");
    printf("\t-s: Sender\n");
    printf("\t-r: Receiver\n");
    return E_INVALIDARG;
}


MQTRANS.CPP

            /////////////////////////////////////
            //                                 //
            // Transactions Sample Application //
            //                                 //
            /////////////////////////////////////

#define UNICODE                     // For all MSMQ applications

#include <stdio.h>

//------------------------------------------------------------------------------
// Include MS DTC specific header files.
//------------------------------------------------------------------------------
#define INITGUID
#include <transact.h>

// Because we are compiling in UNICODE, here is a problem with DTC...
//#include <xolehlp.h>
extern HRESULT DtcGetTransactionManager(
                                    LPSTR  pszHost,
                                    LPSTR   pszTmName,
                                    REFIID rid,
                                    DWORD   dwReserved1,
                                    WORD    wcbReserved2,
                                    void FAR * pvReserved2,
                                    void** ppvObject )  ;

//------------------------------------------------------------------------------
// Include ODBC specific header file.
//------------------------------------------------------------------------------
#ifndef DBNTWIN32
#define DBNTWIN32

#include <SQLEXT.h>

// from  <odbcss.h>
#define SQL_COPT_SS_BASE               1200
#define SQL_COPT_SS_ENLIST_IN_DTC      (SQL_COPT_SS_BASE+7) // Enlist in a Viper transaction

// Defines for use with SQL_ENLIST_IN_DTC
#define SQL_DTC_DONE 0L       // Delimits end of Viper transaction

#endif

//--------------------------------------------------------------------------
//  Enable Ansi ODBC on VC5
//--------------------------------------------------------------------------

#ifdef  SQLExecDirect
#undef  SQLExecDirect
#define SQLExecDirect SQLExecDirectA
#endif

#ifdef  SQLSetConnectOption
#undef  SQLSetConnectOption
#define SQLSetConnectOption  SQLSetConnectOptionA
#endif

#ifdef  SQLError
#undef  SQLError
#define SQLError  SQLErrorA
#endif

#ifdef  SQLConnect
#undef  SQLConnect
#define SQLConnect  SQLConnectA
#endif

//------------------------------------------------------------------------------
// Include MSMQ specific header file.
//------------------------------------------------------------------------------
#include "mq.h"

//------------------------------------------------------------------------------
// Define constants
//------------------------------------------------------------------------------
#define STR_LEN      40
#define MAX_VAR      20
#define MAX_FORMAT  100

//------------------------------------------------------------------------------
// Define datatypes
//------------------------------------------------------------------------------
typedef struct DBCONN
{
   char  pszSrv [STR_LEN];    // data source name, configured through control panel
   char  pszUser [STR_LEN];   // Login user name
   char  pszPasswd[STR_LEN];  // Login user password
   HDBC  hdbc;                // handle to an ODBC database connection
   HSTMT hstmt;               // an ODBC statement handle, for use with SQLExecDirect

}  DBCONN;

//------------------------------------------------------------------------------
// Define Globals
//------------------------------------------------------------------------------

// global DB connection struct for the server
static DBCONN  gSrv =
   {  "MSMQDemo",
      "sa",
      "",
      SQL_NULL_HDBC,
      SQL_NULL_HSTMT
   };

// guid type for MQTransTest queues
static CLSID guidMQTransTestType =
{ 0xb856ab1, 0x16b6, 0x11d0, { 0x80, 0x48, 0x0, 0xa0, 0x24, 0x53, 0xc1, 0x6f } };

//handle to ODBC environment
HENV  g_hEnv = SQL_NULL_HENV ;

//buffer for machine name
WCHAR g_wszMachineName[ MAX_COMPUTERNAME_LENGTH + 1 ];

//--------------------------------------------------------------------------
// Forward declaration of routines used.
//--------------------------------------------------------------------------

void LogonToDB(DBCONN *ptr);
void ExecuteStatement(DBCONN *ptr, char* pszBuf, BOOL ProcessFlag);
BOOL ProcessRetCode(char*   pszFuncName,
                    DBCONN  *ptr,
                    RETCODE retcode,
                    BOOL    fExit = TRUE);
void DoSQLError(DBCONN *ptr);
void FreeODBCHandles(DBCONN *ptr);
void Error(char *s, HRESULT hr);
void Syntax();
void LocateTargetQueue(CLSID *pGuidType, WCHAR wsFormat[MAX_FORMAT]);
void PrepareSendMessageProperties(MSGPROPID     amPropId[MAX_VAR],
                          MQPROPVARIANT aPropVar[MAX_VAR],
                          MQMSGPROPS    &msgprops,
                          DWORD        &TransferSum);
void CreateQueue(CLSID *pGuidType, WCHAR wsFormat[]);
void GetMachineName();
void DisplayDollars (DBCONN *ptr, char *psAccount);

//------------------------------------------------------------------------------
// SENDER MODE:
//
// The Sender side does the following:
//    1. Creates database "SenderAccount".
//    2. Locates a MSMQ queue of type MQTransTest and opens it.
//       (NOTE: for simplicity, this sample assumes there's only one queue of this type)
//   3. In a loop:
//            Prompts the user to enter TransferSum.
//            Creates a transaction using MS DTC.
//            Within the transaction:
//                 Updates "SenderAccount" database (subtracts TransferSum).
//                 Sends a message to Receiver side.
//            Commits the transaction.
//
//    4. Cleanup.
//
//
//
// The transaction in the Sender mode includes two operations:
// (1) Update "SenderAccount" database (subtract TransferSum).
// (2) Send message to Receiver side.
//------------------------------------------------------------------------------

void Sender()
{

   ITransactionDispenser   *pTransactionDispenser;
   ITransaction            *pTransaction;
   BOOL                    fTransactionCommitFlag;
                            // used to decide wother to Commit or Abort

   HRESULT              hr;
   RETCODE              retcode;
   DWORD                dwTransferSum;              // set by user
   char                 sUserString[ STR_LEN ];
   char                 sSQLStatement[ STR_LEN*2 ];

   MQMSGPROPS           msgprops;
   MQPROPVARIANT        aPropVar[MAX_VAR];
   MSGPROPID            amPropId[MAX_VAR];
   WCHAR                wsFormat[MAX_FORMAT];
   QUEUEHANDLE          aqh;

    printf("\nSender Side.\n\n");

   //---------------------------------------------------------------------
   // Build "SenderAccount" database (with the sum $1000)
   //---------------------------------------------------------------------

   printf ("Building SenderAccount with the sum $1000...   ");

   // Get ODBC environment handle
   retcode = SQLAllocEnv(&g_hEnv);

   ProcessRetCode("SQLAllocEnv",0, retcode);

   // Establish connection to database
   LogonToDB(&gSrv);

   // Clear database from previous run.
   ExecuteStatement(&gSrv,"DROP TABLE SenderAccount",FALSE);

   // Create new table in database
   ExecuteStatement(&gSrv,
    "CREATE TABLE SenderAccount (Rate INTEGER CONSTRAINT c1 CHECK (Rate>=0))",TRUE);

   // Insert new data in database
   ExecuteStatement(&gSrv,"INSERT INTO SenderAccount   VALUES(1000)",TRUE);

   printf ("OK.\n\n");

   //-----------------------------------------------------------------------
   // Locate target queue and Open it for send
   //-----------------------------------------------------------------------

   printf ("Searching Receiver queue...   ");

   // Locate target queue
   LocateTargetQueue (&guidMQTransTestType, wsFormat);

   // Open target queue
   hr = MQOpenQueue(wsFormat, MQ_SEND_ACCESS, 0, &aqh);

   if (FAILED(hr))
   {
      Error ("Open Queue ",hr);
   }

   //--------------------------------------------------------------------------
   // Get Transaction Dispenser
   //--------------------------------------------------------------------------

   // Obtain an interface pointer from MS DTC proxy
   hr = DtcGetTransactionManager(
               NULL,                        // pszHost
               NULL,                        // pszTmName
               IID_ITransactionDispenser,   // IID of  interface
               0,                           // Reserved -- must be null
               0,                           // Reserved -- must be null
               0,                           // Reserved -- must be null
               (void **)&pTransactionDispenser  // pointer to pointer to requested interface
                                 );

   if (FAILED(hr))
   {
      Error ("DTCGetTransactionManager",hr);
   }

   //--------------------------------------------------------------------
   // Sender Main Loop
   //--------------------------------------------------------------------
   while (TRUE)
   {

      // Prompt user to enter TransferSum
      printf ("\n\nPlease enter the sum of dollars to transfer, or '0' to quit  ==> ");

      // Read user input
      fgets (sUserString, STR_LEN, stdin);

      // Convert user string to DWORD
      dwTransferSum = atoi(sUserString);

      // Prepare properties of message to send
      PrepareSendMessageProperties (amPropId,
                                    aPropVar,
                                    msgprops,
                                    dwTransferSum);

      //---------------------------------------------------------------------
      // Create transaction (Inside Sender's Main Loop)
      //---------------------------------------------------------------------

      printf ("\nStarting transaction...\n\n");

      // Initiate an MS DTC transaction
      hr = pTransactionDispenser->BeginTransaction (
            0,                         // must be null
            ISOLATIONLEVEL_ISOLATED,   // Isolation Level
            ISOFLAG_RETAIN_DONTCARE,   // Isolation flags
            0,                         // pointer to transaction options object
            &pTransaction);            // pointer to pointer to transaction object

      if (FAILED(hr))
      {
         Error ("BeginTransaction",hr);
      }

      // Default is to commit transaction
      fTransactionCommitFlag = TRUE;

      //
      // SQL is a resource manager in the transaction.
      // It must be enlisted.
      //

      // Enlist database in the transaction
      retcode = SQLSetConnectOption (gSrv.hdbc,
                                     SQL_COPT_SS_ENLIST_IN_DTC,
                                     (UDWORD)pTransaction);

      if (retcode != SQL_SUCCESS)
      {
         ProcessRetCode("SQLSetConnection", &gSrv, retcode, FALSE);
         fTransactionCommitFlag = FALSE;
      }

      // Prepare SQL statement to update SenderAccount
      sprintf (sSQLStatement,
               "UPDATE SenderAccount  SET Rate = Rate - %lu", dwTransferSum) ;

      // Allocate a statement handle for use with SQLExecDirect
      retcode = SQLAllocStmt(gSrv.hdbc, &gSrv.hstmt);

      if (retcode != SQL_SUCCESS)
      {
         ProcessRetCode("SQLAllocStmt", &gSrv, retcode, FALSE);
         fTransactionCommitFlag = FALSE;
      }

      // Update database  (subtract TransferSum from SenderAccount)
      retcode = SQLExecDirect (gSrv.hstmt,(UCHAR *) sSQLStatement, SQL_NTS);

      if (retcode != SQL_SUCCESS)
      {
         ProcessRetCode("SQLExecDirect", &gSrv, retcode, FALSE);
         fTransactionCommitFlag = FALSE;
      }

      // Free the statement handle
      retcode = SQLFreeStmt(gSrv.hstmt, SQL_DROP);

      gSrv.hstmt = SQL_NULL_HSTMT;

      //
      // MSMQ is another resource manager in the transaction.
      // Its enlistment is implicit.
      //

      // Within the transaction: Send message to Receiver Side
      hr = MQSendMessage(aqh,              // Handle to destination queue
                         &msgprops,        // pointer to MQMSGPROPS structure
                         pTransaction);    // pointer to Transaction Object

      if (FAILED(hr))
      {
         printf("\nFailed in MQSendMessage(). hresult- %lxh\n", (DWORD) hr) ;
         fTransactionCommitFlag = FALSE;
      }

      // Commit the transaction
      if (fTransactionCommitFlag)
      {
         printf ("Committing the transaction...   ");

         hr = pTransaction->Commit(0, 0, 0);

         if (FAILED(hr))
            printf ("Failed... Transaction aborted.\n\n");
         else
            printf ("Transaction committed successfully.\n\n");

      }
      else
      {
         printf ("Aborting the transaction...   ");

         hr = pTransaction->Abort(0, 0, 0);

         if (FAILED(hr))
            Error("Transaction Abort",hr);
         else
            printf ("Transaction aborted.\n\n");
      }

      // Release the transaction
      pTransaction->Release();

      // End enlistment of database
      retcode = SQLSetConnectOption (gSrv.hdbc, SQL_COPT_SS_ENLIST_IN_DTC, SQL_DTC_DONE);

      ProcessRetCode ("SQLSetConnectOption", &gSrv, retcode);

      // Display sum of dollars in Sender Account
      DisplayDollars (&gSrv,"SenderAccount");

      // quit loop when nothing was transferred.
      if (dwTransferSum == 0)
         break;
   }

   //--------------------------------------------------------------------------
   // Cleanup
   //--------------------------------------------------------------------------

   // Release Transaction Dispenser
   pTransactionDispenser->Release();

   // Free database
   ExecuteStatement(&gSrv,"DROP TABLE SenderAccount",TRUE);

   // Free ODBC handle
   FreeODBCHandles(&gSrv);

   // Free the ODBC environment handle
   retcode = SQLFreeEnv(g_hEnv);

   if (retcode == SQL_ERROR)
      Error ("SQL FreeEnv ",0);

   // Free MSMQ queue handle
   MQCloseQueue(aqh);

   printf ("\n\nSender Side completed.\n\n");

}

//------------------------------------------------------------------------------
// RECEIVER MODE:
//
// The Receiver side does the following:
//    1. Creates database "ReceiverAccount".
//    2. Creates a MSMQ public queue (with the Transactional property)
//       of type MQTransTest on its own machine and opens it.
//    3. In a loop:
//            Creates a transaction using MS DTC.
//            Within the transaction:
//                 Receives a message from the queue (with the TransferSum).
//                 Updates "ReceiverAccount" database (adds TransferSum).
//            Commits the transaction.
//
//    4. Cleanup.
//
//
//
// The transaction in the Receiver mode include two operations:
// (1) Receive message from queue (sent by Sender Side).
// (2) Update "ReceiverAccount" database  (add TransferSum).
//------------------------------------------------------------------------------

void Receiver()
{
   MSGPROPID            amPropId[MAX_VAR];
   MQMSGPROPS           msgprops;
   MQPROPVARIANT        aPropVar[MAX_VAR];
   DWORD             cProps;
   HRESULT              hr;
   WCHAR             wsFormat[MAX_FORMAT];
   QUEUEHANDLE          aqh;

   ITransactionDispenser   *pTransactionDispenser;
   ITransaction         *pTransaction;
   BOOL              TransactionCommitFlag;  // used to decide Commit or Abort

   RETCODE              retcode;
   DWORD             TransferSum;

   DWORD             MessageBuffer;       // message body is the TransferSum
   char              sSQLStatement[STR_LEN*2];

   printf ("\nReceiver Side.\n\n");

   //-----------------------------------------------------------------------
   // Build "ReceiverAccount" database (with the rate $500)
   //-----------------------------------------------------------------------

   printf ("Building ReceiverAccount with the rate $500...   ");

   // Get ODBC environment handle
   retcode = SQLAllocEnv(&g_hEnv);

   ProcessRetCode("SQLAllocEnv",0, retcode);

   // Establish connection to database.
   LogonToDB(&gSrv);

   // Clear table from previous run.
   ExecuteStatement(&gSrv,"DROP TABLE ReceiverAccount",FALSE);

   // Create new table.
   ExecuteStatement(&gSrv,"CREATE TABLE ReceiverAccount (Rate INTEGER CONSTRAINT c2 CHECK (Rate>0))",TRUE);

   // Insert new data in the table.
   ExecuteStatement(&gSrv,"INSERT INTO ReceiverAccount  VALUES(500)",TRUE);

   printf ("OK.\n\n");

   //-----------------------------------------------------------------------
   // Create queue and Open it for receive
   //-----------------------------------------------------------------------

   printf ("Creating Receiver queue...   ");

   // Create the queue
   CreateQueue (&guidMQTransTestType, wsFormat);

   // Prepare message properties to read
   cProps = 0;

   amPropId[cProps] =             PROPID_M_BODY;

   aPropVar[cProps].vt =          VT_UI1 | VT_VECTOR;
   aPropVar[cProps].caub.cElems =  sizeof(MessageBuffer);
   aPropVar[cProps].caub.pElems =  (unsigned char *)&MessageBuffer;
   cProps++;

   // Create a MSGPROPS structure
   msgprops.cProp =    cProps;
   msgprops.aPropID =  amPropId;
   msgprops.aPropVar = aPropVar;
   msgprops.aStatus =  0;

   // Open the queue
   hr = MQOpenQueue(wsFormat, MQ_RECEIVE_ACCESS, 0, &aqh);

   //
   // Little bit tricky. MQCreateQueue succeeded but it does not mean
   // that MQOpenQueue will, because of replication delay. The queue is
   // registered in MQIS, but it might take a replication interval
   // until the replica reach the server I am connected to.
   // To overcome this, open the queue in a loop.
   //
   if (hr == MQ_ERROR_QUEUE_NOT_FOUND)
   {
       int iCount = 0 ;
       while((hr == MQ_ERROR_QUEUE_NOT_FOUND) && (iCount < 120))
       {
          printf(".");

          // Wait a bit
          iCount++ ;
          Sleep(500);

          // And retry
          hr = MQOpenQueue(wsFormat, MQ_RECEIVE_ACCESS, 0, &aqh);
       }
   }

   if (FAILED(hr))
   {
      Error ("Can't OpenQueue", hr);
   }

   printf("OK.");

   //--------------------------------------------------------------------------
   // Get Transaction Dispenser
   //--------------------------------------------------------------------------

   // Obtain an interface pointer from MS DTC proxy
   hr = DtcGetTransactionManager(
         NULL, NULL, // pszHost, pszTmName
         IID_ITransactionDispenser,         // IID of requested interface
         0,0,0,                             // Reserved -- must be null
         (void **)&pTransactionDispenser);  // pointer to pointer to requested interface

   if (FAILED(hr))
      Error ("DTCGetTransactionManager",hr);

   //--------------------------------------------------------------------------
   // Receiver Main Loop
   //--------------------------------------------------------------------------
   while (TRUE)
   {

      printf ("\n\nWaiting for a message to come...   ");

      // Peek outside the transaction, to avoid database lock
      // for long/infinite period.
      //
      //dwSize = sizeof(wsResponse);
      hr = MQReceiveMessage(
                    aqh,                     // Handle to queue
                    INFINITE,                // Timeout
                    MQ_ACTION_PEEK_CURRENT,  // Peek Action
                    &msgprops,               // Message Properties
                    NULL,                    // Overlap
                    NULL,                    // Receive Callback
                    NULL,                    // Cursor
                    NULL                     // No transaction yet
                           );

      if (FAILED(hr))
         Error("MQReceiveMessage (PEEKING) ",hr);

      //--------------------------------------------------------------------------
      // Create transaction
      //--------------------------------------------------------------------------
      printf ("\n\nStarting transaction...\n\n");

      // Initiate an MS DTC transaction
      hr = pTransactionDispenser->BeginTransaction (
            0,                                 // must be null
            ISOLATIONLEVEL_ISOLATED,         // Isolation Level
            ISOFLAG_RETAIN_DONTCARE,         // Isolation flags
            0,                                 // pointer to transaction options object
            &pTransaction);                    // pointer to pointer to transaction object

      if (FAILED(hr))
         Error ("BeginTransaction",hr);

      // Default is to commit transaction
      TransactionCommitFlag = TRUE;

      //
      // SQL is a resource manager in the transaction.
      // It must be enlisted.
      //

      // Enlist database in the transaction
      retcode = SQLSetConnectOption (gSrv.hdbc, SQL_COPT_SS_ENLIST_IN_DTC, (UDWORD)pTransaction);

      if (retcode != SQL_SUCCESS)
         TransactionCommitFlag = FALSE;

      // Receive the message from the queue
      //dwSize = sizeof(wsResponse);
      hr = MQReceiveMessage(
            aqh,                      // Handle to queue
            INFINITE,                         // Timeout
            MQ_ACTION_RECEIVE,                // Receive Action
            &msgprops,                   // Message Properties
            NULL,NULL,NULL,                   // Overlap, Receive Callback, Cursor
            pTransaction);                    // pointer to transaction object

      if (FAILED(hr))
         TransactionCommitFlag = FALSE;

      // Message buffer holds the TransferSum
      TransferSum = (DWORD)MessageBuffer;

      // Prepare SQL statement to update ReceiverAccount
      sprintf (sSQLStatement, "UPDATE ReceiverAccount   SET Rate = Rate + %i",TransferSum);

      // Allocate a statement handle for use with SQLExecDirect
      retcode = SQLAllocStmt(gSrv.hdbc,&gSrv.hstmt);

      if (retcode != SQL_SUCCESS)
         TransactionCommitFlag = FALSE;

      // Update database  (add TransferSum to ReceiverAccount)
      retcode = SQLExecDirect (gSrv.hstmt,(UCHAR *) sSQLStatement, SQL_NTS);

      if (retcode != SQL_SUCCESS)
         TransactionCommitFlag = FALSE;

      // Free the statement handle
      retcode = SQLFreeStmt(gSrv.hstmt, SQL_DROP);

      gSrv.hstmt = SQL_NULL_HSTMT;

      // Commit the transaction
      if (TransactionCommitFlag)
      {
         printf ("Committing the transaction...   ");

         hr = pTransaction->Commit(0, 0, 0);

         if (FAILED(hr))
            printf ("Failed... Transaction aborted.\n\n");
         else
            printf ("Transaction committed successfully.\n\n");

      }

      // Abort the transaction
      else
      {
         printf ("Aborting the transaction...   ");

         hr = pTransaction->Abort(0, 0, 0);

         if (FAILED(hr))
            Error("Transaction Abort",hr);
         else
            printf ("Transaction aborted.\n\n");

      }

      // Release the transaction
      pTransaction->Release();

      // End enlistment of database
      retcode = SQLSetConnectOption (gSrv.hdbc, SQL_COPT_SS_ENLIST_IN_DTC, SQL_DTC_DONE);

      ProcessRetCode ("SQLSetConnectOption", &gSrv, retcode);

      // Display sum of dollars in Receiver Account
      DisplayDollars (&gSrv, "ReceiverAccount");

      // Decide if to continue loop
      if (TransferSum == 0)
         break;

   }

   //--------------------------------------------------------------------------
   // Cleanup
   //--------------------------------------------------------------------------

   // Release Transaction Dispenser
   pTransactionDispenser->Release();

   // Free database
   ExecuteStatement(&gSrv,"DROP TABLE ReceiverAccount",TRUE);

   // Free ODBC handle
   FreeODBCHandles(&gSrv);

   // Free the ODBC environment handle
   retcode = SQLFreeEnv(g_hEnv);

   if (retcode == SQL_ERROR)
      Error ("SQL FreeEnv ",0);

   // Free queue handle
    MQCloseQueue(aqh);

   // Delete queue from directory
   MQDeleteQueue(wsFormat);

   printf ("\n\nReceiver Side completed.\n\n");
}

//------------------------------------------------------------------------------
//  MAIN
//------------------------------------------------------------------------------
main(int argc, char * * argv)
{
   DWORD dwSize;

    if(argc != 2)
        Syntax();

    // Retrieve machine name
    dwSize = sizeof(g_wszMachineName);
    GetComputerName(g_wszMachineName, &dwSize);

    if(strcmp(argv[1], "-s") == 0)
        Sender();

    else if(strcmp(argv[1], "-r") == 0)
        Receiver();

    else
        Syntax();

    return(1);

}

//------------------------------------------------------------------------------
// Subroutines
//------------------------------------------------------------------------------

void Error(char *s, HRESULT hr)
{

    printf("\n\nError: %s (0x%X)  \n", s, hr);
    exit(1);
}

//------------------------------------------------------------------------------

void Syntax()
{
    printf("\n");
    printf("Syntax: msmqtrans -s | -r\n");
    printf("\t-s - Sender Side\n");
    printf("\t-r - Receiver Side\n");
    exit(1);

}

//------------------------------------------------------------------------------

void LocateTargetQueue (CLSID *pGuidType, WCHAR wsFormat[MAX_FORMAT])
{

   DWORD      dwSize;
   DWORD      i;

   DWORD      cQueue;
   DWORD      cProps;
   HRESULT       hr;
   MQPROPERTYRESTRICTION   aPropRestriction[MAX_VAR];
   MQRESTRICTION        Restriction;
   MQCOLUMNSET          Column;
   QUEUEPROPID          aqPropId[MAX_VAR];
   HANDLE               hEnum;
   MQPROPVARIANT        aPropVar[MAX_VAR];

   //--------------------------------------------------------------------------
    // Prepare Parameters to locate a queue
    //--------------------------------------------------------------------------

    // 1. Restriction = All queue with PROPID_TYPE
    //            equal the type of MQTransTest queue.
    cProps = 0;

    aPropRestriction[cProps].rel = PREQ;
    aPropRestriction[cProps].prop = PROPID_Q_TYPE;
    aPropRestriction[cProps].prval.vt = VT_CLSID;
    aPropRestriction[cProps].prval.puuid = pGuidType;
    cProps++;

    Restriction.cRes = cProps;
    Restriction.paPropRes = aPropRestriction;

    // 2. Columnset (In other words what property I want to retrieve).
    //   Only the instance is important.
    cProps = 0;
    aqPropId[cProps] = PROPID_Q_INSTANCE;
    cProps++;

    Column.cCol = cProps;
    Column.aCol = aqPropId;

    //--------------------------------------------------------------------------
    // Locate the queues. Issue the query
    //--------------------------------------------------------------------------
    hr = MQLocateBegin(NULL,&Restriction,&Column,NULL,&hEnum);

   if (FAILED(hr))
      Error ("Locate Begin ",hr);

    //--------------------------------------------------------------------------
    // Get the results
    //--------------------------------------------------------------------------
    cQueue = MAX_VAR;
    hr = MQLocateNext(hEnum, &cQueue, aPropVar);

   if (FAILED(hr))
      Error ("MQLocateNext ",hr);

    hr = MQLocateEnd(hEnum);

    if(cQueue == 0)
    {
        // Could Not find any queue, so exit
        printf("NOT FOUND...   exiting.\n\n");
        exit(0);
    }

    printf("FOUND.", cQueue);

    dwSize = sizeof(WCHAR)*MAX_FORMAT;

   //Transform the Instance GUID to format name
   hr = MQInstanceToFormatName(aPropVar[0].puuid, wsFormat, &dwSize);

   if (FAILED(hr))
      Error ("Guidto Format Name ",hr);

   // Free the GUID memory that was allocated during the locate
    for(i = 0; i < cQueue; i++)
      MQFreeMemory(aPropVar[i].puuid);

}

//------------------------------------------------------------------------------

void PrepareSendMessageProperties (MSGPROPID     amPropId[MAX_VAR],
                           MQPROPVARIANT aPropVar[MAX_VAR],
                           MQMSGPROPS    &msgprops,
                           DWORD     &TransferSum)
{

   DWORD      cProps;

    cProps = 0;
    amPropId[cProps] =             PROPID_M_BODY;
    aPropVar[cProps].vt =          VT_UI1 | VT_VECTOR;
    aPropVar[cProps].caub.cElems =  sizeof(TransferSum);
    aPropVar[cProps].caub.pElems =  (unsigned char *)&TransferSum;
    cProps++;

    // Create a MSGPROPS structure
    msgprops.cProp =    cProps;

msgprops.aPropID =  amPropId;
    msgprops.aPropVar = aPropVar;
    msgprops.aStatus =  0;

}

//--------------------------------------------------------------------------

void  CreateQueue (CLSID *pGuidType, WCHAR wsFormat[])
{
   QUEUEPROPID       aqPropId[MAX_VAR];
   WCHAR             wsPathName[1000];  //Big path name
   MQPROPVARIANT     aPropVar[MAX_VAR];
   DWORD             cProps;
   MQQUEUEPROPS      qprops;
   DWORD             dwSize;
   HRESULT           hr;

   //---------------------------------------------------------------------
   // Prepare properties to create a queue on local machine
   //---------------------------------------------------------------------
   cProps = 0;

   // Set the PathName
   aqPropId[cProps] =          PROPID_Q_PATHNAME;

   wsprintf(wsPathName, TEXT("%s\\MSMQDemo"), g_wszMachineName);
   aPropVar[cProps].vt =       VT_LPWSTR;
   aPropVar[cProps].pwszVal =  wsPathName;
   cProps++;

   // Set the queue to transactional
   aqPropId[cProps] =        PROPID_Q_TRANSACTION;

   aPropVar[cProps].vt =     VT_UI1;
   aPropVar[cProps].bVal =   MQ_TRANSACTIONAL;
   cProps++;

   // Set the type of the queue (Will be used to locate queues of this type)
   aqPropId[cProps] =        PROPID_Q_TYPE;

   aPropVar[cProps].vt =     VT_CLSID;
   aPropVar[cProps].puuid =  pGuidType;
   cProps++;

   // Create a QUEUEPROPS structure
   qprops.cProp =    cProps;
   qprops.aPropID =  aqPropId;
   qprops.aPropVar = aPropVar;
   qprops.aStatus =  0;

   //-----------------------------------------------------------------------
   // Create the queue
   //-----------------------------------------------------------------------
   dwSize = sizeof(WCHAR)*MAX_FORMAT;
   hr = MQCreateQueue(NULL, &qprops, wsFormat, &dwSize);

   if(FAILED(hr))
   {
      // API Fails, not because the queue exists
      if(hr != MQ_ERROR_QUEUE_EXISTS)
            Error("Cannot create queue.", hr);

      // Queue exist, so get its format name
      // Note: Since queue already exists, this sample assumes
      // that it was created earlier by this program, so we
      // do not check if queue is transactional. If at this point the
      // queue is Not Transactional, the transactions will abort later...
      //
      hr = MQPathNameToFormatName(wsPathName, wsFormat, &dwSize);

      if (FAILED(hr))
         Error ("Cannot retrieve format name",hr);
   }
}

//-------------------------------------------------------------------------------

void LogonToDB(DBCONN *ptr)
{
   RETCODE retcode = 0;

   retcode = SQLAllocConnect(g_hEnv, &(ptr->hdbc) );

   if (ProcessRetCode("SQLAllocConnect",ptr,retcode))
   {
      retcode = SQLConnect(ptr->hdbc,
                  (UCHAR *)(ptr->pszSrv),
                  SQL_NTS,
                  (UCHAR *)(ptr->pszUser),
                  SQL_NTS,
                  (UCHAR *)(ptr->pszPasswd),
                  SQL_NTS
                  );

      ProcessRetCode("SQLConnect",ptr,retcode);
   }
}

//------------------------------------------------------------------------------

void ExecuteStatement(DBCONN *ptr, char* pszBuf,BOOL ProcessFlag)
{
   RETCODE retcode = 0;

   // Allocate a statement handle for use with SQLExecDirect
   retcode = SQLAllocStmt(ptr->hdbc,&(ptr->hstmt));

   if (ProcessFlag)
      ProcessRetCode("SQLAllocStmt",ptr,retcode);

   // Execute the passed string as a SQL statement
    retcode = SQLExecDirect (ptr->hstmt,(UCHAR *) pszBuf,SQL_NTS);

   if (ProcessFlag)
      ProcessRetCode("SQLExecDirect",ptr,retcode);

   // Free the statement handle
   retcode = SQLFreeStmt(ptr->hstmt, SQL_DROP);
   ptr->hstmt = SQL_NULL_HSTMT;

   if (ProcessFlag)
      ProcessRetCode("SQLFreeStmt",ptr,retcode);

}

// ---------------------------------------------------------------------------

void DisplayDollars (DBCONN *ptr, char *psAccount)
{

   DWORD             DollarsSum;               // in SQL database
   SDWORD               cbValue;                  // OUT argument for SQL query
   char              sSQLStatement[STR_LEN*2];
   RETCODE              retcode;

   // Allocate a statement handle for use with SQLExecDirect
   retcode = SQLAllocStmt(ptr->hdbc,&(ptr->hstmt));

   ProcessRetCode("SQLAllocStmt",ptr,retcode);

   // Prepare SQL Statement to issue query
   sprintf (sSQLStatement, "SELECT * FROM %s", psAccount);

   // Issue SQL query
   retcode = SQLExecDirect (ptr->hstmt,(UCHAR *)sSQLStatement,SQL_NTS);

   ProcessRetCode ("SQLExecDirect",ptr,retcode);

   // Prepare data structure to retrieve query results
   retcode = SQLBindCol(ptr->hstmt,1,SQL_C_ULONG,&DollarsSum,0,&cbValue);

   ProcessRetCode ("SQLBindCol",ptr,retcode);

   // Retrieve query results
   retcode = SQLFetch (ptr->hstmt);

   ProcessRetCode ("SQLFetch",ptr,retcode);

   // Display query results
   printf ("Sum of dollars in %s is %d .\n\n",psAccount,DollarsSum);

   // Free the statement handle
   retcode = SQLFreeStmt(ptr->hstmt, SQL_DROP);
   ptr->hstmt = SQL_NULL_HSTMT;

   ProcessRetCode("SQLFreeStmt",ptr,retcode);

}

// ---------------------------------------------------------------------------

void FreeODBCHandles(DBCONN *ptr)
{
   SQLDisconnect(ptr->hdbc);

   SQLFreeConnect(ptr->hdbc);

   ptr->hdbc   = SQL_NULL_HDBC;
   ptr->hstmt  = SQL_NULL_HSTMT;
}

// ---------------------------------------------------------------------------

BOOL ProcessRetCode(char*   pszFuncName,
                    DBCONN  *ptr,
                    RETCODE retcode,
                    BOOL    fExit)
{
   BOOL state = TRUE ;
   BOOL fExitP = fExit ;

   switch (retcode)
   {

   case SQL_SUCCESS:
         fExitP = FALSE ;
         break;

   case SQL_SUCCESS_WITH_INFO:
         fExitP = FALSE ;
         break;

   case SQL_ERROR:
         printf("%s Failed - see more info\n",pszFuncName);
         DoSQLError(ptr);
         state = FALSE;
         break;

   case SQL_INVALID_HANDLE:
         printf("%s Failed - SQL_INVALID_HANDLE\n",pszFuncName);
         state = FALSE;
         break;

   case SQL_NO_DATA_FOUND:
         printf("%s Failed - SQL_NO_DATA_FOUND\n",pszFuncName);
         fExitP = FALSE ;
         state = FALSE;
         break;

   case SQL_STILL_EXECUTING:
         printf("%s Failed - SQL_STILL_EXECUTING\n",pszFuncName);
         fExitP = FALSE ;
         state = FALSE;
         break;

   case SQL_NEED_DATA:
         printf("%s Failed - SQL_NEED_DATA\n",pszFuncName);
         fExitP = FALSE ;
         state = FALSE;
         break;

   default:
         printf("%s Failed - unexpected error, retcode = %x\n",pszFuncName,retcode);
         DoSQLError(ptr);
         state = FALSE;
         break;
   }

   if (fExitP)
   {
      exit(-1) ;
   }
   return state ;
}

// ---------------------------------------------------------------------------

void DoSQLError(DBCONN *ptr)
{

   const INT            MSG_BUF_SIZE = 300;
   UCHAR                szSqlState[MSG_BUF_SIZE];
   UCHAR             szErrorMsg[MSG_BUF_SIZE];

   SQLINTEGER  fNativeError   = 0;
   SWORD    cbErrorMsg     = MSG_BUF_SIZE;
   RETCODE     retcode;

   retcode = SQLError(g_hEnv,
              ptr ? ptr->hdbc : 0,
              ptr ? ptr->hstmt :0,
              szSqlState,
              &fNativeError,
              szErrorMsg,
              MSG_BUF_SIZE,
              &cbErrorMsg
              );

   if (retcode != SQL_NO_DATA_FOUND && retcode != SQL_ERROR)
   {
      if (fNativeError != 0x1645)   // ignore change database to master context message
      {
         printf("SQLError info:\n");
         printf("SqlState: %s, fNativeError: %x\n",szSqlState,fNativeError);
         printf("Error Message: %s\n\n",szErrorMsg);
      }
   }
   else
   {
      printf("SQLError() failed: %x, NO_DATA_FOUND OR SQL_ERROR\n",retcode);
   }

}
// ---------------------------------------------------------------------------


README.TXT

The Platform software development kit (SDK) provides
documentation on how to use notification events to provide asynchronous
handling of messages, which is one kind of trigger functionality. However,
you can also use Performance Monitor counters to monitor for triggering
conditions. This article points you to a sample application that shows you
how to write a trigger using a Performance Monitor counter.

The sample, "MQTrigger," is written in Visual Basic and C++. It uses a
timer event to periodically poll a Performance Monitor counter for a
message count. The sample application displays a dialog box when the
message count in a queue reaches the threshold.


FRMCONFIG.FRM

VERSION 5.00
Begin VB.Form frmConfig
   Caption         =   "Trigger Configuration"
   ClientHeight    =   1305
   ClientLeft      =   60
   ClientTop       =   345
   ClientWidth     =   5520
   ControlBox      =   0   'False
   LinkTopic       =   "Form1"
   ScaleHeight     =   1305
   ScaleWidth      =   5520
   StartUpPosition =   3  'Windows Default
   Begin VB.CommandButton cmdCancel
      Caption         =   "Cancel"
      Height          =   495
      Left            =   2760
      TabIndex        =   2
      Top             =   720
      Width           =   1215
   End
   Begin VB.CommandButton cmdClose
      Caption         =   "Set"
      Height          =   495
      Left            =   4200
      TabIndex        =   3
      Top             =   720
      Width           =   1215
   End
   Begin VB.TextBox txtThreshold
      Height          =   375
      Left            =   960
      TabIndex        =   1
      Top             =   720
      Width           =   615
   End
   Begin VB.TextBox txtQueueName
      Height          =   375
      Left            =   960
      TabIndex        =   0
      Top             =   120
      Width           =   4455
   End
   Begin VB.Label Label3
      Alignment       =   1  'Right Justify
      Caption         =   "Threshold:"
      Height          =   255
      Left            =   120
      TabIndex        =   5
      Top             =   840
      Width           =   735
   End
   Begin VB.Label Label1
      Alignment       =   1  'Right Justify
      Caption         =   "Local Queue:"
      Height          =   375
      Left            =   120
      TabIndex        =   4
      Top             =   120
      Width           =   735
   End
End
Attribute VB_Name = "frmConfig"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False

Private Sub cmdCancel_Click()
    bContinue = False
    Unload Me
End Sub

Private Sub cmdClose_Click()
    'Save new values to the registry
    SaveSetting "MQTrigger", "Settings", "Threshold", txtThreshold
    SaveSetting "MQTrigger", "Settings", "QueueName", txtQueueName
    Unload Me
End Sub

Private Sub Form_Load()
    'Retrieve initial values from the registry
    txtThreshold = GetSetting("MQTrigger", "Settings", "Threshold")
    txtQueueName = GetSetting("MQTrigger", "Settings", "QueueName")
End Sub


FRMMONITOR.FRM

VERSION 5.00
Object = "{6B7E6392-850A-101B-AFC0-4210102A8DA7}#1.2#0"; "COMCTL32.OCX"
Begin VB.Form frmMonitor
   Caption         =   "Trigger Monitor"
   ClientHeight    =   3420
   ClientLeft      =   60
   ClientTop       =   345
   ClientWidth     =   5775
   LinkTopic       =   "Form1"
   ScaleHeight     =   3420
   ScaleWidth      =   5775
   StartUpPosition =   3  'Windows Default
   Begin VB.CommandButton cmdBeginMonitor
      Caption         =   "Begin Monitor"
      Height          =   495
      Left            =   3120
      TabIndex        =   13
      Top             =   2880
      Width           =   1215
   End
   Begin VB.TextBox txtMsgCount
      Height          =   375
      Left            =   4560
      TabIndex        =   12
      Top             =   1920
      Width           =   975
   End
   Begin VB.TextBox txtPollingFrequency
      Height          =   375
      Left            =   2160
      TabIndex        =   11
      Top             =   1920
      Width           =   1215
   End
   Begin VB.CommandButton cmdPollingConfig
      Caption         =   "Polling Frequency"
      Height          =   495
      Left            =   120
      TabIndex        =   9
      Top             =   2880
      Width           =   1575
   End
   Begin VB.Timer Timer1
      Left            =   240
      Top             =   720
   End
   Begin VB.CommandButton cmdClose
      Caption         =   "Close"
      Height          =   495
      Left            =   4440
      TabIndex        =   0
      Top             =   2880
      Width           =   1215
   End
   Begin VB.CommandButton cmdConfigure
      Caption         =   "Settings"
      Height          =   495
      Left            =   1800
      TabIndex        =   1
      Top             =   2880
      Width           =   1215
   End
   Begin ComctlLib.ProgressBar ProgressBar1
      Height          =   375
      Left            =   960
      TabIndex        =   4
      Top             =   1200
      Width           =   4575
      _ExtentX        =   8070
      _ExtentY        =   661
      _Version        =   327682
      Appearance      =   1
   End
   Begin VB.TextBox txtQueueName
      Enabled         =   0   'False
      Height          =   375
      Left            =   960
      TabIndex        =   2
      Top             =   240
      Width           =   4575
   End
   Begin VB.Label Label4
      Caption         =   "Message Count:"
      Height          =   375
      Left            =   3720
      TabIndex        =   14
      Top             =   1920
      Width           =   735
   End
   Begin VB.Label Label3
      Caption         =   "Polling Frequency (sec) :"
      Height          =   255
      Left            =   240
      TabIndex        =   10
      Top             =   2040
      Width           =   1815
   End
   Begin VB.Label Label2
      Alignment       =   1  'Right Justify
      Caption         =   "Count:"
      Height          =   255
      Left            =   120
      TabIndex        =   8
      Top             =   1320
      Width           =   615
   End
   Begin VB.Label lblProgressMax
      Caption         =   "Max"
      Height          =   255
      Left            =   5040
      TabIndex        =   7
      Top             =   840
      Width           =   495
   End
   Begin VB.Label lblThreshold
      Caption         =   "Threshold"
      Height          =   255
      Left            =   3600
      TabIndex        =   6
      Top             =   840
      Width           =   735
   End
   Begin VB.Label lblProgress0
      Caption         =   "0"
      Height          =   255
      Left            =   960
      TabIndex        =   5
      Top             =   840
      Width           =   375
   End
   Begin VB.Label Label1
      Alignment       =   1  'Right Justify
      Caption         =   "Queue:"
      Height          =   255
      Left            =   120
      TabIndex        =   3
      Top             =   360
      Width           =   615
   End
End
Attribute VB_Name = "frmMonitor"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
    Public qTargetQueue As New MSMQQueue
    Public qinfoTargetQueue As New MSMQQueueInfo

Private Sub cmdBeginMonitor_Click()
    If Timer1.Enabled Then
        Timer1.Enabled = False
        cmdBeginMonitor.Caption = "Begin Monitor"
    Else
        Timer1.Enabled = True
        cmdBeginMonitor.Caption = "Stop Monitor"
    End If
End Sub

Private Sub cmdClose_Click()
    'Clean up and exit
    Unload frmConfig
    Unload frmMonitor
End Sub

Private Sub cmdConfigure_Click()
    frmConfig.Show vbModal
    InitGlobals
    InitControls
End Sub

Sub InitControls()
    'initialize controls
    txtQueueName = sQueueName
    txtPollingFrequency = iPollingFrequency
    lblProgress0 = "0"
    lblThreshold = iThreshold
    lblProgressMax = (1 + iThreshold) * 3 \ 2
    lblProgressMax.Refresh
    ProgressBar1.Min = 0
    ProgressBar1.Max = (1 + iThreshold) * 3 \ 2
    Timer1.Enabled = False
    Timer1.Interval = iPollingFrequency * 1000
End Sub

Private Sub cmdPollingConfig_Click()
    frmPollingConfig.Show vbModal
    iPollingFrequency = GetSetting("MQTrigger", "Settings", "PollingFrequency")
    txtPollingFrequency = iPollingFrequency
    Timer1.Interval = iPollingFrequency * 1000
End Sub

Private Sub OpenQueue()
    On Error GoTo ErrHandler

    'verify queue exists
    qinfoTargetQueue.PathName = txtQueueName
    Set qTargetQueue = qinfoTargetQueue.Open(Access:=MQ_PEEK_ACCESS, ShareMode:=MQ_DENY_NONE)
    qTargetQueue.Close
    Exit Sub
ErrHandler:
    MsgBox txtQueueName & " is an invalid Queue pathname", vbOKOnly, "Queue Open Failure"
    cmdConfigure_Click
    Resume

End Sub

Private Sub Form_Load()
    InitControls
    OpenQueue
End Sub

Private Sub Timer1_Timer()
    Dim dwNumMessages As Long
    Dim TriggerMsg As String

    dwNumMessages = GetPerfmonInfo(qinfoTargetQueue.PathName)
    If ProgressBar1.Max < dwNumMessages Then
        ' max the bar do nothing else (to avoid overflowing the progress bar)
        ProgressBar1 = ProgressBar1.Max
    Else
        ProgressBar1 = dwNumMessages
    End If
    txtMsgCount = dwNumMessages
    If dwNumMessages > iThreshold Then
        TriggerMsg = "Trigger: " & txtMsgCount & " messages in queue."
        MsgBox TriggerMsg, vbSystemModal, "Trigger Event"
    End If
End Sub


FRMPOLLINGCONFIG.FRM

VERSION 5.00
Begin VB.Form frmPollingConfig
   Caption         =   "Polling Frequency"
   ClientHeight    =   2025
   ClientLeft      =   60
   ClientTop       =   345
   ClientWidth     =   4590
   LinkTopic       =   "Form1"
   ScaleHeight     =   2025
   ScaleWidth      =   4590
   StartUpPosition =   3  'Windows Default
   Begin VB.CommandButton CmdSet
      Caption         =   "Set"
      Height          =   495
      Left            =   1920
      TabIndex        =   4
      Top             =   1440
      Width           =   1215
   End
   Begin VB.CommandButton cmdCancel
      Caption         =   "Cancel"
      Height          =   495
      Left            =   3240
      TabIndex        =   3
      Top             =   1440
      Width           =   1215
   End
   Begin VB.TextBox txtPollingFrequency
      Height          =   405
      Left            =   1320
      TabIndex        =   2
      Text            =   "Text1"
      Top             =   840
      Width           =   975
   End
   Begin VB.Label Label2
      Caption         =   "Frequency:"
      Height          =   255
      Left            =   240
      TabIndex        =   1
      Top             =   960
      Width           =   855
   End
   Begin VB.Label Label1
      Caption         =   "Enter the polling frequency in seconds. Decimal figures will be truncated to three decimal places."
      Height          =   495
      Left            =   240
      TabIndex        =   0
      Top             =   240
      Width           =   4215
   End
End
Attribute VB_Name = "frmPollingConfig"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False

Private Sub cmdCancel_Click()
    Unload Me
End Sub

Private Sub CmdSet_Click()
    SaveSetting "MQTrigger", "Settings", "PollingFrequency", txtPollingFrequency
    Unload Me
End Sub

Private Sub Form_Load()
    txtPollingFrequency = GetSetting("MQTrigger", "Settings", "PollingFrequency")
End Sub


TRIGGER.BAS

Attribute VB_Name = "Trigger"
Public iThreshold As Integer
Public iPollingFrequency As Integer
Public sQueueName As String
Public bContinue As Boolean

Declare Function GetPerfmonInfo Lib "permain" (ByVal lpwcsInstanceName As String) As Long

Sub Main()
    bContinue = True

    ' Launch Config dialogs if this is the first run of the program.
    If GetSetting("MQTrigger", "Settings", "QueueName") = "" Then
        frmConfig.Show vbModal
    End If

    If bContinue Then
        If GetSetting("MQTrigger", "Settings", "PollingFrequency") = "" Then
            frmPollingConfig.Show vbModal
        End If

        'Set the global values from settings in the registry
        InitGlobals

        'Load the Monitor form
        frmMonitor.Show
    End If
End Sub

Public Sub InitGlobals()
    'Called from Sub Main() and frmMonitor.cmdConfigure_Click()
    'Initialize the global variables from settings in the registry
    iThreshold = GetSetting("MQTrigger", "Settings", "Threshold")
    iPollingFrequency = Val(GetSetting("MQTrigger", "Settings", "PollingFrequency"))
    sQueueName = GetSetting("MQTrigger", "Settings", "QueueName")
End Sub


CS.H

#ifndef __CS_H
#define __CS_H

//------------------------
//
//  class CCriticalSection
//
class CCriticalSection
{
    friend class CS;
    friend class ConditionalCS;

public:
    CCriticalSection(){InitializeCriticalSection(&_cs);}
    ~CCriticalSection(){DeleteCriticalSection(&_cs);}
    void Enter() {EnterCriticalSection(&_cs);}
    void Leave() {LeaveCriticalSection(&_cs);}

private:
    void Lock()     { Enter(); }
    void Unlock()   { Leave(); }

private:
    CRITICAL_SECTION _cs;
};

//------------------------
//
//  class CS
//
class CS {
    CCriticalSection* m_lock;

public:
    CS(CCriticalSection& lock);
   ~CS();
};

//- implementation -------
//
//  class CS
//
inline CS::CS(CCriticalSection& lock) :
    m_lock(&lock)
{
    m_lock->Lock();
}

inline CS::~CS()
{
    m_lock->Unlock();
}

#endif // _CS_H


PERMAIN.CPP

#define UNICODE
#define _UNICODE

#define DBNTWIN32
#include <windows.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <rpc.h>
#include "cs.h"

#define DllExport __declspec( dllexport )
#define PERF_QUEUE_OBJECT   TEXT("MSMQ Queue")

typedef PERF_DATA_BLOCK             PERF_DATA,      *PPERF_DATA;
typedef PERF_OBJECT_TYPE            PERF_OBJECT,    *PPERF_OBJECT;
typedef PERF_INSTANCE_DEFINITION    PERF_INSTANCE,  *PPERF_INSTANCE;
typedef PERF_COUNTER_DEFINITION     PERF_COUNTER,   *PPERF_COUNTER;

LPWSTR* aszTitleArray;   // Array of pointers to title strings
DWORD dwLastCounter=0;
WCHAR wcsObjectID[32];
WCHAR wcsInstanceName[100];
DWORD   dwObjectIndex;    // Holds the the entry of the object in the title array
CCriticalSection g_critRegistry;

//*********************************************************************
//
//  CounterData
//
//      Returns counter data for an object instance.  If pInst or pCount
//      is NULL then NULL is returne.
//
PVOID CounterData (PPERF_INSTANCE pInst, PPERF_COUNTER pCount)
{
PPERF_COUNTER_BLOCK pCounterBlock;

    if (pCount && pInst)
        {
        pCounterBlock = (PPERF_COUNTER_BLOCK)((PCHAR)pInst + pInst->ByteLength);
        return (PVOID)((PCHAR)pCounterBlock + pCount->CounterOffset);
        }
    else
        return NULL;
}

//*********************************************************************
//
//  FirstCounter
//
//      Find the first counter in pObject.
//
//      Returns a pointer to the first counter.  If pObject is NULL
//      then NULL is returned.
//
PPERF_COUNTER FirstCounter (PPERF_OBJECT pObject)
{
    if (pObject)
        return (PPERF_COUNTER)((PCHAR) pObject + pObject->HeaderLength);
    else
        return NULL;
}

//*********************************************************************
//
//  NextCounter
//
//      Find the next counter of pCounter.
//
//      If pCounter is the last counter of an object type, bogus data
//      maybe returned.  The caller should do the checking.
//
//      Returns a pointer to a counter.  If pCounter is NULL then
//      NULL is returned.
//
PPERF_COUNTER NextCounter (PPERF_COUNTER pCounter)
{
    if (pCounter)
        return (PPERF_COUNTER)((PCHAR) pCounter + pCounter->ByteLength);
    else
        return NULL;
}

//*********************************************************************
//
//  FirstInstance
//
//      Returns pointer to the first instance of pObject type.
//      If pObject is NULL then NULL is returned.
//
PPERF_INSTANCE   FirstInstance (PPERF_OBJECT pObject)
{
    if (pObject)
        return (PPERF_INSTANCE)((PCHAR) pObject + pObject->DefinitionLength);
    else
        return NULL;
}

//*********************************************************************
//
//  NextInstance
//
//      Returns pointer to the next instance following pInst.
//
//      If pInst is the last instance, bogus data maybe returned.
//      The caller should do the checking.
//
//      If pInst is NULL, then NULL is returned.
//
PPERF_INSTANCE   NextInstance (PPERF_INSTANCE pInst)
{
PERF_COUNTER_BLOCK *pCounterBlock;

    if (pInst)
        {
        pCounterBlock = (PERF_COUNTER_BLOCK *)((PCHAR) pInst + pInst->ByteLength);
        return (PPERF_INSTANCE)((PCHAR) pCounterBlock + pCounterBlock->ByteLength);
        }
    else
        return NULL;
}

//*********************************************************************
//
//  InstanceName
//
//      Returns the name of the pInst.
//
//      If pInst is NULL then NULL is returned.
//
LPTSTR  InstanceName (PPERF_INSTANCE pInst)
{
    if (pInst)
        return (LPTSTR) ((PCHAR) pInst + pInst->NameOffset);
    else
        return NULL;
}

BOOL GetPerformanceData(PPERF_DATA      pPerfData,
                        PPERF_OBJECT    pObject,
                        LPWSTR* aszTitleArray,
                        LPWSTR lpcsInstanceName,
                        DWORD *pdwMsgInQueue)
{

    PPERF_INSTANCE  pInstance = NULL;

    *pdwMsgInQueue = 0;

    pInstance = FirstInstance (pObject);
    for (int i=0;i<pObject->NumInstances;i++)
    {

        LPWSTR lpwcsInstanceName = InstanceName(pInstance);
        if (wcsicmp(lpcsInstanceName, lpwcsInstanceName) == 0)
        {
            break;
        }

        pInstance = NextInstance(pInstance);
    }

    if (i==pObject->NumInstances)
    {
        return FALSE;
    }

    PPERF_COUNTER pCounter;
    pCounter = FirstCounter (pObject);
    LPWSTR lpwcsNameTitle;

    for (DWORD Index =0; Index < pObject->NumCounters; Index++)
    {
        lpwcsNameTitle = aszTitleArray[pCounter->CounterNameTitleIndex];
        if (wcscmp(L"Messages in Queue", lpwcsNameTitle))
        {
            pCounter = NextCounter (pCounter);
            continue;
        }

        DWORD dwCounterValue;

        //
        // update counters value
        //

        if (pInstance)
        {
            dwCounterValue = *(DWORD *)CounterData(pInstance,pCounter);
        }
        else
        {
            return FALSE;
        }

        *pdwMsgInQueue = dwCounterValue;
        return TRUE;
    }

    return FALSE;
}

//*********************************************************************
//
//  FirstObject
//
//      Returns pointer to the first object in pData.
//      If pData is NULL then NULL is returned.
//
PPERF_OBJECT FirstObject (PPERF_DATA pData)
{
    if (pData)
        return ((PPERF_OBJECT) ((PBYTE) pData + pData->HeaderLength));
    else
        return NULL;
}

//*********************************************************************
//
//  NextObject
//
//      Returns pointer to the next object following pObject.
//
//      If pObject is the last object, bogus data maybe returned.
//      The caller should do the checking.
//
//      If pObject is NULL, then NULL is returned.
//
PPERF_OBJECT NextObject (PPERF_OBJECT pObject)
{
    if (pObject)
        return ((PPERF_OBJECT) ((PBYTE) pObject + pObject->TotalByteLength));
    else
        return NULL;
}

//*********************************************************************
//
//  FindObject
//
//      Returns pointer to object with TitleIndex.  If not found, NULL
//      is returned.
//
PPERF_OBJECT FindObject (PPERF_DATA pData, DWORD TitleIndex)
{
PPERF_OBJECT pObject;
DWORD        i = 0;

    if (pObject = FirstObject (pData))
        while (i < pData->NumObjectTypes)
            {
            if (pObject->ObjectNameTitleIndex == TitleIndex)
                return pObject;

            pObject = NextObject (pObject);
            i++;
            }

    return NULL;
}

//*********************************************************************
//
//  GetPerfData
//
//      Get a new set of performance data.
//
//      *ppData should be NULL initially.
//      This function will allocate a buffer big enough to hold the
//      data requested by szObjectIndex.
//
//      *pDataSize specifies the initial buffer size.  If the size is
//      too small, the function will increase it until it is big enough
//      then return the size through *pDataSize.  Caller should
//      deallocate *ppData if it is no longer being used.
//
//      Returns ERROR_SUCCESS if no error occurs.
//
//      Note: the trial and error loop is quite different from the normal
//            registry operation.  Normally if the buffer is too small,
//            RegQueryValueEx returns the required size.  In this case,
//            the perflib, since the data is dynamic, a buffer big enough
//            for the moment may not be enough for the next. Therefor,
//            the required size is not returned.
//
//            One should start with a resonable size to avoid the overhead
//            of reallocation of memory.
//
DWORD
GetPerfData(
    HKEY hPerfKey,
    LPTSTR szObjectIndex,
    PPERF_DATA* ppData,
    DWORD* pDataSize
    )
{
    DWORD   DataSize;
    DWORD   dwR;
    DWORD   Type;

   *ppData = NULL;
    do  {
        DataSize = *pDataSize;
        dwR = RegQueryValueEx (hPerfKey,
                               szObjectIndex,
                               NULL,
                               &Type,
                               (BYTE*) *ppData,
                               &DataSize);

        if (dwR == ERROR_MORE_DATA)
        {
            *pDataSize += 1024;
            LocalFree (*ppData);
            *ppData = (PPERF_DATA) LocalAlloc (LMEM_FIXED, *pDataSize);
        }

        if (!*ppData)
        {
            LocalFree (*ppData);
            return ERROR_NOT_ENOUGH_MEMORY;
        }

        } while (dwR == ERROR_MORE_DATA);

    return dwR;
}

//*********************************************************************
//
//      RefreshPerfData
//
//  Get a new set of performance data.  pData should be NULL initially.
//
PPERF_DATA RefreshPerfData (HKEY        hPerfKey,
                            LPTSTR      szObjectIndex,
                            PPERF_DATA  pData,
                            DWORD       *pDataSize)
{
    if (GetPerfData (hPerfKey, szObjectIndex, &pData, pDataSize) == ERROR_SUCCESS)
        return pData;
    else
        return NULL;
}
//*********************************************************************
//
//  GetPerfTitleSz
//
//      Retrieves the performance data title strings.
//
//   This call retrieves english version of the title strings.
//
//   For NT 3.1, the counter names are stored in the "Counters" value
//   in the ...\perflib\009 key.  For 3.5 and later, the 009 key is no
//      longer used.  The counter names should be retrieved from "Counter 009"
//      value of HKEY_PERFORMANCE_KEY.
//
//      Caller should provide two pointers, one for buffering the title
//      strings the other for indexing the title strings.  This function will
//      allocate memory for the TitleBuffer and TitleSz.  To get the title
//      string for a particular title index one would just index the TitleSz.
//      *TitleLastIdx returns the highest index can be used.  If TitleSz[N] is
//      NULL then there is no Title for index N.
//
//      Example:  TitleSz[20] points to titile string for title index 20.
//
//      When done with the TitleSz, caller should LocalFree(*TitleBuffer).
//
//      This function returns ERROR_SUCCESS if no error.
//
DWORD   GetPerfTitleSz (HKEY    hKeyMachine,
                        HKEY    hKeyPerf,
                        LPWSTR  *TitleBuffer,
                        LPWSTR  *TitleSz[],
                        DWORD   *TitleLastIdx)
{
HKEYhKey1;
HKEY    hKey2;
DWORD   Type;
DWORD   DataSize;
DWORD   dwR;
DWORD   Len;
DWORD   Index;
DWORD   dwTemp;
BOOL    bNT10;
LPWSTR  szCounterValueName;
LPWSTR  szTitle;

    // Initialize
    //
    hKey1        = NULL;
    hKey2        = NULL;
    *TitleBuffer = NULL;
    *TitleSz     = NULL;

    // Open the perflib key to find out the last counter's index and system version.
    //
    dwR = RegOpenKeyEx (hKeyMachine,
                        TEXT("software\\microsoft\\windows nt\\currentversion\\perflib"),
                        0,
                        KEY_READ,
                        &hKey1);
    if (dwR != ERROR_SUCCESS)
        goto done;

    // Get the last counter's index so we know how much memory to allocate for TitleSz
    //
    DataSize = sizeof (DWORD);
    dwR = RegQueryValueEx (hKey1, TEXT("Last Counter"), 0, &Type, (LPBYTE)TitleLastIdx, &DataSize);
    if (dwR != ERROR_SUCCESS)
        goto done;

    // Find system version, for system earlier than 1.0a, there's no version value.
    //
    dwR = RegQueryValueEx (hKey1, TEXT("Version"), 0, &Type, (LPBYTE)&dwTemp, &DataSize);

    if (dwR != ERROR_SUCCESS)
        // unable to read the value, assume NT 1.0
        bNT10 = TRUE;
    else
        // found the value, so, NT 1.0a or later
        bNT10 = FALSE;

    // Now, get ready for the counter names and indexes.
    //
    if (bNT10)
        {
        // NT 1.0, so make hKey2 point to ...\perflib\009 and get
        //  the counters from value "Counters"
        //
        szCounterValueName = TEXT("Counters");
        dwR = RegOpenKeyEx (hKeyMachine,
                            TEXT("software\\microsoft\\windows nt\\currentversion\\perflib\\009"),
                            0,
                            KEY_READ,
                            &hKey2);
        if (dwR != ERROR_SUCCESS)
            goto done;
        }
    else
        {
        // NT 1.0a or later.  Get the counters in key HKEY_PERFORMANCE_KEY
        //  and from value "Counter 009"
        //
        szCounterValueName = TEXT("Counter 009");
        hKey2 = hKeyPerf;
        }

    // Find out the size of the data.
    //
    dwR = RegQueryValueEx (hKey2, szCounterValueName, 0, &Type, 0, &DataSize);
    if (dwR != ERROR_SUCCESS)
        goto done;

    // Allocate memory
    //
    *TitleBuffer = (LPTSTR)LocalAlloc (LMEM_FIXED, DataSize);
    if (!*TitleBuffer)
        {
        dwR = ERROR_NOT_ENOUGH_MEMORY;
        goto done;
        }

    *TitleSz = (LPTSTR *)LocalAlloc (LPTR, (*TitleLastIdx+1) * sizeof (LPTSTR));
    if (!*TitleSz)
        {
        dwR = ERROR_NOT_ENOUGH_MEMORY;
        goto done;
        }

    // Query the data
    //
    dwR = RegQueryValueEx (hKey2, szCounterValueName, 0, &Type, (BYTE *)*TitleBuffer, &DataSize);
    if (dwR != ERROR_SUCCESS)
        goto done;

    // Setup the TitleSz array of pointers to point to beginning of each title string.
    // TitleBuffer is type REG_MULTI_SZ.
    //
    szTitle = *TitleBuffer;

    while (Len = lstrlen (szTitle))
        {
        Index = _wtoi (szTitle);

        szTitle = szTitle + Len +1;

        if (Index <= *TitleLastIdx)
            (*TitleSz)[Index] = szTitle;

        szTitle = szTitle + lstrlen (szTitle) +1;
        }

done:

    // Done. Now cleanup!
    //
    if (dwR != ERROR_SUCCESS)
        {
        // There was an error, free the allocated memory
        //
        if (*TitleBuffer) LocalFree (*TitleBuffer);
        if (*TitleSz)     LocalFree (*TitleSz);
        }

    // Close the hKeys.
    //
    if (hKey1) RegCloseKey (hKey1);
    if (hKey2 && hKey2 != hKeyPerf) RegCloseKey (hKey2);

    return dwR;

}

DWORD
WINAPI
GetPerfmonInfo(LPCSTR lpwcsInstanceName)
{
    DWORD dwR = 0;
    DWORD Index;
    static fFirst = TRUE;
    LPWSTR sTitleBuffer;      // Buffer which holds title strings
    DWORD dwMsgInQueue;
    WCHAR wcsInstanceName[100];

    swprintf(wcsInstanceName,L"%S", lpwcsInstanceName);

    if (fFirst)
    {
        dwR = GetPerfTitleSz (HKEY_LOCAL_MACHINE,
                              HKEY_PERFORMANCE_DATA,
                              &sTitleBuffer,
                              &aszTitleArray,
                              &dwLastCounter);

        if (FAILED(dwR))
        {
            return 0;
        }
        fFirst = FALSE;
        //
        // Initiate the objects Index
        //
        dwObjectIndex = (DWORD)-1;

        for (Index = 0; Index <= dwLastCounter; Index++)
        {
            if (aszTitleArray[Index] &&
                (lstrcmpi (aszTitleArray[Index], PERF_QUEUE_OBJECT) == 0))
            {
                dwObjectIndex = Index;
            }
        }

        if ((DWORD)-1 == dwObjectIndex)
        {
            return 0;
        }

        swprintf(wcsObjectID,L"%ld", dwObjectIndex);
    }
    //
    // Get the number of counters the object has
    //
    PPERF_OBJECT    pObject;
    PPERF_DATA      pPerfData;
    DWORD dwPerfDataSize = 0;

    pPerfData = RefreshPerfData (HKEY_PERFORMANCE_DATA, wcsObjectID, pPerfData, &dwPerfDataSize);

    pObject= FindObject (pPerfData, dwObjectIndex);
    if (pObject == NULL)
    {
        return 0;
    }

    GetPerformanceData(pPerfData,pObject,aszTitleArray, wcsInstanceName,&dwMsgInQueue);
    LocalFree (pPerfData);
    return dwMsgInQueue;
}


MSMQTEST.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright (C) 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.
\******************************************************************************/

//
// Includes
//
#include <stdio.h>
#include <windows.h>

//
// Unique include file for MSMQ apps
//
#include "mq.h"

//
// Various defines
//
#define MAX_VAR       20
#define MAX_FORMAT   100
#define MAX_BUFFER   500

//
// GUID created with the tool "GUIDGEN"
//
static CLSID guidMQTestType =
{ 0xc30e0960, 0xa2c0, 0x11cf, { 0x97, 0x85, 0x0, 0x60, 0x8c, 0xb3, 0xe8, 0xc } };

//
// Prototypes
//
void Error(char *s, HRESULT hr);
void Syntax();

char mbsMachineName[MAX_COMPUTERNAME_LENGTH + 1];

//-----------------------------------------------------
//
// Receiver Mode
// -------------
// The receiver side does the following:
//    1. Creates a public queue on its own machine
//       of type "guidMQTestType"
//    2. Opens the queue
//    3. In a Loop
//          Receives messages
//          Prints message body and message label
//    4. Cleanup handles
//    5. Deletes the queue from the directory service
//
//-----------------------------------------------------
void Receiver()
{

    MQQUEUEPROPS  qprops;
    MQMSGPROPS    msgprops;
    MQPROPVARIANT aPropVar[MAX_VAR];
    QUEUEPROPID   aqPropId[MAX_VAR];
    MSGPROPID     amPropId[MAX_VAR];
    DWORD         cProps;

    WCHAR         wcsFormat[MAX_FORMAT];

    UCHAR         Buffer[MAX_BUFFER];
    WCHAR         wcsMsgLabel[MQ_MAX_MSG_LABEL_LEN];
    WCHAR         wcsPathName[1000];

    DWORD         dwNumChars;
    QUEUEHANDLE   qh;

    HRESULT       hr;

    printf("\nReceiver Mode on Machine: %s\n\n", mbsMachineName);

    //
    // Prepare properties to create a queue on local machine
    //
    cProps = 0;

    // Set the PathName
    swprintf(wcsPathName, L"%S\\MSMQTest", mbsMachineName);
    aqPropId[cProps]         = PROPID_Q_PATHNAME;
    aPropVar[cProps].vt      = VT_LPWSTR;
    aPropVar[cProps].pwszVal = wcsPathName;
    cProps++;

    // Set the type of the queue
    // (Will be used to locate all the queues of this type)
    aqPropId[cProps]         = PROPID_Q_TYPE;
    aPropVar[cProps].vt      = VT_CLSID;
    aPropVar[cProps].puuid   = &guidMQTestType;
    cProps++;

    // Put a description to the queue
    // (Useful for administration through the MSMQ admin tools)
    aqPropId[cProps]         = PROPID_Q_LABEL;
    aPropVar[cProps].vt      = VT_LPWSTR;
    aPropVar[cProps].pwszVal = L"Sample application of MSMQ SDK";
    cProps++;

    // Create a QUEUEPROPS structure
    qprops.cProp    = cProps;
    qprops.aPropID  = aqPropId;
    qprops.aPropVar = aPropVar;
    qprops.aStatus  = 0;

    //
    // Create the queue
    //
    dwNumChars = MAX_FORMAT;
    hr = MQCreateQueue(
            NULL,           // IN:     Default security
            &qprops,        // IN/OUT: Queue properties
            wcsFormat,      // OUT:    Format name (OUT)
            &dwNumChars);   // IN/OUT: Size of format name

    if (FAILED(hr))
    {
        //
        // API Fails, not because the queue exists
        //
        if (hr != MQ_ERROR_QUEUE_EXISTS)
            Error("Cannot create queue", hr);

        //
        // Queue exist, so get its format name
        //
        printf("Queue already exists. Open it anyway.\n");

        dwNumChars = MAX_FORMAT;
        hr = MQPathNameToFormatName(
                   wcsPathName,     // IN:     Queue pathname
                   wcsFormat,       // OUT:    Format name
                   &dwNumChars);    // IN/OUT: Size of format name

        if (FAILED(hr))
            Error("Cannot retrieve format name", hr);
    }

    //
    // Open the queue for receive access
    //
    hr = MQOpenQueue(
             wcsFormat,          // IN:  Queue format name
             MQ_RECEIVE_ACCESS,  // IN:  Want to receive from queue
             0,                  // IN:  Allow sharing
             &qh);               // OUT: Handle of open queue

    //
    // Little bit tricky. MQCreateQueue succeeded but it does not mean
    // that MQOpenQueue will, because of replication delay. The queue is
    // registered in MQIS, but it might take a replication interval
    // until the replica reach the server I am connected to.
    // To overcome this, open the queue in a loop.
    //
    // (in this specific case, this can happen only if this
    //  program is run on a Backup Server Controller - BSC, or on
    //  a client connected to a BSC)
    // To be totally on the safe side, we should have put some code
    // to exit the loop after a few retries, but hey, this is just a sample.
    //
    while (hr == MQ_ERROR_QUEUE_NOT_FOUND)
    {
       printf(".");

       // Wait a bit
       Sleep(500);

       // And retry
       hr = MQOpenQueue(wcsFormat, MQ_RECEIVE_ACCESS, 0, &qh);
    }

    if (FAILED(hr))
         Error("Cannot open queue", hr);

    //
    // Main receiver loop
    //
    printf("\nWaiting for messages...\n");
    while (1)
    {
        //
        // Prepare message properties to read
        //
        cProps = 0;

        // Ask for the body of the message
        amPropId[cProps]            = PROPID_M_BODY;
        aPropVar[cProps].vt         = VT_UI1 | VT_VECTOR;
        aPropVar[cProps].caub.cElems = sizeof(Buffer);
        aPropVar[cProps].caub.pElems = Buffer;
        cProps++;

        // Ask for the label of the message
        amPropId[cProps]         = PROPID_M_LABEL;
        aPropVar[cProps].vt      = VT_LPWSTR;
        aPropVar[cProps].pwszVal = wcsMsgLabel;
        cProps++;

        // Ask for the length of the label of the message
        amPropId[cProps]         = PROPID_M_LABEL_LEN;
        aPropVar[cProps].vt      = VT_UI4;
        aPropVar[cProps].ulVal   = MQ_MAX_MSG_LABEL_LEN;
        cProps++;

        // Create a MSGPROPS structure
        msgprops.cProp    = cProps;
        msgprops.aPropID  = amPropId;
        msgprops.aPropVar = aPropVar;
        msgprops.aStatus  = 0;

        //
        // Receive the message
        //
        hr = MQReceiveMessage(
               qh,                // IN:     Queue handle
               INFINITE,          // IN:     Timeout
               MQ_ACTION_RECEIVE, // IN:     Read operation
               &msgprops,         // IN/OUT: Message properties to receive
               NULL,              // IN/OUT: No overlap
               NULL,              // IN:     No callback
               NULL,              // IN:     No cursor
               NULL);           // IN:     Not part of a transaction

        if (FAILED(hr))
            Error("Receive message", hr);

        //
        // Display the received message
        //
        printf("%S : %s\n", wcsMsgLabel, Buffer);

        //
        // Check for end of app
        //
        if (stricmp(Buffer, "quit") == 0)
            break;

    } /* while (1) */

    //
    // Cleanup - Close handle to the queue
    //
    MQCloseQueue(qh);

    //
    // Finish - Let's delete the queue from the directory service
    // (We don't need to do it. Leaving the queue in the DS, enables
    //  sender applications to send messages even if the receiver is not
    //  available.)
    //
    hr = MQDeleteQueue(wcsFormat);
    if (FAILED(hr))
        Error("Cannot delete queue", hr);
}

//-----------------------------------------------------
//
// Sender Mode
// -----------
// The sender side does the following:
//    1. Locates all queues of type "guidMQTestType"
//    2. Opens handles to all the queues
//    3. In a loop
//          Sends messages to all those queues
//    4. Cleanup handles
//
//-----------------------------------------------------
void Sender()
{
    DWORD         cProps;

    MQMSGPROPS    msgprops;
    MQPROPVARIANT aPropVar[MAX_VAR];
    QUEUEPROPID   aqPropId[MAX_VAR];
    MSGPROPID     amPropId[MAX_VAR];

    MQPROPERTYRESTRICTION aPropRestriction[MAX_VAR];
    MQRESTRICTION Restriction;

    MQCOLUMNSET      Column;
    HANDLE        hEnum;

    WCHAR         wcsFormat[MAX_FORMAT];

    UCHAR         Buffer[MAX_BUFFER];
    WCHAR         wcsMsgLabel[MQ_MAX_MSG_LABEL_LEN];

    DWORD         i;

    DWORD         cQueue;
    DWORD         dwNumChars;
    QUEUEHANDLE   aqh[MAX_VAR];

    HRESULT       hr;

    printf("\nSender Mode on Machine: %s\n\n", mbsMachineName);

    //
    // Prepare parameters to locate a queue
    //

    //
    // 1. Restriction = Queues with PROPID_TYPE = MSMQTest queue type
    //
    cProps = 0;
    aPropRestriction[cProps].rel         = PREQ;
    aPropRestriction[cProps].prop        = PROPID_Q_TYPE;
    aPropRestriction[cProps].prval.vt    = VT_CLSID;
    aPropRestriction[cProps].prval.puuid = &guidMQTestType;
    cProps++;

    Restriction.cRes      = cProps;
    Restriction.paPropRes = aPropRestriction;

    //
    // 2. Columnset (i.e. queue properties to retrieve) = queue instance
    //
    cProps = 0;
    aqPropId[cProps] = PROPID_Q_INSTANCE;
    cProps++;

    Column.cCol = cProps;
    Column.aCol = aqPropId;

    //
    // Locate the queues. Issue the query
    //
    hr = MQLocateBegin(
             NULL,          // IN:  Context must be NULL
             &Restriction,  // IN:  Restriction
             &Column,       // IN:  Columns (properties) to return
             NULL,          // IN:  No need to sort
             &hEnum);       // OUT: Enumeration handle

    if (FAILED(hr))
        Error("LocateBegin", hr);

    //
    // Get the results (up to MAX_VAR)
    // (For more results, call MQLocateNext in a loop)
    //
    cQueue = MAX_VAR;
    hr = MQLocateNext(
             hEnum,         // IN:     Enumeration handle
             &cQueue,       // IN/OUT: Count of properties
             aPropVar);     // OUT:    Properties of located queues

    if (FAILED(hr))
        Error("LocateNext", hr);

    //
    // And that's it for locate
    //
    hr = MQLocateEnd(hEnum);

    if (cQueue == 0)
    {
        //
        // Could Not find any queue, so exit
        //
        printf("No queue registered");
        exit(0);
    }

    printf("\t%d queue(s) found.\n", cQueue);

    //
    // Open a handle for each of the queues found
    //
    for (i = 0; i < cQueue; i++)
    {
        // Convert the queue instance to a format name
        dwNumChars = MAX_FORMAT;
        hr = MQInstanceToFormatName(
                  aPropVar[i].puuid,    // IN:     Queue instance
                  wcsFormat,            // OUT:    Format name
                  &dwNumChars);         // IN/OUT: Size of format name

        if (FAILED(hr))
            Error("GuidToFormatName", hr);

        //
        // Open the queue for send access
        //
        hr = MQOpenQueue(
                 wcsFormat,           // IN:  Queue format name
                 MQ_SEND_ACCESS,      // IN:  Want to send to queue
                 0,                   // IN:  Must be 0 for send access
                 &aqh[i]);            // OUT: Handle of open queue

        if (FAILED(hr))
            Error("OpenQueue", hr);

        //
        // Free the GUID memory that was allocated during the locate.
        //
        MQFreeMemory(aPropVar[i].puuid);
    }

    printf("\nEnter \"quit\" to exit\n");

    //
    // Build the message label property
    //
    swprintf(wcsMsgLabel, L"Message from %S", mbsMachineName);

    //
    // Main sender loop
    //
    while (1)
    {
        //
        // Get a string from the console
        //
        printf("Enter a string: ");
        if (gets(Buffer) == NULL)
            break;

        //
        // Prepare properties of message to send
        //
        cProps = 0;

        // Set the body of the message
        amPropId[cProps]            = PROPID_M_BODY;
        aPropVar[cProps].vt         = VT_UI1 | VT_VECTOR;
        aPropVar[cProps].caub.cElems = sizeof(Buffer);
        aPropVar[cProps].caub.pElems = Buffer;
        cProps++;

        // Set the label of the message
        amPropId[cProps]            = PROPID_M_LABEL;
        aPropVar[cProps].vt         = VT_LPWSTR;
        aPropVar[cProps].pwszVal    = wcsMsgLabel;
        cProps++;

        // Create a MSGPROPS structure
        msgprops.cProp    = cProps;
        msgprops.aPropID  = amPropId;
        msgprops.aPropVar = aPropVar;
        msgprops.aStatus  = 0;

        //
        // Send the message to all the queue
        //
        for (i = 0; i < cQueue; i++)
        {
            hr = MQSendMessage(
                    aqh[i],     // IN: Queue handle
                    &msgprops,  // IN: Message properties to send
                    NULL);      // IN: Not part of a transaction

            if (FAILED(hr))
                Error("Send message", hr);
        }

        //
        // Check for end of app
        //
        if (stricmp(Buffer, "quit") == 0)
            break;

    } /* while (1) */

    //
    // Close all the queue handles
    //
    for (i = 0; i < cQueue; i++)
        MQCloseQueue(aqh[i]);

}

//-----------------------------------------------------
//
//  MAIN
//
//-----------------------------------------------------
main(int argc, char * * argv)
{
    DWORD dwNumChars;

    if (argc != 2)
        Syntax();

    //
    // Retrieve machine name
    //
    dwNumChars = MAX_COMPUTERNAME_LENGTH + 1;
    GetComputerName(mbsMachineName, &dwNumChars);

    if (strcmp(argv[1], "-s") == 0)
        Sender();

    else if (strcmp(argv[1], "-r") == 0)
        Receiver();

    else
        Syntax();

    printf("\nOK\n");

    return(1);
}

void Error(char *s, HRESULT hr)
{
    printf("Error: %s (0x%X)\n", s, hr);
    exit(1);
}

void Syntax()
{
    printf("\n");
    printf("Syntax: msmqtest -s | -r\n");
    printf("\t-s: Sender\n");
    printf("\t-r: Receiver\n");
    exit(1);
}


DISDRAW.FRM

VERSION 5.00
Begin VB.Form Form1
   Caption         =   "Form1"
   ClientHeight    =   5820
   ClientLeft      =   3345
   ClientTop       =   2295
   ClientWidth     =   6330
   LinkTopic       =   "Form1"
   ScaleHeight     =   5820
   ScaleWidth      =   6330
   Begin VB.Frame Frame1
      Caption         =   "Sent Messages"
      Height          =   975
      Left            =   3120
      TabIndex        =   6
      Top             =   4680
      Width           =   2535
      Begin VB.OptionButton Option1
         Caption         =   "&Express"
         Height          =   252
         Index           =   0
         Left            =   240
         TabIndex        =   4
         Top             =   240
         Value           =   -1  'True
         Width           =   2052
      End
      Begin VB.OptionButton Option1
         Caption         =   "&Recoverable"
         Height          =   252
         Index           =   1
         Left            =   240
         TabIndex        =   5
         Top             =   600
         Width           =   2052
      End
   End
   Begin VB.PictureBox Picture1
      Height          =   3855
      Left            =   240
      MousePointer    =   1  'Arrow
      ScaleHeight     =   253
      ScaleMode       =   3  'Pixel
      ScaleWidth      =   389
      TabIndex        =   3
      Top             =   120
      Width           =   5895
   End
   Begin VB.CommandButton Attach
      Caption         =   "&Attach"
      Default         =   -1  'True
      BeginProperty Font
         Name            =   "MS Sans Serif"
         Size            =   8.25
         Charset         =   0
         Weight          =   700
         Underline       =   0   'False
         Italic          =   0   'False
         Strikethrough   =   0   'False
      EndProperty
      Height          =   375
      Left            =   4800
      TabIndex        =   2
      Top             =   4200
      Width           =   1335
   End
   Begin VB.TextBox FriendName
      Height          =   285
      Left            =   1440
      TabIndex        =   1
      Top             =   4200
      Width           =   2055
   End
   Begin VB.Label Label1
      Caption         =   "Remote &Friend:"
      Height          =   255
      Left            =   240
      TabIndex        =   0
      Top             =   4200
      Width           =   1215
   End
End
Attribute VB_Name = "Form1"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
' ------------------------------------------------------------------------
'               Copyright (C) 1995 Microsoft Corporation
'
' You have a royalty-free right to use, modify, reproduce and distribute
' the Sample Application Files (and/or any modified version) in any way
' you find useful, provided that you agree that Microsoft has no warranty,
' obligations or liability for any Sample Application Files.
' ------------------------------------------------------------------------
'
' Type Guid
'
Const guidDraw = "{151ceac0-acb5-11cf-8b51-0020af929546}"
Option Explicit
Const MaxNumLen = 7
Private Type Line
    X1 As Long
    Y1 As Long
    X2 As Long
    Y2 As Long
End Type

Dim lLastX As Long
Dim lLastY As Long
Dim Lines() As Line
Dim cLines As Integer
Dim lArraySize As Integer
Dim strScreenText As String
Dim fWasText As Integer
Dim strLogin As String
Dim q As MSMQQueue
Attribute q.VB_VarHelpID = -1
Dim WithEvents qevent As MSMQEvent
Attribute qevent.VB_VarHelpID = -1
Dim qFriend As MSMQQueue
Dim msgOut As MSMQMessage

'
'Locate a remote queue
'
Private Sub Attach_Click()
    Dim queryFriend As New MSMQQuery
    Dim qinfoFriend As MSMQQueueInfo
    Dim qinfos As MSMQQueueInfos
    FriendName = UCase(FriendName)
    Set qinfos = queryFriend.LookupQueue( _
        Label:=(FriendName), _
        ServiceTypeGuid:=guidDraw)
    qinfos.Reset
    Set qinfoFriend = qinfos.Next
    If qinfoFriend Is Nothing Then                   'And locate it
        MsgBox "No Such friend, Sorry..."   'No queue defined
    Else
        If Not qFriend Is Nothing Then
          If qFriend.IsOpen Then qFriend.Close
        End If

        Set qFriend = qinfoFriend.Open(MQ_SEND_ACCESS, 0)
        Caption = strLogin + " - Connected to " + FriendName
        Attach.Enabled = False
    End If
End Sub

'
' Application Initialization
'
Private Sub Form_Load()
    Dim strDefaultQueueName As String
    Dim lTempPointer As Long
    Dim query As New MSMQQuery
    Dim qinfo As MSMQQueueInfo
    Dim qinfos As MSMQQueueInfos
    Dim strComputerName As String

    Set msgOut = New MSMQMessage
    strDefaultQueueName = Environ("USERNAME")
    strLogin = InputBox("Your Name Please", "Login", strDefaultQueueName)
    If strLogin = "" Then End
    strLogin = UCase(strLogin)
    Caption = strLogin
    Set qinfos = query.LookupQueue( _
        Label:=strLogin, _
        ServiceTypeGuid:=guidDraw)
    qinfos.Reset             'And locate this queue
    Set qinfo = qinfos.Next
    If qinfo Is Nothing Then
        Set qinfo = New MSMQQueueInfo
        strComputerName = "."
        qinfo.PathName = strComputerName + "\" + strLogin
        qinfo.Label = strLogin
        qinfo.ServiceTypeGuid = guidDraw
        qinfo.Create                 'If there is no such create one.
    End If
    On Error GoTo retry_on_error
        lTempPointer = Screen.MousePointer
        Screen.MousePointer = 11 'ccArrowHourglass
        Set q = qinfo.Open(MQ_RECEIVE_ACCESS, 0)
        On Error GoTo 0
        Screen.MousePointer = lTempPointer
        GoTo all_ok
retry_on_error:
    '
    'We may still not see the queue until the next replication
    ' In this case, we get MQ_ERROR_QUEUE_NOT_FOUND and retry.
    '
    If Err.Number = MQ_ERROR_QUEUE_NOT_FOUND Then
        Err.Clear
        DoEvents
        Resume
    Else
        MsgBox Err.Description, , "Error in Open"
        End
    End If

all_ok:
    'All messages will be received asynchronously
    ' So need an event handler
    Set qevent = New MSMQEvent
    q.EnableNotification qevent
End Sub
'
'Gets points and returns a line
'
Private Function PointsToLine(ByVal X1 As Long, ByVal Y1 As Long, ByVal X2 As Long, ByVal Y2 As Long) As Line
    Dim lineNew As Line
    lineNew.X1 = X1
    lineNew.Y1 = Y1
    lineNew.X2 = X2
    lineNew.Y2 = Y2
    PointsToLine = lineNew
End Function
'
'Draw a line in the picture control
'
Private Sub DrawLine(lineDraw As Line)
    Picture1.Line (lineDraw.X1, lineDraw.Y1)-(lineDraw.X2, lineDraw.Y2)
    fWasText = False
End Sub
'
'Display a line
'
Private Sub AddLine(lineNew As Line)
    DrawLine lineNew
    cLines = cLines + 1
    If (cLines > lArraySize) Then
        lArraySize = cLines * 2
        ReDim Preserve Lines(lArraySize)
    End If
    Lines(cLines - 1) = lineNew
End Sub
'
'Clear the display
'
Private Sub ClearDraw()
    cLines = 0
    strScreenText = ""
    Picture1.Refresh
End Sub
'
'Decode a string into a line
'
Private Function LineToString(lineIn As Line) As String
    Dim strFormat As String
    strFormat = String(MaxNumLen, "0")
    LineToString = Format$(lineIn.X1, strFormat) + Format$(lineIn.Y1, strFormat) + Format$(lineIn.X2, strFormat) + Format$(lineIn.Y2, strFormat)
End Function
'
'Encode a line into a string
'
Private Function StringToLine(strIn As String) As Line
    Dim lineOut As Line
    lineOut.X1 = Val(Mid$(strIn, 1, MaxNumLen))
    lineOut.Y1 = Val(Mid$(strIn, MaxNumLen + 1, MaxNumLen))
    lineOut.X2 = Val(Mid$(strIn, MaxNumLen * 2 + 1, MaxNumLen))
    lineOut.Y2 = Val(Mid$(strIn, MaxNumLen * 3 + 1, MaxNumLen))
    StringToLine = lineOut
End Function

Private Sub Form_Unload(Cancel As Integer)
    If Not q Is Nothing Then
        q.Close
    End If
    If Not qFriend Is Nothing Then qFriend.Close

End Sub

Private Sub FriendName_Change()
        Attach.Enabled = True
End Sub

'
'Message Receive event
'
Private Sub qevent_Arrived(ByVal q As Object, ByVal lCursor As Long)
    Dim msgIn As MSMQMessage
    Dim lineNew As Line
    Dim strTextIn As String

    On Error GoTo ErrorHandler
    Set msgIn = q.Receive(ReceiveTimeout:=100)
    If Not msgIn Is Nothing Then
        strTextIn = msgIn.Body          'Read the body of the message
        If Len(strTextIn) = 1 Then         'If 1 byte long
            TypeChar strTextIn             'it is a character - so display it
        Else
            lineNew = StringToLine(msgIn.Body)   'Otherwise it is a line
            AddLine lineNew                         'so draw it
        End If
    End If
ErrorHandler:
    ' reenable event firing
    q.EnableNotification qevent
End Sub

Private Sub qevent_ArrivedError(ByVal pdispQueue As Object, ByVal lErrorCode As Long, ByVal lCursor As Long)
    MsgBox Hex$(lErrorCode), , "Receive Error!"
    q.EnableNotification qevent
End Sub

Private Sub Option1_Click(Index As Integer)
    msgOut.Delivery = Index
End Sub

'
'Key press event
'
Private Sub Picture1_KeyPress(KeyAscii As Integer)
    TypeChar (Chr(KeyAscii))                'Display the character
    If Not qFriend Is Nothing Then
        If qFriend.IsOpen Then
            msgOut.Priority = 4                 'Set the priority to 4 (high)
            msgOut.Body = Chr(KeyAscii)         'Fill the body with the character
            msgOut.Label = "Key: " + msgOut.Body
            msgOut.Send qFriend                 'And send the message
        End If
    End If
End Sub
'
'Display a character
'(Handles backspace)
'
Private Sub TypeChar(Key As String)
    If Asc(Key) = 8 Then 'BackSpace
        If strScreenText <> "" Then
            strScreenText = Left$(strScreenText, Len(strScreenText) - 1)
            Picture1.Refresh
        End If
    Else
        strScreenText = strScreenText + Key
        If fWasText Then
            Picture1.Print Key;
        Else
            Picture1.Refresh
        End If
    End If
End Sub
'
'Mouse Down Event
'
Private Sub Picture1_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
    If Button = 1 Then      'Remember the
        lLastX = X           'Mouse location
        lLastY = Y
    End If
End Sub
'
'Mouse Move Event
'
Private Sub Picture1_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
    If Button = 1 And X > 0 And Y > 0 Then          'Something to draw?
        Dim lineNew As Line
        lineNew = PointsToLine(lLastX, lLastY, X, Y)  'Get a new line
        AddLine lineNew                             'And display it
        If Not qFriend Is Nothing Then
            If qFriend.IsOpen Then
                msgOut.Priority = 3                     'Set the priority to 3 (low)
                msgOut.Body = LineToString(lineNew)     'Fill the body with the line
                msgOut.Label = Str(lLastX) + "," + Str(lLastY) + " To " + Str(X) + "," + Str(Y)
                msgOut.Send qFriend                     'And send the message
            End If
        End If
        lLastX = X
        lLastY = Y
    End If
End Sub
'
'2nd button click == Clear the display
'
Private Sub Picture1_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
    If Button = 2 Then ClearDraw
End Sub
'
'Repaint the display event
'
Private Sub Picture1_Paint()
    Dim I As Integer
    For I = 0 To cLines - 1
        DrawLine Lines(I)
    Next
    Picture1.CurrentX = 0
    Picture1.CurrentY = 0
    Picture1.Print strScreenText;
    fWasText = True
End Sub


README.TXT

RASberry - A demonstation of the RAS API set.

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 (C) 1993-1995  Microsoft Corporation.  All Rights Reserved.

PURPOSE:
    To demonstrate the Remote Access Service (RAS) API set.  RASberry
    allows the user to enumerate current RAS connections, display the
    status of a selected connection, dial entries from the default
    phone book, and hang up a active connection.

RAS APIs USED:
    RasDial
    RasEnumConnections
    RasEnumEntries
    RasGetConnectStatus
    RasGetErrorString
    RasHangUp

COMMENTS:
    This sample may be built as a 16 or 32 bit application.  The default
    is 32 bits.  To build a 16 bit version, define MAK16 on the build
    command line.

    32 BIT SPECIFICS:
    - Before the application exits, it should insure that the RAS state
      machine has been reset if there has been a hang up.  This may be
      done by calling Sleep(3000).  See documentation on RasHangUp for
      more information.

    - The 32 bit RasDial API can either function syncronously or
      asyncronously.  This sample demonstrates the asyncronous ability.
      The asyncronous RasDial call requires a callback function to
      receive a notification of a change of state in the connection
      process.  The function is not associated with a window or message
      queue.  To provide a single source for both Win32 and Win16
      applications, the RasDialFunc in RASbeery, just posts a message
      to a window, and the window's message handle takes the
      appropriate action.

    16 BIT SPECIFICS:
    - The RasDial API is only asyncronous for Win16.  RasDial sends
      a WM_RASDIALEVENT message to the window specified in the call to
      RasDial.  An application may perform any tasks during the handling
      of this message, but it should not call EndDialog().  If it is
      desired that the window be closed, a shut down message should be
      posted to the window as in this sample.

MODULE MAP:
    RASberry- Implements message handlers for the main window.  Maintains
              the list of current RAS connections.
    StatDlg - Displays the status of a connection selected from the main
              application window.
    PhbkDlg - Displays contents of default phone book and allow the user
              to dail an entry.
    AuthDlg - Obtains authentication information from user before making
              a connection.
    DialDlg - Displays the states of the RAS connection during the initial
              connection phase.
    RasUtil - Utilities used by modules listed above.
    Dispatch- Message dispatching routines
    WinMain - Calls initialization functions and processes the message loop
    Init    - Performs application and instance specific initialization
    About   - Defines a standard about dialog box.
    Misc    - Defines the applications specific commands not related to
                a specific module.


AUTHDLG.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:   authdlg.c
//
//  PURPOSE:   Displays the "AuthDlg" dialog box
//
//  FUNCTIONS:
//    AuthDlg           - Processes messages for "AuthDlg" dialog box.
//    MsgAuthDlgInit    - To initialize the authdlg box with version info
//                      from resources.
//    MsgAuthDlgCommand - Process WM_COMMAND message sent to the authdlg box.
//    CmdAuthDlgOK      - Free the authdlg box and signal continue with dial.
//    CmdAuthDlgCancel  - Free the authdlg box.
//
//  COMMENTS:
//    Dialog Box to get authentication information
//
//

#include <windows.h>            // required for all Windows applications
#include <windowsx.h>
#ifdef WIN16
#include "win16ext.h"           // required only for win16 applications
#endif
#include "globals.h"            // prototypes specific to this application
#include <ras.h>
#include <raserror.h>
#include "authdlg.h"

LRESULT MsgAuthDlgInit(HWND, UINT, WPARAM, LPARAM);
LRESULT MsgAuthDlgCommand(HWND, UINT, WPARAM, LPARAM);
LRESULT CmdAuthDlgOK(HWND, WORD, WORD, HWND);
LRESULT CmdAuthDlgCancel(HWND, WORD, WORD, HWND);

// AuthDlg dialog message table definition.
MSD rgmsdAuthDlg[] =
{
    {WM_COMMAND,      MsgAuthDlgCommand},
    {WM_INITDIALOG,   MsgAuthDlgInit}
};

MSDI msdiAuthDlg =
{
    sizeof(rgmsdAuthDlg) / sizeof(MSD),
    rgmsdAuthDlg,
    edwpNone
};

// AuthDlg dialog command table definition.
CMD rgcmdAuthDlg[] =
{
    {IDOK,     CmdAuthDlgOK},
    {IDCANCEL, CmdAuthDlgCancel}
};

CMDI cmdiAuthDlg =
{
    sizeof(rgcmdAuthDlg) / sizeof(CMD),
    rgcmdAuthDlg,
    edwpNone
};

// Module specific "globals"  Used when a variable needs to be
// accessed in more than on handler function.

//
//  FUNCTION: AuthDlg(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE:  Processes messages for "AuthDlg" dialog box.
//
//  PARAMETERS:
//    hdlg - window handle of the dialog box
//    wMessage - type of message
//    wparam - message-specific information
//    lparam - message-specific information
//
//  RETURN VALUE:
//    TRUE - message handled
//    FALSE - message not handled
//
//  COMMENTS:
//    Dispatches message to appropriate handler
//

LRESULT CALLBACK AuthDlg(HWND hdlg, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    return DispMessage(&msdiAuthDlg, hdlg, uMessage, wparam, lparam);
}

//
//  FUNCTION: MsgAuthDlgInit(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: To initialize the authdlg box
//
//  PARAMETERS:
//    hwnd - The window handing the message.
//    uMessage - The message number. WM_INITDIALOG.
//    wparam - Message specific data (unused).
//    lparam - Message specific data (unused).
//
//  RETURN VALUE:
//    Always returns TRUE - message handled.
//
//  COMMENTS:
//    Registers window message for RAS event.
//    Makes call to RasDial with appropriate params.
//

LRESULT MsgAuthDlgInit(HWND hdlg, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    // Set text limits on edit controls
    SendDlgItemMessage(hdlg, IDE_USERNAME, EM_LIMITTEXT, (WPARAM) UNLEN, 0L );
    SendDlgItemMessage(hdlg, IDE_PASSWORD, EM_LIMITTEXT, (WPARAM) PWLEN, 0L );
    SendDlgItemMessage(hdlg, IDE_DOMAIN,   EM_LIMITTEXT, (WPARAM) DNLEN, 0L );

    // reset password
    g_szPassword[0] = '\0';

    // Set default text in edit controls
    SendDlgItemMessage(hdlg, IDE_USERNAME, WM_SETTEXT, (WPARAM) 0, (LPARAM)(LPSTR) g_szUserName );
    SendDlgItemMessage(hdlg, IDE_PASSWORD, WM_SETTEXT, (WPARAM) 0, (LPARAM)(LPSTR) g_szPassword );
    SendDlgItemMessage(hdlg, IDE_DOMAIN,   WM_SETTEXT, (WPARAM) 0, (LPARAM)(LPSTR) g_szDomain );

    // Center the dialog over the application window
    CenterWindow(hdlg, GetWindow(hdlg, GW_OWNER));

    return TRUE;
}

//
//  FUNCTION: MsgAuthDlgCommand(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Process WM_COMMAND message sent to the authdlg box.
//
//  PARAMETERS:
//    hwnd - The window handing the message.
//    uMessage - The message number. (unused).
//    wparam - Message specific data (unused).
//    lparam - Message specific data (unused).
//
//  RETURN VALUE:
//    Always returns TRUE - message handled.
//
//  COMMENTS:
//    Uses this DispCommand function defined in wndproc.c combined
//    with the cmdiAuthDlg structure defined in this file to handle
//    the command messages for the authdlg dialog box.
//

LRESULT MsgAuthDlgCommand(HWND   hwnd,
                        UINT   uMessage,
                        WPARAM wparam,
                        LPARAM lparam)
{
    return DispCommand(&cmdiAuthDlg, hwnd, wparam, lparam);
}

//
//  FUNCTION: CmdAuthDlgOK(HWND, WORD, WORD,  HWND)
//
//  PURPOSE: Free the authdlg box and related data.
//
//  PARAMETERS:
//    hwnd - The window handling the command.
//    wCommand - The command to be handle. IDOK.
//    wNotify - The notification to be handled (unused).
//    hwndCtrl - NULL (unused).
//
//  RETURN VALUE:
//    Always returns TRUE.
//
//  COMMENTS:
//    Calls EndDialog to finish the dialog session.
//

LRESULT CmdAuthDlgOK(HWND hdlg, WORD wCommand, WORD wNotify, HWND hwndCtrl)
{
    // Get text entered by the user
    SendDlgItemMessage(hdlg, IDE_USERNAME, WM_GETTEXT, (WPARAM) UNLEN+1, (LPARAM)(LPSTR) g_szUserName );
    SendDlgItemMessage(hdlg, IDE_PASSWORD, WM_GETTEXT, (WPARAM) PWLEN+1, (LPARAM)(LPSTR) g_szPassword );
    SendDlgItemMessage(hdlg, IDE_DOMAIN,   WM_GETTEXT, (WPARAM) DNLEN+1, (LPARAM)(LPSTR) g_szDomain );

    EndDialog(hdlg, TRUE);          // Exit the dialog
    return TRUE;
}

//
//  FUNCTION: CmdAuthDlgCancel(HWND, WORD, WORD,  HWND)
//
//  PURPOSE: Free the authdlg box and related data.
//
//  PARAMETERS:
//    hwnd - The window handling the command.
//    wCommand - The command to be handled. IDCANCEL.
//    wNotify - The notification to be handled (unused).
//    hwndCtrl - NULL (unused).
//
//  RETURN VALUE:
//    Always returns TRUE.
//
//  COMMENTS:
//    Calls EndDialog to finish the dialog session.
//

LRESULT CmdAuthDlgCancel(HWND hdlg, WORD wCommand, WORD wNotify, HWND hwndCtrl)
{
    EndDialog(hdlg, FALSE);          // Exit the dialog
    return TRUE;
}


AUTHDLG.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:   authdlg.h
//
//  PURPOSE:  contains authdlg declarations
//

#ifndef _AUTHDLG_H
#define _AUTHDLG_H

//-------------------------------------------------------------------------
//  Authentication dialog defines.

#define IDE_USERNAME  600
#define IDE_PASSWORD  601
#define IDE_DOMAIN    602

#ifdef WIN16
// Dialog box procedures must be exported in 16-bit applications for Windows.
LRESULT CALLBACK __export AuthDlg(HWND, UINT, WPARAM, LPARAM);
#else
LRESULT CALLBACK AuthDlg(HWND, UINT, WPARAM, LPARAM);
#endif

#endif


DIALDLG.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:   dialdlg.c
//
//  PURPOSE:   Displays the "DialDlg" dialog box
//
//  FUNCTIONS:
//    DialDlg           - Processes messages for "DialDlg" dialog box.
//    MsgDialDlgInit    - To initialize the dialdlg box and make the
//                        RasDial call.
//    MsgDialDlgEvent   - Process WM_RASDIALEVENT message.
//    MsgDialDlgCommand - Process WM_COMMAND message.
//    CmdDialDlgCancel  - Free the dialdlg box and related data.
//    RasDialFunc       - RasDial callback function for Win32.
//
//  COMMENTS:
//    Dialog box to display status of connection attempt
//
//

#include <windows.h>            // required for all Windows applications
#include <windowsx.h>
#ifdef WIN16
#include "win16ext.h"           // required only for win16 applications
#endif
#include "globals.h"            // prototypes specific to this application
#include <ras.h>
#include <raserror.h>
#include "rasutil.h"
#include "dialdlg.h"
#include "phbkdlg.h"

LRESULT MsgDialDlgInit(HWND, UINT, WPARAM, LPARAM);
LRESULT MsgDialDlgEvent(HWND, UINT, WPARAM, LPARAM);
LRESULT MsgDialDlgCommand(HWND, UINT, WPARAM, LPARAM);
LRESULT CmdDialDlgCancel(HWND, WORD, WORD, HWND);

#ifdef WIN32
VOID WINAPI RasDialFunc( UINT unMsg, RASCONNSTATE rasconnstate, DWORD dwError );
#endif

// DialDlg dialog message table definition.
MSD rgmsdDialDlg[] =
{
    {WM_RASDIALEVENT, MsgDialDlgEvent},
    {WM_COMMAND,      MsgDialDlgCommand},
    {WM_INITDIALOG,   MsgDialDlgInit}
};

MSDI msdiDialDlg =
{
    sizeof(rgmsdDialDlg) / sizeof(MSD),
    rgmsdDialDlg,
    edwpNone
};

// DialDlg dialog command table definition.
CMD rgcmdDialDlg[] =
{
    {IDCANCEL,  CmdDialDlgCancel}
};

CMDI cmdiDialDlg =
{
    sizeof(rgcmdDialDlg) / sizeof(CMD),
    rgcmdDialDlg,
    edwpNone
};

// Module specific "globals"  Used when a variable needs to be
// accessed in more than on handler function.
HRASCONN hRasConn;

#ifdef WIN32
HWND   g_hDlg;
#endif

//
//  FUNCTION: DialDlg(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE:  Processes messages for "DialDlg" dialog box.
//
//  PARAMETERS:
//    hdlg - window handle of the dialog box
//    wMessage - type of message
//    wparam - message-specific information
//    lparam - message-specific information
//
//  RETURN VALUE:
//    TRUE - message handled
//    FALSE - message not handled
//
//  COMMENTS:
//
//

LRESULT CALLBACK DialDlg(HWND hdlg, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    return DispMessage(&msdiDialDlg, hdlg, uMessage, wparam, lparam);
}

//
//  FUNCTION: MsgDialDlgInit(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: To initialize the dialdlg box and dial
//
//  PARAMETERS:
//    hwnd - The window handing the message.
//    uMessage - WM_INITDLG.
//    wparam - Message specific data (unused).
//    lparam - Message specific data (unused).
//
//  RETURN VALUE:
//    Always returns TRUE - message handled.
//
//  COMMENTS:
//    Registers window message for RAS event.
//    Makes call to RasDial with appropriate params.
//

LRESULT MsgDialDlgInit(HWND hdlg, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    RASDIALPARAMS rdParams;
    char szEntry[RAS_MaxEntryName+1];
    DWORD dwRet;
    char  szBuf[256];
    HWND  hwndEntry;
    UINT  ndx;

    hwndEntry = GetDlgItem( GetParent(hdlg), IDL_ENTRY);
    ndx = (UINT) SendMessage( hwndEntry, LB_GETCURSEL, 0,  0L );
    SendMessage( hwndEntry, LB_GETTEXT, ndx, (LPARAM)(LPSTR)szEntry );
    wsprintf( (LPSTR) szBuf, "Dialing %s...", (LPSTR) szEntry );
    SetWindowText( hdlg, (LPSTR)szBuf );

    // setup RAS Dial Parameters
    rdParams.dwSize = sizeof(RASDIALPARAMS);
    lstrcpy(rdParams.szEntryName, szEntry );
    rdParams.szPhoneNumber[0] = '\0';
    rdParams.szCallbackNumber[0] = '*';
    rdParams.szCallbackNumber[0] = '\0';

    if ( g_bUseCurrent )
    {
        rdParams.szUserName[0] = '\0';
        rdParams.szPassword[0] = '\0';
        rdParams.szDomain[0] = '*';
        rdParams.szDomain[1] = '\0';
    }
    else
    {
        lstrcpy(rdParams.szUserName, g_szUserName );
        lstrcpy(rdParams.szPassword, g_szPassword );
        lstrcpy(rdParams.szDomain,   g_szDomain );
    }

    hRasConn = NULL;

#ifdef WIN16
    rgmsdDialDlg[0].uMessage = RegisterWindowMessage(RASDIALEVENT);
    dwRet = RasDial( NULL, NULL, &rdParams, NULL, hdlg, &hRasConn );
#else
    g_hDlg = hdlg;
    dwRet = RasDial( NULL, NULL, &rdParams, 0L, (RASDIALFUNC) RasDialFunc, &hRasConn );
#endif

    if ( dwRet )
    {
        if ( RasGetErrorString( (UINT)dwRet, (LPSTR)szBuf, 256 ) != 0 )
            wsprintf( (LPSTR)szBuf, "Undefined RAS Dial Error (%ld).", dwRet );

        MessageBox(hdlg, (LPSTR)szBuf, szAppName, MB_OK | MB_ICONSTOP );
        EndDialog(hdlg, FALSE);
        return TRUE;
    }
    // Center the dialog over the application window
    CenterWindow(hdlg, GetWindow(hdlg, GW_OWNER));

    return TRUE;
}

//
//  FUNCTION: MsgDialDlgEvent(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: To display messages during dialdlg
//
//  PARAMETERS:
//    hwnd - The window handing the message.
//    uMessage - WM_RASDIALEVENT.
//    wparam - Message specific data (unused).
//    lparam - Message specific data (unused).
//
//  RETURN VALUE:
//    Always returns TRUE - message handled.
//
//  COMMENTS:
//    Updates the text of IDT_MESSAGE to indicate the current
//    state in the connection.  The dialog is closed upon
//    connect or disconnect status.
//

LRESULT MsgDialDlgEvent(HWND hdlg, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    char szMessage[256];

    LoadString(hInst, GetRasConnState( (RASCONNSTATE) wparam ), szMessage, 64 );
    SetDlgItemText( hdlg, IDT_MESSAGE, (LPCSTR) szMessage );

    if ( lparam )  // error occurred
    {
        if ( RasGetErrorString( (UINT)lparam, szMessage, 256 ) != 0 )
            wsprintf( (LPSTR)szMessage, "Undefined RAS Dial Error." );

        MessageBox(hdlg, (LPSTR)szMessage, szAppName, MB_OK | MB_ICONSTOP );
        PostMessage( hdlg, WM_COMMAND, (WPARAM) IDCANCEL, 0L );
    }
    else if ( RASCS_DONE & wparam )
    {
        EndDialog(hdlg, TRUE);          // Exit the dialog
    }

    return TRUE;
}

//
//  FUNCTION: MsgDialDlgCommand(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Process WM_COMMAND message sent to the dialdlg box.
//
//  PARAMETERS:
//    hwnd - The window handing the message.
//    uMessage - The message number. (unused).
//    wparam - Message specific data (unused).
//    lparam - Message specific data (unused).
//
//  RETURN VALUE:
//    Always returns TRUE - message handled.
//
//  COMMENTS:
//    Uses this DispCommand function defined in wndproc.c combined
//    with the cmdiDialDlg structure defined in this file to handle
//    the command messages for the dialdlg dialog box.
//

LRESULT MsgDialDlgCommand(HWND   hwnd,
                        UINT   uMessage,
                        WPARAM wparam,
                        LPARAM lparam)
{
    return DispCommand(&cmdiDialDlg, hwnd, wparam, lparam);
}

//
//  FUNCTION: CmdDialDlgCancel(HWND, WORD, WORD,  HWND)
//
//  PURPOSE: Free the dialdlg box and related data.
//
//  PARAMETERS:
//    hwnd - The window handling the command.
//    wCommand - IDCANCEL.
//    wNotify - The notification to be handled (unused).
//    hwndCtrl - NULL (unused).
//
//  RETURN VALUE:
//    Always returns TRUE.
//
//  COMMENTS:
//    Closes current connection attempt and calls EndDialog
//    to finish the dialog session.
//

LRESULT CmdDialDlgCancel(HWND hdlg, WORD wCommand, WORD wNotify, HWND hwndCtrl)
{
    RasHangUp( hRasConn );
    EndDialog(hdlg, FALSE);          // Exit the dialog
    return TRUE;
}

#ifdef WIN32
//  FUNCTION: RasDialFunc( UINT, RASCONNSTATE, DWORD )
//
//  PURPOSE: Called by RasDial, passes params onto dialog box
//
//  PARAMETERS:
//    unMsg        - Yype of RAS event that occurred.
//    rasconnstate - Connection state about to be entered.
//    dwError      - Error code that may have occurred.
//
//  COMMENTS:
//    appropriate action is taken in the WM_RASDIALMESSAGE handler

VOID WINAPI RasDialFunc( UINT unMsg, RASCONNSTATE rasconnstate, DWORD dwError )
{
    PostMessage( g_hDlg,
                 rgmsdDialDlg[0].uMessage,
                 (WPARAM) rasconnstate,
                 (LPARAM) dwError );

}

#endif


DIALDLG.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:   dialdlg.h
//
//  PURPOSE:  contains dialdlg declarations
//

#ifndef _DIALDLG_H
#define _DIALDLG_H

//-------------------------------------------------------------------------
//  Dialing dialog defines.

#define IDT_MESSAGE     300

#ifdef WIN16
// Dialog box procedures must be exported in 16-bit applications for Windows.
LRESULT CALLBACK __export DialDlg(HWND, UINT, WPARAM, LPARAM);
#else
LRESULT CALLBACK DialDlg(HWND, UINT, WPARAM, LPARAM);
#endif

#endif


DISPATCH.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:   dispatch.c
//
//  PURPOSE:  Implement the generic message and command dispatchers.
//
//
//  FUNCTIONS:
//    DispMessage - Call the function associated with a message.
//    DispCommand - Call the function associated with a command.
//    DispDefault - Call the appropriate default window procedure.
//
//  COMMENTS:
//

#include <windows.h>            // required for all Windows applications
#include <windowsx.h>
#ifdef WIN16
#include "win16ext.h"           // required only for win16 applications
#endif
#include "globals.h"            // prototypes specific to this application

LRESULT DispDefault(EDWP, HWND, UINT, WPARAM, LPARAM);

//
//  FUNCTION: DispMessage(LPMSDI, HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Call the function associated with a message.
//
//  PARAMETERS:
//    lpmsdi - Structure containing the message dispatch information.
//    hwnd - The window handle
//    uMessage - The message number
//    wparam - Message specific data
//    lparam - Message specific data
//
//  RETURN VALUE:
//    The value returned by the message function that was called.
//
//  COMMENTS:
//    Runs the table of messages stored in lpmsdi->rgmsd searching
//    for a message number that matches uMessage.  If a match is found,
//    call the associated function.  Otherwise, call DispDefault to
//    call the default function, if any, associated with the message
//    structure.  In either case, return the value recieved from the
//    message or default function.
//

LRESULT DispMessage(LPMSDI lpmsdi,
                    HWND   hwnd,
                    UINT   uMessage,
                    WPARAM wparam,
                    LPARAM lparam)
{
    int  imsd = 0;

    MSD *rgmsd = lpmsdi->rgmsd;
    int  cmsd  = lpmsdi->cmsd;

    for (imsd = 0; imsd < cmsd; imsd++)
    {
        if (rgmsd[imsd].uMessage == uMessage)
            return rgmsd[imsd].pfnmsg(hwnd, uMessage, wparam, lparam);
    }

    return DispDefault(lpmsdi->edwp, hwnd, uMessage, wparam, lparam);
}

//
//  FUNCTION: DispCommand(LPCMDI, HWND, WPARAM, LPARAM)
//
//  PURPOSE: Call the function associated with a command.
//
//  PARAMETERS:
//    lpcmdi - Structure containing the command dispatch information.
//    hwnd - The window handle
//    GET_WM_COMMAND_ID(wparam, lparam) - Identifier of the menu item,
//      control, or accelerator.
//    GET_WM_COMMAND_CMD(wparam, lparam) - Notification code.
//    GET_WM_COMMAND_HWND(wparam, lparam) - The control handle or NULL.
//
//  RETURN VALUE:
//    The value returned by the command function that was called.
//
//  COMMENTS:
//    Runs the table of commands stored in lpcmdi->rgcmd searching
//    for a command number that matches wCommand.  If a match is found,
//    call the associated function.  Otherwise, call DispDefault to
//    call the default function, if any, associated with the command
//    structure.  In either case, return the value recieved from the
//    command or default function.
//

LRESULT DispCommand(LPCMDI lpcmdi,
                    HWND   hwnd,
                    WPARAM wparam,
                    LPARAM lparam)
{
    LRESULT lRet = 0;
    WORD    wCommand = GET_WM_COMMAND_ID(wparam, lparam);
    int     icmd;

    CMD    *rgcmd = lpcmdi->rgcmd;
    int     ccmd  = lpcmdi->ccmd;

    // Message packing of wparam and lparam have changed for Win32,
    // so use the GET_WM_COMMAND macro to unpack the commnad

    for (icmd = 0; icmd < ccmd; icmd++)
    {
        if (rgcmd[icmd].wCommand == wCommand)
        {
            return rgcmd[icmd].pfncmd(hwnd,
                                      wCommand,
                                      GET_WM_COMMAND_CMD(wparam, lparam),
                                      GET_WM_COMMAND_HWND(wparam, lparam));
        }
    }

    return DispDefault(lpcmdi->edwp, hwnd, WM_COMMAND, wparam, lparam);
}

//
//  FUNCTION: DispDefault(EDWP, HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Call the appropriate default window procedure.
//
//  PARAMETERS:
//    edwp - Enumerate specifying the appropriate default winow procedure.
//    hwnd - The window handle
//    uMessage - The message number
//    wparam - Message specific data
//    lparam - Message specific data
//
//  RETURN VALUE:
//    If there is a default proc, return the value returned by the
//    default proc.  Otherwise, return 0.
//
//  COMMENTS:
//    Calls the default procedure associated with edwp using the specified
//    parameters.
//

LRESULT DispDefault(EDWP   edwp,
                    HWND   hwnd,
                    UINT   uMessage,
                    WPARAM wparam,
                    LPARAM lparam)
{
    switch (edwp)
    {
        case edwpNone:
            return 0;
        case edwpWindow:
            return DefWindowProc(hwnd, uMessage, wparam, lparam);
        case edwpDialog:
            return DefDlgProc(hwnd, uMessage, wparam, lparam);
        case edwpMDIFrame:
            return DefFrameProc(hwnd, hwndMDIClient, uMessage, wparam, lparam);
        case edwpMDIChild:
            return DefMDIChildProc(hwnd, uMessage, wparam, lparam);
    }
    return 0;
}


GLOBALS.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.
//
// PURPOSE:
//    Contains declarations for all globally scoped names in the program.
//

#include <ras.h> // needed for HRASCONN structure

//-------------------------------------------------------------------------
// Product identifier string defines

//  **TODO** Change these strings to the name of your application.

#define APPNAME       RASberry
#define ICONFILE      RASberry.ico
#define SZAPPNAME     "RASberry"
#define SZDESCRIPTION "Remote Access Service Sample"
#define SZABOUT       "About RASberry"
#define SZVERSION     "Version 1.0"

//-------------------------------------------------------------------------
// Functions for handling main window messages.  The message-dispatching
// mechanism expects all message-handling functions to have the following
// prototype:
//
//     LRESULT FunctionName(HWND, UINT, WPARAM, LPARAM);

// **TODO**  Add message-handling function prototypes here.
//           add the function names to the main window message table in
//           rasberry.c.

LRESULT MsgCreate(HWND, UINT, WPARAM, LPARAM);
LRESULT MsgSetFocus(HWND, UINT, WPARAM, LPARAM);
LRESULT MsgSize(HWND, UINT, WPARAM, LPARAM);
LRESULT MsgCommand(HWND, UINT, WPARAM, LPARAM);
LRESULT MsgDestroy(HWND, UINT, WPARAM, LPARAM);

//-------------------------------------------------------------------------
// Functions for handling main window commands--ie. functions for
// processing WM_COMMAND messages based on the wParam value.
// The message-dispatching mechanism expects all command-handling
// functions to have the following prototype:
//
//     LRESULT FunctionName(HWND, WORD, WORD, HWND);

// **TODO**  Add message-handling function prototypes here.  Be sure to
//           add the function names to the main window command table in
//           rasberry.c.

LRESULT CmdPhbkDlg(HWND, WORD, WORD, HWND);
LRESULT CmdStatDlg(HWND, WORD, WORD, HWND);
LRESULT CmdHangUp(HWND, WORD, WORD, HWND);
LRESULT CmdRefresh(HWND, WORD, WORD, HWND);
LRESULT CmdConnNotify(HWND, WORD, WORD, HWND);
LRESULT CmdAbout(HWND, WORD, WORD, HWND);
LRESULT CmdExit(HWND, WORD, WORD, HWND);

//-------------------------------------------------------------------------
// Global function prototypes.

// **TODO**  Add global function prototypes here.

BOOL InitApplication(HINSTANCE);
BOOL InitInstance(HINSTANCE, int);
BOOL CenterWindow(HWND, HWND);

    // Callback functions.  These are called by Windows.

// **TODO**  Add new callback function prototypes here.  Win16 compiles
//           require the __export keyword to generate proper prolog
//           and epilog code for exported functions.

#ifdef WIN16

LRESULT CALLBACK __export WndProc(HWND, UINT, WPARAM, LPARAM);

#else

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

#endif

//-------------------------------------------------------------------------
// Command ID definitions.  These definitions are used to associate menu
// items with commands.

// **TODO**  Add IDs for all menu commands here.  Be sure that the IDs you
//           add are also added to the main window command table in rasberry.c
//           and the menu statements in the resource file.

// File menu
#define IDM_EXIT         1000
#define IDM_PHONEBOOK    1001

// Connection menu
#define IDM_REFRESH      1100
#define IDM_STATUS       1101
#define IDM_HANGUP       1102

// Help menu
#define IDM_ABOUT        1200

//-------------------------------------------------------------------------
// String Table ID definitions.

#define IDS_APPNAME         1
#define IDS_DESCRIPTION     2
#define IDS_UNDEFINED_ERROR 3

//-------------------------------------------------------------------------
//  Main window defines.

#define IDL_CONN        100
#define IDC_STATIC       -1

//-------------------------------------------------------------------------
//  About dialog defines.

#define IDD_VERFIRST    200
#define IDD_VERLAST     204

//-------------------------------------------------------------------------
// Global variable declarations.

extern HINSTANCE hInst;          // The current instance handle
extern char      szAppName[];    // The name of this application
extern char      szTitle[];      // The title bar text

extern char      g_szUserName[];
extern char      g_szPassword[];
extern char      g_szDomain[];

extern LPHRASCONN g_lphRasConn;

// **TODO**  For NON-MDI applications uncomment line 1 below and comment
//           line 2.  For MDI applications, uncomment line 2 below, comment
//           line 1, and then define hwndMDIClient as a global variable in
//           INIT.C
#define hwndMDIClient NULL        /* (1) Stub for NON-MDI applications. */
// extern HWND hwndMDIClient;     /* (2) For MDI applications.          */

//-------------------------------------------------------------------------
// Message and command dispatch infrastructure.  The following type
// definitions and functions are used by the message and command dispatching
// mechanism and do not need to be changed.

    // Function pointer prototype for message handling functions.
typedef LRESULT (*PFNMSG)(HWND,UINT,WPARAM,LPARAM);

    // Function pointer prototype for command handling functions.
typedef LRESULT (*PFNCMD)(HWND,WORD,WORD,HWND);

    // Enumerated type used to determine which default window procedure
    // should be called by the message- and command-dispatching mechanism
    // if a message or command is not handled explicitly.
typedef enum
{
   edwpNone,            // Do not call any default procedure.
   edwpWindow,          // Call DefWindowProc.
   edwpDialog,          // Call DefDlgProc (This should be used only for
                        // custom dialogs - standard dialog use edwpNone).
   edwpMDIChild,        // Call DefMDIChildProc.
   edwpMDIFrame         // Call DefFrameProc.
} EDWP;                // Enumeration for Default Window Procedures

    // This structure maps messages to message handling functions.
typedef struct _MSD
{
    UINT   uMessage;
    PFNMSG pfnmsg;
} MSD;                 // MeSsage Dispatch structure

    // This structure contains all of the information that a window
    // procedure passes to DispMessage in order to define the message
    // dispatching behavior for the window.
typedef struct _MSDI
{
    int  cmsd;          // Number of message dispatch structs in rgmsd
    MSD *rgmsd;         // Table of message dispatch structures
    EDWP edwp;          // Type of default window handler needed.
} MSDI, FAR *LPMSDI;   // MeSsage Dipatch Information

    // This structure maps command IDs to command handling functions.
typedef struct _CMD
{
    WORD   wCommand;
    PFNCMD pfncmd;
} CMD;                 // CoMmand Dispatch structure

    // This structure contains all of the information that a command
    // message procedure passes to DispCommand in order to define the
    // command dispatching behavior for the window.
typedef struct _CMDI
{
    int  ccmd;          // Number of command dispatch structs in rgcmd
    CMD *rgcmd;         // Table of command dispatch structures
    EDWP edwp;          // Type of default window handler needed.
} CMDI, FAR *LPCMDI;   // CoMmand Dispatch Information

    // Message and command dispatching functions.  They look up messages
    // and commands in the dispatch tables and call the appropriate handler
    // function.
LRESULT DispMessage(LPMSDI, HWND, UINT, WPARAM, LPARAM);
LRESULT DispCommand(LPCMDI, HWND, WPARAM, LPARAM);

    // Message dispatch information for the main window
extern MSDI msdiMain;
    // Command dispatch information for the main window
extern CMDI cmdiMain;

//-------------------------------------------------------------------------
// Version string definitions--Leave these alone.

#define SZRCOMPANYNAME "CompanyName"
#define SZRDESCRIPTION "FileDescription"
#define SZRVERSION     "FileVersion"
#define SZRAPPNAME     "InternalName"
#define SZRCOPYRIGHT   "LegalCopyright"
#define SZRTRADEMARK   "LegalTrademarks"
#define SZRPRODNAME    "ProductName"
#define SZRPRODVER     "ProuctVersion"


INIT.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:   init.c
//
//  PURPOSE:   Performs application and instance specific initialization.
//
//  FUNCTIONS:
//    InitApplication() - Initializes window data and registers window.
//    InitInstance() - Saves instance handle and creates main window.
//
//  COMMENTS:
//

#include <windows.h>            // required for all Windows applications
#ifdef WIN16
#include "win16ext.h"           // required only for win16 applications
#endif
#include "globals.h"            // prototypes specific to this application

HINSTANCE hInst;                // current instance

char szAppName[9];              // The name of this application
char szTitle[40];               // The title bar text

//
//  FUNCTION: InitApplication(HINSTANCE)
//
//  PURPOSE: Initializes window data and registers window class.
//
//  PARAMETERS:
//    hInstance - The handle to the instance of this application that
//          is currently being executed.
//
//  RETURN VALUE:
//    TRUE - Success
//    FALSE - Initialization failed
//
//  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 hInstance)
{
    WNDCLASS  wc;

    // Load the application name and description strings.

    LoadString(hInstance, IDS_APPNAME, szAppName, sizeof(szAppName));
    LoadString(hInstance, IDS_DESCRIPTION, szTitle, sizeof(szTitle));

    // Fill in window class structure with parameters that describe the
    // main window.

    wc.style         = CS_HREDRAW | CS_VREDRAW; // Class style(s).
    wc.lpfnWndProc   = (WNDPROC)WndProc;        // Window Procedure
    wc.cbClsExtra    = 0;                       // No per-class extra data.
    wc.cbWndExtra    = 0;                       // No per-window extra data.
    wc.hInstance     = hInstance;               // Owner of this class
    wc.hIcon         = LoadIcon(hInstance, szAppName); // Icon name from .RC
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW); // Cursor
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); // Default color
    wc.lpszMenuName  = szAppName;               // Menu name from .RC
    wc.lpszClassName = szAppName;               // Name to register as

    // Register the window class and return FALSE if unsuccesful.

    if (!RegisterClass(&wc))
    {
        return FALSE;
    }
    //
    // **TODO** Call module specific application initialization functions here.
    //

    return TRUE;
}

//
//  FUNCTION:  InitInstance(HINSTANCE, int)
//
//  PURPOSE:  Saves instance handle and creates main window.
//
//  PARAMTERS:
//    hInstance - The handle to the instance of this application that
//          is currently being executed.
//    nCmdShow - Specifies how the main window is to be diplayed.
//
//  RETURN VALUE:
//    TRUE - Success
//    FALSE - Initialization failed
//
//  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 hInstance, int nCmdShow)
{
    HWND    hwnd; // Main window handle.

    // Save the instance handle in static variable, which will be used in
    // many subsequence calls from this application to Windows.

    hInst = hInstance; // Store instance handle in our global variable

    // Create a main window for this application instance.
    hwnd = CreateWindow(szAppName,           // See RegisterClass() call.
                        szTitle,             // Text for window title bar.
                        WS_OVERLAPPEDWINDOW, // Window style.
                        CW_USEDEFAULT, 0,    // Use default positioning
                        CW_USEDEFAULT, 0,    // Use default size
                        NULL,                // Overlapped has no parent.
                        NULL,                // Use the window class menu.
                        hInstance,
                        NULL);

    // If window could not be created, return "failure"
    if (!hwnd)
        return FALSE;

    //
    // **TODO** Call module specific instance initialization functions here.
    //

    // Make the window visible; update its client area; and return "success"
    ShowWindow(hwnd, nCmdShow); // Show the window
    UpdateWindow(hwnd);         // Sends WM_PAINT message

    return TRUE;                // We succeeded...
}


MISC.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:   misc.c
//
//  PURPOSE:  Contains all helper functions "global" to the application.
//
//  FUNCTIONS:
//    CenterWindow - Center one window over another.
//
//  COMMENTS:
//
//

#include <windows.h>            // required for all Windows applications
#include <windowsx.h>

#ifdef WIN16
#include "win16ext.h"           // required only for win16 applications
#endif
#include "globals.h"            // prototypes specific to this application

//
//  FUNCTION: CenterWindow(HWND, HWND)
//
//  PURPOSE:  Center one window over another.
//
//  PARAMETERS:
//    hwndChild - The handle of the window to be centered.
//    hwndParent- The handle of the window to center on.
//
//  RETURN VALUE:
//
//    TRUE  - Success
//    FALSE - Failure
//
//  COMMENTS:
//
//    Dialog boxes take on the screen position that they were designed
//    at, which is not always appropriate. Centering the dialog over a
//    particular window usually results in a better position.
//

BOOL CenterWindow(HWND hwndChild, HWND hwndParent)
{
    RECT    rcChild, rcParent;
    int     cxChild, cyChild, cxParent, cyParent;
    int     cxScreen, cyScreen, xNew, yNew;
    HDC     hdc;

    // Get the Height and Width of the child window
    GetWindowRect(hwndChild, &rcChild);
    cxChild = rcChild.right - rcChild.left;
    cyChild = rcChild.bottom - rcChild.top;

    // Get the Height and Width of the parent window
    GetWindowRect(hwndParent, &rcParent);
    cxParent = rcParent.right - rcParent.left;
    cyParent = rcParent.bottom - rcParent.top;

    // Get the display limits
    hdc = GetDC(hwndChild);
    cxScreen = GetDeviceCaps(hdc, HORZRES);
    cyScreen = GetDeviceCaps(hdc, VERTRES);
    ReleaseDC(hwndChild, hdc);

    // Calculate new X position, then adjust for screen
    xNew = rcParent.left + ((cxParent - cxChild) / 2);
    if (xNew < 0)
    {
        xNew = 0;
    }
    else if ((xNew + cxChild) > cxScreen)
    {
        xNew = cxScreen - cxChild;
    }

    // Calculate new Y position, then adjust for screen
    yNew = rcParent.top  + ((cyParent - cyChild) / 2);
    if (yNew < 0)
    {
        yNew = 0;
    }
    else if ((yNew + cyChild) > cyScreen)
    {
        yNew = cyScreen - cyChild;
    }

    // Set it, and return
    return SetWindowPos(hwndChild,
                        NULL,
                        xNew, yNew,
                        0, 0,
                        SWP_NOSIZE | SWP_NOZORDER);
}


PHBKDLG.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:   phbkdlg.c
//
//  PURPOSE:   Displays the "Phonebox Entries" dialog box
//
//  FUNCTIONS:
//    PhbkDlg           - Processes messages for "PhbkDlg" dialog box.
//    MsgPhbkDlgInit    - To initialize the phbkdlg box with version info
//                      from resources.
//    MsgPhbkDlgCommand - Process WM_COMMAND message sent to the phbkdlg box.
//    CmdPhbkDlgCancel  - Free the phbkdlg box and related data.
//    CmdPhbkDlgEntry   - Process LBN_DBLCLK for the entry list box
//    CmdPhbkDlgDial    - Calls AUTHDLG and/or DIALDLG boxes.
//
//  COMMENTS:
//    Displays and allow user to dial an entry from the default phonebook
//
//

#include <windows.h>            // required for all Windows applications
#include <windowsx.h>
#ifdef WIN16
#include "win16ext.h"           // required only for win16 applications
#endif
#include "globals.h"            // prototypes specific to this application
#include <malloc.h>
#include <ras.h>
#include <raserror.h>
#include "phbkdlg.h"
#include "dialdlg.h"
#include "authdlg.h"

LRESULT MsgPhbkDlgInit(HWND, UINT, WPARAM, LPARAM);
LRESULT MsgPhbkDlgCommand(HWND, UINT, WPARAM, LPARAM);
LRESULT CmdPhbkDlgCancel(HWND, WORD, WORD, HWND);
LRESULT CmdPhbkDlgEntry(HWND, WORD, WORD, HWND);
LRESULT CmdPhbkDlgDial(HWND, WORD, WORD, HWND);

// PhbkDlg dialog message table definition.
MSD rgmsdPhbkDlg[] =
{
    {WM_COMMAND,    MsgPhbkDlgCommand},
    {WM_INITDIALOG, MsgPhbkDlgInit}
};

MSDI msdiPhbkDlg =
{
    sizeof(rgmsdPhbkDlg) / sizeof(MSD),
    rgmsdPhbkDlg,
    edwpNone
};

// PhbkDlg dialog command table definition.
CMD rgcmdPhbkDlg[] =
{
    {IDB_DIAL,  CmdPhbkDlgDial},
    {IDCANCEL,  CmdPhbkDlgCancel},
    {IDL_ENTRY, CmdPhbkDlgEntry}
};

CMDI cmdiPhbkDlg =
{
    sizeof(rgcmdPhbkDlg) / sizeof(CMD),
    rgcmdPhbkDlg,
    edwpNone
};

// Module specific "globals"  Used when a variable needs to be
// accessed in more than on handler function.

#ifdef WIN16                     // for Win16 we require that the user
    BOOL g_bUseCurrent = FALSE;  // always enters username and password
#else
    BOOL g_bUseCurrent = TRUE;
#endif

//
//  FUNCTION: PhbkDlg(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE:  Processes messages for "PhbkDlg" dialog box.
//
//  PARAMETERS:
//    hdlg - window handle of the dialog box
//    wMessage - type of message
//    wparam - message-specific information
//    lparam - message-specific information
//
//  RETURN VALUE:
//    TRUE - message handled
//    FALSE - message not handled
//
//  COMMENTS:
//    Dispatches messages for dialog box
//

LRESULT CALLBACK PhbkDlg(HWND hdlg, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    return DispMessage(&msdiPhbkDlg, hdlg, uMessage, wparam, lparam);
}

//
//  FUNCTION: MsgPhbkDlgInit(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: To initialize the phbkdlg box.
//
//  PARAMETERS:
//    hwnd - The window handing the message.
//    uMessage - The message number. WM_INITDIALOG.
//    wparam - Message specific data (unused).
//    lparam - Message specific data (unused).
//
//  RETURN VALUE:
//    Always returns TRUE - message handled.
//
//  COMMENTS:
//    Displays default phone book by calling RasEnumEntries
//

LRESULT MsgPhbkDlgInit(HWND hdlg, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    LPRASENTRYNAME lpRasEntry;
    LPRASENTRYNAME lpTemp;
    DWORD cbBuf;
    DWORD cEntry;
    DWORD dwRet;
    UINT  ndx;
    char  szMessage[256];

    cbBuf = sizeof(RASENTRYNAME);
    if ((lpRasEntry = ( LPRASENTRYNAME ) _fmalloc((UINT)cbBuf)) != NULL )
    {
        lpRasEntry->dwSize = sizeof(RASENTRYNAME);
        dwRet = RasEnumEntries( NULL, NULL, lpRasEntry, &cbBuf, &cEntry );
        if ( dwRet == ERROR_BUFFER_TOO_SMALL )
        {
            if ((lpTemp = ( LPRASENTRYNAME ) _frealloc ( lpRasEntry, (UINT)cbBuf )) != NULL )
            {
                lpRasEntry = lpTemp;
                dwRet = RasEnumEntries( NULL, NULL, lpRasEntry, &cbBuf, &cEntry );
            }
            else
            {
                dwRet = ERROR_NOT_ENOUGH_MEMORY;
            }
        }
        else if ( dwRet != 0 ) // other error
        {
            if ( RasGetErrorString( (UINT)dwRet, szMessage, 256 ) != 0 )
                wsprintf( (LPSTR)szMessage, "Undefined RasEnumEntries Error." );

            MessageBox(hdlg, (LPSTR)szMessage, szAppName, MB_OK | MB_ICONSTOP );
        }

        if ( dwRet == 0 )  // No errors
        {
            for ( ndx = 0; ndx < cEntry; ndx++ )
                SendDlgItemMessage( hdlg,
                                    IDL_ENTRY,
                                    LB_ADDSTRING,
                                    0,
                                    (LPARAM)(LPCSTR) lpRasEntry[ndx].szEntryName);

            if ( ndx > 0 ) // at least one item was added
            {
                SendDlgItemMessage( hdlg, IDL_ENTRY, LB_SETCURSEL, 0, 1L );
                EnableWindow( GetDlgItem( hdlg, IDB_DIAL ), TRUE );
            }
        }

        _ffree( lpRasEntry );
    }

#ifdef WIN16
    // for Win16, we require that the user enters their username
    ShowWindow( GetDlgItem( hdlg, IDC_USECURRENT ), SW_HIDE );
#endif

    CheckDlgButton( hdlg, IDC_USECURRENT, g_bUseCurrent );

    // Center the dialog over the application window
    CenterWindow(hdlg, GetWindow(hdlg, GW_OWNER));

    return TRUE;
}

//
//  FUNCTION: MsgPhbkDlgCommand(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Process WM_COMMAND message sent to the phbkdlg box.
//
//  PARAMETERS:
//    hwnd - The window handing the message.
//    uMessage - The message number. WM_COMMAND.
//    wparam - Message specific data (unused).
//    lparam - Message specific data (unused).
//
//  RETURN VALUE:
//    TRUE for message handled
//    FALSE for message not handled
//
//  COMMENTS:
//    Uses this DispCommand function defined in wndproc.c combined
//    with the cmdiPhbkDlg structure defined in this file to handle
//    the command messages for the phbkdlg dialog box.
//

LRESULT MsgPhbkDlgCommand(HWND   hwnd,
                          UINT   uMessage,
                          WPARAM wparam,
                          LPARAM lparam)
{
    return DispCommand(&cmdiPhbkDlg, hwnd, wparam, lparam);
}

//
//  FUNCTION: CmdPhbkDlgCancel(HWND, WORD, WORD, HWND)
//
//  PURPOSE: Free the phbkdlg box and related data.
//
//  PARAMETERS:
//    hwnd - The window handling the command.
//    wCommand - The command to be handled IDCANCEL.
//    wNotify  - The notification code to be handled (unused).
//    hwndCtrl - NULL (unused).
//
//  RETURN VALUE:
//    Always returns TRUE.
//
//  COMMENTS:
//    Calls EndDialog to finish the dialog session.
//

LRESULT CmdPhbkDlgCancel(HWND hdlg, WORD wCommand, WORD wNotify, HWND hwndCtrl)
{
    // save the state of the check box
    g_bUseCurrent = IsDlgButtonChecked( hdlg, IDC_USECURRENT );
    EndDialog(hdlg, FALSE);          // Exit the dialog
    return TRUE;
}

//
//  FUNCTION: CmdPhbkDlgEntry(HWND, WORD, WORD, HWND)
//
//  PURPOSE: Dial selected phonebook phbkdlg
//
//  PARAMETERS:
//    hwnd - The window handling the command.
//    wCommand - The command to be handled IDL_ENTRY.
//    wNotify  - The notification code to be handled LBN_DBLCLK.
//    hwndCtrl - NULL (unused).
//
//  RETURN VALUE:
//    TRUE if message is processed.
//
//  COMMENTS:
//    Double click on an entry signifies a dial command.
//

LRESULT CmdPhbkDlgEntry(HWND hdlg, WORD wCommand, WORD wNotify, HWND hwndCtrl)
{
    if ( wNotify == LBN_DBLCLK )
    {
        SendMessage( hdlg, WM_COMMAND, IDB_DIAL, 0 );
        return TRUE;
    }
    return FALSE;
}

//
//  FUNCTION: CmdPhbkDialDlg(HWND, WORD, WORD, HWND)
//
//  PURPOSE: Displays the "DialDlg" dialog box
//
//  PARAMETERS:
//    hdlg      - Window handle
//    wCommand  - IDB_DIAL(unused)
//    wNotify   - Notification number (unused)
//    hwndCtrl  - NULL (unused)
//
//  RETURN VALUE:
//
//    Always returns TRUE - Message handled
//
//  COMMENTS:
//    Calls authentication and dialing dialog boxes
//

LRESULT CmdPhbkDlgDial(HWND hdlg, WORD wCommand, WORD wNotify, HWND hwndCtrl)
{
    g_bUseCurrent = IsDlgButtonChecked( hdlg, IDC_USECURRENT );

    // short circut evaluation will cause the AuthDlg box not to be
    // displayed if g_bUseCurrent is true
    if ( g_bUseCurrent || DialogBox(hInst, "AuthDlgBox", hdlg, (DLGPROC)AuthDlg) )
    {
        if( DialogBox(hInst, "DialDlgBox", hdlg, (DLGPROC)DialDlg))
            EndDialog(hdlg, TRUE);          // Exit Phonebook dialog
                                            // if connection established
    }
    return TRUE;
}


PHBKDLG.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:   phbkdlg.h
//
//  PURPOSE:  contains phbkdlg declarations
//

#ifndef _PHBKDLG_H
#define _PHBKDLG_H

//-------------------------------------------------------------------------
//  phbkdlg dialog defines.

#define IDL_ENTRY       400
#define IDB_DIAL        401
#define IDC_USECURRENT  402

extern BOOL g_bUseCurrent;

#ifdef WIN16
// Dialog box procedures must be exported in 16-bit applications for Windows.
LRESULT CALLBACK __export PhbkDlg(HWND, UINT, WPARAM, LPARAM);
#else
LRESULT CALLBACK PhbkDlg(HWND, UINT, WPARAM, LPARAM);
#endif

#endif


RASBERRY.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:   rasberry.c
//
//  PURPOSE:  Implement the windows procedure for the main application
//            windows.
//
//  FUNCTIONS:
//    WndProc      - Processes messages for the main window.
//    MsgCreate    - Handle the WM_CREATE messages for the main window.
//    MsgSize      - Handle the WM_SIZE messages for the main window.
//    MsgCommand   - Handle the WM_COMMAND messages for the main window.
//    MsgDestroy   - Handles the WM_DESTROY message by calling
//                   PostQuitMessage().
//    CmdPhbkDlg   - Displays entries in default phonebook.
//    CmdStatDlg   - Displays status of the selected connection.
//    CmdRefresh   - Refreshes list of connections in listbox.
//    CmdHangup    - Hangs up the selected connection.
//    CmdConnNotify- Handles notification messages for connection listbox.
//    CmdExit      - Handles the file exit command by calling destory
//                   window on the main window.
//
//  COMMENTS:
//
//

#include <windows.h>            // required for all Windows applications
#include <windowsx.h>
#ifdef WIN16
#include "win16ext.h"           // required only for win16 applications
#endif

#include "globals.h"            // prototypes specific to this application
#include <stdlib.h>
#include <malloc.h>
#include <ras.h>
#include <raserror.h>
#include "rasutil.h"
#include "phbkdlg.h"
#include "statdlg.h"

// Main window message table definition.
MSD rgmsd[] =
{
    {WM_CREATE,   MsgCreate},
    {WM_SIZE,     MsgSize},
    {WM_COMMAND,  MsgCommand},
    {WM_DESTROY,  MsgDestroy}
};

MSDI msdiMain =
{
    sizeof(rgmsd) / sizeof(MSD),
    rgmsd,
    edwpWindow
};

// Main window command table definition.
CMD rgcmd[] =
{
    {IDM_PHONEBOOK, CmdPhbkDlg},
    {IDM_STATUS,    CmdStatDlg},
    {IDM_EXIT,      CmdExit},
    {IDM_REFRESH,   CmdRefresh},
    {IDM_HANGUP,    CmdHangUp},
    {IDM_ABOUT,     CmdAbout},
    {IDL_CONN,      CmdConnNotify}
};

CMDI cmdiMain =
{
    sizeof(rgcmd) / sizeof(CMD),
    rgcmd,
    edwpWindow
};

// Application "globals"
char g_szUserName[UNLEN+1];
char g_szPassword[PWLEN+1];
char g_szDomain[DNLEN+1];

LPHRASCONN g_lphRasConn = NULL;

// Module specific "globals"  Used when a variable needs to be
// accessed in more than on handler function.

HWND hwndConn;  // handle of listbox of connections

//
//  FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE:  Processes messages for the main window.
//
//  PARAMETERS:
//    hwnd     - window handle
//    uMessage - message number
//    wparam   - additional information (dependant on message number)
//    lparam   - additional information (dependant on message number)
//
//  RETURN VALUE:
//    The return value depends on the message number.  If the message
//    is implemented in the message dispatch table, the return value is
//    the value returned by the message handling function.  Otherwise,
//    the return value is the value returned by the default window procedure.
//
//  COMMENTS:
//    Call the DispMessage() function with the main window's message dispatch
//    information (msdiMain) and the message specific information.
//

LRESULT CALLBACK WndProc(HWND   hwnd,
                         UINT   uMessage,
                         WPARAM wparam,
                         LPARAM lparam)
{
    return DispMessage(&msdiMain, hwnd, uMessage, wparam, lparam);
}

//
//  FUNCTION: MsgCreate(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Initialized main window
//
//  PARAMETERS:
//
//    hwnd      - Window handle
//    uMessage  - Message number (Unused)
//    wparam    - Extra data     (Unused)
//    lparam    - Extra data     (Unused)
//
//  RETURN VALUE:
//
//    Always returns 0 - Message handled
//
//  COMMENTS:
//    Creates a listbox the size of the window and fills it
//    with a list of RAS connections.
//

LRESULT MsgCreate(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    RECT rc;

    GetClientRect( hwnd, &rc );

    hwndConn  = CreateWindow( "LISTBOX",
                             "Connections",
                             WS_CHILD | WS_VISIBLE | WS_HSCROLL |
                             WS_VSCROLL | LBS_USETABSTOPS | LBS_NOINTEGRALHEIGHT | LBS_SORT,
                             0,
                             0,
                             rc.right,
                             rc.bottom,
                             hwnd,
                             (HMENU) IDL_CONN,
                             hInst,
                             NULL );

    SendMessage( hwnd, WM_COMMAND, IDM_REFRESH, 0L );

    // initialize global variables
    g_szUserName[0] = '\0';
    g_szPassword[0] = '\0';
    g_szDomain[0] = '*';
    g_szDomain[1] = '\0';

    return 0;
}

//
//  FUNCTION: MsgSize(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Resize listbox to client area size
//
//  PARAMETERS:
//
//    hwnd      - Window handle
//    uMessage  - Message number (Unused)
//    wparam    - Extra data     (Unused)
//    lparam    - Extra data     (Unused)
//
//  RETURN VALUE:
//
//    Always returns 0 - Message handled
//
//  COMMENTS:
//
//

LRESULT MsgSize(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    MoveWindow(hwndConn, 0, 0, LOWORD(lparam), HIWORD(lparam), TRUE);
    return 0;
}

//
//  FUNCTION: MsgCommand(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Handle the WM_COMMAND messages for the main window.
//
//  PARAMETERS:
//    hwnd     - window handle
//    uMessage - WM_COMMAND (Unused)
//    GET_WM_COMMAND_ID(wparam, lparam)   - Command identifier
//    GET_WM_COMMAND_HWND(wparam, lparam) - Control handle
//
//  RETURN VALUE:
//    The return value depends on the message number.  If the message
//    is implemented in the message dispatch table, the return value is
//    the value returned by the message handling function.  Otherwise,
//    the return value is the value returned by the default window procedure.
//
//  COMMENTS:
//    Call the DispCommand() function with the main window's command dispatch
//    information (cmdiMain) and the command specific information.
//

LRESULT MsgCommand(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    return DispCommand(&cmdiMain, hwnd, wparam, lparam);
}

//
//  FUNCTION: MsgDestroy(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Calls PostQuitMessage().
//
//  PARAMETERS:
//
//    hwnd      - Window handle  (Unused)
//    uMessage  - Message number (Unused)
//    wparam    - Extra data     (Unused)
//    lparam    - Extra data     (Unused)
//
//  RETURN VALUE:
//
//    Always returns 0 - Message handled
//
//  COMMENTS:
//
//

LRESULT MsgDestroy(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    if ( g_lphRasConn != NULL )
    {
        _ffree((LPVOID) g_lphRasConn );
        g_lphRasConn = NULL;
    }

    PostQuitMessage(0);
#ifdef WIN32
    Sleep(3000);  // let RAS state machine clean up
#endif
    return 0;
}

//
//  FUNCTION: CmdPhbkDlg(HWND, WORD, WORD, HWND)
//
//  PURPOSE: Displays the "PhbkDlg" dialog box
//
//  PARAMETERS:
//    hwnd      - Window handle
//    wCommand  - IDM_ENTRY (unused)
//    wNotify   - Notification number (unused)
//    hwndCtrl  - NULL (unused)
//
//  RETURN VALUE:
//
//    Always returns 0 - Message handled
//
//  COMMENTS:
//    To process the IDM_ENTRY message, call DialogBox() to display the
//    phbkdlg dialog box.

LRESULT CmdPhbkDlg(HWND hwnd, WORD wCommand, WORD wNotify, HWND hwndCtrl)
{
    DialogBox(hInst, "PhbkDlgBox", hwnd, (DLGPROC)PhbkDlg);
    SendMessage( hwnd, WM_COMMAND, IDM_REFRESH, 0L );

    return 0;
}

//
//  FUNCTION: CmdStatDlg(HWND, WORD, WORD, HWND)
//
//  PURPOSE: Displays the "StatDlg" dialog box
//
//  PARAMETERS:
//    hwnd      - Window handle
//    wCommand  - IDM_STATDLG (unused)
//    wNotify   - Notification number (unused)
//    hwndCtrl  - NULL (unused)
//
//  RETURN VALUE:
//
//    Always returns 0 - Message handled
//
//  COMMENTS:
//    To process the IDM_STATDLG message, call DialogBox() to display the
//    statdlg dialog box.

LRESULT CmdStatDlg(HWND hwnd, WORD wCommand, WORD wNotify, HWND hwndCtrl)
{
    DialogBox(hInst, "StatDlgBox", hwnd, (DLGPROC)StatDlg);
    SendMessage( hwnd, WM_COMMAND, IDM_REFRESH, 0L );
    return 0;
}

//
//  FUNCTION: CmdRefesh(HWND, WORD, WORD, HWND)
//
//  PURPOSE: Refreshes the connection list
//
//  PARAMETERS:
//    hwnd     - The window.
//    wCommand - IDM_REFRESH (unused)
//    wNotify  - Notification number (unused)
//    hwndCtrl - NULL (unused)
//
//  RETURN VALUE:
//    Always returns 0 - command handled.
//
//  COMMENTS:
//     Calls RasEnumConnections to get a list of current connections
//

LRESULT CmdRefresh(HWND hwnd, WORD wCommand, WORD wNotify, HWND hwndCtrl)
{
    LPRASCONN  lpRasConn = NULL;
    LPRASCONN  lpTemp = NULL;
    LPHRASCONN lphTemp = NULL;
    DWORD cbBuf = 0;
    DWORD cConn = 0;
    DWORD dwRet = 0;
    UINT  ndx;
    char  szMessage[256];
    HMENU hmenu = GetMenu( hwnd );

    // remove any connections currently in listbox
    SendMessage( hwndConn, LB_RESETCONTENT, 0, 0L );

    if ( g_lphRasConn != NULL )
    {
        _ffree( (LPVOID) g_lphRasConn );
        g_lphRasConn = NULL;
    }

    // enumerate connections
    cbBuf = sizeof(RASCONN);
    if ( ((lpRasConn = ( LPRASCONN ) _fmalloc((UINT)cbBuf)) != NULL ) &&
         ((g_lphRasConn = ( LPHRASCONN ) _fmalloc(sizeof(HRASCONN))) != NULL ) )
    {
        lpRasConn->dwSize = sizeof( RASCONN );
        dwRet = RasEnumConnections( lpRasConn, &cbBuf, &cConn );
        if ( dwRet == ERROR_BUFFER_TOO_SMALL )
        {
            if ( (lpTemp = ( LPRASCONN ) _frealloc(lpRasConn, (UINT)cbBuf) ) != NULL )
            {
                lpRasConn = lpTemp;
                dwRet = RasEnumConnections( lpRasConn, &cbBuf, &cConn );

                if ( NULL != (lphTemp = ( LPHRASCONN )
                              _frealloc((LPVOID)g_lphRasConn, (UINT)(sizeof(HRASCONN)*cConn))) )
                    g_lphRasConn = lphTemp;
                else
                    dwRet = ERROR_NOT_ENOUGH_MEMORY;

            }
            else
            {
                dwRet = ERROR_NOT_ENOUGH_MEMORY;
            }
        }
        else if ( dwRet != 0 ) // other error
        {
            if ( RasGetErrorString( (UINT)dwRet, szMessage, 256 ) != 0 )
                wsprintf( (LPSTR)szMessage, (LPSTR)"Undefined RAS Dial Error." );

            MessageBox(hwnd, szMessage, szAppName, MB_OK | MB_ICONSTOP );
        }

        if ( dwRet == 0 )  // No errors, so populate listbox
        {
            for ( ndx = 0; ndx < cConn; ndx++ )
            {
                g_lphRasConn[ndx] = lpRasConn[ndx].hrasconn;
                SendMessage( hwndConn,
                             LB_ADDSTRING,
                             0,
                             (LPARAM)(LPCSTR) lpRasConn[ndx].szEntryName);
            }

            // update states of menu items
            if ( ndx > 0 ) // at least one item was added
            {
                SendMessage( hwndConn, LB_SETCURSEL, 0, 1L );
                EnableMenuItem( hmenu, IDM_STATUS, MF_ENABLED );
                EnableMenuItem( hmenu, IDM_HANGUP, MF_ENABLED );
            }
            else
            {
                EnableMenuItem( hmenu, IDM_STATUS, MF_GRAYED );
                EnableMenuItem( hmenu, IDM_HANGUP, MF_GRAYED );
            }
        }
        else
        {
            EnableMenuItem( hmenu, IDM_STATUS, MF_GRAYED );
            EnableMenuItem( hmenu, IDM_HANGUP, MF_GRAYED );
        }

        _ffree( lpRasConn );
    }
    else
    {
        if ( g_lphRasConn != NULL )
        {
            _ffree((LPVOID) g_lphRasConn );
            g_lphRasConn = NULL;
        }

        if ( lpRasConn != NULL )
        {
            _ffree( lpRasConn );
        }
    }

    return 0;
}

//
//  FUNCTION: CmdHangUp(HWND, WORD, WORD, HWND)
//
//  PURPOSE: Hangs up the selected connection
//
//  PARAMETERS:
//    hwnd     - The window.
//    wCommand - IDM_HANGUP (unused)
//    wNotify  - Notification number (unused)
//    hwndCtrl - NULL (unused)
//
//  RETURN VALUE:
//    Always returns 0 - command handled.
//
//  COMMENTS:
//
//

LRESULT CmdHangUp(HWND hwnd, WORD wCommand, WORD wNotify, HWND hwndCtrl)
{
    CHAR  szBuf[256];
    CHAR  szBuf2[256];
    UINT  ndx;

    // get the connection text from previous window
    ndx = (UINT) SendMessage( hwndConn, LB_GETCURSEL, 0,  0L );
    SendMessage( hwndConn, LB_GETTEXT, ndx, (LPARAM)(LPSTR)szBuf );

    wsprintf( (LPSTR) szBuf2, "HangUp %s?", (LPSTR) szBuf );
    if ( IDYES == MessageBox( hwnd, (LPSTR)szBuf2, szAppName, MB_ICONQUESTION | MB_YESNO ) )
    {
        RasHangUp( g_lphRasConn[ndx] );

        SendMessage( hwnd, WM_COMMAND, IDM_REFRESH, 0L );
    }
    return 0;
}

//
//  FUNCTION: CmdConnNotify(HWND, WORD, WORD, HWND)
//
//  PURPOSE: notfication from Connection Listbox
//
//  PARAMETERS:
//    hwnd - The window handling the command.
//    wCommand - The command to be handled (unused).
//    wNotify  - The notification code to be handled (LBN_DBLCLK).
//    hwndCtrl - NULL (unused).
//
//  RETURN VALUE:
//    0 if message is processed.
//
//  COMMENTS:
//    Catches DBL_CLK to cause connection status dialog to appear
//

LRESULT CmdConnNotify(HWND hwnd, WORD wCommand, WORD wNotify, HWND hwndCtrl)
{
    if ( wNotify == LBN_DBLCLK )
    {
        SendMessage( hwnd, WM_COMMAND, IDM_STATUS, 0 );
        return 0;
    }
    return 1;
}

//
//  FUNCTION: CmdExit(HWND, WORD, WORD, HWND)
//
//  PURPOSE: Exit the application.
//
//  PARAMETERS:
//    hwnd     - The window.
//    wCommand - IDM_EXIT
//    wNotify  - Notification number (unused)
//    hwndCtrl - NULL (unused)
//
//  RETURN VALUE:
//    Always returns 0 - command handled.
//
//  COMMENTS:
//
//

LRESULT CmdExit(HWND hwnd, WORD wCommand, WORD wNotify, HWND hwndCtrl)
{
    DestroyWindow(hwnd);
    return 0;
}


RASUTIL.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:   rasutil.c
//
//  PURPOSE:  Implement the windows procedure for the main application
//            windows.
//
//  FUNCTIONS:
//    GetRasConnState - maps RASCONNSTATE value in index in string table
//
//  COMMENTS:
//
//

#include <windows.h>            // required for all Windows applications
#include <windowsx.h>
#ifdef WIN16
#include "win16ext.h"           // required only for win16 applications
#endif

#include "globals.h"            // prototypes specific to this application
#include <stdlib.h>
#include <ras.h>
#include <raserror.h>
#include "rasutil.h"

//
//  FUNCTION: GetRasConnState( RASCONNSTATE )
//
//  PURPOSE: get the index to the corresponding string
//
//  PARAMETERS:
//    rasconn - ras connection state
//
//  RETURN VALUE:
//    index into stringtable.
//
//  COMMENTS:
//
UINT GetRasConnState( RASCONNSTATE rasconn )
{
    switch( rasconn )
    {
        case RASCS_OpenPort:
            return IDS_OPENPORT;
        case RASCS_PortOpened:
            return IDS_PORTOPENED;
        case RASCS_ConnectDevice:
            return IDS_CONNECTDEVICE;
        case RASCS_DeviceConnected:
            return IDS_DEVICECONNECTED;
        case RASCS_AllDevicesConnected:
            return IDS_ALLDEVICESCONNECTED;
        case RASCS_Authenticate:
            return IDS_AUTHENTICATE;
        case RASCS_AuthNotify:
            return IDS_AUTHNOTIFY;
        case RASCS_AuthRetry:
            return IDS_AUTHRETRY;
        case RASCS_AuthCallback:
            return IDS_AUTHCALLBACK;
        case RASCS_AuthChangePassword:
            return IDS_AUTHCHANGEPASSWORD;
        case RASCS_AuthProject:
            return IDS_AUTHPROJECT;
        case RASCS_AuthLinkSpeed:
            return IDS_AUTHLINKSPEED;
        case RASCS_AuthAck:
            return IDS_AUTHACK;
        case RASCS_ReAuthenticate:
            return IDS_REAUTHENTICATE;
        case RASCS_Authenticated:
            return IDS_AUTHENTICATED;
        case RASCS_PrepareForCallback:
            return IDS_PREPAREFORCALLBACK;
        case RASCS_WaitForModemReset:
            return IDS_WAITFORMODEMRESET;
        case RASCS_WaitForCallback:
            return IDS_WAITFORCALLBACK;
        case RASCS_Interactive:
            return IDS_INTERACTIVE;
        case RASCS_RetryAuthentication:
            return IDS_RETRYAUTHENTICATION;
        case RASCS_CallbackSetByCaller:
            return IDS_CALLBACKSETBYCALLER;
        case RASCS_PasswordExpired:
            return IDS_PASSWORDEXPIRED;
        case RASCS_Connected:
            return IDS_CONNECTED;
        case RASCS_Disconnected:
            return IDS_DISCONNECTED;
        default:
            return IDS_UNDEFINED_ERROR;
    }
}


RASUTIL.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:   rasutil.h
//
//  PURPOSE:  contains rasutil declarations
//

#ifndef _RASUTIL_H
#define _RASUTIL_H

#define IDS_RASCS                    100
#define IDS_OPENPORT                 IDS_RASCS+ 0
#define IDS_PORTOPENED               IDS_RASCS+ 1
#define IDS_CONNECTDEVICE            IDS_RASCS+ 2
#define IDS_DEVICECONNECTED          IDS_RASCS+ 3
#define IDS_ALLDEVICESCONNECTED      IDS_RASCS+ 4
#define IDS_AUTHENTICATE             IDS_RASCS+ 5
#define IDS_AUTHNOTIFY               IDS_RASCS+ 6
#define IDS_AUTHRETRY                IDS_RASCS+ 7
#define IDS_AUTHCALLBACK             IDS_RASCS+ 8
#define IDS_AUTHCHANGEPASSWORD       IDS_RASCS+ 9
#define IDS_AUTHPROJECT              IDS_RASCS+10
#define IDS_AUTHLINKSPEED            IDS_RASCS+11
#define IDS_AUTHACK                  IDS_RASCS+12
#define IDS_REAUTHENTICATE           IDS_RASCS+13
#define IDS_AUTHENTICATED            IDS_RASCS+14
#define IDS_PREPAREFORCALLBACK       IDS_RASCS+15
#define IDS_WAITFORMODEMRESET        IDS_RASCS+16
#define IDS_WAITFORCALLBACK          IDS_RASCS+17
#define IDS_INTERACTIVE              IDS_RASCS+18
#define IDS_RETRYAUTHENTICATION      IDS_RASCS+19
#define IDS_CALLBACKSETBYCALLER      IDS_RASCS+20
#define IDS_PASSWORDEXPIRED          IDS_RASCS+21
#define IDS_CONNECTED                IDS_RASCS+22
#define IDS_DISCONNECTED             IDS_RASCS+23
#define IDS_RASCS_END                IDS_DISCONNECTED
#define IDS_TRANSCODE                IDS_RASCS+24

UINT GetRasConnState( RASCONNSTATE rasconn );

#endif


ABOUT.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:   about.c
//
//  PURPOSE:   Displays the "About" dialog box
//
//  FUNCTIONS:
//    CmdAbout        - Displays the "About" dialog box
//    About           - Processes messages for "About" dialog box.
//    MsgAboutInit    - To initialize the about box with version info
//                      from resources.
//    MsgAboutCommand - Process WM_COMMAND message sent to the about box.
//    CmdAboutDone    - Free the about box and related data.
//
//  COMMENTS:
//
//

#include <windows.h>            // required for all Windows applications
#include <windowsx.h>

#ifdef WIN16
#include "win16ext.h"           // required only for win16 applications
#endif
#include "globals.h"            // prototypes specific to this application
#include "rasutil.h"

#ifdef WIN16
// Dialog box procedures must be exported in 16-bit applications for Windows.
LRESULT CALLBACK __export About(HWND, UINT, WPARAM, LPARAM);
#else
LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM);
#endif

LRESULT MsgAboutInit(HWND, UINT, WPARAM, LPARAM);
LRESULT MsgAboutCommand(HWND, UINT, WPARAM, LPARAM);
LRESULT CmdAboutDone(HWND, WORD, WORD, HWND);

// About dialog message table definition.
MSD rgmsdAbout[] =
{
    {WM_COMMAND,    MsgAboutCommand},
    {WM_INITDIALOG, MsgAboutInit}
};

MSDI msdiAbout =
{
    sizeof(rgmsdAbout) / sizeof(MSD),
    rgmsdAbout,
    edwpNone
};

// About dialog command table definition.
CMD rgcmdAbout[] =
{
    {IDOK,     CmdAboutDone},
    {IDCANCEL, CmdAboutDone}
};

CMDI cmdiAbout =
{
    sizeof(rgcmdAbout) / sizeof(CMD),
    rgcmdAbout,
    edwpNone
};

// Module specific "globals"  Used when a variable needs to be
// accessed in more than on handler function.

HFONT hFontCopyright;

//
//  FUNCTION: CmdAbout(HWND, WORD, WORD, HWND)
//
//  PURPOSE: Displays the "About" dialog box
//
//  PARAMETERS:
//    hwnd      - Window handle
//    wCommand  - IDM_ABOUT (unused)
//    wNotify   - Notification number (unused)
//    hwndCtrl  - NULL (unused)
//
//  RETURN VALUE:
//
//    Always returns 0 - Message handled
//
//  COMMENTS:
//    To process the IDM_ABOUT message, call DialogBox() to display the
//    about dialog box.

LRESULT CmdAbout(HWND hwnd, WORD wCommand, WORD wNotify, HWND hwndCtrl)
{
    DialogBox(hInst, "AboutBox", hwnd, (DLGPROC)About);
    return 0;
}

//
//  FUNCTION: About(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE:  Processes messages for "About" dialog box.
//
//  PARAMETERS:
//    hdlg - window handle of the dialog box
//    wMessage - type of message
//    wparam - message-specific information
//    lparam - message-specific information
//
//  RETURN VALUE:
//    TRUE - message handled
//    FALSE - message not handled
//
//  COMMENTS:
//
//     Display version information from the version section of the
//     application resource.
//
//     Wait for user to click on "Ok" button, then close the dialog box.
//

LRESULT CALLBACK About(HWND hdlg, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    return DispMessage(&msdiAbout, hdlg, uMessage, wparam, lparam);
}

//
//  FUNCTION: MsgAboutInit(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: To initialize the about box with version info from resources.
//
//  PARAMETERS:
//    hwnd - The window handing the message.
//    uMessage - The message number. (unused).
//    wparam - Message specific data (unused).
//    lparam - Message specific data (unused).
//
//  RETURN VALUE:
//    Always returns 0 - message handled.
//
//  COMMENTS:
//    Uses the version apis to retrieve version information for
//    each of the static text boxes in the about box.
//

LRESULT MsgAboutInit(HWND hdlg, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    #define POINTSIZE 8

    char  szFullPath[256];
    DWORD dwVerHnd;
    DWORD dwVerInfoSize;
    HDC   hDC;
    int   iLogPixelsY, iPointSize;

    // Center the dialog over the application window
    CenterWindow(hdlg, GetWindow(hdlg, GW_OWNER));

    // Set the copyright font to something smaller than default
    hDC = GetDC(hdlg);
    iLogPixelsY = GetDeviceCaps(hDC, LOGPIXELSY);
    ReleaseDC(hdlg, hDC);
    iPointSize = MulDiv(iLogPixelsY, POINTSIZE, 72);
    iPointSize *= -1;

    if (PRIMARYLANGID(GetUserDefaultLangID()) == LANG_JAPANESE)
        hFontCopyright = CreateFont(iPointSize,
                                    0, 0, 0,
                                    FW_DONTCARE,
                                    0, 0, 0, 0,
                                    0, 0, 0, 0,
                                    "System");
    else
        hFontCopyright = CreateFont(iPointSize,
                                   0, 0, 0,
                                   FW_BOLD,
                                   0, 0, 0, 0,
                                   0, 0, 0, 0,
                                   "Arial");

    SendDlgItemMessage(hdlg,
                       IDD_VERLAST,
                       WM_SETFONT,
                       (WPARAM)hFontCopyright,
                       0L);

    // Get version information from the application
    GetModuleFileName(hInst, szFullPath, sizeof(szFullPath));
    dwVerInfoSize = GetFileVersionInfoSize(szFullPath, &dwVerHnd);
    if (dwVerInfoSize)
    {
        // If we were able to get the information, process it:
        HANDLE  hMem;
        LPVOID  lpvMem;
        char    szGetName[256];
        int     cchRoot;
        int     i;

        hMem = GlobalAlloc(GMEM_MOVEABLE, dwVerInfoSize);
        lpvMem = GlobalLock(hMem);
        GetFileVersionInfo(szFullPath, dwVerHnd, dwVerInfoSize, lpvMem);
        LoadString(hInst, IDS_TRANSCODE, szGetName, sizeof(szGetName));
        cchRoot = lstrlen(szGetName);

        // Walk through the dialog items that we want to replace:
        for (i = IDD_VERFIRST; i <= IDD_VERLAST; i++)
        {
            BOOL  fRet;
            UINT  cchVer = 0;
            LPSTR lszVer = NULL;
            char  szResult[256];

            GetDlgItemText(hdlg, i, szResult, sizeof(szResult));
            lstrcpy(&szGetName[cchRoot], szResult);
            fRet = VerQueryValue(lpvMem, szGetName, &lszVer, &cchVer);

            if (fRet && cchVer && lszVer)
            {
                // Replace dialog item text with version info
                lstrcpy(szResult, lszVer);
                SetDlgItemText(hdlg, i, szResult);
            }
        }
        GlobalUnlock(hMem);
        GlobalFree(hMem);
    }
    return TRUE;
}

//
//  FUNCTION: MsgAboutCommand(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Process WM_COMMAND message sent to the about box.
//
//  PARAMETERS:
//    hwnd - The window handing the message.
//    uMessage - The message number. (unused).
//    wparam - Message specific data (unused).
//    lparam - Message specific data (unused).
//
//  RETURN VALUE:
//    Always returns 0 - message handled.
//
//  COMMENTS:
//    Uses this DispCommand function defined in wndproc.c combined
//    with the cmdiAbout structure defined in this file to handle
//    the command messages for the about dialog box.
//

LRESULT MsgAboutCommand(HWND   hwnd,
                        UINT   uMessage,
                        WPARAM wparam,
                        LPARAM lparam)
{
    return DispCommand(&cmdiAbout, hwnd, wparam, lparam);
}

//
//  FUNCTION: CmdAboutDone(HWND, WORD, HWND)
//
//  PURPOSE: Free the about box and related data.
//
//  PARAMETERS:
//    hwnd - The window handling the command.
//    wCommand - The command to be handled (unused).
//    wNotify   - Notification number (unused)
//    hwndCtrl - NULL (unused).
//
//  RETURN VALUE:
//    Always returns TRUE.
//
//  COMMENTS:
//    Calls EndDialog to finish the dialog session.
//

LRESULT CmdAboutDone(HWND hdlg, WORD wCommand, WORD wNotify, HWND hwndCtrl)
{
    if (hFontCopyright)
       DeleteObject(hFontCopyright);

    EndDialog(hdlg, TRUE);          // Exit the dialog
    return TRUE;
}


STATDLG.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:   statdlg.c
//
//  PURPOSE:   Displays the "StatDlg" dialog box
//
//  FUNCTIONS:
//    StatDlg           - Processes messages for "StatDlg" dialog box.
//    MsgStatDlgInit    - To initialize the statdlg box.
//    MsgStatDlgCommand - Process WM_COMMAND message sent to the statdlg box.
//    CmdStatDlgRefresh - Display the connection status.
//    CmdStatDlgOK      - Free the statdlg box and related data.
//
//  COMMENTS:
//    Dialog box to display the status of the selected connection
//

#include <windows.h>            // required for all Windows applications
#include <windowsx.h>

#ifdef WIN16
#include "win16ext.h"           // required only for win16 applications
#endif

#include "globals.h"            // prototypes specific to this application
#include <stdlib.h>
#include <ras.h>
#include <raserror.h>
#include "rasutil.h"
#include "statdlg.h"

LRESULT MsgStatDlgInit(HWND, UINT, WPARAM, LPARAM);
LRESULT MsgStatDlgCommand(HWND, UINT, WPARAM, LPARAM);
LRESULT CmdStatDlgRefresh(HWND, WORD, WORD, HWND);
LRESULT CmdStatDlgOK(HWND, WORD, WORD, HWND);

// StatDlg dialog message table definition.
MSD rgmsdStatDlg[] =
{
    {WM_COMMAND,    MsgStatDlgCommand},
    {WM_INITDIALOG, MsgStatDlgInit}
};

MSDI msdiStatDlg =
{
    sizeof(rgmsdStatDlg) / sizeof(MSD),
    rgmsdStatDlg,
    edwpNone
};

// StatDlg dialog command table definition.
CMD rgcmdStatDlg[] =
{
    {IDB_REFRESH, CmdStatDlgRefresh},
    {IDOK,        CmdStatDlgOK}
};

CMDI cmdiStatDlg =
{
    sizeof(rgcmdStatDlg) / sizeof(CMD),
    rgcmdStatDlg,
    edwpNone
};

// Module specific "globals"  Used when a variable needs to be
// accessed in more than on handler function.

//
//  FUNCTION: StatDlg(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE:  Processes messages for "StatDlg" dialog box.
//
//  PARAMETERS:
//    hdlg - window handle of the dialog box
//    wMessage - type of message
//    wparam - message-specific information
//    lparam - message-specific information
//
//  RETURN VALUE:
//    TRUE - message handled
//    FALSE - message not handled
//
//  COMMENTS:
//    Dispatch messages
//

LRESULT CALLBACK StatDlg(HWND hdlg, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    return DispMessage(&msdiStatDlg, hdlg, uMessage, wparam, lparam);
}

//
//  FUNCTION: MsgStatDlgInit(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: To initialize the statdlg box.
//
//  PARAMETERS:
//    hwnd - The window handing the message.
//    uMessage - The message number. WM_INITDLG.
//    wparam - Message specific data (unused).
//    lparam - Message specific data (unused).
//
//  RETURN VALUE:
//    Always returns 0 - message handled.
//
//  COMMENTS:
//

LRESULT MsgStatDlgInit(HWND hdlg, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    // Center the dialog over the application window
    CenterWindow(hdlg, GetWindow(hdlg, GW_OWNER));

    SendMessage(hdlg, WM_COMMAND, (WPARAM) IDB_REFRESH, 0L );

    return TRUE;
}

//
//  FUNCTION: MsgStatDlgCommand(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Process WM_COMMAND message sent to the statdlg box.
//
//  PARAMETERS:
//    hwnd - The window handing the message.
//    uMessage - The message number. WM_COMMAND.
//    wparam - Message specific data (unused).
//    lparam - Message specific data (unused).
//
//  RETURN VALUE:
//    TRUE for message handled.
//    FALSE for message not handled.
//
//  COMMENTS:
//    Uses this DispCommand function defined in wndproc.c combined
//    with the cmdiStatDlg structure defined in this file to handle
//    the command messages for the statdlg dialog box.
//

LRESULT MsgStatDlgCommand(HWND   hwnd,
                        UINT   uMessage,
                        WPARAM wparam,
                        LPARAM lparam)
{
    return DispCommand(&cmdiStatDlg, hwnd, wparam, lparam);
}

//
//  FUNCTION: CmdStatDlgRefresh(HWND, WORD, WORD, HWND)
//
//  PURPOSE: Refresh the information displayed in the dialog box.
//
//  PARAMETERS:
//    hwnd - The window handling the command.
//    wCommand - The command to be handled IDB_REFRESH.
//    wNotify  - The notifcation message (unused).
//    hwndCtrl - NULL (unused).
//
//  RETURN VALUE:
//    Always returns TRUE.
//
//  COMMENTS:
//    Calls RasGetConnectStatus to populate dialog box.
//

LRESULT CmdStatDlgRefresh(HWND hdlg, WORD wCommand, WORD wNotify, HWND hwndCtrl)
{
    HRASCONN hrasconn;
    RASCONNSTATUS rasStatus;
    DWORD dwRet;
    CHAR  szBuf[256];
    HWND  hwndConn;
    UINT  ndx;

    // get the connection text from previous window
    hwndConn = GetDlgItem( GetParent(hdlg), IDL_CONN);
    ndx = (UINT) SendMessage( hwndConn, LB_GETCURSEL, 0,  0L );
    SendMessage( hwndConn, LB_GETTEXT, ndx, (LPARAM)(LPSTR)szBuf );
    SetDlgItemText(hdlg, IDE_CONNECTION, (LPSTR)szBuf );

    // get to HRASCONN
    hrasconn = g_lphRasConn[ndx];

    // get connection status
    rasStatus.dwSize = sizeof(RASCONNSTATUS);
    dwRet = RasGetConnectStatus( hrasconn, &rasStatus );
    if ( dwRet )
    {
        if ( RasGetErrorString( (UINT)dwRet, (LPSTR)szBuf, 256 ) != 0 )
            wsprintf( (LPSTR)szBuf, "Undefined RAS Connect Status Error (%ld).", dwRet );

        MessageBox(hdlg, (LPSTR)szBuf, szAppName, MB_OK | MB_ICONSTOP );
        EndDialog(hdlg, FALSE);
        return TRUE;
    }
    else
    {
        LoadString( hInst,
                    GetRasConnState( rasStatus.rasconnstate ),
                    (LPSTR)szBuf,
                    256 );
        SetDlgItemText(hdlg, IDE_STATUS, (LPSTR)szBuf);

        if ( rasStatus.dwError ) // there is an error
        {
            if ( RasGetErrorString( (UINT)rasStatus.dwError, (LPSTR)szBuf, 256 ) != 0 )
                wsprintf( (LPSTR)szBuf, "Undefined Connection Error (%ld).", rasStatus.dwError );
        }
        else
        {
            wsprintf( (LPSTR)szBuf, "No Error" );
        }
        SetDlgItemText(hdlg, IDE_ERROR, (LPSTR)szBuf);

        SetDlgItemText(hdlg, IDE_DEVICE, rasStatus.szDeviceName);

        SetDlgItemText(hdlg, IDE_TYPE, rasStatus.szDeviceType);

    }

    return TRUE;
}

//
//  FUNCTION: CmdStatDlgOK(HWND, WORD, HWND)
//
//  PURPOSE: Free the statdlg box and related data.
//
//  PARAMETERS:
//    hwnd - The window handling the command.
//    wCommand - The command to be handled (unused).
//    wNotify  - The notifcation message (unused).
//    hwndCtrl - NULL (unused).
//
//  RETURN VALUE:
//    Always returns TRUE.
//
//  COMMENTS:
//    Calls EndDialog to finish the dialog session.
//

LRESULT CmdStatDlgOK(HWND hdlg, WORD wCommand, WORD wNotify, HWND hwndCtrl)
{
    EndDialog(hdlg, TRUE);          // Exit the dialog
    return TRUE;
}


STATDLG.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:   statdlg.h
//
//  PURPOSE:  contains statdlg declarations
//

#ifndef _STATDLG_H
#define _STATDLG_H

//-------------------------------------------------------------------------
//  Status dialog defines.

#define IDE_CONNECTION  500
#define IDE_STATUS      501
#define IDE_DEVICE      502
#define IDE_ERROR       503
#define IDE_TYPE        504
#define IDB_REFRESH     510

#ifdef WIN16
// Dialog box procedures must be exported in 16-bit applications for Windows.
LRESULT CALLBACK __export StatDlg(HWND, UINT, WPARAM, LPARAM);
#else
LRESULT CALLBACK StatDlg(HWND, UINT, WPARAM, LPARAM);
#endif

#endif


WIN16EXT.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.
//
// PURPOSE: Defines additions to windows.h included in WIN32, but not
//          in win16.

#include <ver.h>

// Windows NT defines APIENTRY, but 3.x doesn't
#define APIENTRY far pascal

// Windows 3.x uses a FARPROC for dialogs
#define DLGPROC FARPROC

// Windows NT defines the following in windowsx.h
#define GET_WM_COMMAND_ID(w,l) (w)
#define GET_WM_COMMAND_CMD(w,l) HIWORD(l)
#define GET_WM_COMMAND_HWND(w,l) LOWORD(l)


WINMAIN.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:   winmain.c
//
//  PURPOSE:   Calls initialization functions and processes the message loop
//
//
//  PLATFORMS: Windows 3.1, Windows 95, Windows NT
//
//  FUNCTIONS:
//    WinMain() - calls initialization functions, processes message loop
//
//  COMMENTS:
//
//

#include <windows.h>            // required for all Windows applications
#ifdef WIN16
#include "win16ext.h"           // required only for win16 applications
#endif
#include "globals.h"            // prototypes specific to this application

//
//  FUNCTION: WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
//
//  PURPOSE: calls initialization function, processes message loop
//
//  PARAMETERS:
//
//    hInstance - The handle to the instance of this application that
//          is currently being executed.
//
//    hPrevInstance - The handle to the instance of this application
//          that was last executed.  If this is the only instance
//          of this application executing, hPrevInstance is NULL.
//          In WindowsNT, this parameter is always NULL.
//
//    lpCmdLine - A pointer to a null terminated string specifying the
//          command line of the application.
//
//    nCmdShow - Specifies how the main window is to be diplayed.
//
//  RETURN VALUE:
//    If the function terminates before entering the message loop,
//      return FALSE.
//    Otherwise, return the WPARAM value sent by the WM_QUIT message.
//
//
//  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 APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
    MSG msg;
    HANDLE hAccelTable;

    // Other instances of app running?
    if (!hPrevInstance)
    {
        // Initialize shared things
        if (!InitApplication(hInstance))
        {
            return FALSE;               // Exits if unable to initialize
        }
    }

    // Perform initializations that apply to a specific instance
    if (!InitInstance(hInstance, nCmdShow))
    {
        return FALSE;
    }

    hAccelTable = LoadAccelerators(hInstance, szAppName);

    // Acquire and dispatch messages until a WM_QUIT message is received.
    while (GetMessage(&msg, NULL, 0, 0))
    {
        //
        // **TODO** Add other Translation functions (for modeless dialogs
        //  and/or MDI windows) here.
        //

        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    //
    // **TODO** Call module specific instance free/delete functions here.
    //

    // Returns the value from PostQuitMessage
    return msg.wParam;
}


README.TXT

RASHOST

This sample demonstrates how an ISV can build a DLL to provide security
services to the RAS server.  See rasshost.h for more information.


RASHOST.CPP

///////////////////////////////////////////////////////////////////////////////
//
//  File Name
//      RASHOST.CPP
//
//  Description
//      This file contains the initialization functions for the DLL library
//      and for the Security Host protocol. The NT RAS manager will call into
//      our DLL when a dialing user needs to be validated.
//
//  Author
//      Irving De la Cruz
//
//  Revision: 1.1
//
// Written for Microsoft Developer Support
// Copyright 1995 - 1998 Microsoft Corporation. All rights reserved.
//
#include "RASHOST.H"

HINSTANCE                       ghLibHandle = NULL;
RASSECURITYDIALOGSENDPROC       gpfnSendProc = NULL;
RASSECURITYDIALOGRECEIVEPROC    gpfnReceiveProc = NULL;

///////////////////////////////////////////////////////////////////////////////
//    LibMain()
//
//    Parameters
//      { Refer to the Win32 SDK documentation for DLL entry points }
//
//    Purpose
//      Entry point of this DLL
//
//    Return Value
//      TRUE if the DLL should proceed with the dwReason case, FALSE otherwise.
//
BOOL APIENTRY LibMain (HINSTANCE hDll, DWORD dwReason, LPVOID lpReserved)
{
    if (DLL_PROCESS_DETACH == dwReason && ghLibHandle)
    {
        FreeLibrary (ghLibHandle);
    }
    return TRUE;
}

///////////////////////////////////////////////////////////////////////////////
//    RasSecurityDialogBegin()
//
//    Parameters
//      hPort       Handle to the COM port where the user is logging in
//                  (A RAS handle, not a standard handle)
//      pSendBuf    Pointer to a buffer where we can copy stuff to be sent
//                  over the wire
//      SendBufSize Size of the outgoing buffer
//      pRecvBuf    Pointer a buffer where stuff is received from the
//                  remote client machine
//      RecvBufSize Size of the incoming buffer
//      RasSecurityDialogComplete   Pointer to a callback function we must
//                  call after we have completed our validation, indicating
//                  the RAS manager weather or not we granted access to the
//                  caller.
//
//    Purpose
//      This is the entry point for our security DLL. When the RAS manager
//      receives a call from a user, after it does its user validation, it
//      will call into us at this entry point to allow us to do extra
//      validation.
//      NOTE: It is very important that this function does not BLOCK. If
//            it does, the RAS manager will be block and no other calls
//            will be accepted. That is why we copy the incoming
//            parameters into a private structure and then spawn a thread
//            to do the actual validation work.
//            The passed parameters will be valid until we call the
//            ending function.
//
//    Return Value
//      ERROR_SUCCESS is we were able to start the validation. Otherwise
//      return and error from WINERROR.H or RASERROR.H
//
DWORD WINAPI RasSecurityDialogBegin (HPORT hPort,
                                     PBYTE pSendBuf,
                                     DWORD SendBufSize,
                                     PBYTE pRecvBuf,
                                     DWORD RecvBufSize,
                                     VOID  (WINAPI *RasSecurityDialogComplete)(SECURITY_MESSAGE*))
{
    // Initialize the global pointer to the entry points in the RAS manager
    // DLL. I use these entry points to send and receive data to the remote
    // client machine. These functions will send and receive RAW buffers of
    // data to the other machine just before PPP-framing starts.
    // These functions have not been exported in any publish import library
    // so we must do a dynamic binding to them.
    if (NULL == ghLibHandle)
    {
        ghLibHandle = LoadLibrary ("RASMAN.DLL");
        if (NULL == ghLibHandle)
        {
            return ERROR_DLL_NOT_FOUND;
        }
    }
    // Obtain the send and received entry points from the RAS manager DLL.
    if (NULL == gpfnSendProc)
    {
        gpfnSendProc = (RASSECURITYDIALOGSENDPROC)GetProcAddress (ghLibHandle, TEXT("RasSecurityDialogSend"));
        if (NULL == gpfnSendProc)
        {
            return ERROR_PROC_NOT_FOUND;
        }
    }
    if (NULL == gpfnReceiveProc)
    {
        gpfnReceiveProc = (RASSECURITYDIALOGRECEIVEPROC)GetProcAddress (ghLibHandle, TEXT("RasSecurityDialogReceive"));
        if (NULL == gpfnReceiveProc)
        {
            return ERROR_PROC_NOT_FOUND;
        }
    }

    // Save the parameters passed and give them to the working thread
    // that will handle the authentication of the caller. The thread
    // should release this memory.
    // The parameters passed to use should be valid until we call the
    // dialog end function or until we get call in the termination
    // dialog entry point by the RAS system supervisor (the RAS manager)
    PWORK_THREAD_DATA pCallData = (PWORK_THREAD_DATA)HeapAlloc (GetProcessHeap(),
                                                                HEAP_ZERO_MEMORY,
                                                                sizeof(WORK_THREAD_DATA));
    if (NULL == pCallData)
    {
        return ERROR_OUTOFMEMORY;
    }
    // Initialize the members.
    pCallData->hPort = hPort;
    pCallData->pSendBuf = pSendBuf;
    pCallData->SendBufSize = SendBufSize;
    pCallData->pRecvBuf = pRecvBuf;
    pCallData->RecvBufSize = RecvBufSize;
    pCallData->pfnSecurityDialogEnd = RasSecurityDialogComplete;

    // For each call, spawn a working thread that will make the neccesary
    // authentication of the dial-up caller. We should never block the call
    // to RasSecurityDialogBegin(). If we do so, RAS will also block and no
    // more call would be accepted.
    DWORD dwThreadID;
    HANDLE hThread = CreateThread (NULL,
                                   0,
                                   (LPTHREAD_START_ROUTINE)CallWorkerThread,
                                   (LPVOID)pCallData,
                                   0,
                                   &dwThreadID);
    if (NULL == hThread)
    {
        // Call GetLastError() before calling HeapFree().
        DWORD dwError = GetLastError();
        HeapFree (GetProcessHeap(), 0, pCallData);
        return dwError;
    }
    return ERROR_SUCCESS;
}

///////////////////////////////////////////////////////////////////////////////
//    RasSecurityDialogEnd()
//
//    Parameters
//      hPort   Handle to the COM port (A RAS handle, not a standard handle)
//
//    Purpose
//      Stub function. Need to export this by the security DLL even if
//      not needed. We need to return non-zero to indicate RAS we are no
//      longer responsible for the port connection. It will take care
//      of the rest.
//
//    Return Value
//      ERROR_PORT_DISCONNECTED always
//
DWORD WINAPI RasSecurityDialogEnd (HPORT hPort)
{
    return ERROR_PORT_DISCONNECTED;
}

///////////////////////////////////////////////////////////////////////////////
//    CallWorkerThread()
//
//    Parameters
//      pCallData   Pointer to the data structure with the data for the
//                  instance we are handling.
//
//    Purpose
//      This is the function for the thread that interacts with a dialing RAS
//      user. Here we query the user for its identity and based on it we
//      compute a challenge to for it. Once the response from the client
//      is received, we validate it and decide wether or not we allow access.
//
//    Return Value
//      None
//
void WINAPI CallWorkerThread (PWORK_THREAD_DATA pCallData)
{
    // Initialize the appropiate structures
    SECURITY_MESSAGE smResult = { 0 };
    smResult.hPort = pCallData->hPort;
    lstrcpy (smResult.UserName, TEXT("(RASHOST - Unknown)"));

    DWORD dwBytes = pCallData->RecvBufSize;
    ZeroMemory (pCallData->pRecvBuf, dwBytes);
    dwBytes = sizeof(RASHOST_DATA);

    // Initialize the structure that we'll send to the caller
    RASHOST_DATA HostData = { 0 };
    HostData.dwSize = sizeof(RASHOST_DATA);

    // Create an event to wait for client responses
    HANDLE hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);

    // We must copy the data from the structure to the buffer RAS
    // manager uses to transmit the information. This buffer
    // cannot be larger than 1500 bytes
    CopyMemory (pCallData->pSendBuf, &HostData, sizeof(RASHOST_DATA));
    DWORD dwError = 0;

    gpfnSendProc (pCallData->hPort,
                  pCallData->pSendBuf,
                  (WORD)sizeof(RASHOST_DATA));

    // Wait for an answer from the client responder
    gpfnReceiveProc (pCallData->hPort,
                     pCallData->pRecvBuf,
                     (WORD *)&dwBytes,
                     0,
                     hEvent);
    DWORD dwWait;
    dwWait = WaitForSingleObject (hEvent, CLIENT_RESPONSE_TIMEOUT);
    if (WAIT_OBJECT_0 != dwWait)
    {
        dwError = ERROR_SMM_TIMEOUT;
        goto ErrorExit;
    }

    // Copy the client data from the incoming buffer to the data structure
    CopyMemory (&HostData, pCallData->pRecvBuf, sizeof(RASHOST_DATA));

    // Save this information for RAS authentication with the NT RAS manager.
    // The user name must be the same as the one in the NT account database
    // on the domain where the user is granted access.
    lstrcpy (smResult.UserName, HostData.szUserName);
    lstrcpy (smResult.Domain, HostData.szUserDomain);

    //lstrcpy (smResult.UserName, "IrvingD");

    BOOL fAccessGranted; // Weather or not access was granted
    // With the received client response, validate the access for the client
    dwError = ValidateCallingUser (&HostData, &fAccessGranted);
    if (dwError)
    {
        goto ErrorExit;
    }

    if (fAccessGranted)
    {
        // Access has been granted
        smResult.dwMsgId = SECURITYMSG_SUCCESS;
    }
    else
    {
        // The caller did not respond correctly
        smResult.dwMsgId = SECURITYMSG_FAILURE;
    }

ErrorExit:
    if (dwError)
    {
        // There was an error, deny access.
        if (ERROR_SMM_TIMEOUT == dwError)
        {
            smResult.dwMsgId = SECURITYMSG_FAILURE;
        }
        else
        {
            smResult.dwMsgId = SECURITYMSG_ERROR;
            smResult.dwError = dwError;
        }
    }
    // Release the synchronization objects
    CloseHandle (hEvent);
    // Tell the RAS manager we are finished by calling the END dialog funcition.
    pCallData->pfnSecurityDialogEnd (&smResult);
    // Free the memory of this call instance before exiting the thread
    HeapFree (GetProcessHeap(), 0, pCallData);
}

///////////////////////////////////////////////////////////////////////////////
//    ValidateCallingUser()
//
//    Parameters
//      pData               Pointer to the host data structure
//      pfAccessGranted     We return TRUE here is access is granted.
//                          FALSE otherwise.
//
//    Purpose
//      Here we validate the answer to the challenge sent to the client.
//
//    Return Value
//      ERROR_SUCCESS if successful. Otherwiser an error code from WINERROR.H
//      or RASERROR.H
//
DWORD WINAPI ValidateCallingUser (PRASHOST_DATA       pData,
                                  BOOL *              pfAccessGranted)
{
    DWORD dwResult = ERROR_SUCCESS;
    *pfAccessGranted = FALSE;

    // Do stuff here

    return dwResult;
}

// End of file for RASHOST.CPP


RASHOST.H

///////////////////////////////////////////////////////////////////////////////
//
//  File Name
//      RASHOST.H
//
//  Description
//      This file defines all the data structures used in the files and the
//      appropiate constants.
//      In this file we also define the function prototypes for all the
//      methods used in the project files.
//
//  Author
//      Irving De la Cruz
//
//  Revision: 1.1
//
// Written for Microsoft Developer Support
// Copyright 1995 - 1998 Microsoft Corporation. All rights reserved.
//
#ifndef _RASHOST_H
#define _RASHOST_H

#pragma warning (disable : 4100)
#pragma warning (disable : 4201)
#pragma warning (disable : 4237)
#pragma warning (disable : 4514)
#pragma warning (disable : 4702)
#pragma warning (disable : 4710)

#define STRICT
#include <WINDOWS.H>
#include <RASERROR.H>
extern "C"
{
#include "RASSHOST.H"
};

// Constants
#define CLIENT_RESPONSE_TIMEOUT     15000

// Private data structures
typedef struct _WORK_THREAD_DATA
{
    HPORT  hPort;
    PBYTE  pSendBuf;
    DWORD  SendBufSize;
    PBYTE  pRecvBuf;
    DWORD  RecvBufSize;
    VOID  (WINAPI *pfnSecurityDialogEnd)(SECURITY_MESSAGE*);
} WORK_THREAD_DATA, *PWORK_THREAD_DATA;

typedef struct _RASHOST_DATA
{
    DWORD   dwSize;
    TCHAR   szUserName[64];
    TCHAR   szUserDomain[64];
    BYTE    Buffer[512];
} RASHOST_DATA, *PRASHOST_DATA;

// Function prototypes for dynamic bindings
typedef DWORD (WINAPI * RASSECURITYDIALOGSENDPROC)(HPORT, PBYTE, WORD);
typedef DWORD (WINAPI * RASSECURITYDIALOGRECEIVEPROC)(HPORT, PBYTE, PWORD, DWORD, HANDLE);

// Global variables
extern RASSECURITYDIALOGSENDPROC    gpfnSendProc;
extern RASSECURITYDIALOGRECEIVEPROC gpfnReceiveProc;

///////////////////////////////////////////////////////////////////////////////
// Function prototypes

// Data transmission functions
void WINAPI CallWorkerThread
                    (PWORK_THREAD_DATA          pCallData);

// User validation function
DWORD WINAPI ValidateCallingUser
                    (PRASHOST_DATA              pData,
                     BOOL *                     pfAccessGranted);

#endif  // _RASHOST_H

// End of file for RASHOST.H


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by RASHOST.RC
//

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#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


SECHOST.H

/********************************************************************/
/**               Copyright(c) 1989 Microsoft Corporation.   **/
/********************************************************************/

//***
//
// Filename:    sechost.h
//
// Description: This header defines the interface between third party security
//              DLLs and the supervisor.
//
// History:
//Nov 11,1994.NarenGCreated original version.
//

#ifndef _SECHOST_
#define _SECHOST_

#include <lmcons.h>                 // Defines for DNLEN and UNLEN

#define DEVICE_NAME_LEN             32

typedef DWORD  HPORT;

typedef struct _SECURITY_MESSAGE
{
    DWORD dwMsgId;

    HPORT hPort;

    DWORD dwError;                  // Should be non-zero only if error
                                    // occurred during the security dialog.
                                    // Should contain errors from winerror.h
                                    // or raserror.h
    CHAR  UserName[UNLEN+1];        // Should always contain username if
                                    // dwMsgId is SUCCESS/FAILURE

    CHAR  Domain[DNLEN+1];          // Should always contain domain if
                                    // dwMsgId is SUCCESS/FAILURE

} SECURITY_MESSAGE, *PSECURITY_MESSAGE;

// Values for dwMsgId in SECURITY_MESSAGE structure

#define SECURITYMSG_SUCCESS     1
#define SECURITYMSG_FAILURE     2
#define SECURITYMSG_ERROR       3

// Used by RasSecurityGetInfo call

typedef struct _RAS_SECURITY_INFO
{

    DWORD LastError;                    // SUCCESS = receive completed
                                        // PENDING = receive pending
                                        // else completed with error

    DWORD BytesReceived;                // only valid if LastError == SUCCESS

    CHAR  DeviceName[DEVICE_NAME_LEN+1];

}RAS_SECURITY_INFO,*PRAS_SECURITY_INFO;

typedef DWORD (WINAPI *RASSECURITYPROC)();

//
// Called by third party DLL to notify the supervisor of termination of
// the security dialog
//

VOID WINAPI
RasSecurityDialogComplete(
    IN SECURITY_MESSAGE * pSecMsg       // Pointer to the above info. structure
);

//
// Called by supervisor into the security DLL to notify it to begin the
// security dialog for a client.
//
// Should return errors from winerror.h or raserror.h
//

DWORD WINAPI
RasSecurityDialogBegin(
    IN HPORT  hPort,        // RAS handle to port
    IN PBYTE  pSendBuf,     // Pointer to the buffer used in
                            // RasSecurityDialogSend
    IN DWORD  SendBufSize,  // Size of above bufer in bytes
    IN PBYTE  pRecvBuf,     // Pointer to the buffer used in
                            // RasSecurityDialogReceive
    IN DWORD  RecvBufSize,  // Size of above buffer
    IN VOID  (WINAPI *RasSecurityDialogComplete)( SECURITY_MESSAGE* )
                            // Pointer to function RasSecurityDialogComplete.
                            // Guaranteed to be the same on every call.
);

//
// Called by supervisor into the security DLL to notify it to stop the
// security dialog for a client. If this call returns an error, then it is not
// neccesary for the dll to call RasSecurityDialogComplete. Otherwise the DLL
// must call RasSecurityDialogComplete.
//
// Should return errors from winerror.h or raserror.h
//

DWORD WINAPI
RasSecurityDialogEnd(
    IN HPORT    hPort           // RAS handle to port.
);

//
// Called to send data to remote host
// Will return errors from winerror.h or raserror.h
//

DWORD WINAPI
RasSecurityDialogSend(
    IN HPORT    hPort,          // RAS handle to port.
    IN PBYTE    pBuffer,        // Pointer to buffer containing data to send
    IN WORD     BufferLength    // Length of above buffer.
);

//
// Called to receive data from remote host
// Will return errors from winerror.h or raserror.h
//

DWORD WINAPI
RasSecurityDialogReceive(
    IN HPORT    hPort,          // RAS handle to port.
    IN PBYTE    pBuffer,        // Pointer to buffer to receive data
    IN PWORD    pBufferLength,  // length of data received in bytes.
    IN DWORD    Timeout,        // in seconds
    IN HANDLE   hEvent          // Event to set when receive completes or
                                // timeouts
);

//
// Called to get Information about port.
// Will return errors from winerror.h or raserror.h
//

DWORD WINAPI
RasSecurityDialogGetInfo(
    IN HPORT                hPort,      // RAS handle to port.
    IN RAS_SECURITY_INFO*   pBuffer     // Pointer to get info structure.
);

#endif


README.TXT

RAS Server API

See rassapi.h


ADMINDLL.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
*       RasAdmin API usage and  is meant to help user with
*       RasAdmin API Calling convention. Use this code at your own risk.
*       Microsoft disclaims any implied warranty. This sample code can
*       be copied and distributed.

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

/********************************************************************
*  admindll.c -- Sample program demonstrating the use of RasAdmin
*                     CallOut API's
*
*****************************************************************************/
/***************************************************************************
*  Functions:
*        Init(void);
*        Init2(void);
*****************************************************************************/

#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include <stdlib.h>
#include <lm.h>
#include <stdio.h>
#include <time.h>
#include <rassapi.h>
#include <memory.h>
WCHAR*
StrDupWFromA(
    CHAR* psz );

#define Number_Of_Flags 6
#define Num_Ip_Address 256
#define ConstIpAddress 0x010A0F0E
HANDLE IPFile = NULL;
HANDLE ConDisFile = NULL;
HANDLE ErrLogFile = NULL;
HANDLE ErrLogFileIP = NULL;
BOOL IPFileInValid = FALSE;
BOOL ConnectDisconnectInValid = FALSE;
BOOL ErrorLogInValid = FALSE;
BOOL ErrLogIPInValid = FALSE;
VOID File_Print(RAS_PORT_1 *            pRasPort1,
    RAS_PORT_STATISTICS *   pRasStats,
    RAS_PARAMETERS *        pRasParams
);
VOID Debug_Print(RAS_PORT_1 *            pRasPort1,
    RAS_PORT_STATISTICS *   pRasStats,
    RAS_PARAMETERS *        pRasParams
);
VOID Init(void);
VOID Init2(void);
typedef struct Static_IP_Database
{
WCHAR wszUserName[UNLEN+1];
WCHAR wszPortName[RASSAPI_MAX_PORT_NAME];
DWORD IpAddress;
} Static_IP_Database;
Static_IP_Database  Static_IP[Num_Ip_Address];

typedef struct Static_Statistic{
      BOOL bTaken;
WCHAR wszUserName[UNLEN+1];
WCHAR wszPortName[RASSAPI_MAX_PORT_NAME];
DWORD NumMediaParms;
DWORD SizeMediaParms;
DWORD dwStartSessionTime;
RAS_PORT_STATISTICS  pRasStats;
      RAS_PARAMETERS        pRasParams;
} Static_Statistic;

Static_Statistic  Static_Check_Stats[Num_Ip_Address];
BOOL First_Call = TRUE;

WCHAR*
StrDupWFromA(
    CHAR* psz )

    /* Returns heap block containing a copy of 0-terminated string 'psz' or
    ** NULL on error or if 'psz' is NULL.  The output string is converted to
    ** UNICODE.  It is caller's responsibility to Free the returned string.
    */
{
    WCHAR* pszNew = NULL;

    if (psz)
    {
DWORD cb;

cb = MultiByteToWideChar( CP_ACP, 0, psz, -1, NULL, 0 );

pszNew = malloc( cb * sizeof(TCHAR) );
if (!pszNew)
{
    return NULL;
}

cb = MultiByteToWideChar( CP_ACP, 0, psz, -1, pszNew, cb );
if (cb == 0)
{
    free( pszNew );
    return NULL;
}
    }

    return pszNew;
}

// Format and write debug information to OutputDebugString
ULONG
_cdecl
DbgPrint(
    PCH Format,
    ...
    )
{
    CHAR szbuffer[255];
    WCHAR buffer[255];
    WCHAR *temp=NULL;
    LPINT lpResult=0;
    va_list marker;
    va_start (marker,Format);
    if (IsTextUnicode(Format, sizeof(marker),lpResult))
{
temp=StrDupWFromA(Format);
     wvsprintfW (buffer,temp, marker);
     OutputDebugString (buffer);
      free(temp);
}
    else
{
     wvsprintfA (szbuffer,Format, marker);
     temp=StrDupWFromA(szbuffer);
     OutputDebugString (temp);
     free(temp);
     }
    return TRUE;
}

//Called from RasAdminGetIpAddressForUser which in trun gets called by RasMan
VOID Init(void)
{
INT i=0;

     IPFile = CreateFile(TEXT("IpAddress.log"),
GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ,
(LPSECURITY_ATTRIBUTES) NULL,
CREATE_ALWAYS,
0,
       (HANDLE) NULL);
     if (IPFile== INVALID_HANDLE_VALUE) {
IPFileInValid = TRUE;
    }
     ErrLogFileIP = CreateFile(TEXT("IpAddError.log"),
GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ,
(LPSECURITY_ATTRIBUTES) NULL,
CREATE_ALWAYS,
0,
       (HANDLE) NULL);
     if (ErrLogFileIP== INVALID_HANDLE_VALUE) {
ErrLogIPInValid = TRUE;
    }

//First call initialize my Stats Database, I Fill out the struct if Call is Accepted the call.
    First_Call=FALSE;
}
//Called by RasAdminAcceptNewConnection which in trun gets called by RAS Server (Rassrv)
VOID Init2(void)
{
INT i=0;
     ConDisFile = CreateFile(TEXT("ConDis.log"),
GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ,
(LPSECURITY_ATTRIBUTES) NULL,
OPEN_ALWAYS,
0,
       (HANDLE) NULL);
     if (ConDisFile== INVALID_HANDLE_VALUE) {
  ConnectDisconnectInValid = TRUE;
    }
     ErrLogFile = CreateFile(TEXT("AdminErr.log"),
GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ,
(LPSECURITY_ATTRIBUTES) NULL,
OPEN_ALWAYS,
0,
       (HANDLE) NULL);
     if (ErrLogFile == INVALID_HANDLE_VALUE) {
ErrorLogInValid = TRUE;
    }
    for (i=0;i < Num_Ip_Address; i++);
  Static_Check_Stats[i].bTaken=FALSE;
}

DWORD
APIENTRY
RasAdminGetIpAddressForUser(
    IN WCHAR *      lpwszUserName,
    IN WCHAR *      lpwszPortName,
    IN OUT IPADDR * pIpAddress,
    OUT BOOL *      pfNotifyDLL
)
{
DWORD i=0;
DWORD dwBytesWritten=0;
CHAR buf[120];
//to check if the call is first rasman call out. If yes, call Initialize routine
    if (First_Call) Init();
    DbgPrint("First_Call  %d\n",First_Call);
    DbgPrint("\nGetIpAddressForUser called UserName=%ws,Port=%ws,IpAddress=%d\n",
      lpwszUserName,lpwszPortName,(DWORD)*pIpAddress );

    for (i=1;i < Num_Ip_Address;i++)
{
if (Static_IP[i].IpAddress != 1)
    {
     Static_IP[i].IpAddress = 1;
     wcscpy(Static_IP[i].wszUserName, lpwszUserName);
     wcscpy(Static_IP[i].wszPortName, lpwszPortName);
     *pIpAddress = i + ConstIpAddress;
      break;
    }
}
    if (IPFileInValid)
 {
 DbgPrint("GetIpAddressForUser setting IpAddress=%d\n",(DWORD)*pIpAddress );
 }
    else
{
 sprintf(buf,"GetIpAddressForUser setting IpAddress=%d\n",(DWORD)*pIpAddress );
       WriteFile(IPFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);

      }
    *pfNotifyDLL = TRUE;

    return( NO_ERROR );
}

VOID
APIENTRY
RasAdminReleaseIpAddress(
    IN WCHAR *      lpwszUserName,
    IN WCHAR *      lpwszPortName,
    IN OUT IPADDR * pIpAddress
)
{
DWORD dwBytesWritten=0;
CHAR buf[120];
    DbgPrint( "\nReleaseIpAddressr called UserName=%ws,Port=%ws,IpAddress=%d\n",
      lpwszUserName,lpwszPortName,(DWORD)*pIpAddress );
     if (wcscmp(Static_IP[(DWORD)*pIpAddress-ConstIpAddress].wszUserName,lpwszUserName) == 0)
 {
  if (wcscmp(Static_IP[(DWORD)*pIpAddress-ConstIpAddress].wszPortName,lpwszPortName) == 0)
      {
       Static_IP[(DWORD)*pIpAddress-ConstIpAddress].IpAddress = 0;
      }
      else
      {
       DbgPrint("ERROR ReleaseIP PortName Incorrect, Cannot free IP Address       %ws\n",Static_IP[(DWORD)*pIpAddress-ConstIpAddress].wszPortName);
       if (!(ErrLogIPInValid))
    {
 sprintf(buf,"ERROR ReleaseIP PortName Incorrect, Cannot free IP Address       %ws\n",Static_IP[(DWORD)*pIpAddress-ConstIpAddress].wszPortName);
    WriteFile(ErrLogFileIP, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    }
      }
 }
 else
 {
 DbgPrint("ERROR ReleaseIP UserName Incorrect, Cannot free IP Address       %ws\n",Static_IP[(DWORD)*pIpAddress-ConstIpAddress].wszUserName);
 if (!(ErrLogIPInValid))
   {
     sprintf(buf,"ERROR ReleaseIP UserName Incorrect, Cannot free IP Address       %ws\n",Static_IP[(DWORD)*pIpAddress-ConstIpAddress].wszUserName);
   WriteFile(ErrLogFileIP, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   }
 }
}

BOOL
APIENTRY
RasAdminAcceptNewConnection(
    RAS_PORT_1 *            pRasPort1,
    RAS_PORT_STATISTICS *   pRasStats,
    RAS_PARAMETERS *        pRasParams
)
{
INT i=0;
DWORD dwResult;
WCHAR szUserAccountServer[UNLEN+1];
PRAS_USER_0 ppRasUser0;
RAS_USER_0 RasUser0;
WCHAR Buf[512];
WCHAR *lpszString;
DWORD dwBytesWritten=0;
CHAR szBuf[120];
WCHAR lpszServerName[50];
DWORD Permission;
lpszString = &Buf[0];
ppRasUser0 = &RasUser0;

//to check if the call is first rassrv call out. If yes, call Initialize routine

if (First_Call) {Init2(); First_Call = FALSE;}
DbgPrint("ConnectDisconnect   %d\n",ConDisFile);
DbgPrint("ConnectDisconnectInValid %d\n",ConnectDisconnectInValid);
DbgPrint("ErrorLogFile   %d\n",ErrLogFile);
DbgPrint("ErrorLogInValid %d\n",ErrorLogInValid);
if (ConnectDisconnectInValid)
   {
    DbgPrint( "\nRasAdminAcceptNewConnection called \n" );
    Debug_Print( pRasPort1, pRasStats, pRasParams);
  }
else
  {
    sprintf(szBuf, "\nRasAdminAcceptNewConnection called \n" );
    WriteFile(ConDisFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
    File_Print( pRasPort1, pRasStats, pRasParams);
  }
   if (pRasPort1->rasport0.fAdvancedServer > 0){
  dwResult = RasAdminGetUserAccountServer(pRasPort1->rasport0.wszLogonDomain, NULL, &szUserAccountServer[0]);}
  else {
      lstrcpy(lpszServerName, TEXT("\\\\"));
      lstrcat(&lpszServerName[0], pRasPort1->rasport0.wszLogonDomain);
      DbgPrint("%ws\n",lpszServerName);
      dwResult = RasAdminGetUserAccountServer(NULL, &lpszServerName[0], &szUserAccountServer[0]);
      }

  if (dwResult == ERROR_SUCCESS)
     {
     DbgPrint("UserAccount Succeeded\n");
     dwResult = RasAdminUserGetInfo(szUserAccountServer, pRasPort1->rasport0.wszUserName, ppRasUser0);
     if (dwResult == ERROR_SUCCESS)
 {
       DbgPrint("AdminUserGetInfo Succeeded");
       DbgPrint(" Priviledge   %d\n",ppRasUser0->bfPrivilege);
       Permission = ppRasUser0->bfPrivilege - (ppRasUser0->bfPrivilege & RASPRIV_DialinPrivilege);
       DbgPrint("Permission %d\n",Permission);
 if (Permission == RASPRIV_AdminSetCallback || Permission == RASPRIV_CallerSetCallback)
    {
    DbgPrint ("Don't Accept Call\n");
    return (FALSE);
    }
 }
     else
{
      DbgPrint("RasAdminUserGetInfo     %d\n",dwResult);
dwResult = RasAdminGetErrorString((UINT) dwResult, lpszString, 512);
if (dwResult == ERROR_SUCCESS)
    {
    DbgPrint("ERROR  RasAdminUserGetInfo      %s\n",lpszString);
     sprintf(szBuf, "ERROR  RasAdminUserGetInfo      %s\n",lpszString);
  WriteFile(ErrLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
    }
 else
    {
    DbgPrint("ERROR  RasAdminGetErrorString      %d\n",dwResult);
    sprintf(szBuf, "ERROR  RasAdminGetErrorString      %d\n",dwResult);
  WriteFile(ErrLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
    }
 }
    }
  else
    {
    DbgPrint("RasAdminGetUserAccountServer     %d\n",dwResult);
    dwResult = RasAdminGetErrorString((UINT)dwResult, lpszString, 512);
     if (dwResult == ERROR_SUCCESS)
  {
  DbgPrint("ERROR  RasAdminUserAccountServer      %s\n",lpszString);
  sprintf(szBuf, "ERROR  RasAdminUserAccountServer      %s\n",lpszString);
WriteFile(ErrLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
  }
  else
    {
    DbgPrint("ERROR  RasAdminGetErrorString      %d\n",dwResult);
    sprintf(szBuf, "ERROR  RasAdminGetErrorString      %d\n",dwResult);
  WriteFile(ErrLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
    }
    }

  if (( lstrcmp( pRasPort1->rasport0.wszUserName, TEXT("Administrator") ) ) == 0)
       {
DbgPrint("Do not Accept Connection\n");
      FlushFileBuffers(ErrLogFile);
      FlushFileBuffers(ConDisFile);
return( FALSE );
       }
    else
       {
  for (i=0;i < Num_Ip_Address; i++)
     if (!(Static_Check_Stats[i].bTaken))
  {
   wcscpy(Static_Check_Stats[i].wszUserName, pRasPort1->rasport0.wszUserName);
   wcscpy(Static_Check_Stats[i].wszPortName, pRasPort1->rasport0.wszPortName);
       Static_Check_Stats[i].bTaken = TRUE;
   Static_Check_Stats[i].NumMediaParms = pRasPort1->NumMediaParms;
   Static_Check_Stats[i].SizeMediaParms = pRasPort1->SizeMediaParms;
   Static_Check_Stats[i].dwStartSessionTime = pRasPort1->rasport0.dwStartSessionTime;
   memcpy(&Static_Check_Stats[i].pRasStats,pRasStats, sizeof(RAS_PORT_STATISTICS));
   memcpy(&Static_Check_Stats[i].pRasParams,pRasParams, sizeof(pRasParams));
   break;
  }
       DbgPrint("Accept Connection\n");
       FlushFileBuffers(ErrLogFile);
       FlushFileBuffers(ConDisFile);
       return( TRUE );
       }
}

VOID
APIENTRY
RasAdminConnectionHangupNotification(
    RAS_PORT_1 *            pRasPort1,
    RAS_PORT_STATISTICS *   pRasStats,
    RAS_PARAMETERS *        pRasParams
)
{
INT i=0;
DWORD dwBytesWritten=0;
CHAR buf[120];
//if (First_Call) Init2();
if (ConnectDisconnectInValid)
   {
    DbgPrint( "\nRasAdminConnectionHangupNotification called \n" );
    Debug_Print( pRasPort1, pRasStats, pRasParams);
  }
else
  {
    sprintf(buf, "\nRasAdminConnectionHangupNotification called \n" );
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    File_Print( pRasPort1, pRasStats, pRasParams);
  }

//Sanity checks
      for (i=0; i < Num_Ip_Address; i++)
       {
if (Static_Check_Stats[i].bTaken)
     {
      if (wcscmp(Static_Check_Stats[i].wszPortName,pRasPort1->rasport0.wszPortName) == 0)
  {
if (ErrorLogInValid)
   {
   if (wcscmp(Static_Check_Stats[i].wszUserName,pRasPort1->rasport0.wszUserName) != 0)
DbgPrint("ERROR User Name Differs    %ws ->Passed,   %ws ->Stored\n",pRasPort1->rasport0.wszUserName,Static_Check_Stats[i].wszUserName);
       Static_Check_Stats[i].bTaken = FALSE;
   if (Static_Check_Stats[i].NumMediaParms != pRasPort1->NumMediaParms)
 DbgPrint("ERROR NumMediaParm    %d ->Passed,   %d  ->Stored\n",Static_Check_Stats[i].NumMediaParms != pRasPort1->NumMediaParms);
   if (Static_Check_Stats[i].SizeMediaParms != pRasPort1->SizeMediaParms)
 DbgPrint("ERROR SizeMediaParm    %d ->Passed,   %d  ->Stored\n",Static_Check_Stats[i].SizeMediaParms != pRasPort1->SizeMediaParms);
   if (Static_Check_Stats[i].dwStartSessionTime != pRasPort1->rasport0.dwStartSessionTime)
 DbgPrint("ERROR StartSessionTime    %d ->Passed,   %d  ->Stored\n",Static_Check_Stats[i].dwStartSessionTime, pRasPort1->rasport0.dwStartSessionTime);
   if (pRasStats->dwBytesXmited < Static_Check_Stats[i].pRasStats.dwBytesXmited)
  DbgPrint( "ERROR dwBytesXmited Passed=%d   , dwBytesXmited Stored=%d\n", pRasStats->dwBytesXmited,Static_Check_Stats[i].pRasStats.dwBytesXmited);
   if (pRasStats->dwBytesRcved < Static_Check_Stats[i].pRasStats.dwBytesRcved)
  DbgPrint( "ERROR dwBytesRcved Passed=%d    , dwBytesRcved Stored=%d\n", pRasStats->dwBytesRcved,Static_Check_Stats[i].pRasStats.dwBytesRcved );
   if (pRasStats->dwFramesXmited < Static_Check_Stats[i].pRasStats.dwFramesXmited)
 DbgPrint( "ERROR dwFramesXmited Passed=%d , dwFramesXmited Stored=%d\n", pRasStats->dwFramesXmited,Static_Check_Stats[i].pRasStats.dwFramesXmited );
   if (pRasStats->dwFramesRcved < Static_Check_Stats[i].pRasStats.dwFramesRcved)
 DbgPrint( "ERROR dwFramesRcved Passed=%d , dwFramesRcved Stored=%d\n", pRasStats->dwFramesRcved,Static_Check_Stats[i].pRasStats.dwFramesRcved);
   if (pRasStats->dwCrcErr < Static_Check_Stats[i].pRasStats.dwCrcErr)
DbgPrint( "ERROR dwCrcErr Passed=%d , dwCrcErr Stored=%d\n", pRasStats->dwCrcErr,Static_Check_Stats[i].pRasStats.dwCrcErr);
   if (pRasStats->dwTimeoutErr < Static_Check_Stats[i].pRasStats.dwTimeoutErr)
DbgPrint( "ERROR dwTimeoutErr Passed=%d  , dwTimeoutErr Stored=%d\n", pRasStats->dwTimeoutErr,Static_Check_Stats[i].pRasStats.dwTimeoutErr);
   if (pRasStats->dwAlignmentErr < Static_Check_Stats[i].pRasStats.dwAlignmentErr)
DbgPrint( "ERROR dwAlignmentErr Passed=%d , dwAlignmentErr Stored=%d\n", pRasStats->dwAlignmentErr,Static_Check_Stats[i].pRasStats.dwAlignmentErr);
   if (pRasStats->dwHardwareOverrunErr < Static_Check_Stats[i].pRasStats.dwHardwareOverrunErr)
DbgPrint( "ERROR dwHardwareOverrunErr Passed=%d  , dwHardwareOverrunErr Stored=%d\n", pRasStats->dwHardwareOverrunErr,Static_Check_Stats[i].pRasStats.dwHardwareOverrunErr);
   if (pRasStats->dwFramingErr < Static_Check_Stats[i].pRasStats.dwFramingErr)
       DbgPrint( "ERROR dwFramingErr Passed=%d , dwFramingErr Stored=%d\n", pRasStats->dwFramingErr,Static_Check_Stats[i].pRasStats.dwFramingErr);
   if (pRasStats->dwBufferOverrunErr < Static_Check_Stats[i].pRasStats.dwBufferOverrunErr)
       DbgPrint( "ERROR dwBufferOverrunErr Passed=%d , dwBufferOverrunErr Stored=%d\n", pRasStats->dwBufferOverrunErr,Static_Check_Stats[i].pRasStats.dwBufferOverrunErr);
   if (pRasStats->dwBytesXmitedUncompressed < Static_Check_Stats[i].pRasStats.dwBytesXmitedUncompressed)
       DbgPrint( "ERROR dwBytesXmitedUncompressed Passed=%d   ,  dwBytesXmitedUncompressed Stored=%d\n",
    pRasStats->dwBytesXmitedUncompressed,Static_Check_Stats[i].pRasStats.dwBytesXmitedUncompressed);
   if (pRasStats->dwBytesRcvedUncompressed < Static_Check_Stats[i].pRasStats.dwBytesRcvedUncompressed)
       DbgPrint( "ERROR dwBytesRcvedUncompressed  Passed=%d    ,dwBytesRcvedUncompressed Stored=%d\n",
    pRasStats->dwBytesRcvedUncompressed,Static_Check_Stats[i].pRasStats.dwBytesRcvedUncompressed);
   if (pRasStats->dwBytesXmitedCompressed < Static_Check_Stats[i].pRasStats.dwBytesXmitedCompressed)
       DbgPrint( "ERROR dwBytesXmitedCompressed Passed=%d   ,dwBytesXmitedCompressed Stored=%d\n",
    pRasStats->dwBytesXmitedCompressed,Static_Check_Stats[i].pRasStats.dwBytesXmitedCompressed);
   if (pRasStats->dwBytesRcvedCompressed < Static_Check_Stats[i].pRasStats.dwBytesRcvedCompressed)
      DbgPrint( "ERROR dwBytesRcvedCompressed Passed=%d    ,dwBytesRcvedCompressed Stored=%d\n",
    pRasStats->dwBytesRcvedCompressed,Static_Check_Stats[i].pRasStats.dwBytesRcvedCompressed);
      if (memcmp(&Static_Check_Stats[i].pRasParams,pRasParams, sizeof(pRasParams)) != 0)
      DbgPrint( "ERROR RASParams Passed   &  RASParasms Stored are different\n");
 }
     else
 {
   if (wcscmp(Static_Check_Stats[i].wszUserName,pRasPort1->rasport0.wszUserName) != 0)
{sprintf(buf,"ERROR User Name Differs    %ws ->Passed,   %ws ->Stored\n",pRasPort1->rasport0.wszUserName,Static_Check_Stats[i].wszUserName);
   WriteFile(ErrLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
  }
       Static_Check_Stats[i].bTaken = FALSE;
   if (Static_Check_Stats[i].NumMediaParms != pRasPort1->NumMediaParms)
 {sprintf(buf,"ERROR NumMediaParm    %d ->Passed,   %d  ->Stored\n",Static_Check_Stats[i].NumMediaParms != pRasPort1->NumMediaParms);
   WriteFile(ErrLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
  }
   if (Static_Check_Stats[i].SizeMediaParms != pRasPort1->SizeMediaParms)
 {sprintf(buf,"ERROR SizeMediaParm    %d ->Passed,   %d  ->Stored\n",Static_Check_Stats[i].SizeMediaParms != pRasPort1->SizeMediaParms);
   WriteFile(ErrLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
  }
   if (Static_Check_Stats[i].dwStartSessionTime != pRasPort1->rasport0.dwStartSessionTime)
 {sprintf(buf,"ERROR StartSessionTime    %d ->Passed,   %d  ->Stored\n",Static_Check_Stats[i].dwStartSessionTime != pRasPort1->rasport0.dwStartSessionTime);
   WriteFile(ErrLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
  }
   if (pRasStats->dwBytesXmited < Static_Check_Stats[i].pRasStats.dwBytesXmited)
 { sprintf(buf, "ERROR dwBytesXmited Passed=%d   , dwBytesXmited Stored=%d\n", pRasStats->dwBytesXmited,Static_Check_Stats[i].pRasStats.dwBytesXmited);
   WriteFile(ErrLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
  }
   if (pRasStats->dwBytesRcved < Static_Check_Stats[i].pRasStats.dwBytesRcved)
 { sprintf(buf, "ERROR dwBytesRcved Passed=%d    , dwBytesRcved Stored=%d\n", pRasStats->dwBytesRcved,Static_Check_Stats[i].pRasStats.dwBytesRcved );
   WriteFile(ErrLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
  }
   if (pRasStats->dwFramesXmited < Static_Check_Stats[i].pRasStats.dwFramesXmited)
 {sprintf(buf, "ERROR dwFramesXmited Passed=%d , dwFramesXmited Stored=%d\n", pRasStats->dwFramesXmited,Static_Check_Stats[i].pRasStats.dwFramesXmited );
   WriteFile(ErrLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
  }
   if (pRasStats->dwFramesRcved < Static_Check_Stats[i].pRasStats.dwFramesRcved)
 {sprintf(buf, "ERROR dwFramesRcved Passed=%d , dwFramesRcved Stored=%d\n", pRasStats->dwFramesRcved,Static_Check_Stats[i].pRasStats.dwFramesRcved);
   WriteFile(ErrLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
  }
   if (pRasStats->dwCrcErr < Static_Check_Stats[i].pRasStats.dwCrcErr)
{sprintf(buf, "ERROR dwCrcErr Passed=%d , dwCrcErr Stored=%d\n", pRasStats->dwCrcErr,Static_Check_Stats[i].pRasStats.dwCrcErr);
   WriteFile(ErrLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
  }
   if (pRasStats->dwTimeoutErr < Static_Check_Stats[i].pRasStats.dwTimeoutErr)
{sprintf(buf, "ERROR dwTimeoutErr Passed=%d  , dwTimeoutErr Stored=%d\n", pRasStats->dwTimeoutErr,Static_Check_Stats[i].pRasStats.dwTimeoutErr);
   WriteFile(ErrLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
  }
   if (pRasStats->dwAlignmentErr < Static_Check_Stats[i].pRasStats.dwAlignmentErr)
{sprintf(buf, "ERROR dwAlignmentErr Passed=%d , dwAlignmentErr Stored=%d\n", pRasStats->dwAlignmentErr,Static_Check_Stats[i].pRasStats.dwAlignmentErr);
   WriteFile(ErrLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
  }
   if (pRasStats->dwHardwareOverrunErr < Static_Check_Stats[i].pRasStats.dwHardwareOverrunErr)
{sprintf(buf, "ERROR dwHardwareOverrunErr Passed=%d  , dwHardwareOverrunErr Stored=%d\n", pRasStats->dwHardwareOverrunErr,Static_Check_Stats[i].pRasStats.dwHardwareOverrunErr);
   WriteFile(ErrLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
  }
   if (pRasStats->dwFramingErr < Static_Check_Stats[i].pRasStats.dwFramingErr)
      {sprintf(buf, "ERROR dwFramingErr Passed=%d , dwFramingErr Stored=%d\n", pRasStats->dwFramingErr,Static_Check_Stats[i].pRasStats.dwFramingErr);
   WriteFile(ErrLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
  }
   if (pRasStats->dwBufferOverrunErr < Static_Check_Stats[i].pRasStats.dwBufferOverrunErr)
      {sprintf(buf, "ERROR dwBufferOverrunErr Passed=%d , dwBufferOverrunErr Stored=%d\n", pRasStats->dwBufferOverrunErr,Static_Check_Stats[i].pRasStats.dwBufferOverrunErr);
   WriteFile(ErrLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
  }
   if (pRasStats->dwBytesXmitedUncompressed < Static_Check_Stats[i].pRasStats.dwBytesXmitedUncompressed)
     { sprintf(buf, "ERROR dwBytesXmitedUncompressed Passed=%d   ,  dwBytesXmitedUncompressed Stored=%d\n",
    pRasStats->dwBytesXmitedUncompressed,Static_Check_Stats[i].pRasStats.dwBytesXmitedUncompressed);
   WriteFile(ErrLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
  }
   if (pRasStats->dwBytesRcvedUncompressed < Static_Check_Stats[i].pRasStats.dwBytesRcvedUncompressed)
      {sprintf(buf, "ERROR dwBytesRcvedUncompressed  Passed=%d    ,dwBytesRcvedUncompressed Stored=%d\n",
    pRasStats->dwBytesRcvedUncompressed,Static_Check_Stats[i].pRasStats.dwBytesRcvedUncompressed);
   WriteFile(ErrLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
  }
   if (pRasStats->dwBytesXmitedCompressed < Static_Check_Stats[i].pRasStats.dwBytesXmitedCompressed)
      {sprintf(buf, "ERROR dwBytesXmitedCompressed Passed=%d   ,dwBytesXmitedCompressed Stored=%d\n",
    pRasStats->dwBytesXmitedCompressed,Static_Check_Stats[i].pRasStats.dwBytesXmitedCompressed);
   WriteFile(ErrLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
  }
   if (pRasStats->dwBytesRcvedCompressed < Static_Check_Stats[i].pRasStats.dwBytesRcvedCompressed)
{ sprintf(buf, "ERROR dwBytesRcvedCompressed Passed=%d    ,dwBytesRcvedCompressed Stored=%d\n",
    pRasStats->dwBytesRcvedCompressed,Static_Check_Stats[i].pRasStats.dwBytesRcvedCompressed);
   WriteFile(ErrLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
     }
      if (memcmp(&Static_Check_Stats[i].pRasParams,pRasParams, sizeof(pRasParams)) != 0)
{ sprintf(buf, "ERROR RASParams Passed   &  RASParasms Stored are different\n");
   WriteFile(ErrLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
     }

  }
  break;
    }
     else
  {       if (i == 255){
  if (ErrorLogInValid)
       {DbgPrint("ERROR Port Name  Differs    %ws ->Passed,   %ws ->Stored\n",pRasPort1->rasport0.wszPortName,Static_Check_Stats[i].wszPortName);}
     else
       {sprintf(buf,"ERROR Port Name  Differs    %ws ->Passed,   %ws ->Stored\n",pRasPort1->rasport0.wszPortName,Static_Check_Stats[i].wszPortName);
   WriteFile(ErrLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
  }
  }
 }
       }
     }
    FlushFileBuffers(ErrLogFile);
    FlushFileBuffers(ConDisFile);

}

VOID Debug_Print(RAS_PORT_1 *            pRasPort1,
    RAS_PORT_STATISTICS *   pRasStats,
    RAS_PARAMETERS *        pRasParams
)
{
WCHAR *Media_Name[] = {TEXT("MEDIA_UNKNOWN"),TEXT( "MEDIA_SERIAL"),
  TEXT("MEDIA_RAS10_SERIAL"),TEXT( "MEDIA_ISDN")};
WCHAR *Flag_Name[] = {TEXT("USER_AUTHENTICATED"),TEXT( "MESSENGER_PRESENT"),
 TEXT("PPP_CLIENT"),TEXT("GATEWAY_ACTIVE"),TEXT("REMOTE_LISTEN"),TEXT("PORT_MULTILINKED")};
WCHAR *Line_Condition[]={TEXT("RAS_PORT_NON_OPERATIONAL"),TEXT("RAS_PORT_DISCONNECTED"),
  TEXT("RAS_PORT_CALLING_BACK"),TEXT("RAS_PORT_LISTENING"),TEXT("RAS_PORT_AUTHENTICATING"),
  TEXT("RAS_PORT_AUTHENTICATED"),TEXT("RAS_PORT_INITIALIZING")};
WCHAR *Hdw_Error[]={TEXT("RAS_MODEM_OPERATIONAL"),TEXT("RAS_MODEM_NOT_RESPONDING"),TEXT("RAS_MODEM_HARDWARE_FAILURE"),
  TEXT("RAS_MODEM_INCORRECT_RESPONSE"),TEXT("RAS_MODEM_UNKNOWN")};
INT  i=0;
INT temp = 0;
INT temp1 =0;
DWORD dwsize=0;
CHAR szBuf[100];
    DbgPrint( "\nRasAdminAcceptNewConnection called \n" );
    DbgPrint( "pRasPort1->rasport0.wszPortName=%ws\n",
pRasPort1->rasport0.wszPortName );
    DbgPrint( "pRasPort1->rasport0.wszDeviceType=%ws\n",
    pRasPort1->rasport0.wszDeviceType );
    DbgPrint( "pRasPort1->rasport0.wszDeviceName=%ws\n",
    pRasPort1->rasport0.wszDeviceName );
    DbgPrint( "pRasPort1->rasport0.wszMediaName=%ws\n",
pRasPort1->rasport0.wszMediaName );
    DbgPrint( "pRasPort1->rasport0.reserved=%d\n",
    pRasPort1->rasport0.reserved );
    DbgPrint("                 %ws\n",Media_Name[pRasPort1->rasport0.reserved]);
    DbgPrint( "pRasPort1->rasport0.Flags=%d\n",
pRasPort1->rasport0.Flags );
//printing Flags
    temp1 = 1;
    for (i=0; i <= Number_Of_Flags; i++)
 {
 temp = (pRasPort1->rasport0.Flags & temp1);
 if (temp)
     {
       DbgPrint("Flag       %ws   \n",Flag_Name[i]);
     }
  temp1 = temp1*2;
 }
    DbgPrint( "pRasPort1->rasport0.wszUserName=%ws\n",
pRasPort1->rasport0.wszUserName );
    DbgPrint( "pRasPort1->rasport0.wszComputer=%ws\n",
pRasPort1->rasport0.wszComputer );
    DbgPrint( "pRasPort1->rasport0.dwStartSessionTime;=%d\n",
    pRasPort1->rasport0.dwStartSessionTime );
    DbgPrint("Formatted session start time/date :  %s\n",ctime(&pRasPort1->rasport0.dwStartSessionTime));
    DbgPrint( "pRasPort1->rasport0.wszLogonDomain=%ws\n",
    pRasPort1->rasport0.wszLogonDomain );
    DbgPrint( "pRasPort1->rasport0.fAdvancedServer=%d\n",
    pRasPort1->rasport0.fAdvancedServer );
    DbgPrint( "pRasPort1->LineCondition=%d\n", pRasPort1->LineCondition );
    DbgPrint("                     %ws\n",Line_Condition[pRasPort1->LineCondition-1]);
    DbgPrint( "pRasPort1->HardwareCondition=%d\n",pRasPort1->HardwareCondition);
    DbgPrint("                     %ws\n",Hdw_Error[pRasPort1->HardwareCondition-1]);
    DbgPrint( "pRasPort1->LineSpeed=%d   bits/sec\n",pRasPort1->LineSpeed);
    DbgPrint( "pRasPort1->NumStatistics=%d\n",pRasPort1->NumStatistics);
    DbgPrint( "pRasPort1->NumMediaParms=%d\n",pRasPort1->NumMediaParms);
    DbgPrint( "pRasPort1->SizeMediaParms=%d\n",pRasPort1->SizeMediaParms);
    DbgPrint( "\npRasPort1->ProjResult.nbf.dwError=%d\n",
pRasPort1->ProjResult.nbf.dwError );
    if (pRasPort1->ProjResult.nbf.dwError == NO_ERROR)
       {
DbgPrint( "pRasPort1->ProjResult.nbf.dwNetBiosError=%d\n",
    pRasPort1->ProjResult.nbf.dwNetBiosError );
DbgPrint( "pRasPort1->ProjResult.nbf.szName=%s\n",
    pRasPort1->ProjResult.nbf.szName );
DbgPrint( "pRasPort1->ProjResult.nbf.wszWksta=%ws\n",
    pRasPort1->ProjResult.nbf.wszWksta );
      }
   DbgPrint( "pRasPort1->ProjResult.ip.dwError=%d\n",
pRasPort1->ProjResult.ip.dwError );

if (pRasPort1->ProjResult.ip.dwError == NO_ERROR)
      {
DbgPrint( "pRasPort1->ProjResult.ip.wszAddress=%ws\n",
pRasPort1->ProjResult.ip.wszAddress );
       }
    DbgPrint( "pRasPort1->ProjResult.ipx.dwError=%d\n",
pRasPort1->ProjResult.ipx.dwError );
    if (pRasPort1->ProjResult.ipx.dwError == NO_ERROR)
      {
     DbgPrint( "pRasPort1->ProjResult.ipx.wszAddress=%ws\n",
pRasPort1->ProjResult.ipx.wszAddress );
      }
    DbgPrint( "pRasPort1->ProjResult.at.dwError=%d\n",
pRasPort1->ProjResult.at.dwError );
    if (pRasPort1->ProjResult.at.dwError == NO_ERROR)
      {
      DbgPrint( "pRasPort1->ProjResult.at.wszAddress=%ws\n",
pRasPort1->ProjResult.at.wszAddress );
       }
    DbgPrint( "\ndwBytesXmited=%d\n", pRasStats->dwBytesXmited );
    DbgPrint( "dwBytesRcved=%d\n", pRasStats->dwBytesRcved );
    DbgPrint( "dwFramesXmited=%d\n", pRasStats->dwFramesXmited );
    DbgPrint( "dwFramesRcved=%d\n", pRasStats->dwFramesRcved);
    DbgPrint( "dwCrcErr=%d\n", pRasStats->dwCrcErr);
    DbgPrint( "dwTimeoutErr=%d\n", pRasStats->dwTimeoutErr);
    DbgPrint( "dwAlignmentErr=%d\n", pRasStats->dwAlignmentErr);
    DbgPrint( "dwHardwareOverrunErr=%d\n", pRasStats->dwHardwareOverrunErr);
    DbgPrint( "dwFramingErr=%d\n", pRasStats->dwFramingErr);
    DbgPrint( "dwBufferOverrunErr=%d\n", pRasStats->dwBufferOverrunErr);
    DbgPrint( "dwBytesXmitedUncompressed=%d\n",
    pRasStats->dwBytesXmitedUncompressed);
    DbgPrint( "dwBytesRcvedUncompressed=%d\n",
    pRasStats->dwBytesRcvedUncompressed);
    DbgPrint( "dwBytesXmitedCompressed=%d\n",
    pRasStats->dwBytesXmitedCompressed);
    DbgPrint( "dwBytesRcvedCompressed=%d\n",
    pRasStats->dwBytesRcvedCompressed);
    for (i=0; i<pRasPort1->NumMediaParms;i++)
      {
      DbgPrint( "pRasParams->P_Key= %s\n",pRasParams[i].P_Key);
      DbgPrint( "pRasParams->P_Type= %d\n",pRasParams[i].P_Type);
      if (pRasParams[i].P_Type == 1)
  {
  DbgPrint("pRasParams->P_Value.Length=%d\n",pRasParams[i].P_Value.String.Length);
  sprintf(szBuf,"                                                                \n");
  strncpy(szBuf, pRasParams[i].P_Value.String.Data, pRasParams[i].P_Value.String.Length);
  DbgPrint("pRasParams->P_Value.Data= %s\n",szBuf);
  }
      if (pRasParams[i].P_Type == 0)
 {
 DbgPrint("pRasParams->P_Value.Number=%d\n", pRasParams[i].P_Value.Number);
 }
      DbgPrint( "pRasParams->P_Attributes= %d\n",pRasParams[i].P_Attributes);
      }
}

VOID File_Print(RAS_PORT_1 *            pRasPort1,
    RAS_PORT_STATISTICS *   pRasStats,
    RAS_PARAMETERS *        pRasParams
)
{
char *Media_Name[] = {"MEDIA_UNKNOWN", "MEDIA_SERIAL",
  "MEDIA_RAS10_SERIAL", "MEDIA_ISDN"};
char *Flag_Name[] = {"USER_AUTHENTICATED", "MESSENGER_PRESENT",
 "PPP_CLIENT","GATEWAY_ACTIVE","REMOTE_LISTEN","PORT_MULTILINKED"};
char *Line_Condition[]={"RAS_PORT_NON_OPERATIONAL","RAS_PORT_DISCONNECTED",
  "RAS_PORT_CALLING_BACK","RAS_PORT_LISTENING","RAS_PORT_AUTHENTICATING",
  "RAS_PORT_AUTHENTICATED","RAS_PORT_INITIALIZING"};
char *Hdw_Error[]={"RAS_MODEM_OPERATIONAL","RAS_MODEM_NOT_RESPONDING","RAS_MODEM_HARDWARE_FAILURE",
  "RAS_MODEM_INCORRECT_RESPONSE","RAS_MODEM_UNKNOWN"};
INT  i=0;
INT temp = 0;
INT temp1 =0;
DWORD dwsize=0;
CHAR szBuf[50];
CHAR buf[120];
DWORD dwBytesWritten=0;
    sprintf(buf, "pRasPort1->rasport0.wszPortName=%ws\n",
pRasPort1->rasport0.wszPortName );
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf,"pRasPort1->rasport0.wszDeviceType=%ws\n",
    pRasPort1->rasport0.wszDeviceType );
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf,"pRasPort1->rasport0.wszDeviceName=%ws\n",
    pRasPort1->rasport0.wszDeviceName );
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "pRasPort1->rasport0.wszMediaName=%ws\n",
pRasPort1->rasport0.wszMediaName );
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf,"pRasPort1->rasport0.reserved=%d\n",
    pRasPort1->rasport0.reserved );
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf,"                 %s\n",Media_Name[pRasPort1->rasport0.reserved]);
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "pRasPort1->rasport0.Flags=%d\n",
pRasPort1->rasport0.Flags );
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
//printing Flags
    temp1 = 1;
    for (i=0; i <= Number_Of_Flags; i++)
 {
 temp = (pRasPort1->rasport0.Flags & temp1);
 if (temp)
     {
       sprintf(buf,"Flag       %s   \n",Flag_Name[i]);
     WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
     }
  temp1 = temp1*2;
 }
    sprintf(buf, "pRasPort1->rasport0.wszUserName=%ws\n",
pRasPort1->rasport0.wszUserName );
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "pRasPort1->rasport0.wszComputer=%ws\n",
pRasPort1->rasport0.wszComputer );
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "pRasPort1->rasport0.dwStartSessionTime;=%d\n",
    pRasPort1->rasport0.dwStartSessionTime );
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf,"Formatted session start time/date :  %s\n",ctime(&pRasPort1->rasport0.dwStartSessionTime));
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "pRasPort1->rasport0.wszLogonDomain=%ws\n",
    pRasPort1->rasport0.wszLogonDomain );
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "pRasPort1->rasport0.fAdvancedServer=%d\n",
    pRasPort1->rasport0.fAdvancedServer );
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "pRasPort1->LineCondition=%d\n", pRasPort1->LineCondition );
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf,"                     %s\n",Line_Condition[pRasPort1->LineCondition-1]);
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "pRasPort1->HardwareCondition=%d\n",pRasPort1->HardwareCondition);
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf,"                     %s\n",Hdw_Error[pRasPort1->HardwareCondition-1]);
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "pRasPort1->LineSpeed=%d   bits/sec\n",pRasPort1->LineSpeed);
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "pRasPort1->NumStatistics=%d\n",pRasPort1->NumStatistics);
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "pRasPort1->NumMediaParms=%d\n",pRasPort1->NumMediaParms);
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "pRasPort1->SizeMediaParms=%d\n",pRasPort1->SizeMediaParms);
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "\npRasPort1->ProjResult.nbf.dwError=%d\n",
pRasPort1->ProjResult.nbf.dwError );
    if (pRasPort1->ProjResult.nbf.dwError == NO_ERROR)
       {
sprintf(buf, "pRasPort1->ProjResult.nbf.dwNetBiosError=%d\n",
    pRasPort1->ProjResult.nbf.dwNetBiosError );
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
sprintf(buf, "pRasPort1->ProjResult.nbf.szName=%s\n",
    pRasPort1->ProjResult.nbf.szName );
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
sprintf(buf, "pRasPort1->ProjResult.nbf.wszWksta=%ws\n",
    pRasPort1->ProjResult.nbf.wszWksta );
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
      }
   sprintf(buf, "pRasPort1->ProjResult.ip.dwError=%d\n",
pRasPort1->ProjResult.ip.dwError );
   WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   if (pRasPort1->ProjResult.ip.dwError == NO_ERROR)
      {
sprintf(buf, "pRasPort1->ProjResult.ip.wszAddress=%ws\n",
pRasPort1->ProjResult.ip.wszAddress );
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
       }
    sprintf(buf, "pRasPort1->ProjResult.ipx.dwError=%d\n",
pRasPort1->ProjResult.ipx.dwError );
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    if (pRasPort1->ProjResult.ipx.dwError == NO_ERROR)
      {
     sprintf(buf, "pRasPort1->ProjResult.ipx.wszAddress=%ws\n",
pRasPort1->ProjResult.ipx.wszAddress );
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
      }
    sprintf(buf, "pRasPort1->ProjResult.at.dwError=%d\n",
pRasPort1->ProjResult.at.dwError );
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    if (pRasPort1->ProjResult.at.dwError == NO_ERROR)
      {
      sprintf(buf, "pRasPort1->ProjResult.at.wszAddress=%ws\n",
pRasPort1->ProjResult.at.wszAddress );
       }
    sprintf(buf, "\ndwBytesXmited=%d\n", pRasStats->dwBytesXmited );
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "dwBytesRcved=%d\n", pRasStats->dwBytesRcved );
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "dwFramesXmited=%d\n", pRasStats->dwFramesXmited );
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "dwFramesRcved=%d\n", pRasStats->dwFramesRcved);
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "dwCrcErr=%d\n", pRasStats->dwCrcErr);
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "dwTimeoutErr=%d\n", pRasStats->dwTimeoutErr);
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "dwAlignmentErr=%d\n", pRasStats->dwAlignmentErr);
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "dwHardwareOverrunErr=%d\n", pRasStats->dwHardwareOverrunErr);
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "dwFramingErr=%d\n", pRasStats->dwFramingErr);
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "dwBufferOverrunErr=%d\n", pRasStats->dwBufferOverrunErr);
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "dwBytesXmitedUncompressed=%d\n",
    pRasStats->dwBytesXmitedUncompressed);
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "dwBytesRcvedUncompressed=%d\n",
    pRasStats->dwBytesRcvedUncompressed);
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "dwBytesXmitedCompressed=%d\n",
    pRasStats->dwBytesXmitedCompressed);
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "dwBytesRcvedCompressed=%d\n",
    pRasStats->dwBytesRcvedCompressed);
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    for (i=0; i<pRasPort1->NumMediaParms;i++)
      {
      sprintf(buf, "pRasParams->P_Key= %s\n",pRasParams[i].P_Key);
      WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
      sprintf(buf, "pRasParams->P_Type= %d\n",pRasParams[i].P_Type);
      WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
      if (pRasParams[i].P_Type == 1)
  {
  sprintf(buf,"pRasParams->P_Value.Length=%d\n",pRasParams[i].P_Value.String.Length);
WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
  sprintf(szBuf,"                                                                \n");
  strncpy(szBuf, pRasParams[i].P_Value.String.Data, pRasParams[i].P_Value.String.Length);
  sprintf(buf,"pRasParams->P_Value.Data= %s\n",szBuf);
       WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
  }
      if (pRasParams[i].P_Type == 0)
 {
 sprintf(buf,"pRasParams->P_Value.Number=%d\n", pRasParams[i].P_Value.Number);
      WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
 }
      sprintf(buf, "pRasParams->P_Attributes= %d\n",pRasParams[i].P_Attributes);
      WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
      }
}


ADMAPI.C

/******************************************************************************
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1992 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This sample source code is only intended as a supplement to
*       RasAdmin API usage and is meant to help user with
*       RasAdmin API Calling convention. Use this code at your own risk.
*       Microsoft disclaims any implied warranty. This sample code can
*       be copied and distributed.

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

/********************************************************************
*  admapit.c -- Sample program demonstrating the use of different
*                     RasAdmin API
*
*  Comments:
*                   RasAdmin expects \\Servername  as it's arguments.
* RasAdminGetErrorString API returns error string. However
* this API may return an Error 87 (Invalid Parameter) for the Non
* RAS Error codes. This API is design for  only Ras related error codes.
* For more Info. check the documentation. I am using this API to for all the
* errors but I print the error codes before calling this API
*****************************************************************************/
/***************************************************************************
*  Functions:
*      Init2(void);
*      Init1(void);
*      PrintGetStringError(DWORD dwResult);
*      ServerGetInfo(WCHAR *RasSrv);
*      PrintPriv(void);
*      UserPrivilege(void);
*      PrintClearStats(WCHAR * RasSrv, WCHAR *wszPortName);
*      RasAdminClearDisc(LPVOID param);
*****************************************************************************/

#define sleeptime 60000
#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include <stdlib.h>
#include <lm.h>
#include <stdio.h>
#include <time.h>
#include <ctype.h>
#include <conio.h>
#include <rassapi.h>

#include <memory.h>
WCHAR*
StrDupWFromA(
    CHAR* psz );

#define Number_Of_Flags 6
HANDLE LogFile = NULL;
HANDLE cLogFile = NULL;
HANDLE ErrLogFile = NULL;
VOID File_Print(RAS_PORT_1 *            pRasPort1,
    RAS_PORT_STATISTICS *   pRasStats,
    RAS_PARAMETERS *        pRasParams
);
VOID Debug_Print(RAS_PORT_1 *            pRasPort1,
    RAS_PORT_STATISTICS *   pRasStats,
    RAS_PARAMETERS *        pRasParams
);
VOID Init2(void);
VOID Init1(void);
VOID PrintGetStringError(DWORD dwResult);
INT ServerGetInfo(WCHAR *RasSrv);
VOID PrintPriv(void);
VOID UserPrivilege(void);
VOID PrintClearStats(WCHAR * RasSrv, WCHAR *wszPortName);
DWORD WINAPI RasAdminClearDisc(LPVOID param);

PRAS_PORT_1             pRasPort1;
PRAS_PORT_STATISTICS    pRasStats;
RAS_PARAMETERS *        pRasParams;
PRAS_SERVER_0           pRasServer0;
PRAS_PORT_0             pRasPort0;
RAS_PORT_1             RasPort1;
RAS_PORT_STATISTICS    RasStats;
RAS_SERVER_0           RasServer0;

BOOL First_Call = TRUE;
DWORD Status =0;
DWORD dwResult = 0;
DWORD dwBytesWritten=0;
CHAR szBuf[120];
BOOL ErrLogInValid = FALSE;
BOOL cErrLogInValid = FALSE;
BOOL LogInValid = TRUE;
WCHAR szUserAccountServer[UNLEN+1];
PRAS_USER_0 ppRasUser0;
RAS_USER_0 RasUser0;
WCHAR lpszServerName[50];
WCHAR RasSrv[64];
BOOL Quit=FALSE;
/*************************************************************************/
/***************** Start Main ********************************************/
/*************************************************************************/

WCHAR*
StrDupWFromA(
    CHAR* psz )

    /* Returns heap block containing a copy of 0-terminated string 'psz' or
    ** NULL on error or if 'psz' is NULL.  The output string is converted to
    ** UNICODE.  It is caller's responsibility to Free the returned string.
    */
{
    WCHAR* pszNew = NULL;

    if (psz)
    {
DWORD cb;

cb = MultiByteToWideChar( CP_ACP, 0, psz, -1, NULL, 0 );

pszNew = malloc( cb * sizeof(TCHAR) );
if (!pszNew)
{
    return NULL;
}

cb = MultiByteToWideChar( CP_ACP, 0, psz, -1, pszNew, cb );
if (cb == 0)
{
    free( pszNew );
    return NULL;
}
    }

    return pszNew;
}

// Format and write debug information to OutputDebugString
ULONG
_cdecl
DbgPrint(
    PCH Format,
    ...
    )
{
    CHAR szbuffer[255];
    WCHAR buffer[255];
    WCHAR *temp=NULL;
    LPINT lpResult=0;
    va_list marker;
    va_start (marker,Format);
    if (IsTextUnicode(Format, sizeof(Format),lpResult))
{
temp=StrDupWFromA(Format);
     wvsprintfW (buffer,temp, marker);
     OutputDebugString (buffer);
      free(temp);
}
    else
{
     wvsprintfA (szbuffer,Format, marker);
     temp=StrDupWFromA(szbuffer);
     OutputDebugString (temp);
     free(temp);
     }
    return TRUE;
}

// Init Functions
VOID Init1(void)
{
//Stores port  information in this file
LogInValid = FALSE;
     LogFile = CreateFile(TEXT("Admin.log"),
GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ,
(LPSECURITY_ATTRIBUTES) NULL,
CREATE_ALWAYS,
0,
       (HANDLE) NULL);
     if (LogFile== INVALID_HANDLE_VALUE) {
LogInValid = TRUE;
    }

}
VOID Init2(void)
{
cErrLogInValid = FALSE;
ErrLogInValid = FALSE;
//Stores Statistics and port disconnect information in this file
     cLogFile = CreateFile(TEXT("AdminStats.log"),
GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ,
(LPSECURITY_ATTRIBUTES) NULL,
CREATE_ALWAYS,
0,
       (HANDLE) NULL);
     if (cLogFile== INVALID_HANDLE_VALUE) {
cErrLogInValid = TRUE;
    }
//Stores Privlege changes information of a user in this file
     ErrLogFile = CreateFile(TEXT("AdminUser.log"),
GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ,
(LPSECURITY_ATTRIBUTES) NULL,
CREATE_ALWAYS,
0,
       (HANDLE) NULL);
     if (ErrLogFile== INVALID_HANDLE_VALUE) {
ErrLogInValid = TRUE;
    }

}

//Function to Print Error Strings

VOID PrintGetStringError(DWORD dwResult)
{
CHAR szBuf[120];
DWORD dwBytesWritten=0;

     DbgPrint("ERROR  RasAdminGetErrorString      %d\n",dwResult);
     sprintf(szBuf, "ERROR  RasAdminGetErrorString      %d\n",dwResult);
     WriteFile(LogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
}

// Function to get Server  Info.
INT ServerGetInfo(WCHAR *RasSrv)
{
DWORD Status;
DWORD dwResult;
CHAR szBuf[120];
DWORD dwBytesWritten=0;
WCHAR Buf[512];
WCHAR *lpszString;
lpszString=&Buf[0];

// Calling RasAdminServerGetInfo
// Paramters:
// IN   WCHAR * lpszServerName              //name of the RAS Server
// OUT PRAS_SERVER_0 pRasServer0    // Pointer to server inf.

Status=RasAdminServerGetInfo(RasSrv, pRasServer0);
if (Status != ERROR_SUCCESS)
   {
sprintf(szBuf, "ERROR RasAdminServerGetInfo   %d\n",Status);
dwResult = RasAdminGetErrorString((UINT) Status, lpszString, 512);
if (dwResult == ERROR_SUCCESS)
    {
    DbgPrint("ERROR  RasAdminServerGetInfo      %ws\n",lpszString);
     sprintf(szBuf, "ERROR  RasAdminServerGetInfo      %ws\n",lpszString);
  WriteFile(LogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
    }
 else
    PrintGetStringError(dwResult);
      return(1);
   }
else
   {
    sprintf(szBuf, "TotalPorts =  %d   PortsInUse = %d   RasVersion = %d  \n",
       pRasServer0->TotalPorts, pRasServer0->PortsInUse, pRasServer0->RasVersion);
    WriteFile(LogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
   }
return(0);
}

// Function to print Privileges of a user
VOID PrintPriv(void)
{
DWORD Permission;
DWORD dwBytesWritten=0;
CHAR szBuf[120];

     sprintf(szBuf,"\n User Privileges\n");
     WriteFile(ErrLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
     sprintf(szBuf,"Account Server Name= %ws     UserName= %ws\n",szUserAccountServer, pRasPort1->rasport0.wszUserName);
     WriteFile(ErrLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
     sprintf(szBuf," Priviledge   %d\n",ppRasUser0->bfPrivilege);
     WriteFile(ErrLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
     Permission = ppRasUser0->bfPrivilege - (ppRasUser0->bfPrivilege & RASPRIV_DialinPrivilege);
     sprintf(szBuf,"CallBack Permission %d\n",Permission);
     WriteFile(ErrLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
}

//Function to get and change User Dialin Privileges. It goes through all the Dialin Privileges for a user.

VOID UserPrivilege(void)
{
DWORD dwResult;
CHAR szBuf[120];
DWORD dwBytesWritten=0;
WCHAR Buf[512];
WCHAR *lpszString;
static DWORD LoopPriv=0;
static BYTE Dialin=1;
lpszString=&Buf[0];

// API Called:     RasAdminGetUserAccountServer
// Parameters:
//  IN WCHAR * lpszDomainName
// IN WCHAR * lpszServerName
// OUT WCHAR * lpszUserAccountServer

   if (pRasPort1->rasport0.fAdvancedServer > 0)
     {
      dwResult = RasAdminGetUserAccountServer(pRasPort1->rasport0.wszLogonDomain,
 NULL, &szUserAccountServer[0]);
     }
  else
      {
      wcscpy(lpszServerName, RasSrv);
      dwResult = RasAdminGetUserAccountServer(NULL, &lpszServerName[0], &szUserAccountServer[0]);
      }

// API Called:     RasAdmimUserGetInfo
// Parameters:
//  IN WCHAR * lpszUserAccountServer
// IN WCHAR * lpszUserName
// OUT RAS_USER_0 * ppRasUser0

  if (dwResult == ERROR_SUCCESS)
     {
     dwResult = RasAdminUserGetInfo(szUserAccountServer, pRasPort1->rasport0.wszUserName, ppRasUser0);
     if (dwResult == ERROR_SUCCESS)
 {
  PrintPriv();
 }
     else
{
sprintf(szBuf,"RasAdminUserGetInfo     %d\n",dwResult);
WriteFile(ErrLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
dwResult = RasAdminGetErrorString((UINT) dwResult, lpszString, 512);
if (dwResult == ERROR_SUCCESS)
    {
    sprintf(szBuf, "ERROR  RasAdminUserGetInfo      %ws\n",lpszString);
    WriteFile(ErrLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
    }
else
    PrintGetStringError(dwResult);
}
      if (LoopPriv > 3)
{LoopPriv =0; Dialin = 1;}
      if (LoopPriv < 4)
 {
  if (LoopPriv == 3) wcscpy(ppRasUser0->szPhoneNumber, TEXT("99999999"));
  if (LoopPriv == 0) {ppRasUser0->bfPrivilege =  Dialin;Dialin = Dialin * 2;LoopPriv++;}
  else {ppRasUser0->bfPrivilege = (RASPRIV_DialinPrivilege | Dialin  );
Dialin = Dialin * 2 ;
LoopPriv++;}
 }
// API Called:     RasAdmimUserSetInfo
// Parameters:
//  IN WCHAR * lpszUserAccountServer
// IN WCHAR * lpszUserName
// IN RAS_USER_0 * ppRasUser0

     dwResult = RasAdminUserSetInfo(szUserAccountServer, pRasPort1->rasport0.wszUserName,ppRasUser0);
     if (dwResult != ERROR_SUCCESS)
{
sprintf(szBuf,"ERROR: RasAdminUserSetInfo     %d\n",dwResult);
WriteFile(ErrLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
dwResult = RasAdminGetErrorString((UINT) dwResult, lpszString, 512);
if (dwResult == ERROR_SUCCESS)
    {
     sprintf(szBuf, "ERROR  RasAdminUserSetInfo      %ws\n",lpszString);
     WriteFile(ErrLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
    }
 else
    PrintGetStringError(dwResult);
 }
// API Called:     RasAdmimUserSetInfo
// Parameters:
//  IN WCHAR * lpszUserAccountServer
// IN WCHAR * lpszUserName
// IN RAS_USER_0 * ppRasUser0
     if (LoopPriv == 0)
     {
      ppRasUser0->bfPrivilege = (ppRasUser0->bfPrivilege | RASPRIV_DialinPrivilege);
      dwResult = RasAdminUserSetInfo(szUserAccountServer, pRasPort1->rasport0.wszUserName, ppRasUser0);
if (dwResult == ERROR_SUCCESS)
   {
   PrintPriv();
   }
else
   {
   sprintf(szBuf,"RasAdminUserSetInfo     %d\n",dwResult);
   WriteFile(ErrLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
   dwResult = RasAdminGetErrorString((UINT) dwResult, lpszString, 512);
   if (dwResult == ERROR_SUCCESS)
      {
      sprintf(szBuf, "ERROR  RasAdminUserSetInfo      %ws\n",lpszString);
      WriteFile(ErrLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
      }
   else
      PrintGetStringError(dwResult);
   }
     }
// API Called:     RasAdmimUserGetInfo
// Parameters:
//  IN WCHAR * lpszUserAccountServer
// IN WCHAR * lpszUserName
// OUT RAS_USER_0 * ppRasUser0
 dwResult = RasAdminUserGetInfo(szUserAccountServer, pRasPort1->rasport0.wszUserName, ppRasUser0);
 if (dwResult != ERROR_SUCCESS)
     {
    sprintf(szBuf,"ERROR: RasAdminUserGetInfo     %d\n",dwResult);
    WriteFile(ErrLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
    dwResult = RasAdminGetErrorString((UINT) dwResult, lpszString, 512);
    if (dwResult == ERROR_SUCCESS)
       {
       sprintf(szBuf, "ERROR  RasAdminUserGetInfo      %ws\n",lpszString);
       WriteFile(ErrLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
       }
    else
      PrintGetStringError(dwResult);
    }
     }
  else
    {
    DbgPrint("RasAdminGetUserAccountServer     %d\n",dwResult);
    dwResult = RasAdminGetErrorString((UINT)dwResult, lpszString, 512);
     if (dwResult == ERROR_SUCCESS)
  {
  sprintf(szBuf, "ERROR  RasAdminUserAccountServer      %ws\n",lpszString);
  WriteFile(ErrLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
  }
      else
 PrintGetStringError(dwResult);
    }

}

//To print port information, statistics and rasparams on debugger.

VOID Debug_Print(RAS_PORT_1 *            pRasPort1,
    RAS_PORT_STATISTICS *   pRasStats,
    RAS_PARAMETERS *        pRasParams
)
{
TCHAR *Media_Name[] = {TEXT("MEDIA_UNKNOWN"),TEXT( "MEDIA_SERIAL"),
  TEXT("MEDIA_RAS10_SERIAL"),TEXT( "MEDIA_ISDN")};
TCHAR *Flag_Name[] = {TEXT("USER_AUTHENTICATED"),TEXT( "MESSENGER_PRESENT"),
 TEXT("PPP_CLIENT"),TEXT("GATEWAY_ACTIVE"),TEXT("REMOTE_LISTEN"),TEXT("PORT_MULTILINKED")};
TCHAR *Line_Condition[]={TEXT("RAS_PORT_NON_OPERATIONAL"),TEXT("RAS_PORT_DISCONNECTED"),
  TEXT("RAS_PORT_CALLING_BACK"),TEXT("RAS_PORT_LISTENING"),TEXT("RAS_PORT_AUTHENTICATING"),
  TEXT("RAS_PORT_AUTHENTICATED"),TEXT("RAS_PORT_INITIALIZING")};
TCHAR *Hdw_Error[]={TEXT("RAS_MODEM_OPERATIONAL"),TEXT("RAS_MODEM_NOT_RESPONDING"),TEXT("RAS_MODEM_HARDWARE_FAILURE"),
  TEXT("RAS_MODEM_INCORRECT_RESPONSE"),TEXT("RAS_MODEM_UNKNOWN")};
INT  i=0;
INT temp = 0;
INT temp1 =0;
DWORD dwsize=0;
CHAR szBuf[100];
DWORD dwBytesWritten=0;

    DbgPrint( "pRasPort1->rasport0.wszPortName=%ws\n",
pRasPort1->rasport0.wszPortName );
    DbgPrint( "pRasPort1->rasport0.wszDeviceType=%ws\n",
    pRasPort1->rasport0.wszDeviceType );
    DbgPrint( "pRasPort1->rasport0.wszDeviceName=%ws\n",
    pRasPort1->rasport0.wszDeviceName );
    DbgPrint( "pRasPort1->rasport0.wszMediaName=%ws\n",
pRasPort1->rasport0.wszMediaName );
    DbgPrint( "pRasPort1->rasport0.reserved=%d\n",
    pRasPort1->rasport0.reserved );
    DbgPrint("                 %s\n",Media_Name[pRasPort1->rasport0.reserved]);
    DbgPrint( "pRasPort1->rasport0.Flags=%d\n",
pRasPort1->rasport0.Flags );
//printing Flags
    temp1 = 1;
    for (i=0; i <= Number_Of_Flags; i++)
 {
 temp = (pRasPort1->rasport0.Flags & temp1);
 if (temp)
     {
       DbgPrint("Flag       %s   \n",Flag_Name[i]);
     }
  temp1 = temp1*2;
 }
    DbgPrint( "pRasPort1->rasport0.wszUserName=%ws\n",
pRasPort1->rasport0.wszUserName );
    DbgPrint( "pRasPort1->rasport0.wszComputer=%ws\n",
pRasPort1->rasport0.wszComputer );
    DbgPrint( "pRasPort1->rasport0.dwStartSessionTime;=%d\n",
    pRasPort1->rasport0.dwStartSessionTime );
    sprintf(szBuf,"Formatted session start time/date :  %s\n",ctime(&pRasPort1->rasport0.dwStartSessionTime));
    DbgPrint("%s",szBuf);
    DbgPrint( "pRasPort1->rasport0.wszLogonDomain=%ws\n",
    pRasPort1->rasport0.wszLogonDomain );
    DbgPrint( "pRasPort1->rasport0.fAdvancedServer=%d\n",
    pRasPort1->rasport0.fAdvancedServer );
    DbgPrint( "pRasPort1->LineCondition=%d\n", pRasPort1->LineCondition );
    DbgPrint("                     %s\n",Line_Condition[pRasPort1->LineCondition-1]);
    DbgPrint( "pRasPort1->HardwareCondition=%d\n",pRasPort1->HardwareCondition);
    DbgPrint("                     %s\n",Hdw_Error[pRasPort1->HardwareCondition-1]);
    DbgPrint( "pRasPort1->LineSpeed=%d   bits/sec\n",pRasPort1->LineSpeed);
    DbgPrint( "pRasPort1->NumStatistics=%d\n",pRasPort1->NumStatistics);
    DbgPrint( "pRasPort1->NumMediaParms=%d\n",pRasPort1->NumMediaParms);
    DbgPrint( "pRasPort1->SizeMediaParms=%d\n",pRasPort1->SizeMediaParms);
    DbgPrint( "\npRasPort1->ProjResult.nbf.dwError=%d\n",
pRasPort1->ProjResult.nbf.dwError );
    if (pRasPort1->ProjResult.nbf.dwError == NO_ERROR)
       {
DbgPrint( "pRasPort1->ProjResult.nbf.dwNetBiosError=%d\n",
    pRasPort1->ProjResult.nbf.dwNetBiosError );
DbgPrint( "pRasPort1->ProjResult.nbf.szName=%s\n",
    pRasPort1->ProjResult.nbf.szName );
DbgPrint( "pRasPort1->ProjResult.nbf.wszWksta=%ws\n",
    pRasPort1->ProjResult.nbf.wszWksta );
      }
   DbgPrint( "pRasPort1->ProjResult.ip.dwError=%d\n",
pRasPort1->ProjResult.ip.dwError );
   if (pRasPort1->ProjResult.ip.dwError == NO_ERROR)
      {
DbgPrint( "pRasPort1->ProjResult.ip.wszAddress=%ws\n",
pRasPort1->ProjResult.ip.wszAddress );
       }
    DbgPrint( "pRasPort1->ProjResult.ipx.dwError=%d\n",
pRasPort1->ProjResult.ipx.dwError );
    if (pRasPort1->ProjResult.ipx.dwError == NO_ERROR)
      {
     DbgPrint( "pRasPort1->ProjResult.ipx.wszAddress=%ws\n",
pRasPort1->ProjResult.ipx.wszAddress );
      }
    DbgPrint( "pRasPort1->ProjResult.at.dwError=%d\n",
pRasPort1->ProjResult.at.dwError );
    if (pRasPort1->ProjResult.at.dwError == NO_ERROR)
      {
      DbgPrint( "pRasPort1->ProjResult.at.wszAddress=%ws\n",
pRasPort1->ProjResult.at.wszAddress );
       }
    DbgPrint( "\ndwBytesXmited=%d\n", pRasStats->dwBytesXmited );
    DbgPrint( "dwBytesRcved=%d\n", pRasStats->dwBytesRcved );
    DbgPrint( "dwFramesXmited=%d\n", pRasStats->dwFramesXmited );
    DbgPrint( "dwFramesRcved=%d\n", pRasStats->dwFramesRcved);
    DbgPrint( "dwCrcErr=%d\n", pRasStats->dwCrcErr);
    DbgPrint( "dwTimeoutErr=%d\n", pRasStats->dwTimeoutErr);
    DbgPrint( "dwAlignmentErr=%d\n", pRasStats->dwAlignmentErr);
    DbgPrint( "dwHardwareOverrunErr=%d\n", pRasStats->dwHardwareOverrunErr);
    DbgPrint( "dwFramingErr=%d\n", pRasStats->dwFramingErr);
    DbgPrint( "dwBufferOverrunErr=%d\n", pRasStats->dwBufferOverrunErr);
    DbgPrint( "dwBytesXmitedUncompressed=%d\n",
    pRasStats->dwBytesXmitedUncompressed);
    DbgPrint( "dwBytesRcvedUncompressed=%d\n",
    pRasStats->dwBytesRcvedUncompressed);
    DbgPrint( "dwBytesXmitedCompressed=%d\n",
    pRasStats->dwBytesXmitedCompressed);
    DbgPrint( "dwBytesRcvedCompressed=%d\n",
    pRasStats->dwBytesRcvedCompressed);
    for (i=0; i<pRasPort1->NumMediaParms;i++)
      {
      DbgPrint( "pRasParams->P_Key= %s\n",pRasParams[i].P_Key);
      DbgPrint( "pRasParams->P_Type= %d\n",pRasParams[i].P_Type);
      if (pRasParams[i].P_Type == 1)
  {
  DbgPrint("pRasParams->P_Value.Length=%d\n",pRasParams[i].P_Value.String.Length);
  sprintf(szBuf,"                                                                \n");
  strncpy(szBuf, pRasParams[i].P_Value.String.Data, pRasParams[i].P_Value.String.Length);
  DbgPrint("pRasParams->P_Value.Data= %s\n",szBuf);
  }
      if (pRasParams[i].P_Type == 0)
 {
 DbgPrint("pRasParams->P_Value.Number=%d\n", pRasParams[i].P_Value.Number);
 }
      DbgPrint( "pRasParams->P_Attributes= %d\n",pRasParams[i].P_Attributes);
      }
}

//Function to print Port Info, statistics and rasparam to a file
VOID File_Print(RAS_PORT_1 *            pRasPort1,
    RAS_PORT_STATISTICS *   pRasStats,
    RAS_PARAMETERS *        pRasParams
)
{
char *Media_Name[] = {"MEDIA_UNKNOWN", "MEDIA_SERIAL",
  "MEDIA_RAS10_SERIAL", "MEDIA_ISDN"};
char *Flag_Name[] = {"USER_AUTHENTICATED", "MESSENGER_PRESENT",
 "PPP_CLIENT","GATEWAY_ACTIVE","REMOTE_LISTEN","PORT_MULTILINKED"};
char *Line_Condition[]={"RAS_PORT_NON_OPERATIONAL","RAS_PORT_DISCONNECTED",
  "RAS_PORT_CALLING_BACK","RAS_PORT_LISTENING","RAS_PORT_AUTHENTICATING",
  "RAS_PORT_AUTHENTICATED","RAS_PORT_INITIALIZING"};
char *Hdw_Error[]={"RAS_MODEM_OPERATIONAL","RAS_MODEM_NOT_RESPONDING","RAS_MODEM_HARDWARE_FAILURE",
  "RAS_MODEM_INCORRECT_RESPONSE","RAS_MODEM_UNKNOWN"};
INT  i=0;
INT temp = 0;
INT temp1 =0;
DWORD dwsize=0;
CHAR szBuf[50];
CHAR buf[120];
DWORD dwBytesWritten=0;
    sprintf(buf, "pRasPort1->rasport0.wszPortName=%ws\n",
pRasPort1->rasport0.wszPortName );
    WriteFile(LogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf,"pRasPort1->rasport0.wszDeviceType=%ws\n",
    pRasPort1->rasport0.wszDeviceType );
    WriteFile(LogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf,"pRasPort1->rasport0.wszDeviceName=%ws\n",
    pRasPort1->rasport0.wszDeviceName );
    WriteFile(LogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "pRasPort1->rasport0.wszMediaName=%ws\n",
pRasPort1->rasport0.wszMediaName );
    WriteFile(LogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf,"pRasPort1->rasport0.reserved=%d\n",
    pRasPort1->rasport0.reserved );
    WriteFile(LogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf,"                 %s\n",Media_Name[pRasPort1->rasport0.reserved]);
    WriteFile(LogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "pRasPort1->rasport0.Flags=%d\n",
pRasPort1->rasport0.Flags );
    WriteFile(LogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
//printing Flags
    temp1 = 1;
    for (i=0; i <= Number_Of_Flags; i++)
 {
 temp = (pRasPort1->rasport0.Flags & temp1);
 if (temp)
     {
       sprintf(buf,"Flag       %s   \n",Flag_Name[i]);
     WriteFile(LogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
     }
  temp1 = temp1*2;
 }
    sprintf(buf, "pRasPort1->rasport0.wszUserName=%ws\n",
pRasPort1->rasport0.wszUserName );
    WriteFile(LogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "pRasPort1->rasport0.wszComputer=%ws\n",
pRasPort1->rasport0.wszComputer );
    WriteFile(LogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "pRasPort1->rasport0.dwStartSessionTime;=%d\n",
    pRasPort1->rasport0.dwStartSessionTime );
    WriteFile(LogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf,"Formatted session start time/date :  %s\n",ctime(&pRasPort1->rasport0.dwStartSessionTime));
    WriteFile(LogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "pRasPort1->rasport0.wszLogonDomain=%ws\n",
    pRasPort1->rasport0.wszLogonDomain );
    WriteFile(LogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "pRasPort1->rasport0.fAdvancedServer=%d\n",
    pRasPort1->rasport0.fAdvancedServer );
    WriteFile(LogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "pRasPort1->LineCondition=%d\n", pRasPort1->LineCondition );
    WriteFile(LogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf,"                     %s\n",Line_Condition[pRasPort1->LineCondition-1]);
    WriteFile(LogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "pRasPort1->HardwareCondition=%d\n",pRasPort1->HardwareCondition);
    WriteFile(LogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf,"                     %s\n",Hdw_Error[pRasPort1->HardwareCondition-1]);
    WriteFile(LogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "pRasPort1->LineSpeed=%d   bits/sec\n",pRasPort1->LineSpeed);
    WriteFile(LogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "pRasPort1->NumStatistics=%d\n",pRasPort1->NumStatistics);
    WriteFile(LogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "pRasPort1->NumMediaParms=%d\n",pRasPort1->NumMediaParms);
    WriteFile(LogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "pRasPort1->SizeMediaParms=%d\n",pRasPort1->SizeMediaParms);
    WriteFile(LogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "\npRasPort1->ProjResult.nbf.dwError=%d\n",
pRasPort1->ProjResult.nbf.dwError );
    if (pRasPort1->ProjResult.nbf.dwError == NO_ERROR)
       {
sprintf(buf, "pRasPort1->ProjResult.nbf.dwNetBiosError=%d\n",
    pRasPort1->ProjResult.nbf.dwNetBiosError );
    WriteFile(LogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
sprintf(buf, "pRasPort1->ProjResult.nbf.szName=%s\n",
    pRasPort1->ProjResult.nbf.szName );
    WriteFile(LogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
sprintf(buf, "pRasPort1->ProjResult.nbf.wszWksta=%ws\n",
    pRasPort1->ProjResult.nbf.wszWksta );
    WriteFile(LogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
      }
   sprintf(buf, "pRasPort1->ProjResult.ip.dwError=%d\n",
pRasPort1->ProjResult.ip.dwError );
   WriteFile(LogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   if (pRasPort1->ProjResult.ip.dwError == NO_ERROR)
      {
sprintf(buf, "pRasPort1->ProjResult.ip.wszAddress=%ws\n",
pRasPort1->ProjResult.ip.wszAddress );
    WriteFile(LogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
       }
    sprintf(buf, "pRasPort1->ProjResult.ipx.dwError=%d\n",
pRasPort1->ProjResult.ipx.dwError );
    WriteFile(LogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    if (pRasPort1->ProjResult.ipx.dwError == NO_ERROR)
      {
     sprintf(buf, "pRasPort1->ProjResult.ipx.wszAddress=%ws\n",
pRasPort1->ProjResult.ipx.wszAddress );
    WriteFile(LogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
      }
    sprintf(buf, "pRasPort1->ProjResult.at.dwError=%d\n",
pRasPort1->ProjResult.at.dwError );
    WriteFile(LogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    if (pRasPort1->ProjResult.at.dwError == NO_ERROR)
      {
      sprintf(buf, "pRasPort1->ProjResult.at.wszAddress=%ws\n",
pRasPort1->ProjResult.at.wszAddress );
       }
    sprintf(buf, "\ndwBytesXmited=%d\n", pRasStats->dwBytesXmited );
    WriteFile(LogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "dwBytesRcved=%d\n", pRasStats->dwBytesRcved );
    WriteFile(LogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "dwFramesXmited=%d\n", pRasStats->dwFramesXmited );
    WriteFile(LogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "dwFramesRcved=%d\n", pRasStats->dwFramesRcved);
    WriteFile(LogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "dwCrcErr=%d\n", pRasStats->dwCrcErr);
    WriteFile(LogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "dwTimeoutErr=%d\n", pRasStats->dwTimeoutErr);
    WriteFile(LogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "dwAlignmentErr=%d\n", pRasStats->dwAlignmentErr);
    WriteFile(LogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "dwHardwareOverrunErr=%d\n", pRasStats->dwHardwareOverrunErr);
    WriteFile(LogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "dwFramingErr=%d\n", pRasStats->dwFramingErr);
    WriteFile(LogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "dwBufferOverrunErr=%d\n", pRasStats->dwBufferOverrunErr);
    WriteFile(LogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "dwBytesXmitedUncompressed=%d\n",
    pRasStats->dwBytesXmitedUncompressed);
    WriteFile(LogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "dwBytesRcvedUncompressed=%d\n",
    pRasStats->dwBytesRcvedUncompressed);
    WriteFile(LogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "dwBytesXmitedCompressed=%d\n",
    pRasStats->dwBytesXmitedCompressed);
    WriteFile(LogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "dwBytesRcvedCompressed=%d\n",
    pRasStats->dwBytesRcvedCompressed);
    WriteFile(LogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    for (i=0; i<pRasPort1->NumMediaParms;i++)
      {
      sprintf(buf, "pRasParams->P_Key= %s\n",pRasParams[i].P_Key);
      WriteFile(LogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
      sprintf(buf, "pRasParams->P_Type= %d\n",pRasParams[i].P_Type);
      WriteFile(LogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
      if (pRasParams[i].P_Type == 1)
  {
  sprintf(buf,"pRasParams->P_Value.Length=%d\n",pRasParams[i].P_Value.String.Length);
WriteFile(LogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
  sprintf(szBuf,"                                                                \n");
  strncpy(szBuf, pRasParams[i].P_Value.String.Data, pRasParams[i].P_Value.String.Length);
  sprintf(buf,"pRasParams->P_Value.Data= %s\n",szBuf);

WriteFile(LogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
  }
      if (pRasParams[i].P_Type == 0)
 {
 sprintf(buf,"pRasParams->P_Value.Number=%d\n", pRasParams[i].P_Value.Number);
      WriteFile(LogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
 }
      sprintf(buf, "pRasParams->P_Attributes= %d\n",pRasParams[i].P_Attributes);
      WriteFile(LogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
      }
}

//Function to  clear port statistics.

VOID PrintClearStats(WCHAR * RasSrv, WCHAR *wszPortName)
{
RAS_PORT_1             RasPort1;
RAS_PORT_STATISTICS    RasStats;
RAS_PORT_STATISTICS    NewRasStats;
RAS_PARAMETERS *        pRasParams=NULL;
DWORD dwBytesWritten=0;
CHAR szBuf[120];
CHAR buf[120];
WCHAR Buf[512];
WCHAR *lpszString;
lpszString=&Buf[0];

// API Called:     RasAdmimPortGetInfo
// Parameters:
//  IN WCHAR * lpszUserAccountServer
// IN WCHAR * lpszPortName
// OUT RAS_PORT_1 * pRasPort1
// OUT RAS_PORT_STATISTICS * pRasPortStatistics
// OUT RAS_PARAMETERS ** ppRasParams

// ppRasParams should be freed by calling RasAdminFreeBuffer

Status = RasAdminPortGetInfo ( RasSrv, wszPortName, &RasPort1, &RasStats, &pRasParams);
     if (Status != ERROR_SUCCESS)
  {
  sprintf(szBuf, "ERROR RasAdminGetInfo   %d\n",Status);
  WriteFile(cLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
  dwResult = RasAdminGetErrorString((UINT) Status, lpszString, 512);
 if (dwResult == ERROR_SUCCESS)
     {
     DbgPrint("ERROR  RasAdminPortGetInfo      %ws\n",lpszString);
     sprintf(szBuf, "ERROR  RasAdminPortGetInfo      %ws\n",lpszString);
     WriteFile(cLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
     }
 else
     PrintGetStringError(dwResult);
}
     else
{

// API Called:     RasAdmimPortClearStatistics
// Parameters:
//  IN WCHAR * lpszServerName
// IN WCHAR * lpszPortName

Status = RasAdminPortClearStatistics(RasSrv, wszPortName);
if (Status != ERROR_SUCCESS)
  {
  sprintf(szBuf, "ERROR RasAdminPortClearStatistics   %d\n",Status);
  WriteFile(cLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
  dwResult = RasAdminGetErrorString((UINT) Status, lpszString, 512);
 if (dwResult == ERROR_SUCCESS)
     {
     DbgPrint("ERROR  RasAdminPortClearStatistics      %ws\n",lpszString);
     sprintf(szBuf, "ERROR  RasAdminPortClearStatistics      %ws\n",lpszString);
     WriteFile(cLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
     }
 else
     PrintGetStringError(dwResult);
  }
       else
  {
  if (pRasParams != NULL)
RasAdminFreeBuffer(pRasParams);

  Status = RasAdminPortGetInfo ( RasSrv, wszPortName, &RasPort1, &NewRasStats,&pRasParams);
  sprintf(buf,"Port name is   %ws\n",wszPortName);
  printf("%s",buf);
  WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
  sprintf(buf, "\ndwBytesXmited=%d                    new= %d\n", RasStats.dwBytesXmited,NewRasStats.dwBytesXmited );
  printf("%s",buf);
  WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
  sprintf(buf, "dwBytesRcved=%d                       new= %d\n", RasStats.dwBytesRcved,NewRasStats.dwBytesRcved );
  printf("%s",buf);
  WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
  sprintf(buf, "dwFramesXmited=%d                   new= %d\n", RasStats.dwFramesXmited, NewRasStats.dwFramesXmited );
  printf("%s",buf);
  WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
  sprintf(buf, "dwFramesRcved=%d                    new= %d\n", RasStats.dwFramesRcved, NewRasStats.dwFramesRcved);
  printf("%s",buf);
  WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
  sprintf(buf, "dwCrcErr=%d                              new= %d\n", RasStats.dwCrcErr, NewRasStats.dwCrcErr);
  printf("%s",buf);
  WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
  sprintf(buf, "dwTimeoutErr=%d                       new= %d\n", RasStats.dwTimeoutErr, NewRasStats.dwTimeoutErr);
  printf("%s",buf);
  WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
  sprintf(buf, "dwAlignmentErr=%d                  new= %d\n", RasStats.dwAlignmentErr, NewRasStats.dwAlignmentErr);
  printf("%s",buf);
  WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
  sprintf(buf, "dwHardwareOverrunErr=%d        new= %d\n", RasStats.dwHardwareOverrunErr, NewRasStats.dwHardwareOverrunErr);
  printf("%s",buf);
  WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
  sprintf(buf, "dwFramingErr=%d                     new= %d\n", RasStats.dwFramingErr, NewRasStats.dwFramingErr);
  printf("%s",buf);
  WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
  sprintf(buf, "dwBufferOverrunErr=%d             new= %d\n", RasStats.dwBufferOverrunErr, NewRasStats.dwBufferOverrunErr);
  printf("%s",buf);
  WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
  sprintf(buf, "dwBytesXmitedUncompressed=%d   new= %d\n",
    RasStats.dwBytesXmitedUncompressed, NewRasStats.dwBytesXmitedUncompressed);
  printf("%s",buf);
  WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
  sprintf(buf, "dwBytesRcvedUncompressed=%d   new= %d\n",
    RasStats.dwBytesRcvedUncompressed, NewRasStats.dwBytesRcvedUncompressed);
  printf("%s",buf);
  WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
  sprintf(buf, "dwBytesXmitedCompressed=%d     new= %d\n",
    RasStats.dwBytesXmitedCompressed, NewRasStats.dwBytesXmitedCompressed);
  printf("%s",buf);
  WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
  sprintf(buf, "dwBytesRcvedCompressed=%d      new= %d\n",
    RasStats.dwBytesRcvedCompressed, NewRasStats.dwBytesRcvedCompressed);
  printf("%s",buf);
  WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
}
     if (pRasParams != NULL)
RasAdminFreeBuffer(pRasParams);
   }
}

//Function to reset Port statistic

DWORD WINAPI RasAdminClearDisc(LPVOID param)
{
CHAR option= ' ';
WORD i=0;
DWORD Status=0;
WORD Entries=0;
DWORD PortOption = 0;
RAS_PORT_0 *            pRasPort0=NULL;
DWORD dwBytesWritten=0;
CHAR szBuf[120];
WCHAR Buf[512];
WCHAR *lpszString;
INT k=0;
lpszString=&Buf[0];
while (!Quit)
  {
    Status = RasAdminPortEnum( RasSrv, &pRasPort0, &Entries);
    if (Status != ERROR_SUCCESS)
{
sprintf(szBuf, "ERROR RasAdminPortEnum   %d\n",Status);
dwResult = RasAdminGetErrorString((UINT) Status, lpszString, 512);
if (dwResult == ERROR_SUCCESS)
    {
    sprintf(szBuf,"ERROR  RasAdminPortEnum      %ws\n",lpszString);
    WriteFile(cLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
    sprintf(szBuf, "ERROR  RasAdminPortEnum      %ws\n",lpszString);
    WriteFile(cLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
    }
 else
    PrintGetStringError(dwResult);
}
    else
{
// Print the configured ports on the screen and ask user to choose the port to reset the statistic or disconnect.
// Right now, it lists all the ports  and lets the user pick up the port. A good idea is to let user only delete
// connected ports.
printf("Select to C - Clear Statistic, D - Disconnect Port Q - Quit\n");
do
     {option=_getch();
       option =toupper(option);
      printf("%c\n",option);}   while (((option) != 'C') && ((option) != 'D') && ((option) != 'Q'));
if (option == 'Q')
   {
  Quit = TRUE;
     }
if (option == 'C')
   {
// Print all the ports and then let user  choose the port to clear statistics
 i = 0;
 while (i < Entries)
      {
      while ((i < Entries) && (k <=5))
  {
//Better to check if the port is connected here
  printf(" %d=%ws",i,pRasPort0[i].wszPortName);
  i++;
  k++;
  }
      printf("\n");
      k=1;
      }
  printf("Enter your option 0 - %d  ",Entries-1);
  while ((scanf("%i",&PortOption) < 0) && (scanf("%i",&PortOption)> Entries-1))
       {};

  PrintClearStats(RasSrv, pRasPort0[PortOption].wszPortName);
  }
if (option == 'D')
   {
// Print all the ports and then let user  choose the port to clear statistics
 i = 0;
 while (i < Entries)
      {
      while ((i < Entries) && (k <=5))
  {
//better to check if the port is connected here
  printf(" %d=%ws",i,pRasPort0[i].wszPortName);
  i++;
  k++;
  }
      printf("\n");
      k=1;
      }
  printf("Enter your option 0 - %d ",Entries-1);
  while ((scanf("%i",&PortOption) < 0) && (scanf("%i",&PortOption) > Entries-1))
       {};

// API Called:     RasAdmimPortDisconnect
// Parameters:
//  IN WCHAR * lpszServerName
// IN WCHAR * lpszPortName

  Status = RasAdminPortDisconnect(RasSrv, pRasPort0[PortOption].wszPortName);
    if (Status != ERROR_SUCCESS)
{
sprintf(szBuf, "ERROR RasAdminPortDisconnect   %d\n",Status);
dwResult = RasAdminGetErrorString((UINT) Status, lpszString, 512);
if (dwResult == ERROR_SUCCESS)
    {
    sprintf(szBuf,"ERROR  RasAdminPortDisconnect      %ws\n",lpszString);
    WriteFile(cLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
    sprintf(szBuf, "ERROR  RasAdminPortDisconnect      %ws\n",lpszString);
    WriteFile(cLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
    }
 else
    PrintGetStringError(dwResult);
}
    else
{
printf("Port Disconnected successfully\n");
}
     }
   }
 if (pRasPort0 != NULL)
     RasAdminFreeBuffer(pRasPort0);
  }
return(0);
}

void _cdecl main(int argc, char **argv)
{
INT LoopPriv=0;
INT Error = 0;
CHAR szBuf[120];
DWORD dwBytesWritten=0;
DWORD Dialin = 1;
WCHAR Buf[512];
WCHAR *lpszString;
WORD Entries;
WORD i=0;
DWORD id=0;
HANDLE ConnectDiscThread;
lpszString=&Buf[0];
pRasPort1 = &RasPort1;
pRasStats = &RasStats;
pRasServer0 = &RasServer0;
ppRasUser0 = &RasUser0;
if (argc > 1)
    {
    mbstowcs(RasSrv, argv[1], 32) ;
    }
else
    {
    printf ("to start type Admapit \\\\RasServerName\n");
    exit(0);
    }
Init1();
Init2();
Error=ServerGetInfo(RasSrv);
if (Error)
    {
    printf ("Ras Server is not started on the system");
    exit(0);
    }
    ConnectDiscThread = CreateThread (NULL,
 0,
&RasAdminClearDisc,
       (LPVOID) id,
 0,
       &id);

while (!Quit)
  {
// API Called:     RasAdmimPortEnum
// Parameters:
//  IN WCHAR * lpszServerName
// OUT PRAS_PORT_0 *  ppRasPort0
// OUT WORD *  pcEntriesRead

// Free up ppRasPort0 by calling RasAdminFreeBuffer API

    Status = RasAdminPortEnum( RasSrv, &pRasPort0, &Entries);
    if (Status != ERROR_SUCCESS)
{
sprintf(szBuf, "ERROR RasAdminPortEnum   %d\n",Status);
      WriteFile(LogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
dwResult = RasAdminGetErrorString((UINT) Status, lpszString, 512);
if (dwResult == ERROR_SUCCESS)
    {
    sprintf(szBuf,"ERROR  RasAdminPortEnum      %ws\n",lpszString);
    WriteFile(LogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
    sprintf(szBuf, "ERROR  RasAdminPortEnum      %ws\n",lpszString);
    WriteFile(LogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
    }
 else
    PrintGetStringError(dwResult);
}
    else
{
//A good idea is to check for port Authentication before making this call
for (i=0; i<Entries; i++)
     {
// API Called:     RasAdmimPortGetInfo
// Parameters:
//  IN WCHAR * lpszUserAccountServer
// IN WCHAR * lpszPortName
// OUT RAS_PORT_1 * pRasPort1
// OUT RAS_PORT_STATISTICS * pRasPortStatistics
// OUT RAS_PARAMETERS ** ppRasParams

// ppRasParams should be freed by calling RasAdminFreeBuffer

     Status = RasAdminPortGetInfo ( RasSrv, pRasPort0[i].wszPortName, pRasPort1, pRasStats, &pRasParams);
     if (Status != ERROR_SUCCESS)
{
 sprintf(szBuf, "ERROR RasAdminGetInfo   %d\n",Status);
 WriteFile(LogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
 dwResult = RasAdminGetErrorString((UINT) Status, lpszString, 512);
 if (dwResult == ERROR_SUCCESS)
     {
     DbgPrint("ERROR  RasAdminPortGetInfo      %ws\n",lpszString);
     sprintf(szBuf, "ERROR  RasAdminPortGetInfo      %ws\n",lpszString);
     WriteFile(LogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
     }
 else
    PrintGetStringError(dwResult);
}
     else
{
 File_Print(pRasPort1, pRasStats, pRasParams);
     Debug_Print(pRasPort1, pRasStats, pRasParams);
}
//Call user privilege API only if the user is connected, or you provide the user name. If you pass NULL for all the paramters to user priv API's then the API
// will hit an Assert
   if ( pRasPort1->LineCondition == RAS_PORT_AUTHENTICATED)
     UserPrivilege();
     if (pRasParams != NULL) {
    RasAdminFreeBuffer(pRasParams);
    }
     }//End Loop
}
     CloseHandle(LogFile);
     if (pRasPort0 != NULL)
RasAdminFreeBuffer(pRasPort0);
     Sleep(sleeptime);
     Init1();
     ServerGetInfo(RasSrv);
  }

  WaitForSingleObject(ConnectDiscThread,INFINITE);
CloseHandle(LogFile);
CloseHandle(cLogFile);
CloseHandle(ErrLogFile);

} // Main End


README.TXT

RPC Sample Directory Tree

The RPC directory contains several sample RPC distributed applications.

Directory        Description

CALLBACK         Fibonacci number implemented as static callback.
CLUUID           Manager entry point vector sample.
DATA\INOUT       Directional attributes.
DATA\DUNION      Discriminated union.
DATA\XMIT        Transmit as attribute.
DATA\REPAS       Represent as attribute.
DICT             Client-server transaction processing.
DOCTOR           RPC version of Eliza-like therapist program.
DYNEPT           Dynamic Endpoint demonstration.
HANDLES\AUTO     Auto handles.
HANDLES\USRDEF   User-defined handles.
HANDLES\CXHNDL   Context handles.
HELLO            Print a string "hello, world" on the server.
INTEROP          Demonstrates OSF DCE RPC and MS RPC interoperbility.
MANDEL           Mandelbrot set Windows sample.
NS\CDS           Gateway protocol for DCE CDS nane service.
NS\NHELLO        Classic "hello, world" sample using name service.
OBJECT\CALLAS    Object sample illustrating call_as.
OBJECT\OHELLO    Object sample illustrating a custom interface.
PICKLE\PICKLP    Procedure serialization (a.k.a. pickling).
PICKLE\PICKLT    Data type serialization (a.k.a. pickling).
PIPES            Use of the [pipe] attribute.
RPCSSM           RpcSs/m memory package sample.
RPCSVC           A Windows NT system service which uses RPC.
STROUT           Dynamically allocated strings with [out] attribute.
WINTYP           A client-server bitmap processor.
WHELLO           Windows version of "hello, world" sample.
YIELD            Demonstrate yield capability for MS Windows 3.x.

The source files within each directory (with the exception
of the Windows samples) follow the naming convention:

README.TXT     Instructions to build, run the sample
<dirname>.IDL  Interface definition language file
<dirname>.ACF  Application configuration file
<dirname>C.C   Client main program
<dirname>S.C   Server main program
<dirname>P.C   Remote procedures
MAKEFILE       Nmake file to build Windows NT or Windows 95 version
MAKEFILE.DOS   Nmake file to build MS-DOS version
MAKEFILE.WIN   Nmake file to build Win 3.x version

The Microsoft Windows sample programs use several additional
files and do not conform to this naming convention. See the
README.TXT file in each directory for specific information
about the files.

NOTE for building client applications on MS-DOS / WIN 3.x
---------------------------------------------------------

You will first need to install the Microsoft Visual C/C++ version 1.5
development environment and the 16-bit RPC SDK on your Windows NT or
Windows 95 machine.

There are two different versions of RPC.H for MS-DOS and WIN 3.x.
Please add the proper path to your INCLUDE environment variable.


README.TXT

CALLBACK

The CALLBACK program demonstrates the use of the callback attribute to
allow a server application to execute code on a client application. The
distributed program generates a Fibonacci number with a static callback.

The directory samples\rpc\callback contains the following files for
building the sample distributed application CALLBACK:

File          Description

README.TXT      Readme file for the CALLBACK sample
CALL.IDL        Interface definition language file
CALL.ACF        Application configuration file
CALLC.C         Client main program
CALLS.C         Server main program
CALLP.C         Remote procedures
MAKEFILE        Nmake file to build for Windows NT or Windows 95
MAKEFILE.DOS    Nmake file to build for MS-DOS

-------------------------------------------
BUILDING CLIENT AND SERVER APPLICATIONS FOR
MICROSOFT WINDOWS NT OR WINDOWS 95
-------------------------------------------

The following environment variables should be already set for you:

  set CPU=i386
  set INCLUDE=%SDKROOT%\h
  set LIB=%SDKROOT%\lib
  set PATH=%SDKROOT%\system32;%SDKROOT%\bin

where %SDKROOT% is the root directory for the 32-bit Windows SDK.

For mips, set CPU=mips
For alpha, set CPU=alpha

Build the sample distributed application:

  nmake cleanall
  nmake

This builds the executable programs callc.exe (client) and calls.exe
(server).

-----------------------------------------------------------------------
BUILDING THE CLIENT APPLICATION FOR MS-DOS
-----------------------------------------------------------------------

After installing the Microsoft Visual C/C++ version 1.50 development
environment and the 16-bit RPC SDK on a Windows NT or Windows 95
computer, you can build the sample client application from Windows NT
or Windows 95:

  nmake -f makefile.dos cleanall
  nmake -f makefile.dos

This builds the client application callc.exe.

You may also execute the Microsoft Visual C/C++ compiler under MS-DOS.
This requires a two-step build process.

  Step One: Compile the .IDL files under Windows NT or Windows 95:

     nmake -a -f makefile.dos hello.h

  Step Two: Compile the C sources (stub and application) under MS-DOS:

     nmake -f makefile.dos

-----------------------------------------------------------------------
RUNNING THE CLIENT AND SERVER APPLICATIONS
-----------------------------------------------------------------------

On the server, enter:

  calls

On the client, enter:

  net start workstation
  callc

Note:  The client and server applications can run on the same Microsoft
Windows NT computer when you use different screen groups.

Several command-line switches are available to change settings for this
program. For a listing of the switches available from the client
program, enter:

  callc -?

For a listing of switches available from the server program, enter:

  calls -?


CALL.IDL

[ uuid (D694C1C0-09D7-1068-94AF-00DD010662DA),
  version(1.0),
  pointer_default(unique)
]
interface callback
{

short Fibonacci([in] short int nValue);

[callback] short Fibonacci2([in] short int nValue);

void Shutdown(void);

}


CALLC.C

/****************************************************************************
                  Microsoft RPC Version 2.0
            Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                      Callback Example

    FILE:       callc.c

    USAGE:      callc  -n network_address
                       -p protocol_sequence
                       -e endpoint
                       -o options
                       -v value to compute fibonacci number

    PURPOSE:    Client side of RPC distributed application

    FUNCTIONS:  main() - binds to server and calls remote procedure

    COMMENTS:   This sample program generates a Fibonacci number by
                static callback.

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

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include "call.h"    // header file generated by MIDL compiler

void Usage(char * pszProgramName)
{
    fprintf(stderr, "Usage:  %s\n", pszProgramName);
    fprintf(stderr, " -p protocol_sequence\n");
    fprintf(stderr, " -n network_address\n");
    fprintf(stderr, " -e endpoint\n");
    fprintf(stderr, " -o options\n");
    fprintf(stderr, " -v value_for_Fibonacci\n");
    exit(1);
}

void _CRTAPI1 main(int argc, char **argv)
{
    RPC_STATUS status;
    unsigned char * pszUuid             = NULL;
    unsigned char * pszProtocolSequence = "ncacn_np";
    unsigned char * pszNetworkAddress   = NULL;
    unsigned char * pszEndpoint         = "\\pipe\\callback";
    unsigned char * pszOptions          = NULL;
    unsigned char * pszStringBinding    = NULL;
    short sValue                        = 2;
    short sFibNumber;
    int i;

    /* allow the user to override settings with command line switches */
    for (i = 1; i < argc; i++) {
        if ((*argv[i] == '-') || (*argv[i] == '/')) {
            switch (tolower(*(argv[i]+1))) {
            case 'p':  // protocol sequence
                pszProtocolSequence = argv[++i];
                break;
            case 'n':  // network address
                pszNetworkAddress = argv[++i];
                break;
            case 'e':
                pszEndpoint = argv[++i];
                break;
            case 'o':
                pszOptions = argv[++i];
                break;
            case 'v':
                sValue = atoi(argv[++i]);
                break;
            case 'h':
            case '?':
            default:
                Usage(argv[0]);
            }
        }
        else
            Usage(argv[0]);
    }

    /* Use a convenience function to concatenate the elements of  */
    /* the string binding into the proper sequence.               */
    status = RpcStringBindingCompose(pszUuid,
                                     pszProtocolSequence,
                                     pszNetworkAddress,
                                     pszEndpoint,
                                     pszOptions,
                                     &pszStringBinding);
    printf("RpcStringBindingCompose returned 0x%x\n", status);
    printf("pszStringBinding = %s\n", pszStringBinding);
    if (status) {
        exit(status);
    }

    /* Set the binding handle that will be used to bind to the server. */
    status = RpcBindingFromStringBinding(pszStringBinding,
                                         &callback_IfHandle);
    printf("RpcBindingFromStringBinding returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    printf("Calling 'Fibonacci(n)' for n = %d\n", sValue);

    RpcTryExcept {
        sFibNumber = Fibonacci(sValue);  // make call
        printf("The Fibonacci number of %d = %d\n", sValue, sFibNumber);
        printf("Calling the remote procedure 'Shutdown'\n");
        Shutdown();  // shut down the server side
    }
    RpcExcept(1) {
        printf("Runtime reported exception %ld\n", RpcExceptionCode() );
    }
    RpcEndExcept

    /* The calls to the remote procedures are complete. */
    /* Free the string and the binding handle           */
    status = RpcStringFree(&pszStringBinding);  // remote calls done; unbind
    printf("RpcStringFree returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    status = RpcBindingFree(&callback_IfHandle);  // remote calls done; unbind
    printf("RpcBindingFree returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    exit(0);

}  // end main()

/* callback function */
short Fibonacci2(short n)
{
    short nsub1, nsub2;

    printf("Callback:  Fibonacci2 called with n = %d\n", n);
    if ((n == 0) || (n == 1))
        return(1);
    else {
        nsub1 = n - 1;
        nsub2 = n - 2;
        return(Fibonacci(nsub1) + Fibonacci(nsub2));
    }
}

/*********************************************************************/
/*                 MIDL allocate and free                            */
/*********************************************************************/

void __RPC_FAR * __RPC_USER midl_user_allocate(size_t len)
{
    return(malloc(len));
}

void __RPC_USER midl_user_free(void __RPC_FAR * ptr)
{
    free(ptr);
}

/* end file callc.c */


CALLP.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                      Callback Example

    FILE:       callp.c

    PURPOSE:    Remote procedures that are linked with the server
                side of RPC distributed application

    FUNCTIONS:  Fibonacci() -

    COMMENTS:   This sample program generates a Fibonacci number by
                static callback.

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

#include <stdlib.h>
#include <stdio.h>
#include "call.h"    // header file generated by MIDL compiler

short Fibonacci(short n)
{
    short nsub1, nsub2;

    printf("Fibonacci() called with value %d\n", n);
    if ((n == 0) || (n == 1))
        return(1);
    else {
        nsub1 = n - 1;
        nsub2 = n - 2;
        return(Fibonacci2(nsub1) + Fibonacci2(nsub2));
    }
}

void Shutdown(void)
{
    RPC_STATUS status;

    printf("calling RpcMgmtStopServerListening\n");
    status = RpcMgmtStopServerListening(NULL);
    printf("RpcMgmtStopServerListening returned: 0x%x\n", status);
    if (status) {
        exit(status);
    }

    printf("calling RpcServerUnregisterIf\n");
    status = RpcServerUnregisterIf(NULL, NULL, FALSE);
    printf("RpcServerUnregisterIf returned 0x%x\n", status);
    if (status) {
        exit(status);
    }
}

/* end file callp.c */


CALLS.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                       Callback Example

    FILE:       calls.c

    USAGE:      calls  -p protocol_sequence
                       -e endpoint
                       -m max calls
                       -n min calls
                       -f flag for RpcServerListen

    PURPOSE:    Server side of RPC distributed application call

    FUNCTIONS:  main() - registers server as RPC server

    COMMENTS:   This sample program generates a Fibonacci number by
                static callback.

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

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "call.h"    // header file generated by MIDL compiler

#define PURPOSE \
"This Microsoft RPC Version 2.0 sample program demonstrates\n\
the use of the [string] attribute. For more information\n\
about the attributes and the RPC API functions, see the\n\
RPC programming guide and reference.\n\n"

void Usage(char * pszProgramName)
{
    fprintf(stderr, "%s", PURPOSE);
    fprintf(stderr, "Usage:  %s\n", pszProgramName);
    fprintf(stderr, " -p protocol_sequence\n");
    fprintf(stderr, " -e endpoint\n");
    fprintf(stderr, " -m maxcalls\n");
    fprintf(stderr, " -n mincalls\n");
    fprintf(stderr, " -f flag_wait_op\n");
    exit(1);
}

/* main:  register the interface, start listening for clients */
void _CRTAPI1 main(int argc, char * argv[])
{
    RPC_STATUS status;
    unsigned char * pszProtocolSequence = "ncacn_np";
    unsigned char * pszSecurity         = NULL;
    unsigned char * pszEndpoint         = "\\pipe\\callback";
    unsigned int    cMinCalls           = 1;
    unsigned int    cMaxCalls           = 20;
    unsigned int    fDontWait           = FALSE;
    int i;

    /* allow the user to override settings with command line switches */
    for (i = 1; i < argc; i++) {
        if ((*argv[i] == '-') || (*argv[i] == '/')) {
            switch (tolower(*(argv[i]+1))) {
            case 'p':  // protocol sequence
                pszProtocolSequence = argv[++i];
                break;
            case 'e':
                pszEndpoint = argv[++i];
                break;
            case 'm':
                cMaxCalls = (unsigned int) atoi(argv[++i]);
                break;
            case 'n':
                cMinCalls = (unsigned int) atoi(argv[++i]);
                break;
            case 'f':
                fDontWait = (unsigned int) atoi(argv[++i]);
                break;
            case 'h':
            case '?':
            default:
                Usage(argv[0]);
            }
        }
        else
            Usage(argv[0]);
    }

    status = RpcServerUseProtseqEp(pszProtocolSequence,
                                   cMaxCalls,
                                   pszEndpoint,
                                   pszSecurity);  // Security descriptor
    printf("RpcServerUseProtseqEp returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    status = RpcServerRegisterIf(callback_ServerIfHandle,  // interface to register
                                 NULL,   // MgrTypeUuid
                                 NULL);  // MgrEpv; null means use default
    printf("RpcServerRegisterIf returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    printf("Calling RpcServerListen\n");
    status = RpcServerListen(cMinCalls,
                             cMaxCalls,
                             fDontWait);
    printf("RpcServerListen returned: 0x%x\n", status);
    if (status) {
        exit(status);
    }

    if (fDontWait) {
        printf("Calling RpcMgmtWaitServerListen\n");
        status = RpcMgmtWaitServerListen();  //  wait operation
        printf("RpcMgmtWaitServerListen returned: 0x%x\n", status);
        if (status) {
            exit(status);
        }
    }

}  // end main()

/*********************************************************************/
/*                 MIDL allocate and free                            */
/*********************************************************************/

void __RPC_FAR * __RPC_USER midl_user_allocate(size_t len)
{
    return(malloc(len));
}

void __RPC_USER midl_user_free(void __RPC_FAR * ptr)
{
    free(ptr);
}

/* end file calls.c */


CALL.ACF

[implicit_handle(handle_t callback_IfHandle)]
interface callback
{

}


README.TXT

CLUUID

The CLUUID program demonstrates how to supply multiple implementations of
the remote procedure specified in the interface. It also demonstrates
how the client selects among the implementations by providing a client
object uuid.

SUMMARY
=======

The server calls RpcObjectSetType to associate a client object uuid with
the object uuid in the Object Registry Table. The server initializes a
manager entry point vector (manager epv) and then calls RpcRegisterIf to
associate the interface uuid and the object uuid with the manager epv in
the Interface Registry Table.

When the client makes a remote procedure call, the client object uuid is
mapped to the object uuid in the Object Registry Table. The resulting
object uuid and the interface uuid are mapped to a manager entry point
vector in the Interface Registry Table.

By default, in this example, the server registers two implementations of
the "hello, world" function HelloProc and HelloProc2. The HelloProc2
implementation is associated with the object uuid
"11111111-1111-1111-1111-111111111111". When the client makes a procedure
call with a null uuid, the client's request is mapped to the original
HelloProc. When the client makes a procedure call with the client object
uuid "11111111-1111-1111-1111-11111111111", the client's request is mapped
to HelloProc2 (which prints the string in reverse).

FILES
=====

The directory samples\rpc\cluuid contains the following files for
building the sample distributed application CLUUID:

File          Description

README.TXT    Readme file for the cluuid sample
CLUUID.IDL    Interface definition language file
CLUUID.ACF    Attribute configuration file
CLUUIDC.C     Client main program
CLUUIDS.C     Server main program
CLUUIDP.C     Remote procedures
MAKEFILE      Nmake file to build for Windows NT or Windows 95
MAKEFILE.DOS  Nmake file to build for MS-DOS

-------------------------------------------
BUILDING CLIENT AND SERVER APPLICATIONS FOR
MICROSOFT WINDOWS NT OR WINDOWS 95
-------------------------------------------

The following environment variables should be already set for you:

  set CPU=i386
  set INCLUDE=%SDKROOT%\h
  set LIB=%SDKROOT%\lib
  set PATH=%SDKROOT%\system32;%SDKROOT%\bin

where %SDKROOT% is the root directory for the 32-bit Windows SDK.

For mips, set CPU=mips
For alpha, set CPU=alpha

Build the sample distributed application:

  nmake cleanall
  nmake

This builds the executable programs cluuidc.exe(client) and
cluuids.exe (server).

-----------------------------------------------------------------------
BUILDING THE CLIENT APPLICATION FOR MS-DOS
-----------------------------------------------------------------------

After installing the Microsoft Visual C/C++ version 1.50 development
environment and the 16-bit RPC SDK on a Windows NT or Windows 95
computer, you can build the sample client application from Windows NT
or Windows 95:

  nmake -f makefile.dos cleanall
  nmake -f makefile.dos

This builds the client application cluuidc.exe.

You may also execute the Microsoft Visual C/C++ compiler under MS-DOS.
This requires a two-step build process.

  Step One: Compile the .IDL files under Windows NT or Windows 95
     nmake -a -f makefile.dos cluuid.h

  Step Two: Compile the C sources (stub and application) under MS-DOS
     nmake -f makefile.dos

------------------------------------------
RUNNING THE CLIENT AND SERVER APPLICATIONS
------------------------------------------

On the server, enter:

  cluuids

On the client, enter:

  net start workstation
  cluuidc

To call the second implementation of the function,
on the client, enter:

  cluuidc -u "11111111-1111-1111-1111-111111111111"

Note: The client and server applications can run on the same Microsoft
Windows NT computer when you use different screen groups.

Several command line switches are available to change settings for this
program. For a listing of the switches available from the client program,
enter:

  cluuidc -?

For a listing of switches available from the server program, enter:

  cluuids -?

---------------------
RUNNING ON WINDOWS 95
---------------------

If you wish to run the server on Windows 95, you will need to use
the LRPC protocol.

To start the server, enter:

 cluuids -p ncalrpc

To start the client (from the same machine), enter:

 cluuidc -p ncalrpc


CLUUID.IDL

[ uuid (4AF72C40-4DAA-1068-A9AD-08002B2EB23C),
  version(1.0),
  pointer_default(unique)
]
interface cluuid
{

void HelloProc([in, string] unsigned char * pszString);

void Shutdown(void);

}


CLUUIDC.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                       Cluuid Example

    FILE:        cluuidc.c

    USAGE:       cluuidc  -n network_address
                          -p protocol_sequence
                          -e endpoint
                          -o options
                          -s string_displayed_on_server
                          -u client object uuid

    PURPOSE:     Client side of RPC distributed application

    FUNCTIONS:   main() - binds to server and calls remote procedure

    COMMENTS:    This distributed application prints a string such as
                 "hello, world" on the server. The client manages its
                 connection to the server. The client uses the implicit
                 binding handle ImpHandle defined in the file cluuid.h.

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

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include "cluuid.h"    // header file generated by MIDL compiler

void Usage(char * pszProgramName)
{
    fprintf(stderr, "Usage:  %s\n", pszProgramName);
    fprintf(stderr, " -p protocol_sequence\n");
    fprintf(stderr, " -n network_address\n");
    fprintf(stderr, " -e endpoint\n");
    fprintf(stderr, " -o options\n");
    fprintf(stderr, " -s string\n");
    fprintf(stderr, " -u uuid\n");
    exit(1);
}

void _CRTAPI1 main(int argc, char **argv)
{
    RPC_STATUS status;
    unsigned char * pszUuid             = NULL;
    unsigned char * pszProtocolSequence = "ncacn_np";
    unsigned char * pszNetworkAddress   = NULL;
    unsigned char * pszEndpoint         = "\\pipe\\cluuid";
    unsigned char * pszOptions          = NULL;
    unsigned char * pszStringBinding    = NULL;
    unsigned char * pszString           = "hello, world";
    unsigned long ulCode;
    int i;

    /* allow the user to override settings with command line switches */
    for (i = 1; i < argc; i++) {
        if ((*argv[i] == '-') || (*argv[i] == '/')) {
            switch (tolower(*(argv[i]+1))) {
            case 'p':  // protocol sequence
                pszProtocolSequence = argv[++i];
                break;
            case 'n':  // network address
                pszNetworkAddress = argv[++i];
                break;
            case 'e':
                pszEndpoint = argv[++i];
                break;
            case 'o':
                pszOptions = argv[++i];
                break;
            case 's':
                pszString = argv[++i];
                break;
            case 'u':
                pszUuid = argv[++i];
                break;
            case 'h':
            case '?':
            default:
                Usage(argv[0]);
            }
        }
        else
            Usage(argv[0]);
    }

    /* Use a convenience function to concatenate the elements of  */
    /* the string binding into the proper sequence.               */
    status = RpcStringBindingCompose(pszUuid,
                                     pszProtocolSequence,
                                     pszNetworkAddress,
                                     pszEndpoint,
                                     pszOptions,
                                     &pszStringBinding);
    printf("RpcStringBindingCompose returned 0x%x\n", status);
    printf("pszStringBinding = %s\n", pszStringBinding);
    if (status) {
        exit(status);
    }

    /* Set the binding handle that will be used to bind to the server. */
    status = RpcBindingFromStringBinding(pszStringBinding,
                                         &ImpHandle);
    printf("RpcBindingFromStringBinding returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    printf("Calling the remote procedure 'HelloProc'\n");
    printf("  print the string '%s' on the server\n", pszString);

    RpcTryExcept {
        HelloProc(pszString);  /* make call with user message */
        printf("Calling the remote procedure 'Shutdown'\n");
        Shutdown();             // shut down the server side
    }
    RpcExcept(1) {
        ulCode = RpcExceptionCode();
        printf("Runtime reported exception 0x%lx = %ld\n", ulCode, ulCode);
    }
    RpcEndExcept

    /*  The calls to the remote procedures are complete. */
    /*  Free the string and the binding handle           */

    status = RpcStringFree(&pszStringBinding);  // remote calls done; unbind
    printf("RpcStringFree returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    status = RpcBindingFree(&ImpHandle);  // remote calls done; unbind
    printf("RpcBindingFree returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    exit(0);
}

/*********************************************************************/
/*                 MIDL allocate and free                            */
/*********************************************************************/

void __RPC_FAR * __RPC_USER midl_user_allocate(size_t len)
{
    return(malloc(len));
}

void __RPC_USER midl_user_free(void __RPC_FAR * ptr)
{
    free(ptr);
}

/* end file cluuidc.c */


CLUUIDP.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                         Cluuid Example

    FILE:       cluuidp.c

    PURPOSE:    Remote procedures that are linked with the server
                side of RPC distributed application

    FUNCTIONS:  HelloProc() - prints "hello, world" or other string
                HelloProc2() - prints string backwards

    COMMENTS:   This sample program demonstrates how to supply
                multiple implementations of the remote procedure
                specified in the interface. It also demonstrates
                how the client selects among the implementations
                by providing a client object uuid.

                The server calls RpcObjectSetType to associate a
                client object uuid with the object uuid in the
                Object Registry Table. The server initializes a
                manager entry point vector (manager epv) and
                then calls RpcRegisterIf to associate the interface
                uuid and the object uuid with the manager epv in the
                Interface Registry Table.

                When the client makes a remote procedure call,
                the client object uuid is mapped to the object uuid
                in the Object Registry Table. The resulting
                object uuid and the interface uuid are mapped to
                a manager entry point vector in the Interface
                Registry Table.

                By default, in this example, the server registers
                two implementations of the "hello, world" function
                HelloProc and HelloProc2.  The HelloProc2
                implementation is associated with the object uuid
                "11111111-1111-1111-1111-111111111111". When
                the client makes a procedure call with a null
                uuid, the client's request is mapped to the
                original HelloProc.  When the client makes a
                procedure call with the client object uuid
                "11111111-1111-1111-1111-11111111111", the
                client's request is mapped to HelloProc2 (which
                prints the string in reverse).

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

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "cluuid.h"    // header file generated by MIDL compiler

void HelloProc(unsigned char * pszString)
{
    printf("%s\n", pszString);
}

void HelloProc2(unsigned char * pszString)
{
    printf("%s\n", strrev(pszString));
}

void Shutdown(void)
{
    RPC_STATUS status;

    printf("Calling RpcMgmtStopServerListening\n");
    status = RpcMgmtStopServerListening(NULL);
    printf("RpcMgmtStopServerListening returned: 0x%x\n", status);
    if (status) {
        exit(status);
    }

    printf("Calling RpcServerUnregisterIf\n");
    status = RpcServerUnregisterIf(NULL, NULL, FALSE);
    printf("RpcServerUnregisterIf returned 0x%x\n", status);
    if (status) {
        exit(status);
    }
}

/* end of file cluuidp.c */


CLUUIDS.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                      Cluuid Example

    FILE:       cluuids.c

    USAGE:      cluuids  -p protocol_sequence
                         -e endpoint
                         -m max calls
                         -n min calls
                         -f flag for RpcServerListen
                         -1 client object uuid
                         -2 manager epv uuid

    PURPOSE:    Server side of RPC distributed application hello

    FUNCTIONS:  main() - registers server as RPC server

    COMMENTS:   Print "hello, world" on the server.
                When you supply a type UUID, the client must
                supply the same UUID.

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

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "cluuid.h"    // header file generated by MIDL compiler

// the second implementation of the remote procedure
extern void HelloProc2(unsigned char * pszString);

#define PURPOSE \
"This Microsoft RPC Version 2.0 sample program demonstrates how\n\
to supply multiple implementations of the remote procedure\n\
specified in the interface. It also demonstrates how the client\n\
selects among the implementations by providing a client object uuid.\n\n"

#define NULL_UUID_STRING "00000000-0000-0000-0000-000000000000"

void Usage(char * pszProgramName)
{
    fprintf(stderr, "%s", PURPOSE);
    fprintf(stderr, "Usage:  %s\n", pszProgramName);
    fprintf(stderr, " -p protocol_sequence\n");
    fprintf(stderr, " -e endpoint\n");
    fprintf(stderr, " -m maxcalls\n");
    fprintf(stderr, " -n mincalls\n");
    fprintf(stderr, " -f flag_wait_op\n");
    fprintf(stderr, " -1 client uuid\n");
    fprintf(stderr, " -2 manager uuid\n");
    exit(1);
}

/* main:  register the interface, start listening for clients */
void _CRTAPI1 main(int argc, char * argv[])
{
    RPC_STATUS status;
    UUID MgrTypeUuid, ClientUuid;
    unsigned char * pszProtocolSequence = "ncacn_np";
    unsigned char * pszSecurity         = NULL;
    unsigned char * pszClientUuid       = NULL_UUID_STRING;
    unsigned char * pszMgrTypeUuid      = "11111111-1111-1111-1111-111111111111";
    unsigned char * pszEndpoint         = "\\pipe\\cluuid";
    unsigned int    cMinCalls           = 1;
    unsigned int    cMaxCalls           = 20;
    unsigned int    fDontWait           = FALSE;
    int i;

    cluuid_SERVER_EPV epv2;    // the mgr_epv for the 2nd implementation

    /* allow the user to override settings with command line switches */
    for (i = 1; i < argc; i++) {
        if ((*argv[i] == '-') || (*argv[i] == '/')) {
            switch (tolower(*(argv[i]+1))) {
            case 'p':  // protocol sequence
                pszProtocolSequence = argv[++i];
                break;
            case 'e':
                pszEndpoint = argv[++i];
                break;
            case 'm':
                cMaxCalls = (unsigned int) atoi(argv[++i]);
                break;
            case 'n':
                cMinCalls = (unsigned int) atoi(argv[++i]);
                break;
            case 'f':
                fDontWait = (unsigned int) atoi(argv[++i]);
                break;
            case '1':
                pszMgrTypeUuid = argv[++i];
                break;
            case '2':
                pszClientUuid = argv[++i];
                break;
            case 'h':
            case '?':
            default:
                Usage(argv[0]);
            }
        }
        else
            Usage(argv[0]);
    }

    status = RpcServerUseProtseqEp(pszProtocolSequence,
                                   cMaxCalls,
                                   pszEndpoint,
                                   pszSecurity);  // Security descriptor
    printf("RpcServerUseProtseqEp returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    status = UuidFromString(pszClientUuid, &ClientUuid);
    printf("UuidFromString returned 0x%x = %d\n", status, status);
    if (status) {
        exit(status);
    }

    status = UuidFromString(pszMgrTypeUuid, &MgrTypeUuid);
    printf("UuidFromString returned 0x%x = %d\n", status, status);
    if (status) {
        exit(status);
    }
    if (strcmp (pszMgrTypeUuid, NULL_UUID_STRING) == 0) {
        printf("Register object using non-null uuid %s\n", pszMgrTypeUuid);
        exit(1);
    }

    if (strcmp (pszClientUuid, NULL_UUID_STRING) == 0) {
        printf("Register object using non-null uuid %s\n", pszMgrTypeUuid);
        ClientUuid = MgrTypeUuid;
    }

    RpcObjectSetType(&ClientUuid, &MgrTypeUuid);  // associate type UUID with nil UUID
    printf("RpcObjectSetType returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    status = RpcServerRegisterIf(cluuid_ServerIfHandle, // interface to register
                                 NULL,   // MgrTypeUuid
                                 NULL);  // MgrEpv; null means use default
    printf("RpcServerRegisterIf returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    /* register the second manager epv and associate it with the
       specified uuid.  the second uuid must be non-null so that
       it will not conflict with the NULL uuid already registered
       for this interface
    */
    epv2.HelloProc = HelloProc2;
    epv2.Shutdown = Shutdown;
    status = RpcServerRegisterIf(cluuid_ServerIfHandle, // interface to register
                                 &MgrTypeUuid,  // MgrTypeUuid
                                 &epv2);        // 2nd manager epv
    printf("RpcServerRegisterIf returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    printf("Calling RpcServerListen\n");
    status = RpcServerListen(cMinCalls,
                             cMaxCalls,
                             fDontWait);
    printf("RpcServerListen returned: 0x%x\n", status);
    if (status) {
        exit(status);
    }

    if (fDontWait) {
        printf("Calling RpcMgmtWaitServerListen\n");
        status = RpcMgmtWaitServerListen();  //  wait operation
        printf("RpcMgmtWaitServerListen returned: 0x%x\n", status);
        if (status) {
            exit(status);
        }
    }

} // end main()

/*********************************************************************/
/*                 MIDL allocate and free                            */
/*********************************************************************/

void __RPC_FAR * __RPC_USER midl_user_allocate(size_t len)
{
    return(malloc(len));
}

void __RPC_USER midl_user_free(void __RPC_FAR * ptr)
{
    free(ptr);
}

/* end file cluuids.c */


CLUUID.ACF

[implicit_handle(handle_t ImpHandle)]
interface cluuid
{

}


README.TXT

DUNION

The DUNION program illustrates discriminated unions.

FILES
=====

The directory samples\rpc\handles\data\dunion contains the following files
to build the distributed application DUNION:

File          Description

README.TXT    Readme file for the DUNION sample
DUNION.IDL    Interface definition language file
DUNION.ACF    Attribute configuration file
DUNIONC.C     Client main program
DUNIONS.C     Server main program
DUNIONP.C     Remote procedures
MAKEFILE      Nmake file to build for Windows NT or Windows 95
MAKEFILE.DOS  Nmake file to build for MS-DOS

-------------------------------------------
BUILDING CLIENT AND SERVER APPLICATIONS FOR
MICROSOFT WINDOWS NT OR WINDOWS 95
-------------------------------------------

The following environment variables should be already set for you:

  set CPU=i386
  set INCLUDE=%SDKROOT%\h
  set LIB=%SDKROOT%\lib
  set PATH=%SDKROOT%\system32;%SDKROOT%\bin

where %SDKROOT% is the root directory for the 32-bit Windows SDK.

For mips, set CPU=mips
For alpha, set CPU=alpha

Build the sample distributed application:

 nmake cleanall
 nmake

This builds the executable programs dunionc.exe
(client) and dunions.exe (server).

------------------------------------------
BUILDING THE CLIENT APPLICATION FOR MS-DOS
------------------------------------------

After installing the Microsoft Visual C/C++ version 1.50 development
environment and the 16-bit RPC SDK on a Windows NT or Windows 95
computer, you can build the sample client application from Windows NT
or Windows 95:

  nmake -f makefile.dos cleanall
  nmake -f makefile.dos

This builds the client application dunionc.exe.

You may also execute the Microsoft Visual C/C++ compiler under MS-DOS.
This requires a two-step build process.

  Step One: Compile the .IDL files under Windows NT or Windows 95:

     nmake -a -f makefile.dos dunion.h

  Step Two: Compile the C sources (stub and application) under MS-DOS:

     nmake -f makefile.dos

------------------------------------------
RUNNING THE CLIENT AND SERVER APPLICATIONS
------------------------------------------

On the server, enter:

  dunions

On the client, enter:

  net start workstation
  dunionc

Note:  The client and server applications can run on the same Microsoft
Windows NT computer when you use different screen groups.

Several command line switches are available to change settings for this
program. For a listing of the switches available from the client program,
enter:

  dunionc -?

For a listing of switches available from the server program, enter:

  dunions -?


DUNION.IDL

[ uuid (87E5BFA0-CA46-1067-B31B-00DD010662DA),
  version(1.0),
  pointer_default(unique)
]
interface dunion
{

typedef [switch_type(short)] union {
    [case(0)] short          sVal;
    [case(1)] float          fVal;
    [case(2)] unsigned char  chVal;
    [default] ;
} DISCRIM_UNION_PARAM_TYPE;

typedef DISCRIM_UNION_PARAM_TYPE DISCRIM_UNION_FIELD_TYPE;

typedef struct {
    short                                          sDiscrim;
    [switch_is(sDiscrim)] DISCRIM_UNION_FIELD_TYPE u;
} DISCRIM_UNION_STRUCT_TYPE;

void UnionParamProc(
    [in, switch_is(sDiscrim)] DISCRIM_UNION_PARAM_TYPE *UnionParam,
    [in]                      short                    sDiscrim
    );

void UnionStructProc(
    [in] DISCRIM_UNION_STRUCT_TYPE *UnionStruct);

void Shutdown(void);

}


DUNIONC.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                    Discriminated Union Example

    FILE:       dunionc.c

    USAGE:      dunionc  -n network_address
                         -p protocol_sequence
                         -e endpoint
                         -o options
                         -d discriminant
                         -v union_value

    PURPOSE:    Client side of RPC distributed application

    FUNCTIONS:  main() - binds to server and calls remote procedure

    COMMENTS:   This distributed application illustrates discriminated
                union.

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

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include "dunion.h"    // header file generated by MIDL compiler

void Usage(char * pszProgramName)
{
    fprintf(stderr, "Usage:  %s\n", pszProgramName);
    fprintf(stderr, " -p protocol_sequence\n");
    fprintf(stderr, " -n network_address\n");
    fprintf(stderr, " -e endpoint\n");
    fprintf(stderr, " -o options\n");
    fprintf(stderr, " -d discriminant\n");
    fprintf(stderr, " -v union_value\n");
    exit(1);
}

void DisplayUnionValue(DISCRIM_UNION_PARAM_TYPE *up,
                       short                    sDiscrim)
{
    printf("sDiscrim = %d, data = ", sDiscrim);
    switch(sDiscrim) {
    case 0:
        printf("short: %d\n", up->sVal);
        break;
    case 1:
        printf("float: %f\n", up->fVal);
        break;
    case 2:
        printf("char: %c\n", up->chVal);
        break;
    default:
        printf("invalid\n");
        break;
    }
}

void _CRTAPI1 main(int argc, char **argv)
{
    RPC_STATUS status;
    unsigned char * pszUuid             = NULL;
    unsigned char * pszProtocolSequence = "ncacn_np";
    unsigned char * pszNetworkAddress   = NULL;
    unsigned char * pszEndpoint         = "\\pipe\\dunion";
    unsigned char * pszOptions          = NULL;
    unsigned char * pszStringBinding    = NULL;
    int i;

    short sDiscrim = 0;
    DISCRIM_UNION_PARAM_TYPE  up = {1};
    DISCRIM_UNION_STRUCT_TYPE us = {0, 1};

    /* allow the user to override settings with command line switches */
    for (i = 1; i < argc; i++) {
        if ((*argv[i] == '-') || (*argv[i] == '/')) {
            switch (tolower(*(argv[i]+1))) {
            case 'p':  // protocol sequence
                pszProtocolSequence = argv[++i];
                break;
            case 'n':  // network address
                pszNetworkAddress = argv[++i];
                break;
            case 'e':
                pszEndpoint = argv[++i];
                break;
            case 'o':
                pszOptions = argv[++i];
                break;
            case 'd':
                sDiscrim = (short) atoi(argv[++i]);
                if ((sDiscrim > 3) || (sDiscrim < 0))
                    sDiscrim = 0;
                us.sDiscrim = sDiscrim;
                break;
            case 'v':
                switch(sDiscrim) {
                case 0:
                    up.sVal =  (short) atoi(argv[++i]);
                    us.u.sVal = up.sVal;
                    break;
                case 1:
                    up.fVal =  (float) atof(argv[++i]);
                    us.u.fVal = up.fVal;
                    break;
                case 2:
                    up.chVal = *(argv[++i]);
                    us.u.chVal = up.chVal;
                    break;
                default:
                    break;
                }
                break;
            case 'h':
            case '?':
            default:
                Usage(argv[0]);
            }
        }
        else
            Usage(argv[0]);
    }

    /* Use a convenience function to concatenate the elements of  */
    /* the string binding into the proper sequence.               */
    status = RpcStringBindingCompose(pszUuid,
                                     pszProtocolSequence,
                                     pszNetworkAddress,
                                     pszEndpoint,
                                     pszOptions,
                                     &pszStringBinding);
    printf("RpcStringBindingCompose returned 0x%x\n", status);
    printf("pszStringBinding = %s\n", pszStringBinding);
    if (status) {
        exit(status);
    }

    /* Set the binding handle that will be used to bind to the server. */
    status = RpcBindingFromStringBinding(pszStringBinding,
                                         &hDiscrim);
    printf("RpcBindingFromStringBinding returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    printf("Calling the remote procedure 'UnionParamProc'\n");

    RpcTryExcept {
        DisplayUnionValue(&up, sDiscrim);  // display value before call
        UnionParamProc(&up, sDiscrim);     // call the remote procedure

        UnionStructProc(&us);              // call the remote procedure
        DisplayUnionValue(&up, sDiscrim);  // display value after call

        Shutdown();                    // Shut down the server
    }
    RpcExcept(1) {
        printf("Runtime reported exception %ld\n", RpcExceptionCode() );
    }
    RpcEndExcept

    /*  The remote procedure call is complete.  Free the binding handle */
    status = RpcBindingFree(&hDiscrim);
    printf("RpcBindingFree returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    exit(0);

}  // end main()

/*********************************************************************/
/*                 MIDL allocate and free                            */
/*********************************************************************/

void __RPC_FAR * __RPC_USER midl_user_allocate(size_t len)
{
    return(malloc(len));
}

void __RPC_USER midl_user_free(void __RPC_FAR * ptr)
{
    free(ptr);
}

/* end file dunionc.c */


DUNIONP.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                   Discriminated Union Example

    FILE:       unionp.c

    PURPOSE:    Remote procedures that are linked with the server
                side of RPC distributed application

    FUNCTIONS:  UnionParamProc()  - union, discriminant are parameters
                UnionStructProc() - union, discriminant in structure

    COMMENTS:   This distributed application illustrates distriminated
                union.

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

#include <stdlib.h>
#include <stdio.h>
#include "dunion.h"    // header file generated by MIDL compiler

void UnionParamProc(DISCRIM_UNION_PARAM_TYPE *up,
                    short                    sDiscrim)
{
    printf("sDiscrim = %d, data = ", sDiscrim);
    switch(sDiscrim) {
    case 0:
        printf("short %d\n", up->sVal);
        break;
    case 1:
        printf("float %f\n", up->fVal);
        break;
    case 2:
        printf("char %c\n", up->chVal);
        break;
    default:
        printf("invalid\n");
        break;
    }
}

void UnionStructProc(DISCRIM_UNION_STRUCT_TYPE *u)
{
    printf("sDiscrim = %d, data = ", u->sDiscrim);
    switch(u->sDiscrim) {
    case 0:
        printf("short %d\n", u->u.sVal);
        break;
    case 1:
        printf("float %f\n", u->u.fVal);
        break;
    case 2:
        printf("char %c\n", u->u.chVal);
        break;
    default:
        printf("invalid\n");
        break;
    }
}

void Shutdown(void)
{
    RPC_STATUS status;

    printf("Calling RpcMgmtStopServerListening\n");
    status = RpcMgmtStopServerListening(NULL);
    printf("RpcMgmtStopServerListening returned: 0x%x\n", status);
    if (status) {
        exit(status);
    }

    printf("Calling RpcServerUnregisterIf\n");
    status = RpcServerUnregisterIf(NULL, NULL, FALSE);
    printf("RpcServerUnregisterIf returned 0x%x\n", status);
    if (status) {
        exit(status);
    }
}

/* end file dunionp.c */


DUNIONS.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                    Discriminated Union Example

    FILE:       dunions.c

    USAGE:      dunions  -p protocol_sequence
                         -e endpoint
                         -m max calls
                         -n min calls
                         -f flag for RpcServerListen

    PURPOSE:    Server side of RPC distributed application dunion

    FUNCTIONS:  main() - registers server as RPC server

    COMMENTS:   This distributed application illustrates discriminated
                union.

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

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include "dunion.h"    // header file generated by MIDL compiler

#define PURPOSE \
"This Microsoft RPC Version 2.0 sample program demonstrates\n\
two ways to specify a discriminated union. For more information\n\
about the attributes and the RPC API functions, see the\n\
RPC programming guide and reference.\n\n"

void Usage(char * pszProgramName)
{
    fprintf(stderr, "%s", PURPOSE);
    fprintf(stderr, "Usage:  %s\n", pszProgramName);
    fprintf(stderr, " -p protocol_sequence\n");
    fprintf(stderr, " -e endpoint\n");
    fprintf(stderr, " -m maxcalls\n");
    fprintf(stderr, " -n mincalls\n");
    fprintf(stderr, " -f flag_wait_op\n");
    exit(1);
}

void _CRTAPI1 main(int argc, char * argv[])
{
    RPC_STATUS status;
    unsigned char * pszProtocolSequence = "ncacn_np";
    unsigned char * pszSecurity         = NULL;
    unsigned char * pszEndpoint         = "\\pipe\\dunion";
    unsigned int    cMinCalls           = 1;
    unsigned int    cMaxCalls           = 20;
    unsigned int    fDontWait           = FALSE;
    int i;

    /* allow the user to override settings with command line switches */
    for (i = 1; i < argc; i++) {
        if ((*argv[i] == '-') || (*argv[i] == '/')) {
            switch (tolower(*(argv[i]+1))) {
            case 'p':  // protocol sequence
                pszProtocolSequence = argv[++i];
                break;
            case 'e':
                pszEndpoint = argv[++i];
                break;
            case 'm':
                cMaxCalls = (unsigned int) atoi(argv[++i]);
                break;
            case 'n':
                cMinCalls = (unsigned int) atoi(argv[++i]);
                break;
            case 'f':
                fDontWait = (unsigned int) atoi(argv[++i]);
                break;
            case 'h':
            case '?':
            default:
                Usage(argv[0]);
            }
        }
        else
            Usage(argv[0]);
     }

     status = RpcServerUseProtseqEp(pszProtocolSequence,
                                    cMaxCalls,
                                    pszEndpoint,
                                    pszSecurity);  // Security descriptor
     printf("RpcServerUseProtseqEp returned 0x%x\n", status);
     if (status) {
         exit(status);
     }

     status = RpcServerRegisterIf(dunion_ServerIfHandle,// interface to register
                                  NULL,   // MgrTypeUuid
                                  NULL);  // MgrEpv; null means use default
     printf("RpcServerRegisterIf returned 0x%x\n", status);
     if (status) {
         exit(status);
     }

     printf("Calling RpcServerListen\n");
     status = RpcServerListen(cMinCalls,
                              cMaxCalls,
                              fDontWait);
     printf("RpcServerListen returned: 0x%x\n", status);
     if (status) {
         exit(status);
     }

     if (fDontWait) {
         printf("Calling RpcMgmtWaitServerListen\n");
         status = RpcMgmtWaitServerListen();  //  wait operation
         printf("RpcMgmtWaitServerListen returned: 0x%x\n", status);
         if (status) {
             exit(status);
         }
     }

}  // end main()

/*********************************************************************/
/*                 MIDL allocate and free                            */
/*********************************************************************/

void __RPC_FAR * __RPC_USER midl_user_allocate(size_t len)
{
    return(malloc(len));
}

void __RPC_USER midl_user_free(void __RPC_FAR * ptr)
{
    free(ptr);
}

/* end file dunions.c */


DUNION.ACF

[implicit_handle(handle_t hDiscrim)]
interface dunion
{

}


README.TXT

INOUT

The INOUT program demonstrates the use of [in,out] parameters.

FILES
=====

The directory samples\rpc\data\inout contains the following files for
building the sample distributed application INOUT:

File          Description

README.TXT    Readme file for the INOUT sample
INOUT.IDL     Interface definition language file
INOUT.ACF     Attribute configuration file
INOUTC.C      Client main program
INOUTS.C      Server main program
INOUTP.C      Remote procedures
MAKEFILE      Nmake file to build for Windows NT or Windows 95
MAKEFILE.DOS  Nmake file to build for MS-DOS

-------------------------------------------
BUILDING CLIENT AND SERVER APPLICATIONS FOR
MICROSOFT WINDOWS NT OR WINDOWS 95
-------------------------------------------

The following environment variables should be already set for you:

  set CPU=i386
  set INCLUDE=%SDKROOT%\h
  set LIB=%SDKROOT%\lib
  set PATH=%SDKROOT%\system32;%SDKROOT%\bin

where %SDKROOT% is the root directory for the 32-bit Windows SDK.

For mips, set CPU=mips
For alpha, set CPU=alpha

Build the sample distributed application:

  nmake cleanall
  nmake

This builds the executable programs inoutc.exe (client) and
inouts.exe (server).

------------------------------------------
BUILDING THE CLIENT APPLICATION FOR MS-DOS
------------------------------------------

After installing the Microsoft Visual C/C++ version 1.50 development
environment and the 16-bit RPC SDK on a Windows NT or Windows 95
computer, you can build the sample client application from Windows NT
or Windows 95:

  nmake -f makefile.dos cleanall
  nmake -f makefile.dos

This builds the client application inoutc.exe.

You may also execute the Microsoft Visual C/C++ compiler under MS-DOS.
This requires a two-step build process.

  Step One: Compile the .IDL files under Windows NT or Windows 95:

     nmake -a -f makefile.dos inout.h

  Step Two: Compile the C sources (stub and application) under MS-DOS:

     nmake -f makefile.dos

------------------------------------------
RUNNING THE CLIENT AND SERVER APPLICATIONS
------------------------------------------

On the server, enter:

  inouts

On the client, enter:

  net start workstation
  inoutc

Note: The client and server applications can run on the same Microsoft
Windows NT computer when you use different screen groups.

Several command-line switches are available to change settings for this
program. For a listing of the switches available from the client program,
enter:

  inoutc -?

For a listing of switches available from the server program, enter:

  inouts -?


INOUT.IDL

[ uuid (6E3B5060-CA46-1067-B31A-00DD010662DA),
  version(1.0),
  pointer_default(unique)
]
interface inout
{

void InOutProc([in]      short   s1,
               [in, out] short * ps2,
               [out]     float * pf3);

void Shutdown(void);

}


INOUTC.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                      InOut Example

    FILE:       inoutc.c

    USAGE:      inoutc   -n network_address
                         -p protocol_sequence
                         -e endpoint
                         -o options
                         -1 short_value_1
                         -2 short_value_2
                         -3 float_value_3

    PURPOSE:    Client side of RPC distributed application inout

    FUNCTIONS:  main() - binds to server and calls remote procedure

    COMMENTS:   This distributed application demonstrates in, out
                parameters.

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

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include "inout.h"    // header file generated by MIDL compiler

#define PURPOSE \
"This Microsoft RPC Version 2.0 sample program demonstrates\n\
use of the [in], [out], and [in, out] attributes. For more\n\
information about attributes and RPC API functions, see the\n\
Microsoft RPC programming guide and reference.\n\n"

#define DESCRIPTION \
"One [in], one [out], and one [in,out] parameter are defined\n\
for the function InOutProc(). This program displays the values\n\
of these parameters before and after the remote procedure call.\n\n"

void Usage(char * pszProgramName)
{
    fprintf(stderr, "%s", PURPOSE);
    fprintf(stderr, "Usage:  %s\n", pszProgramName);
    fprintf(stderr, " -p protocol_sequence\n");
    fprintf(stderr, " -n network_address\n");
    fprintf(stderr, " -e endpoint\n");
    fprintf(stderr, " -o options\n");
    fprintf(stderr, " -1 parameter_1\n");
    fprintf(stderr, " -2 parameter_2\n");
    fprintf(stderr, " -3 parameter_3\n");
    exit(1);
}

void _CRTAPI1 main(int argc, char **argv)
{
    RPC_STATUS status;
    unsigned char * pszUuid             = NULL;
    unsigned char * pszProtocolSequence = "ncacn_np";
    unsigned char * pszNetworkAddress   = NULL;
    unsigned char * pszEndpoint         = "\\pipe\\inout";
    unsigned char * pszOptions          = NULL;
    unsigned char * pszStringBinding    = NULL;
    short   s1 = 257;
    short   s2 = 631;
    float   f3 = (float) 0.406;
    int i;

    /* allow the user to override settings with command line switches */
    for (i = 1; i < argc; i++) {
        if ((*argv[i] == '-') || (*argv[i] == '/')) {
            switch (tolower(*(argv[i]+1))) {
            case 'p':  // protocol sequence
                pszProtocolSequence = argv[++i];
                break;
            case 'n':  // network address
                pszNetworkAddress = argv[++i];
                break;
            case 'e':
                pszEndpoint = argv[++i];
                break;
            case 'o':
                pszOptions = argv[++i];
                break;
            case '1':
                s1 = (short) atoi(argv[++i]);
                break;
            case '2':
                s2 = (short) atoi(argv[++i]);
                break;
            case '3':
                f3 = (float) atof(argv[++i]);
                break;
            case 'h':
            case '?':
            default:
                Usage(argv[0]);
            }
        }
        else
            Usage(argv[0]);
    }

    printf("%s", DESCRIPTION);

    /* Use a convenience function to concatenate the elements of  */
    /* the string binding into the proper sequence.               */
    status = RpcStringBindingCompose(pszUuid,
                                     pszProtocolSequence,
                                     pszNetworkAddress,
                                     pszEndpoint,
                                     pszOptions,
                                     &pszStringBinding);
    printf("RpcStringBindingCompose returned 0x%x\n", status);
    printf("pszStringBinding = %s\n", pszStringBinding);
    if (status) {
        exit(status);
    }

    /* Set the binding handle that will be used to bind to the server. */
    status = RpcBindingFromStringBinding(pszStringBinding,
                                         &inout_IfHandle);
    printf("RpcBindingFromStringBinding returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    printf("Calling the remote procedure 'InOutProc'\n");
    printf("  parameters = %d %d %0.3f\n", s1, s2, f3);

    RpcTryExcept {
        InOutProc(s1, &s2, &f3);  // call the remote procedure

        printf("Returning from the remote procedure 'InOutProc'\n");
        printf("  parameters = %d %d %0.3f\n", s1, s2, f3);

        Shutdown();
    }
    RpcExcept(1) {
        printf("Runtime reported exception %ld\n", RpcExceptionCode() );
    }
    RpcEndExcept

    /* The call to the remote procedure is complete. */
    /* Free the string and binding handle.           */
    status = RpcBindingFree(&inout_IfHandle);
    printf("RpcBindingFree returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    status = RpcStringFree(&pszStringBinding);
    printf("RpcStringFree returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    exit(0);

}  // end main()

/*********************************************************************/
/*                 MIDL allocate and free                            */
/*********************************************************************/

void __RPC_FAR * __RPC_USER midl_user_allocate(size_t len)
{
    return(malloc(len));
}

void __RPC_USER midl_user_free(void __RPC_FAR * ptr)
{
    free(ptr);
}

/* end file inoutc.c */


INOUTP.C

/****************************************************************************
                  Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                       inout Example

    FILE:       inoutp.c

    PURPOSE:    Remote procedures that are linked with the server
                side of RPC distributed application

    FUNCTIONS:  InOutProc() - demonstrates in, out parameters

    COMMENTS:

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

#include <stdlib.h>
#include <stdio.h>
#include "inout.h"    // header file generated by MIDL compiler

#define CONSTANT 257

void InOutProc(short    s1,
               short * ps2,
               float * pf3)
{
    printf("on entry, *pf3 = %f\n", *pf3);

    *pf3 = (float) s1 / (float) *ps2;
    printf("%d / %d = %0.3f\n", s1, *ps2, *pf3);

    *ps2 = (short)CONSTANT - s1;
    printf("%d - %d = %d\n", CONSTANT, s1, *ps2);

    s1++;

    return;
}

void Shutdown(void)
{
    RPC_STATUS status;

    printf("Calling RpcMgmtStopServerListening\n");
    status = RpcMgmtStopServerListening(NULL);
    printf("RpcMgmtStopServerListening returned: 0x%x\n", status);
    if (status) {
        exit(status);
    }

    printf("Calling RpcServerUnregisterIf\n");
    status = RpcServerUnregisterIf(NULL, NULL, FALSE);
    printf("RpcServerUnregisterIf returned 0x%x\n", status);
    if (status) {
        exit(status);
    }
}

/* end file inoutp.c */


INOUTS.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                        inout Example

    FILE:       inouts.c

    USAGE:      inouts   -p protocol_sequence
                         -e endpoint
                         -m max calls
                         -n min calls
                         -f flag for RpcServerListen

    PURPOSE:    Server side of RPC distributed application inout

    FUNCTIONS:  main() - registers server as RPC server

    COMMENTS:   This distributed application demonstrates in, out
                parameters.

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

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include "inout.h"    // header file generated by MIDL compiler

#define PURPOSE \
"This Microsoft RPC Version 2.0 sample program demonstrates\n\
the [in] and [out] directional attributes. For more information\n\
about these attributes and the RPC API functions, see the\n\
RPC programming guide and reference.\n\n"

void Usage(char * pszProgramName)
{
    fprintf(stderr, "%s", PURPOSE);
    fprintf(stderr, "Usage:  %s\n", pszProgramName);
    fprintf(stderr, " -p protocol_sequence\n");
    fprintf(stderr, " -e endpoint\n");
    fprintf(stderr, " -m maxcalls\n");
    fprintf(stderr, " -n mincalls\n");
    fprintf(stderr, " -f flag_wait_op\n");
    exit(1);
}

void _CRTAPI1 main(int argc, char * argv[])
{
    RPC_STATUS status;
    unsigned char * pszProtocolSequence = "ncacn_np";
    unsigned char * pszSecurity         = NULL;
    unsigned char * pszEndpoint         = "\\pipe\\inout";
    unsigned int    cMinCalls           = 1;
    unsigned int    cMaxCalls           = 20;
    unsigned int    fDontWait           = FALSE;
    int i;

    /* allow the user to override settings with command line switches */
    for (i = 1; i < argc; i++) {
        if ((*argv[i] == '-') || (*argv[i] == '/')) {
            switch (tolower(*(argv[i]+1))) {
            case 'p':  // protocol sequence
                pszProtocolSequence = argv[++i];
                break;
            case 'e':
                pszEndpoint = argv[++i];
                break;
            case 'm':
                cMaxCalls = (unsigned int) atoi(argv[++i]);
                break;
            case 'n':
                cMinCalls = (unsigned int) atoi(argv[++i]);
                break;
            case 'f':
                fDontWait = (unsigned int) atoi(argv[++i]);
                break;
            case 'h':
            case '?':
            default:
                Usage(argv[0]);
            }
        }
        else
            Usage(argv[0]);
    }

    status = RpcServerUseProtseqEp(pszProtocolSequence,
                                   cMaxCalls,
                                   pszEndpoint,
                                   pszSecurity);  // Security descriptor
    printf("RpcServerUseProtseqEp returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    status = RpcServerRegisterIf(inout_ServerIfHandle,  // interface to register
                                 NULL,   // MgrTypeUuid
                                 NULL);  // MgrEpv; null means use default
    printf("RpcServerRegisterIf returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    printf("Calling RpcServerListen\n");
    status = RpcServerListen(cMinCalls,
                             cMaxCalls,
                             fDontWait);
    printf("RpcServerListen returned: 0x%x\n", status);
    if (status) {
        exit(status);
    }

    if (fDontWait) {
        printf("Calling RpcMgmtWaitServerListen\n");
        status = RpcMgmtWaitServerListen();  //  wait operation
        printf("RpcMgmtWaitServerListen returned: 0x%x\n", status);
        if (status) {
            exit(status);
        }
    }

}  // end main()

/*********************************************************************/
/*                 MIDL allocate and free                            */
/*********************************************************************/

void __RPC_FAR * __RPC_USER midl_user_allocate(size_t len)
{
    return(malloc(len));
}

void __RPC_USER midl_user_free(void __RPC_FAR * ptr)
{
    free(ptr);
}

/* end file inouts.c */


INOUT.ACF

[implicit_handle(handle_t inout_IfHandle)]
interface inout
{

}


README.TXT

REPAS

The REPAS program demonstrates the represent_as ACF attribute.

FILES
=====

The directory samples\rpc\data\repas contains the following files for
building the sample distributed application REPAS:

File          Description

README.TXT    Readme file for the REPAS sample
REPAS.IDL     Interface definition language file
REPASC.ACF    Client version of Attribute configuration file
REPASS.ACF    Server version of Attribute configuration file
REPASC.C      Client main program
REPASS.C      Server main program
REPASP.C      Remote procedures
REPASCU.C     Client Utility functions
REPASSU.C     Server Utility functions
MAKEFILE      Nmake file to build for Windows NT or Windows 95

-------------------------------------------
BUILDING CLIENT AND SERVER APPLICATIONS FOR
MICROSOFT WINDOWS NT OR WINDOWS 95
-------------------------------------------

The following environment variables should already be set for you:

  set CPU=i386
  set INCLUDE=%SDKROOT%\h
  set LIB=%SDKROOT%\lib
  set PATH=%SDKROOT%\system32;%SDKROOT%\bin

where %SDKROOT% is the root directory for the 32-bit Windows SDK.

For mips, set CPU=mips
For alpha, set CPU=alpha

Build the sample distributed application:

  nmake cleanall
  nmake

This builds the executable programs repasc.exe (client) and
repass.exe (server).

-----------------------------------------------------------------------
BUILDING THE CLIENT APPLICATION FOR MS-DOS
-----------------------------------------------------------------------

DOS build not yet ready...

------------------------------------------
RUNNING THE CLIENT AND SERVER APPLICATIONS
------------------------------------------

On the server, enter:

  repass

On the client, enter:

  net start workstation
  repasc

Note:  The client and server applications can run on the same Microsoft
Windows NT computer when you use different screen groups.

Several command line switches are available to change settings for this
program. For a listing of the switches available from the client program,
enter:

  repasc -?

For a listing of switches available from the server
program, enter:

  repass -?


REPAS.IDL

[ uuid(56BF67C0-6503-1068-BF67-00DD010FBF25),
  version(1.0),
  pointer_default(unique)
]
interface repas
{

const short STRING_SIZE = 100;

typedef char       CHAR_STRING[STRING_SIZE];
typedef wchar_t    WCHAR_STRING[STRING_SIZE];

// note that the client and server side need different header files,
// since the represent_as ACF attribute changes the application's
// signatures for the next two functions

// client side functions defined with ASCII for both functions
// server side functions defined with UNICODE for both functions

// this function uses UNICODE on the wire (converted by client stub)
void ModifyMyWString([in, out] WCHAR_STRING *pStr);

// this function uses ASCII on the wire (converted by server stub)
void ModifyMyString([in, out] CHAR_STRING * pStr );

void Shutdown(void);

}


REPASC.ACF

[implicit_handle(handle_t hrepas)]
interface repas
{
typedef [represent_as( CHAR_STRING )] WCHAR_STRING;

}


REPASC.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                       repas Example

    FILE:       repasc.c

    USAGE:      repasc  -n network_address
                       -p protocol_sequence
                       -e endpoint
                       -o options
                       -c count of elements in linked list
                       -v value of first element in linked list
                       -d delta between values in linked list

    PURPOSE:    Client side of RPC distributed application.
                This sample demonstrates the represent_as example.
                A char string is sent over the network as a unicode string.

    RELATED:    repass.c - server main
                repasp.c - remote procedures
                repascu.c - client utility procedures

    FUNCTIONS:  main() - bind to server and call remote procedure

    COMMENTS:   This sample program generates a client and server can share
                an interface, but one side can use a different representation
                than the other.

                The client side in this example does all operations using
                character strings, and the server side does all operations
                using UNICODE strings.  Two procedures are provided, one
                defined with ASCII strings, one with UNICODE strings.
                The wire format reflects these definitions, yet the client
                and server see pure ASCII and pure UNICODE respectively.

                The [represent_as] attribute (used in the client and server
                side acf files) requires the four user-supplied functions
                whose names start with the name of the transmitted type
                (in the client side's case: WCHAR_STRING)

                The [in, out] attributes applied to remote procedure
                parameters require the two user-supplied functions
                midl_user_allocate and midl_user_free.

                The other functions are utilities that are used to
                build or display the data structures.

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

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include "repasc.h"    // client's side header file generated by MIDL compiler

#define PURPOSE \
"This Microsoft RPC Version 2.0 sample program demonstrates\n\
the use of the [represent_as] attribute. For more information\n\
about the attributes and the RPC API functions, see the\n\
RPC programming guide and reference.\n\n"

#define MAX_ELEMENTS 50

void Usage(char * pszProgramName)
{
    fprintf(stderr, "%s", PURPOSE);
    fprintf(stderr, "Usage:  %s\n", pszProgramName);
    fprintf(stderr, " -p protocol_sequence\n");
    fprintf(stderr, " -n network_address\n");
    fprintf(stderr, " -e endpoint\n");
    fprintf(stderr, " -o options\n");
    fprintf(stderr, " -c count_of_elements\n");
    fprintf(stderr, " -v value\n");
    fprintf(stderr, " -d delta\n");
    exit(1);
}

void _CRTAPI1 main(int argc, char **argv)
{
    RPC_STATUS status;
    unsigned char * pszUuid             = NULL;
    unsigned char * pszProtocolSequence = "ncacn_np";
    unsigned char * pszNetworkAddress   = NULL;
    unsigned char * pszEndpoint         = "\\pipe\\repas";
    unsigned char * pszOptions          = NULL;
    unsigned char * pszStringBinding    = NULL;
    int i;
    int cElements = 10;
    short sValue = 100;
    short sDelta = 10;

    char    FirstBuffer[100];

    /* allow the user to override settings with command line switches */
    for (i = 1; i < argc; i++) {
        if ((*argv[i] == '-') || (*argv[i] == '/')) {
            switch (tolower(*(argv[i]+1))) {
            case 'p':  // protocol sequence
                pszProtocolSequence = argv[++i];
                break;
            case 'n':  // network address
                pszNetworkAddress = argv[++i];
                break;
            case 'e':
                pszEndpoint = argv[++i];
                break;
            case 'o':
                pszOptions = argv[++i];
                break;
            case 'c':
                cElements = atoi(argv[++i]);
                if (cElements > MAX_ELEMENTS)
                    cElements = MAX_ELEMENTS;
                break;
            case 'v':
                sValue = (short)atoi(argv[++i]);
                break;
            case 'd':
                sDelta = (short)atoi(argv[++i]);
                break;
            case 'h':
            case '?':
            default:
                Usage(argv[0]);
            }
        }
        else
            Usage(argv[0]);
    }

    /* Use a convenience function to concatenate the elements of the string */
    /* binding into the syntax needed by RpcBindingFromStringBinding.       */
    status = RpcStringBindingCompose(pszUuid,
                                     pszProtocolSequence,
                                     pszNetworkAddress,
                                     pszEndpoint,
                                     pszOptions,
                                     &pszStringBinding);
    printf("RpcStringBindingCompose returned 0x%x\n", status);
    printf("pszStringBinding = %s\n", pszStringBinding);
    if (status) {
        exit(status);
    }

    /* Set the binding handle that will be used to bind to the server. */
    status = RpcBindingFromStringBinding(pszStringBinding,
                                         &hrepas);
    printf("RpcBindingFromStringBinding returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    RpcTryExcept {
        // initialize a string to be shipped
        strcpy(FirstBuffer,"This ASCII string is sent from the client to the server as UNICODE");

        printf("\nThe Client side is about to send:\n\"%s\"\n\n", FirstBuffer );

        printf("Calling the remote procedure 'ModifyMyWString'\n");
        // note that the client sees the interface using ascii, not unicode
        ModifyMyWString( &FirstBuffer );
        printf("The Client side got back:\n\"%s\"\n\n", FirstBuffer );

        // initialize a string to be shipped
        strcpy(FirstBuffer,"And this ASCII string is sent from the client to the server as ASCII");

        printf("The Client side is about to send:\n\"%s\"\n\n", FirstBuffer );
        printf("Calling the remote procedure 'ModifyMyString'\n");
        // note that the client sees the interface using ascii
        ModifyMyString( &FirstBuffer );
        printf("The Client side got back:\n\"%s\"\n\n", FirstBuffer );

        printf("Calling the remote procedure 'Shutdown'\n");
        Shutdown();  // shut down the server side
    }
    RpcExcept(1) {
        printf("Runtime reported exception %ld\n", RpcExceptionCode() );
    }
    RpcEndExcept

    /* The calls to the remote procedures are complete.            */
    /* Free the string and the binding handle using RPC API calls. */
    status = RpcStringFree(&pszStringBinding);
    printf("RpcStringFree returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    status = RpcBindingFree(&hrepas);
    printf("RpcBindingFree returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    exit(0);

}  // end main()

/* end file repasc.c */


REPASCU.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                        repas Example

    FILE:       repasu.c

    PURPOSE:    Utility functions used by both client and server
                sides of the RPC distributed application.
                This sample demonstrates the transmit_as example.
                A doubly-linked list is transmitted over the network
                as a sized array.

    RELATED:    repass.c - server main
                repasp.c - remote procedures
                repasc.c - client main

    FUNCTIONS:  CHAR_STRING_to_local    - convert CHAR_STRING to WCHAR_STRING
                CHAR_STRING_from_local  - convert WCHAR_STRING to CHAR_STRING
                CHAR_STRING_free_inst   - free WCHAR_STRING memory
                CHAR_STRING_free_local  - free CHAR_STRING memory
                midl_user_allocate - user-supplied memory allocator
                midl_user_free - user-supplied routine to free memory

    COMMENTS:   This sample program generates a client and server can share
                an interface, but one side can use a different representation
                than the other.

                The client side in this example does all operations using
                character strings, and the server side does all operations
                using UNICODE strings.  Two procedures are provided, one
                defined with ASCII strings, one with UNICODE strings.
                The wire format reflects these definitions, yet the client
                and server see pure ASCII and pure UNICODE respectively.

                The [represent_as] attribute (used in the client and server
                side acf files) requires the four user-supplied functions
                whose names start with the name of the transmitted type
                (in the client side's case: WCHAR_STRING)

                The [in, out] attributes applied to remote procedure
                parameters require the two user-supplied functions
                midl_user_allocate and midl_user_free.

                The other functions are utilities that are used to
                build or display the data structures.

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

#include <stdlib.h>
#include <stdio.h>
#include "repasc.h"    // header file generated by MIDL compiler for client

void __RPC_USER
WCHAR_STRING_from_local(
    CHAR_STRING __RPC_FAR * pLocal,
    WCHAR_STRING __RPC_FAR * __RPC_FAR * pWire )
{
    WCHAR_STRING    *   pWireString;

    pWireString = midl_user_allocate( sizeof( WCHAR_STRING ) );

    *pWire = pWireString;

    mbstowcs( *pWireString, *pLocal, STRING_SIZE );
}

void __RPC_USER
WCHAR_STRING_to_local(
    WCHAR_STRING __RPC_FAR * pWire,
    CHAR_STRING __RPC_FAR * pLocal )
{
    wcstombs( *pLocal, *pWire, STRING_SIZE );
}

void __RPC_USER
WCHAR_STRING_free_inst(
    WCHAR_STRING __RPC_FAR * pWire)
{
    midl_user_free( pWire );
}

void __RPC_USER
WCHAR_STRING_free_local(
    CHAR_STRING __RPC_FAR * pLocal)
{
    midl_user_free( pLocal );
}

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

void __RPC_FAR * __RPC_USER midl_user_allocate(size_t len)
{
    return(malloc(len));
}

void __RPC_USER midl_user_free(void __RPC_FAR * ptr)
{
    free(ptr);
}

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

/* end file repascu.c */


REPASP.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                         repas Example

    FILE:       repasp.c

    PURPOSE:    Remote procedures that are linked with the server
                side of RPC distributed application

    FUNCTIONS:  ModifyListProc() - changes the doubly-linked list
                Shutdown() - shuts down the server side

    COMMENTS:   Related to repass.c

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

#include <stdlib.h>
#include <stdio.h>
#include "repass.h"    // header file generated by MIDL compiler

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

Function:   ModifyMyWString

Parameters: pStr : Pointer to pointer to UNICODE string

Returns:    none

Purpose:    Display the string passed in, modify it, and return

Comments:   This sample is meant to demonstrate a typical use of the
            represent_as attribute:  The client and server have different
            local views of the data, although the IDL file describes the
            wire contract.

****************************************************************************/
void ModifyMyWString(WCHAR_STRING * pStr)
{
    wprintf(L"\nModifyMyWString: received UNICODE string:\n%s\n\n", *pStr );
    wcscpy(*pStr, L"This string comes back on the wire as UNICODE");
    wprintf(L"ModifyMyWString: sending UNICODE string:\n%s\n\n", *pStr );

}

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

Function:   ModifyMyString

Parameters: pStr : Pointer to pointer to UNICODE string

Returns:    none

Purpose:    Display the string passed in, modify it, and return

Comments:   This sample is meant to demonstrate a typical use of the
            represent_as attribute:  The client and server have different
            local views of the data, although the IDL file describes the
            wire contract.

****************************************************************************/
void ModifyMyString(WCHAR_STRING * pStr)
{
    wprintf(L"\nModifyMyString: received UNICODE string:\n%s\n\n", *pStr );
    wcscpy(*pStr, L"This UNICODE string comes back on the wire as ASCII");
    wprintf(L"ModifyMyString: sending UNICODE string:\n%s\n\n", *pStr );

}

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

Function:   Shutdown

Parameters: none

Returns:    none

Purpose:    Make the server stop listening for client applications.

Comments:   The two NULL parameters passed to RpcServerUnregisterIf are
            a show of brute force:  they tell the function to turn
            off all registered interfaces.  See the RPC API function
            reference for more information about these functions.

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

void Shutdown(void)
{
    RPC_STATUS status;

    printf("Calling RpcMgmtStopServerListening\n");
    status = RpcMgmtStopServerListening(NULL);
    printf("RpcMgmtStopServerListening returned: 0x%x\n", status);
    if (status) {
        exit(status);
    }

    printf("Calling RpcServerUnregisterIf\n");
    status = RpcServerUnregisterIf(NULL, NULL, FALSE);
    printf("RpcServerUnregisterIf returned 0x%x\n", status);
    if (status) {
        exit(status);
    }
}

/* end file repasp.c */


REPASS.ACF

[implicit_handle(handle_t hRepas)]
interface repas
{
typedef [represent_as( WCHAR_STRING )] CHAR_STRING;

}


REPASS.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                        repas Example

    FILE:       repass.c

    USAGE:      repass -p protocol_sequence
                       -e endpoint
                       -m max calls
                       -n min calls
                       -f flag for RpcServerListen

    PURPOSE:    Server side of RPC distributed application repas

    FUNCTIONS:  main() - registers server as RPC server

    COMMENTS:   This sample program generates a client and server can share
                an interface, but one side can use a different representation
                than the other.

                The client side in this example does all operations using
                character strings, and the server side does all operations
                using UNICODE strings.  Two procedures are provided, one
                defined with ASCII strings, one with UNICODE strings.
                The wire format reflects these definitions, yet the client
                and server see pure ASCII and pure UNICODE respectively.

                The [represent_as] attribute (used in the client and server
                side acf files) requires the four user-supplied functions
                whose names start with the name of the transmitted type
                (in the client side's case: WCHAR_STRING)

                The [in, out] attributes applied to remote procedure
                parameters require the two user-supplied functions
                midl_user_allocate and midl_user_free.

                The other functions are utilities that are used to
                build or display the data structures.

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

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include "repass.h"     // header file generated by MIDL compiler

#define PURPOSE \
"This Microsoft RPC Version 2.0 sample program demonstrates\n\
the use of the [transmit_as] attribute. For more information\n\
about the attributes and the RPC API functions, see the\n\
RPC programming guide and reference.\n\n"

void Usage(char * pszProgramName)
{
    fprintf(stderr, "%s", PURPOSE);
    fprintf(stderr, "Usage:  %s\n", pszProgramName);
    fprintf(stderr, " -p protocol_sequence\n");
    fprintf(stderr, " -e endpoint\n");
    fprintf(stderr, " -m maxcalls\n");
    fprintf(stderr, " -n mincalls\n");
    fprintf(stderr, " -f flag_wait_op\n");
    exit(1);
}

/* main:  register the interface, start listening for clients */
void _CRTAPI1 main(int argc, char * argv[])
{
    RPC_STATUS status;
    unsigned char * pszProtocolSequence = "ncacn_np";
    unsigned char * pszSecurity         = NULL;
    unsigned char * pszEndpoint         = "\\pipe\\repas";
    unsigned int    cMinCalls           = 1;
    unsigned int    cMaxCalls           = 20;
    unsigned int    fDontWait           = FALSE;
    int i;

    /* allow the user to override settings with command line switches */
    for (i = 1; i < argc; i++) {
        if ((*argv[i] == '-') || (*argv[i] == '/')) {
            switch (tolower(*(argv[i]+1))) {
            case 'p':  // protocol sequence
                pszProtocolSequence = argv[++i];
                break;
            case 'e':
                pszEndpoint = argv[++i];
                break;
            case 'm':
                cMaxCalls = (unsigned int) atoi(argv[++i]);
                break;
            case 'n':
                cMinCalls = (unsigned int) atoi(argv[++i]);
                break;
            case 'f':
                fDontWait = (unsigned int) atoi(argv[++i]);
                break;
            case 'h':
            case '?':
            default:
                Usage(argv[0]);
            }
        }
        else
            Usage(argv[0]);
    }

    status = RpcServerUseProtseqEp(pszProtocolSequence,
                                   cMaxCalls,
                                   pszEndpoint,
                                   pszSecurity);  // Security descriptor
    printf("RpcServerUseProtseqEp returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    status = RpcServerRegisterIf(repas_ServerIfHandle,  // interface to register
                                 NULL,   // MgrTypeUuid
                                 NULL);  // MgrEpv; null means use default
    printf("RpcServerRegisterIf returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    printf("Calling RpcServerListen\n");
    status = RpcServerListen(cMinCalls,
                             cMaxCalls,
                             fDontWait);
    printf("RpcServerListen returned: 0x%x\n", status);
    if (status) {
        exit(status);
    }

    if (fDontWait) {
        printf("Calling RpcMgmtWaitServerListen\n");
        status = RpcMgmtWaitServerListen();  //  wait operation
        printf("RpcMgmtWaitServerListen returned: 0x%x\n", status);
        if (status) {
            exit(status);
        }
    }

}  // end main()

/* end file repass.c */


REPASSU.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                        repas Example

    FILE:       repasu.c

    PURPOSE:    Utility functions used by both client and server
                sides of the RPC distributed application.
                This sample demonstrates the transmit_as example.
                A doubly-linked list is transmitted over the network
                as a sized array.

    RELATED:    repass.c - server main
                repasp.c - remote procedures
                repasc.c - client main

    FUNCTIONS:  WCHAR_STRING_to_local    - convert WCHAR_STRING to CHAR_STRING
                WCHAR_STRING_from_local  - convert CHAR_STRING to WCHAR_STRING
                WCHAR_STRING_free_inst   - free CHAR_STRING memory
                WCHAR_STRING_free_local  - free WCHAR_STRING memory
                midl_user_allocate - user-supplied memory allocator
                midl_user_free - user-supplied routine to free memory

    COMMENTS:   This sample program generates a client and server can share
                an interface, but one side can use a different representation
                than the other.

                The client side in this example does all operations using
                character strings, and the server side does all operations
                using UNICODE strings.  Two procedures are provided, one
                defined with ASCII strings, one with UNICODE strings.
                The wire format reflects these definitions, yet the client
                and server see pure ASCII and pure UNICODE respectively.

                The [represent_as] attribute (used in the client and server
                side acf files) requires the four user-supplied functions
                whose names start with the name of the transmitted type
                (in the client side's case: WCHAR_STRING)

                The [in, out] attributes applied to remote procedure
                parameters require the two user-supplied functions
                midl_user_allocate and midl_user_free.

                The other functions are utilities that are used to
                build or display the data structures.

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

#include <stdlib.h>
#include <stdio.h>
#include "repass.h"    // header file generated by MIDL compiler for client

void __RPC_USER
CHAR_STRING_from_local(
    WCHAR_STRING __RPC_FAR * pLocal,
    CHAR_STRING __RPC_FAR * __RPC_FAR * pWire )
{
    CHAR_STRING    *   pWireString;

    pWireString = midl_user_allocate( sizeof( CHAR_STRING ) );

    *pWire = pWireString;

    wcstombs( *pWireString, *pLocal, STRING_SIZE );
}

void __RPC_USER
CHAR_STRING_to_local(
    CHAR_STRING __RPC_FAR * pWire,
    WCHAR_STRING __RPC_FAR * pLocal )
{
    mbstowcs( *pLocal, *pWire, STRING_SIZE );
}

void __RPC_USER
CHAR_STRING_free_inst(
    CHAR_STRING __RPC_FAR * pWire)
{
    midl_user_free( pWire );
}

void __RPC_USER
CHAR_STRING_free_local(
    WCHAR_STRING __RPC_FAR * pLocal)
{
    midl_user_free( pLocal );
}

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

void __RPC_FAR * __RPC_USER midl_user_allocate(size_t len)
{
    return(malloc(len));
}

void __RPC_USER midl_user_free(void __RPC_FAR * ptr)
{
    free(ptr);
}

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

/* end file repassu.c */


README.TXT

XMIT

The XMIT program demonstrates the type-conversion attribute transmit_as.

FILES
=====

The directory samples\rpc\data\xmit contains the following files for
building the sample distributed application XMIT:

File          Description

README.TXT    Readme file for the XMIT sample
XMIT.IDL      Interface definition language file
XMIT.ACF      Attribute configuration file
XMITC.C       Client main program
XMITS.C       Server main program
XMITP.C       Remote procedures
XMITU.C       Utility functions for client, server
XMITU.H       Utility function prototypes
MAKEFILE      Nmake file to build for Windows NT or Windows 95
MAKEFILE.DOS  Nmake file to build for MS-DOS

-------------------------------------------
BUILDING CLIENT AND SERVER APPLICATIONS FOR
MICROSOFT WINDOWS NT OR WINDOWS 95:
-------------------------------------------

The following environment variables should be set for you already.

  set CPU=i386
  set INCLUDE=%SDKROOT%\h
  set LIB=%SDKROOT%\lib
  set PATH=%SDKROOT%\system32;%SDKROOT%\bin;

Where %SDKROOT% is the root directory for the 32-bit Windows SDK.

For mips, set CPU=mips
For alpha, set CPU=alpha

Build the sample distributed application:
  nmake cleanall
  nmake

This builds the executable programs xmitc.exe
(client) and xmits.exe (server).

-----------------------------------------------------------------------
BUILDING THE CLIENT APPLICATION FOR MS-DOS
-----------------------------------------------------------------------

After installing the Microsoft Visual C/C++ version 1.50 development
environment and the 16-bit RPC SDK on a Windows NT or Windows 95
computer, you can build the sample client application from Windows NT
or Windows 95.

  nmake -f makefile.dos cleanall
  nmake -f makefile.dos

This builds the client application xmitc.exe.

You may also execute the Microsoft Visual C/C++ compiler under MS-DOS.
This requires a two step build process.

  Step One: Compile the .IDL files under Windows NT or Windows 95
     nmake -a -f makefile.dos xmit.idl

  Step Two: Compile the C sources (stub and application) under MS-DOS
     nmake -f makefile.dos

------------------------------------------
RUNNING THE CLIENT AND SERVER APPLICATIONS
------------------------------------------

On the server, enter

  xmits

On the client, enter

  net start workstation
  xmitc

Note:  The client and server applications can run on
the same Microsoft Windows NT computer when you use
different screen groups.

Several command line switches are available to change
settings for this program. For a listing of the switches
available from the client program, enter

  xmitc -?

For a listing of switches available from the server
program, enter

  xmits -?


XMIT.ACF

[implicit_handle(handle_t hXmit)]
interface xmit
{

}


XMIT.IDL

[ uuid(56BF67C0-6503-1068-BF67-00DD010FBF25),
  version(1.0),
  pointer_default(unique)
]
interface xmit
{

typedef struct _DOUBLE_LINK_LIST {
    short sNumber;
    struct _DOUBLE_LINK_LIST * pNext;
    struct _DOUBLE_LINK_LIST * pPrevious;
} DOUBLE_LINK_LIST;

typedef struct _DOUBLE_XMIT_TYPE {
    short sSize;
    [size_is(sSize)] short asNumber[];
} DOUBLE_XMIT_TYPE;

/* transmit a double-linked list as an array */
typedef [transmit_as(DOUBLE_XMIT_TYPE)] DOUBLE_LINK_LIST DOUBLE_LINK_TYPE;

void ModifyListProc([in, out] DOUBLE_LINK_TYPE *pHead);

void Shutdown(void);

}


XMITC.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                       xmit Example

    FILE:       xmitc.c

    USAGE:      xmitc  -n network_address
                       -p protocol_sequence
                       -e endpoint
                       -o options
                       -c count of elements in linked list
                       -v value of first element in linked list
                       -d delta between values in linked list

    PURPOSE:    Client side of RPC distributed application.
                This sample demonstrates the transmit_as example.
                A doubly-linked list is transmitted over the network
                as a sized array.

    RELATED:    xmits.c - server main
                xmitp.c - remote procedures
                xmitu.c - utility procedures

    FUNCTIONS:  main() - bind to server and call remote procedure

    COMMENTS:   This sample program generates a linked list to
                demonstrate how a list with aliasing can be transmitted
                using the transmit_as attribute as a sized array.
                The pointers are rebuilt on the server side.

                The [transmit_as] attribute (used in the typedef of
                DOUBLE_LINK_TYPE in the file XMIT.IDL) requires the
                four user-supplied functions whose names start with
                the name of the presented type, DOUBLE_LINK_TYPE.

                The [in, out] attributes applied to remote procedure
                parameters require the two user-supplied functions
                midl_user_allocate and midl_user_free.

                The other functions are utilities that are used to
                build or display the data structures.

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

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include "xmit.h"     // header file generated by MIDL compiler
#include "xmitu.h"    // utility function prototypes

#define PURPOSE \
"This Microsoft RPC Version 2.0 sample program demonstrates\n\
the use of the [transmit_as] attribute. For more information\n\
about the attributes and the RPC API functions, see the\n\
RPC programming guide and reference.\n\n"

#define MAX_ELEMENTS 50

void Usage(char * pszProgramName)
{
    fprintf(stderr, "%s", PURPOSE);
    fprintf(stderr, "Usage:  %s\n", pszProgramName);
    fprintf(stderr, " -p protocol_sequence\n");
    fprintf(stderr, " -n network_address\n");
    fprintf(stderr, " -e endpoint\n");
    fprintf(stderr, " -o options\n");
    fprintf(stderr, " -c count_of_elements\n");
    fprintf(stderr, " -v value\n");
    fprintf(stderr, " -d delta\n");
    exit(1);
}

void _CRTAPI1 main(int argc, char **argv)
{
    RPC_STATUS status;
    unsigned char * pszUuid             = NULL;
    unsigned char * pszProtocolSequence = "ncacn_np";
    unsigned char * pszNetworkAddress   = NULL;
    unsigned char * pszEndpoint         = "\\pipe\\xmit";
    unsigned char * pszOptions          = NULL;
    unsigned char * pszStringBinding    = NULL;
    int i;
    int cElements = 10;
    short sValue = 100;
    short sDelta = 10;

    DOUBLE_LINK_TYPE *pFirst, *pCurrent;

    /* allow the user to override settings with command line switches */
    for (i = 1; i < argc; i++) {
        if ((*argv[i] == '-') || (*argv[i] == '/')) {
            switch (tolower(*(argv[i]+1))) {
            case 'p':  // protocol sequence
                pszProtocolSequence = argv[++i];
                break;
            case 'n':  // network address
                pszNetworkAddress = argv[++i];
                break;
            case 'e':
                pszEndpoint = argv[++i];
                break;
            case 'o':
                pszOptions = argv[++i];
                break;
            case 'c':
                cElements = atoi(argv[++i]);
                if (cElements > MAX_ELEMENTS)
                    cElements = MAX_ELEMENTS;
                break;
            case 'v':
                sValue = (short)atoi(argv[++i]);
                break;
            case 'd':
                sDelta = (short)atoi(argv[++i]);
                break;
            case 'h':
            case '?':
            default:
                Usage(argv[0]);
            }
        }
        else
            Usage(argv[0]);
    }

    /* initialize a list with a number of elements */
    pFirst = InsertNewNode(sValue, NULL);
    pCurrent = pFirst;   // assign some values to the list nodes
    sValue += sDelta;    // make them different values

    for (i = 1; i < cElements; i++) {
        pCurrent = InsertNewNode(sValue, pCurrent);
        sValue += sDelta;
    }
    ListWalkProc(pFirst);

    /* Use a convenience function to concatenate the elements of the string */
    /* binding into the syntax needed by RpcBindingFromStringBinding.       */
    status = RpcStringBindingCompose(pszUuid,
                                     pszProtocolSequence,
                                     pszNetworkAddress,
                                     pszEndpoint,
                                     pszOptions,
                                     &pszStringBinding);
    printf("RpcStringBindingCompose returned 0x%x\n", status);
    printf("pszStringBinding = %s\n", pszStringBinding);
    if (status) {
        exit(status);
    }

    /* Set the binding handle that will be used to bind to the server. */
    status = RpcBindingFromStringBinding(pszStringBinding,
                                         &hXmit);
    printf("RpcBindingFromStringBinding returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    RpcTryExcept {
        printf("Calling the remote procedure 'ModifyListProc'\n");
        ModifyListProc(pFirst);  // call the remote procedure

        printf("Calling the remote procedure 'Shutdown'\n");
        Shutdown();  // shut down the server side
    }
    RpcExcept(1) {
        printf("Runtime reported exception %ld\n", RpcExceptionCode() );
    }
    RpcEndExcept

    printf("After ModifyListProc, the list appears as follows:\n");
    ListWalkProc(pFirst);  // call the utility that displays the list

    /* The calls to the remote procedures are complete.            */
    /* Free the string and the binding handle using RPC API calls. */
    status = RpcStringFree(&pszStringBinding);
    printf("RpcStringFree returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    status = RpcBindingFree(&hXmit);
    printf("RpcBindingFree returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    exit(0);

}  // end main()

/* end file xmitc.c */


XMITP.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                         xmit Example

    FILE:       xmitp.c

    PURPOSE:    Remote procedures that are linked with the server
                side of RPC distributed application

    FUNCTIONS:  ModifyListProc() - changes the doubly-linked list
                Shutdown() - shuts down the server side

    COMMENTS:   Related to xmits.c

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

#include <stdlib.h>
#include <stdio.h>
#include "xmit.h"    // header file generated by MIDL compiler

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

Function:   ModifyListProc

Parameters: pFirst : Pointer to the head of a doubly-linked list.

Returns:    none

Purpose:    Display the list passed to the function.
            For each element in the list, add another element.
            Display the modified list.

Comments:   This sample is meant to demonstrate a typical use of the
            transmit_as attribute:  A complex data structure is simplified
            for transmission over the network, restored on the server,
            then manipulated by the remote function on the server.
            The modified data is returned to the client.

            Calls InsertNewNode(param1, param2), a utility routine
            in xmits.c that creates a new node, assigns to it the
            value param1, and inserts it into the list after param2.

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

DOUBLE_LINK_TYPE * InsertNewNode(short sValue, DOUBLE_LINK_TYPE * pPrevious);

void ModifyListProc(DOUBLE_LINK_TYPE * pFirst)
{
    DOUBLE_LINK_TYPE * pList = pFirst;
    short newNumber;

    printf("ModifyListProc: Display contents of doubly linked list:\n");
    while (pList != NULL) {
         printf("pList @0x%x = %d, Next = 0x%x\n",
                pList, pList->sNumber, pList->pNext);
         pList = pList->pNext;
    }

    printf("ModifyListProc: Add one node for every node in tree\n");
    for (pList = pFirst; pList != NULL; pList = pList->pNext) {
        newNumber = pList->sNumber + 1;
        pList = InsertNewNode(newNumber, pList);
    }

    printf("ModifyListProc: Display modified contents of doubly linked list:\n");
    for (pList = pFirst; pList != NULL; pList = pList->pNext) {
        printf("pList @0x%x = %d, Next = 0x%x\n",
               pList, pList->sNumber, pList->pNext);
    }
}

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

Function:   Shutdown

Parameters: none

Returns:    none

Purpose:    Make the server stop listening for client applications.

Comments:   The two NULL parameters passed to RpcServerUnregisterIf are
            a show of brute force:  they tell the function to turn
            off all registered interfaces.  See the RPC API function
            reference for more information about these functions.

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

void Shutdown(void)
{
    RPC_STATUS status;

    printf("Calling RpcMgmtStopServerListening\n");
    status = RpcMgmtStopServerListening(NULL);
    printf("RpcMgmtStopServerListening returned: 0x%x\n", status);
    if (status) {
        exit(status);
    }

    printf("Calling RpcServerUnregisterIf\n");
    status = RpcServerUnregisterIf(NULL, NULL, FALSE);
    printf("RpcServerUnregisterIf returned 0x%x\n", status);
    if (status) {
        exit(status);
    }
}

/* end file xmitp.c */


XMITS.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                        xmit Example

    FILE:       xmits.c

    USAGE:      xmits  -p protocol_sequence
                       -e endpoint
                       -m max calls
                       -n min calls
                       -f flag for RpcServerListen

    PURPOSE:    Server side of RPC distributed application xmit

    FUNCTIONS:  main() - registers server as RPC server

    COMMENTS:   This sample program generates a linked list to
                demonstrate how the list can be transmitted over
                the network more efficiently as a sized array.
                The pointers are rebuilt on the server side.

                The [transmit_as] attribute (used in the typedef of
                DOUBLE_LINK_TYPE in the file XMIT.IDL) requires the
                four user-supplied functions whose names start with
                the name of the presented type, DOUBLE_LINK_TYPE.

                The [in, out] attributes applied to remote procedure
                parameters require the two user-supplied functions
                midl_user_allocate and midl_user_free.

                The other functions are utilities that are used to
                build or display the data structures.

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

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include "xmit.h"     // header file generated by MIDL compiler
#include "xmitu.h"    // Function prototypes for utility functions

#define PURPOSE \
"This Microsoft RPC Version 2.0 sample program demonstrates\n\
the use of the [transmit_as] attribute. For more information\n\
about the attributes and the RPC API functions, see the\n\
RPC programming guide and reference.\n\n"

void Usage(char * pszProgramName)
{
    fprintf(stderr, "%s", PURPOSE);
    fprintf(stderr, "Usage:  %s\n", pszProgramName);
    fprintf(stderr, " -p protocol_sequence\n");
    fprintf(stderr, " -e endpoint\n");
    fprintf(stderr, " -m maxcalls\n");
    fprintf(stderr, " -n mincalls\n");
    fprintf(stderr, " -f flag_wait_op\n");
    exit(1);
}

/* main:  register the interface, start listening for clients */
void _CRTAPI1 main(int argc, char * argv[])
{
    RPC_STATUS status;
    unsigned char * pszProtocolSequence = "ncacn_np";
    unsigned char * pszSecurity         = NULL;
    unsigned char * pszEndpoint         = "\\pipe\\xmit";
    unsigned int    cMinCalls           = 1;
    unsigned int    cMaxCalls           = 20;
    unsigned int    fDontWait           = FALSE;
    int i;

    /* allow the user to override settings with command line switches */
    for (i = 1; i < argc; i++) {
        if ((*argv[i] == '-') || (*argv[i] == '/')) {
            switch (tolower(*(argv[i]+1))) {
            case 'p':  // protocol sequence
                pszProtocolSequence = argv[++i];
                break;
            case 'e':
                pszEndpoint = argv[++i];
                break;
            case 'm':
                cMaxCalls = (unsigned int) atoi(argv[++i]);
                break;
            case 'n':
                cMinCalls = (unsigned int) atoi(argv[++i]);
                break;
            case 'f':
                fDontWait = (unsigned int) atoi(argv[++i]);
                break;
            case 'h':
            case '?':
            default:
                Usage(argv[0]);
            }
        }
        else
            Usage(argv[0]);
    }

    status = RpcServerUseProtseqEp(pszProtocolSequence,
                                   cMaxCalls,
                                   pszEndpoint,
                                   pszSecurity);  // Security descriptor
    printf("RpcServerUseProtseqEp returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    status = RpcServerRegisterIf(xmit_ServerIfHandle,  // interface to register
                                 NULL,   // MgrTypeUuid
                                 NULL);  // MgrEpv; null means use default
    printf("RpcServerRegisterIf returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    printf("Calling RpcServerListen\n");
    status = RpcServerListen(cMinCalls,
                             cMaxCalls,
                             fDontWait);
    printf("RpcServerListen returned: 0x%x\n", status);
    if (status) {
        exit(status);
    }

    if (fDontWait) {
        printf("Calling RpcMgmtWaitServerListen\n");
        status = RpcMgmtWaitServerListen();  //  wait operation
        printf("RpcMgmtWaitServerListen returned: 0x%x\n", status);
        if (status) {
            exit(status);
        }
    }

}  // end main()

/* end file xmits.c */


XMITU.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                        xmit Example

    FILE:       xmitu.c

    PURPOSE:    Utility functions used by both client and server
                sides of the RPC distributed application.
                This sample demonstrates the transmit_as example.
                A doubly-linked list is transmitted over the network
                as a sized array.

    RELATED:    xmits.c - server main
                xmitp.c - remote procedures
                xmitc.c - client main

    FUNCTIONS:  DOUBLE_LINK_TYPE_to_xmit - convert list to array
                DOUBLE_LINK_TYPE_from_xmit - convert array to list
                DOUBLE_LINK_TYPE_free_inst - free linked list memory
                DOUBLE_LINK_TYPE_free_xmit - free array memory
                midl_user_allocate - user-supplied memory allocator
                midl_user_free - user-supplied routine to free memory

                ArrayWalkProc - utility to display the array
                ListWalkProc - utility to display the linked list
                InsertNewNode - utility to add a node to the list

    COMMENTS:   This sample program generates a linked list to
                demonstrate how a list with aliasing can be transmitted
                using the transmit_as attribute as a sized array.
                The pointers are rebuilt on the server side.

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

#include <stdlib.h>
#include <stdio.h>
#include "xmit.h"    // header file generated by MIDL compiler
#include "xmitu.h"

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

void ArrayWalkProc(DOUBLE_XMIT_TYPE * pArray)
{
    int i;

    printf("Display contents of transmitted array:\n");
    for (i = 0; i < pArray->sSize; i++)
        printf("pArray->asNumber[%d] = %d\n", i, pArray->asNumber[i]);
}

void ListWalkProc(DOUBLE_LINK_TYPE * pList)
{
    printf("Display contents of doubly linked list:\n");
    while (pList != NULL) {
        printf("pList @0x%lx = %d, Next = 0x%lx\n",
               pList, pList->sNumber, pList->pNext);
        pList = pList->pNext;
    }
}

DOUBLE_LINK_TYPE * InsertNewNode(short sValue, DOUBLE_LINK_TYPE * pPrevious)
{
    DOUBLE_LINK_TYPE * pNew;

    do {
        pNew = (DOUBLE_LINK_TYPE *)midl_user_allocate(sizeof(DOUBLE_LINK_TYPE));
    } while (pNew == pPrevious);

    pNew->pNext = NULL;      // initialize
    pNew->pPrevious = NULL;  // initialize
    pNew->sNumber = sValue;  // insert b between a and c

    pNew->pPrevious = pPrevious;             // prev(b) = a
    if (pPrevious == NULL)
        pNew->pNext = NULL;
    else {
        pNew->pNext = pPrevious->pNext;      // next(b) = c
        pPrevious->pNext = pNew;             // next(a) = b
        if (pNew->pNext != NULL)
            (pNew->pNext)->pPrevious = pNew; // prev(c) = b
    }

    return(pNew);
}

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

void __RPC_FAR * __RPC_USER midl_user_allocate(size_t len)
{
    return(malloc(len));
}

void __RPC_USER midl_user_free(void __RPC_FAR * ptr)
{
    free(ptr);
}

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

/* convert from linked list to array */
void __RPC_USER
DOUBLE_LINK_TYPE_to_xmit(
    DOUBLE_LINK_TYPE __RPC_FAR * pList,
    DOUBLE_XMIT_TYPE __RPC_FAR * __RPC_FAR * ppArray)
{
    short cCount = 0;
    DOUBLE_LINK_TYPE * pHead = pList;  // save pointer to start
    DOUBLE_XMIT_TYPE * pArray;

    /* count the number of elements to allocate memory */
    for (; pList != NULL; pList = pList->pNext)
        cCount++;

    /* allocate the memory for the array */
    pArray = (DOUBLE_XMIT_TYPE *) midl_user_allocate
             (sizeof(DOUBLE_XMIT_TYPE) + (cCount * sizeof(short)));
    pArray->sSize = cCount;

    /* copy the linked list contents into the array */
    for (cCount = 0, pList = pHead; pList != NULL; pList = pList->pNext)
        pArray->asNumber[cCount++] = pList->sNumber;

    /* return the address of the pointer to the array */
    *ppArray = pArray;
}

/* convert from array to linked list */
void __RPC_USER
DOUBLE_LINK_TYPE_from_xmit(
    DOUBLE_XMIT_TYPE __RPC_FAR * pArray,
    DOUBLE_LINK_TYPE __RPC_FAR * pList)
{
    DOUBLE_LINK_TYPE *pCurrent;
    int i;

    if (pArray->sSize <= 0) {  // error checking
        pList = NULL;
        return;
    }

    if (pList == NULL)
        pList = InsertNewNode(pArray->asNumber[0], NULL);
    else {
        DOUBLE_LINK_TYPE_free_inst(pList);  // free all other nodes
        pList->sNumber = pArray->asNumber[0];
        pList->pNext = NULL;
    }

    pCurrent = pList;
    for (i = 1; i < pArray->sSize; i++)  // write new values
        pCurrent = InsertNewNode(pArray->asNumber[i], pCurrent);
}

/* free the doubly linked list */
/* move forward through list, freeing the previous entry */
void __RPC_USER
DOUBLE_LINK_TYPE_free_inst(
    DOUBLE_LINK_TYPE __RPC_FAR * pList)
{
    while (pList->pNext != NULL)  // go to end of list
        pList = pList->pNext;

    pList = pList->pPrevious;
    while (pList != NULL) {  // back through list
        midl_user_free(pList->pNext);
        pList = pList->pPrevious;
    }
}

/* free the array structure */
void __RPC_USER
DOUBLE_LINK_TYPE_free_xmit(
    DOUBLE_XMIT_TYPE __RPC_FAR * pArray)
{
    midl_user_free(pArray);
}

/* end file xmitu.c */


XMITU.H

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                        xmit Example

    FILE:       xmitu.h

    PURPOSE:    Function prototypes for functions in xmitu.c

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

#ifndef _XMITU_H_
#define _XMITU_H_

DOUBLE_LINK_TYPE *
InsertNewNode(
    short sValue,
    DOUBLE_LINK_TYPE * pPrevious
    );

void
ArrayWalkProc(
    DOUBLE_XMIT_TYPE * pArray
    );

void
ListWalkProc(
    DOUBLE_LINK_TYPE * pList
    );

void __RPC_USER
DOUBLE_LINK_TYPE_to_xmit(
     DOUBLE_LINK_TYPE __RPC_FAR *             pList,
     DOUBLE_XMIT_TYPE __RPC_FAR * __RPC_FAR * ppArray
     );

void __RPC_USER
DOUBLE_LINK_TYPE_from_xmit(
     DOUBLE_XMIT_TYPE __RPC_FAR * pArray,
     DOUBLE_LINK_TYPE __RPC_FAR * pList);

void __RPC_USER
DOUBLE_LINK_TYPE_free_inst(
    DOUBLE_LINK_TYPE __RPC_FAR * pList
    );

void __RPC_USER
DOUBLE_LINK_TYPE_free_xmit(
    DOUBLE_XMIT_TYPE __RPC_FAR * pArray
    );

void __RPC_FAR * __RPC_USER midl_user_allocate(size_t len);

void __RPC_USER midl_user_free(void __RPC_FAR * ptr);

#endif

/* end file xmitu.h */


README.TXT

DICT

The DICT program demonstrates use of the implicit_handle, context_handle,
in, and out attributes.

SUMMARY
=======

This sample program is an example of a non-trivial local application
converted to a distributed RPC application.

The local program is a character-oriented "play" program, which lets a
user insert, delete, and iterate over a dictionary. The dictionary is
based on splay trees, which are printed out in a format described below.

We remoted the basic program by taking the interface to the dictionary
(given in dict0.h) and creating an interface to a remote dictionary,
(given in replay.idl) which is remoted using RPC.

Following the local dictionary interface, which is minimal and uniform,
the remote interface is also uniform. However, since the local dictionary
is generic (can store any item type, using void* pointers to items),
the interface needed to change to deal with predefined (Record) type
items in order to be remoted using the Microsoft RPC system. Also,
since the local implementation allows a "peek" at the root of the tree
using a DICT_CURR_ITEM macro, the whole interface needed to change.

Remote dictionaries are represented by context handles to demonstrate
the use of [context_handle]. Context handles are currently initialized
using a global primitive handle [implicit_handle].

State was added to remote dictionaries in order to allow sharing them,
and maintain reference counts. At this time, however, access to shared
dictionaries is *not* serialized! By default each client gets his own
copy of the dictionaries. To use shared dictionaries, start the client
using the -s switch.

The use of context to maintain state is also demonstrated by
differentiating between a private iterator, activated by "n" for "next"
and "p" for "previous", and a global shared iterator, activated by "N"
for "Next" and "P" for previous. To start iterators, use "h" - go to
the "head" of the dictionary, or "t" - go to the "tail" of the dictionary.
The private iterator can be reset to DICT_CURR_ITEM using "r".

FILES
=====

The directory samples\rpc\dict contains the following files for building
the DICT distributed application:

File          Description

README.TXT    Readme file for the DICT sample
REPLAY.IDL    Interface definition language file
REPLAY.ACF    Attribute configuration file
CLIENT.C      Client main program
SERVER.C      Server main program
PLAY.C
PLAY.H
UTIL0.C
UTIL0.H
DICT0.C
DICT0.H
REPLAY.C
MAKEFILE      Nmake file to build for Windows NT or Windows 95
MAKEFILE.DOS  Nmake file to build for MS-DOS

------------------------------------
Building on Windows NT or Windows 95
------------------------------------

The following environment variables should be already set for you:

  set CPU=i386
  set INCLUDE=%SDKROOT%\h
  set LIB=%SDKROOT%\lib
  set PATH=%SDKROOT%\system32;%SDKROOT%\bin

where %SDKROOT% is the root directory for the 32-bit Windows SDK.

For mips, set CPU=mips
For alpha, set CPU=alpha

Build the sample distributed application:

  nmake cleanall
  nmake

------------------------------------------
Building the client application for MS-DOS
------------------------------------------

After installing the Microsoft Visual C/C++ version 1.50 development
environment and the 16-bit RPC SDK on a Windows NT or Windows 95
computer, you can build the sample client application from Windows NT
or Windows 95:

  nmake -f makefile.dos cleanall
  nmake -f makefile.dos

This builds the client application client.exe.

You may also execute the Microsoft Visual C/C++ compiler under MS-DOS.
This requires a two-step build process.

  Step One: Compile the .IDL files under Windows NT or Windows 95:

     nmake -a -f makefile.dos replay.h

  Step Two: Compile the C sources (stub and application) under MS-DOS:

     nmake -f makefile.dos

Using the program:
------------------

To use the local dictionary example, type "play".

To use the remote versiom, type "server" on the server side. On the client
side, for each client use "client" for a fresh,private copy of the
dictionary, or "client -s" for a shared copy of the dictionary. The
following command line options are available on the client side.

Options:
--------

The -? displays the following message:

Usage : client [-s] [-n <server>] [-v <view_option>]

The -s option is used for using a shared version of the dictionary.
    If none exist, a fresh shared version is created.

The -v<option> is used for getting different views of the dictionary:
    -v normal (default) - default view of the dictionary
    (see explanation below)
    -v flat - Flat printout
    -v rev (or -v reverse) - reverses the tree to print left subtree first
    followed by the node, followed by the right subtree, at every level

The -n <server_name> is used for specifying a server machine.  Without it
    the server is assumed to run on the same machine.

    If you use the -n option you have to make sure that the server is
    ready to receive calls; that is you have to start the server, by typing:

    -> server

    to the console, even if the server is your own machine.

Program structure:
==================

Basic application:
------------------

  dict0.h + dict0.c - These files contain a basic generic dictionary
    implementation, based on Self Adjusting Trees (Splay trees),
    invented by Sleator and Tarjan. In the remote dictionary
    application (replay) the generic dictionary code runs on the server,
    which maintains a data base to be shared by clients.

    Note that the generic dictionary data structures contain untyped
    pointers ((void)*), and thus require a special layer of typed trees
    (strongly typed tree nodes) containing items of RPC-able types
    in order to convert the local dictionary package and distribute it
    using the Microsoft RPC system, and particularly the MIDL compiler.

    If you are interested in RPC (as opposed to splay trees) treat
    the generic dictionary as a "black box", and ignore the implementation
    details. Otherwise, you are probably looking at the wrong README
    file (and should read Tarjan and Sleators paper first anyhow...)

Local dictionary:
-----------------

  play.h + play.c - These files contain a simple character-oriented
    interactive demo / test program for the dictionary.

Remote dictionary:
------------------

  replay.idl - The Interface Definition Language (IDL) file contains:

    o type definitions for the remotable objects required (such as the
      Record item type, RecordTreeNode, etc)

    o the context handle definition (a (void)* in this case) providing
      a handle on a remote dictionary

    o the signatures for the remote dictionary operations

  replay.acf - An Attribute Configuration File (ACF), used here to
    specify the initial binding method. We use implicit_binding to
    bind to the server initially, and to initialize the context handle.

  replay.c - This file contains the implementation of the remote, or
    virtual, dictionary (VDict_...) operations. These are required
    only on the server side, and are replaced by caller stubs
    (in replay_c.c) on the client side.

Utlities:
---------

  util0.h + util0.c - These files contain utility routines used by both
    the client and server applications to allocate and to free dictionaries
    and trees, as well as pretty-print routines, used to print trees on
    both sides. See description of the user interface portion below for
    further discussion of the tree printing format.

Client side:
------------

  client.c - This file contains the driver of the client side program.
    The client binds to the server, initializes a dictionary, and activates
    a context handle for the dictionary on the server side. It then goes
    into a test loop which prints the dictionary tree and prompts the user
    for additional input by printing the following usage message:

Usage:
Type a single character, followed by an optional key as follows:

    i <key> :: Insert <key> into dictionary
    d <key> :: Delete <key> from dictionary
    f <key> :: Find <key> in dictionary
    N       :: next of current item in dictionary
    P       :: previous of current item in dictionary
    n       :: Next of local current item in dictionary
    p       :: Previous of local current item in dictionary
    h       :: Head (first item) of dictionary
    t       :: Tail (last item) of dictionary
    ?       :: Print this message
    q       :: Quit

where <key> is <integer> <string>

next op (i d x f n N p P h t ? q):

Server side:
------------

  server.c - This file contains a pretty standard server main loop for
    the remote dictionary "replay" demo.

Tree print format:
------------------

The tree printing routine prints the keys in a binary search tree using
the following "prinTree" recursive algorithm:

    prinTree (right subtree, with current indentation + one "tab");
    print the key at the root (with current indentation);
    prinTree (left subtree, with current indentation + one "tab");

Keys are a pair of the form <integer, string>.  Assume the following tree:

                           -----------
                           |0 : jack |
                           -----------
                          /           \
                     ----------      -----------
                     |0 : don |      |0 : jane |
                     ----------      -----------
                     /       \
                    /         \
               -----------  ----------
               |0 : adam |  |0 : eve |
               -----------  ----------

The above print algorithm would print it as follows:

        0 : jane
    0 : jack
            0 : eve
        0 : don
            0 : adam

The left child of a node is printed on the first line following the node
which is indented by one more tab then the given node.
The right child of a node is printed on the last line preceding the node
which is indented by one more tab then the given node.
If you tilt your hand to the left while examining the printed tree format,
it closely resembles the "traditional" format of a rooted binary tree.

Known Limitations:
------------------

Shared dictionaries are unprotected, so serialized access to the
dictionary is assumed. Use at your own risk.


DICT0.C

/*************************************************************/
/**                                                         **/
/**                 Microsoft RPC Examples                  **/
/**                 Dictionary Application                  **/
/**           Copyright 1992 - 1998 Microsoft Corporation   **/
/**                                                         **/
/*************************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>

#include <rpc.h>
#include <rpcndr.h>
#include "dict0.h"

/************************************************************************/
TreeNode Dumbo;
// volatile
TreeNode *Dummy = &Dumbo;   // a global dummy node

#define ROTATELEFT  tmp=t->right; t->right=tmp->left; tmp->left=t; t=tmp
#define ROTATERIGHT tmp=t->left; t->left=tmp->right; tmp->right=t; t=tmp
#define LINKLEFT    tmp=t; t=t->right; l=l->right=tmp
#define LINKRIGHT   tmp=t; t=t->left; r=r->left=tmp
#define ASSEMBLE    r->left=t->right; l->right=t->left; \
                    t->left=Dummy->right; t->right=Dummy->left
/************************************************************************/

/************************************************************************
 Basic structure declarations from dict0.h:

typedef struct tnode {
    struct tnode *left;             // left child pointer
    struct tnode *right;            // right child pointer
    void *item;                     // pointer to some structure
} TreeNode;

typedef struct dictnode {
    TreeNode *root;                 // pointer to the root of a SAT
    long size;                      // number of records in dictionary
    void * state;                   // reserved for state info
    cmp_rec_func cmp_rec;           // pointer to a comparison function
    TreeNode* (*splay)(TreeNode *, void *, cmp_rec_func);
                                    // pointer to a splay function
    void (*print_rec)(void *);      // one line print function
} Dictionary;

#define DICT_CURR_ITEM(pDict)       pDict->root->item

typedef enum {
    SUCCESS,
    ITEM_ALREADY_PRESENT,
    ITEM_NOT_FOUND,
    FIRST_ITEM,
    LAST_ITEM,
    EMPTY_DICTIONARY,
    NULL_ITEM
} Dict_Status;
**************************************************************************/

/*************************************************************************/
/***    Minimal Dictionary Operations:                                 ***/
/***                                                                   ***/
/***    Dictionary *Dict_New(Cmp_rec*, Splay*, print_rec*)             ***/
/***                                                                   ***/
/***    Dict_Status Dict_Find(Dictionary*, Item*)                      ***/
/***    Dict_Status Dict_Next(Dictionary*, Item*)                      ***/
/***    Dict_Status Dict_Prev(Dictionary*, Item*)                      ***/
/***    Dict_Status Dict_Insert(Dictionary*, Item*)                    ***/
/***    Dict_Status Dict_Delete(Dictionary*, Item**)                   ***/
/***                                                                   ***/
/***    Item* DICT_CURR_ITEM(Dict*)                                    ***/
/*************************************************************************/

#define TreeNode_New(pnode, pitem) pnode = \
    (TreeNode*) MIDL_user_allocate (sizeof(TreeNode)); \
    pnode->left = pnode->right = NULL; pnode->item = pitem;

Dictionary*
Dict_New(                    // returns a new dictionary node
    int (*cmp_rec)           // pointer to item comparison function
        (void *, void *),
    TreeNode* (*splay)       // pointer to a splay function
        (TreeNode *, void *, cmp_rec_func),
    void (*print_rec)        // pointer to one line item print routine
        (void *) )
{
    Dictionary* dp;

    dp = (Dictionary*) MIDL_user_allocate(sizeof(Dictionary));
    dp->root = NULL;
    dp->size = 0;
    dp->state = NULL;
    dp->print_rec = print_rec;
    dp->splay = splay;
    dp->cmp_rec = cmp_rec;
    return(dp);
}

Dict_Status
Dict_Find(
    Dictionary* dp,     // Dictionary to be searched.
    void* item)         // Item searched for.
{
    int keycmp;
    TreeNode* t;

    if (dp->root == NULL)
        return (EMPTY_DICTIONARY);
    if (item == NULL)
        return(NULL_ITEM);
    t = dp->root = dp->splay(dp->root, item, dp->cmp_rec);
    keycmp = (dp->cmp_rec)( t->item, item );
    if (keycmp != 0)
        return(ITEM_NOT_FOUND);
    else return(SUCCESS);
}

Dict_Status
Dict_Next(
    Dictionary* dp,     // Dictionary to be searched.
    void* item)         // A Key item.  Advance to successor of item in dp.
{
    TreeNode* t;
    int keycmp;

    if (dp->root == NULL)
        return (EMPTY_DICTIONARY);
    if (item == NULL) {
        dp->root = tdSplayLeft(dp->root);
        return(SUCCESS);
    }
    if (item == DICT_CURR_ITEM(dp)) {
        t = dp->root;
        keycmp = 0;
    }
    else {
        dp->root = t = dp->splay(dp->root, item, dp->cmp_rec);
        keycmp = (dp->cmp_rec) (item, t->item);
    }
    if (keycmp < 0)
        return(SUCCESS);
    else if (t->right == NULL) {
        return(LAST_ITEM);
    }
    else {
        t = dp->root;
        dp->root = tdSplayLeft(t->right);
        dp->root->left = t;
        t->right = NULL;
        return(SUCCESS);
    }
}

Dict_Status
Dict_Prev(
    Dictionary* dp,     // Dictionary to be searched.
    void* item)         // A Key item.  Retreat to predecessor of item in dp.
{
    TreeNode* t;
    int keycmp;

    if (dp->root == NULL)
        return (EMPTY_DICTIONARY);
    if (item == NULL) {
        dp->root = tdSplayRight(dp->root);
        return(SUCCESS);
    }
    if (item == DICT_CURR_ITEM(dp)) {
        t = dp->root;
        keycmp = 0;
    }
    else {
        dp->root = t = dp->splay(dp->root, item, dp->cmp_rec);
        keycmp = (dp->cmp_rec) (item, t->item);
    }
    if (keycmp > 0)
        return(SUCCESS);
    else if (t->left == NULL) {
        return(FIRST_ITEM);
    }
    else {
        t = dp->root;
        dp->root = tdSplayRight(t->left);
        dp->root->right = t;
        t->left = NULL;
        return(SUCCESS);
    }
}

Dict_Status
Dict_Insert(            // insert the given item into the tree
    Dictionary* dp,     // the modified dictionary
    void* item)         // the item to be inserted
{
    int keycmp;
    TreeNode *t, *newNode;

    if (item == NULL)
        return(NULL_ITEM);
    if (dp->root == NULL) {
        TreeNode_New(newNode, item);
        dp->root = newNode;
        dp->size++;
        return(SUCCESS);
    }
    t = dp->root = dp->splay(dp->root, item, dp->cmp_rec);
    keycmp = (dp->cmp_rec)( t->item, item );
    if (keycmp == 0)
        return(ITEM_ALREADY_PRESENT);

    TreeNode_New(newNode, item);
    if (keycmp < 0) {   // t->item < item
        newNode->right = t->right;
        t->right = NULL;
        newNode->left = t;
    }
    else {
        newNode->left = t->left;
        t->left = NULL;
        newNode->right = t;
    }
    dp->root = newNode;
    dp->size++;
    return(SUCCESS);
}

Dict_Status
Dict_Delete(            // delete the given item into from the tree
    Dictionary* dp,     // the modified dictionary
    void** pItem)       // IN: points to the (key) item to be deleted
                        // OUT: points to the item just deleted
{
    TreeNode *t, *r;
    int keycmp;
    void* item = *pItem;
    t = dp->root;

    if (item == NULL)
        return(NULL_ITEM);
    if (dp->root == NULL)
        return (EMPTY_DICTIONARY);
    if (item == DICT_CURR_ITEM(dp))
        keycmp = 0;
    else {
        dp->root = t = dp->splay(dp->root, item, dp->cmp_rec);
        keycmp = (dp->cmp_rec)( item, t->item );
    }
    if (keycmp != 0)
        return(ITEM_NOT_FOUND);
    *pItem = DICT_CURR_ITEM(dp);

    if (t->left == NULL) {
        dp->root = t->right;
    }
    else if ((r = t->right) == NULL) {
        dp->root = t->left;
    }
    else  {
        r = tdSplayLeft(r);
        // at this point r->left == NULL
        r->left = t->left;
        dp->root = r;
    }
    t->left = t->right = t->item = NULL;
    MIDL_user_free(t);
    dp->size--;
    return(SUCCESS);
}

/*************************************************************************/
/*****              Core functions (internal)                        *****/
/*************************************************************************/

TreeNode *              // returns the new root
tdSplay(                // general top down splay
    TreeNode *root,     // the current root of the tree
    void *keyItem,      // pointer to a "key item" searched for
    int (*cmp)(void *, void *) )
                        // pointer to a comparison function
{
    TreeNode* t=root;   // current search point
    TreeNode* l;        // root of "left subtree" < keyItem
    TreeNode* r;        // root of "right subtree" > keyItem
    int kcmpt, kcmpleft, kcmpright;
                        // cash comparison results
    TreeNode* tmp;

/***/
    Dummy = &Dumbo;
    l = Dummy;
    r = Dummy;
/***/

    if ( (root == NULL) || ((*cmp)(keyItem, root->item) == 0) )
        return(root);
    Dummy->left = Dummy->right = NULL;
    while ( (kcmpt = (*cmp)(keyItem, t->item)) != 0 ) {
        if ( kcmpt < 0 ) {
            if ( t->left == NULL )
                break;
            if ( (kcmpleft = (*cmp)(keyItem, t->left->item)) == 0 ) {
                LINKRIGHT;
            }
            else if ( kcmpleft < 0 ) {
                ROTATERIGHT;
                if ( t->left != NULL ) {
                    LINKRIGHT;
                }
            }
            else { // keyItem > t->left->item
                LINKRIGHT;
                if ( t->right != NULL ) {
                    LINKLEFT;
                }
            }
        }
        else { // keyItem > t->item
            if ( t->right == NULL )
                break;
            if ( (kcmpright = (*cmp)(keyItem, t->right->item)) == 0 ) {
                LINKLEFT;
            }
            else if ( kcmpright > 0 ) {
                ROTATELEFT;
                if ( t->right != NULL ) {
                    LINKLEFT;
                }
            }
            else { // keyItem < t->right->item
                LINKLEFT;
                if ( t->left != NULL ) {
                    LINKRIGHT;
                }
            }
        }
    }

    ASSEMBLE;
    return(t);
}

TreeNode*
tdSplayLeft(TreeNode* root)
{
    TreeNode* t=root;   // current "search" point
    TreeNode* l=Dummy;  // root of "left subtree" < keyItem
    TreeNode* r=Dummy;  // root of "right subtree" > keyItem
    TreeNode* tmp;

    if ( (t == NULL) || (t->left == NULL) )
        return(t);
    if ( t->left->left == NULL ) {
        ROTATERIGHT;
        return(t);
    }
    Dummy->left = Dummy->right = NULL;
    while ( t->left != NULL ) {
        ROTATERIGHT;
        if ( t->left != NULL ) {
            LINKRIGHT;
        }
    }

    ASSEMBLE;
    return(t);
}

TreeNode*
tdSplayRight(TreeNode* root)
{
    TreeNode* t=root;   // current "search" point
    TreeNode* l=Dummy;  // root of "left subtree" < keyItem
    TreeNode* r=Dummy;  // root of "right subtree" > keyItem
    TreeNode* tmp;

    if ( (t == NULL) || (t->right == NULL) )
        return(t);
    Dummy->left = Dummy->right = NULL;
    while ( t->right != NULL ) {
        ROTATELEFT;
        if ( t->right != NULL ) {
            LINKLEFT;
        }
    }

    ASSEMBLE;
    return(t);
}


DICT0.H

/*************************************************************/
/**                                                         **/
/**                 Microsoft RPC Examples                  **/
/**                 Dictionary Application                  **/
/**           Copyright 1992 - 1998 Microsoft Corporation   **/
/**                                                         **/
/*************************************************************/

/**************************************************************/
/*  user interface header file for top down splay package     */
/*  based on Sleator & Tarjan's Self Adjusting Trees          */
/**************************************************************/

typedef int (*cmp_rec_func)(void *, void *);
                            /* type of a comparison function */

typedef struct tnode {
    struct tnode *left;     /* left child pointer */
    struct tnode *right;    /* right child pointer */
    void *item;             /* pointer to some structure */
} TreeNode;

typedef struct dictnode {
    TreeNode *root;                 /* pointer to the root of a SAT     */
    long size;                      /* number of records in dictionary  */
    void * state;                   /* reserved for state info          */
    cmp_rec_func cmp_rec;           /* pointer to a comparison function */
    TreeNode* (*splay)(TreeNode *, void *, cmp_rec_func);
                                    /* pointer to a splay function      */
    void (*print_rec)(void *);      /* one line print function          */
} Dictionary;

/*************************************************************************/
/*****              Core functions (internal)                        *****/
/*************************************************************************/

TreeNode *              /* returns the new root                 */
tdSplay(                /* general top down splay               */
    TreeNode *root,     /* the current root of the tree         */
    void *keyItem,      /* pointer to a "key item" searched for */
    int (*cmp)(void *, void *) );
                        /* pointer to a comparison function     */

TreeNode*
tdSplayLeft(TreeNode* root);

TreeNode*
tdSplayRight(TreeNode* root);

void
print_tree_sat(         /* prints the binary tree (indented right subtree,
                           followed by the root, followed by the indented
                           right dubtree) */
    Dictionary * dp,
    int indent);        /* number of blanks to indent subtrees */

/*************************************************************************/
/***    Minimal Dictionary Operations:                                 ***/
/***                                                                   ***/
/***    Dictionary *Dict_New(Cmp_rec*, Splay*, print_rec*)             ***/
/***                                                                   ***/
/***    Dict_Status Dict_Find(Dictionary*, Item*)                      ***/
/***    Dict_Status Dict_Next(Dictionary*, Item*)                      ***/
/***    Dict_Status Dict_Prev(Dictionary*, Item*)                      ***/
/***    Dict_Status Dict_Insert(Dictionary*, Item*)                    ***/
/***    Dict_Status Dict_Delete(Dictionary*, Item**)                   ***/
/***                                                                   ***/
/***    Item* DICT_CURR_ITEM(Dict*)                                    ***/
/*************************************************************************/

typedef enum {
    SUCCESS,
    ITEM_ALREADY_PRESENT,
    ITEM_NOT_FOUND,
    FIRST_ITEM,
    LAST_ITEM,
    EMPTY_DICTIONARY,
    NULL_ITEM
} Dict_Status;

#define DICT_CURR_ITEM(pDict)  ( (pDict)->root->item )

#define DICT_EMPTY(pDict)      ( (pDict)->root == NULL )

Dictionary*
Dict_New(                    /* returns a new dictionary node          */
    int (*cmp_rec)           /* pointer to item comparison function    */
        (void *, void *),
    TreeNode* (*splay)       /* pointer to a splay function            */
        (TreeNode *, void *, cmp_rec_func),

    void (*print_rec)        /* pointer to one line item print routine */
        (void *) );

Dict_Status
Dict_Find(
    Dictionary* dp,     /* Dictionary to be searched. */
    void* item);        /* Item searched for. */

Dict_Status
Dict_Next(
    Dictionary* dp,     /* Dictionary to be searched. */
    void* item);        /* A Key item.  Advance to successor of item in dp. */

Dict_Status
Dict_Prev(
    Dictionary* dp,     /* Dictionary to be searched. */
    void* item);        /* A Key item.  Retreat to predecessor of item in dp. */

Dict_Status
Dict_Insert(            /* insert the given item into the tree */
    Dictionary* dp,     /* the modified dictionary */
    void* item);        /* the item to be inserted */

Dict_Status
Dict_Delete(            /* delete the given item into from the tree */
    Dictionary* dp,     /* the modified dictionary */
    void** pItem);      /* IN: points to the (key) item to be deleted */
                        /* OUT: points to the item just deleted */


PLAY.C

/*************************************************************/
/**                                                         **/
/**                 Microsoft RPC Examples                  **/
/**                 Dictionary Application                  **/
/**        Copyright 1992 - 1998 Microsoft Corporation      **/
/**                                                         **/
/*************************************************************/

/*
 *************************************************************************
 *                                                                       *
 * Local dictionary :play" example                                       *
 *                                                                       *
 * Description:                                                          *
 * This file contains a simple interactive loop of calls to the          *
 * dictionary.  The interface is identical to the remote dictionary      *
 * program described in the readme file.                                 *
 *                                                                       *
 *************************************************************************
*/

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>

#include "dict0.h"
#include "play.h"
#include "util0.h"

#define TAB_STOPS 3

void Usage()
{
  printf("Usage : play \n\n");
  exit(1);
}

/*************************************************************************/
/***                     Remote Dictionary Test Loop                   ***/
/*************************************************************************/

void
Usage_Msg()
{
    printf("\nUsage: \nType a single character, followed by an optional key as follows:\n\n");
    printf("    i <key> :: Insert <key> into dictionary\n");
    printf("    d <key> :: Delete <key> from dictionary\n");
    printf("    f <key> :: Find <key> in dictionary\n");
    printf("    n       :: Next of local current item in dictionary\n");
    printf("    p       :: Previous of local current item in dictionary\n");
    printf("    h       :: Head (first item) of dictionary\n");
    printf("    t       :: Tail (last item) of dictionary\n");
    printf("    ?       :: Print this message\n");
    printf("    q       :: Quit\n\n");
    printf("where <key> is <integer> <string>\n");
}

/*************************************************************************/
/***    Minimal Dictionary Operations:                                 ***/
/***                                                                   ***/
/***    Dictionary *Dict_New(Cmp_rec*, Splay*, print_rec*)             ***/
/***                                                                   ***/
/***    Dict_Status Dict_Find(Dictionary*, Item*)                      ***/
/***    Dict_Status Dict_Next(Dictionary*, Item*)                      ***/
/***    Dict_Status Dict_Prev(Dictionary*, Item*)                      ***/
/***    Dict_Status Dict_Insert(Dictionary*, Item*)                    ***/
/***    Dict_Status Dict_Delete(Dictionary*, Item**)                   ***/
/***                                                                   ***/
/***    Item* DICT_CURR_ITEM(Dict*)                                    ***/
/*************************************************************************/

void
TestLoop( Dictionary * pdict )
{
    char currName[80];
    char name[80];
    char op = 0;
    char buffer[80];

    Record r, currRecord;
    Record *pcurrRecord = &currRecord;
    Record *pr = &r;

    Dict_Status status;
    short i;

    pcurrRecord->name = currName;
    pr->name = name;

    Usage_Msg();

    while ( op != 'q' ) {

        printf("\nnext op (i d x f n p h t ? q): ");
        gets(buffer);
        op = buffer[0];

        if (op == 'i' || op == 'd' || op == 'f' ||
            op == '+' || op == '-' || op == 'I')
              sscanf(buffer+1, "%d %s", &pr->key, pr->name);

        switch (op) {
            case 'h':
                // get Head of list (first record);

                status = Dict_Next(pdict, NULL);
                ItemCopy(DICT_CURR_ITEM(pdict), pcurrRecord);
                ItemCopy(DICT_CURR_ITEM(pdict), pr);
                break;

            case 't':
                // get Tail of list (last record)

                status = Dict_Prev(pdict, NULL);
                ItemCopy(DICT_CURR_ITEM(pdict), pcurrRecord);
                ItemCopy(DICT_CURR_ITEM(pdict), pr);
                break;

            case 'n':
                // get Next record

                status = Dict_Next(pdict, pcurrRecord);
                ItemCopy(DICT_CURR_ITEM(pdict), pcurrRecord);
                break;

            case 'p':
                // get Previous record

                status = Dict_Prev(pdict, pcurrRecord);
                ItemCopy(DICT_CURR_ITEM(pdict), pcurrRecord);
                break;

            case 'N':
                // get Next record

                status = Dict_Next(pdict, pr);
                ItemCopy(DICT_CURR_ITEM(pdict), pr);
                break;

            case 'P':
                // get Previous record

                status = Dict_Prev(pdict, pr);
                ItemCopy(DICT_CURR_ITEM(pdict), pr);
                break;

            case 'r':
                ItemCopy(DICT_CURR_ITEM(pdict), pcurrRecord);
                break;

            case '+':
                // get Next record

                status = Dict_Next(pdict, pr);
                break;

            case '-':
                // get Previous record

                status = Dict_Prev(pdict, pr);
                break;

            case 'f':
                // Find <key>

                status = Dict_Find(pdict, pr);
                break;

            case 'i':
                // Insert <key>

                status = Dict_Insert(pdict,
                                     makeRecord(pr->key, pr->name) );
                break;

            case 'I':
                // Insert (<num'>,<name>) for all num': 3 < num' < num

                for (i = 3; i < pr->key; i++) {
                    status = Dict_Insert(pdict,
                                         makeRecord(i, pr->name) );
                }
                break;

            case 'd':
                // Delete <key>

                if (pdict != NULL) {
                    status = Dict_Delete(pdict, (void **)&pr);
                    freeRecord(pr);
                    pr = &r;
                }
                break;

            case 'x':
                // Delete DICT_CURR_ITEM

                if ((pdict != NULL) && (pdict->root != NULL)) {
                    pr = DICT_CURR_ITEM(pdict);
                    status = Dict_Delete(pdict, (void **) &pr);
                    freeRecord(pr);
                    pr = &r;
                }
                break;

            case 'X':
                // Empty the whole dictionary

                RecordTreeNodeFree((RecordTreeNode*)pdict->root);
                pdict->root = NULL;
                pr = &r;
                break;

            case '?':
                Usage_Msg();
                break;
        }

        if (op != '?' && op != 'q')
            Dict_Print(pdict, TAB_STOPS);
    }
}

Dict_Status
Dict_New_Dict(OUT Dictionary ** ppdict)
{
    static Dictionary * pdict;

    pdict = Dict_New(comp, tdSplay, printRecord);
    Init_dict(pdict);

    *ppdict = pdict;
    return(DICT_SUCCESS);
}

void
Init_dict(Dictionary * dp)
{
    Record* rp;

    rp = makeRecord((short)0, "jack_smith"); Dict_Insert(dp, rp);
    rp = makeRecord((short)0, "john_doe"); Dict_Insert(dp, rp);
    rp = makeRecord((short)1, "jean_doe"); Dict_Insert(dp, rp);
    rp = makeRecord((short)0, "joana_smith"); Dict_Insert(dp, rp);
    rp = makeRecord((short)1, "michael_jones"); Dict_Insert(dp, rp);
    rp = makeRecord((short)0, "mike_jacobs"); Dict_Insert(dp, rp);
    rp = makeRecord((short)2, "bill_jackson"); Dict_Insert(dp, rp);
    rp = makeRecord((short)0, "jane_doe"); Dict_Insert(dp, rp);
    rp = makeRecord((short)0, "dianne_jackson"); Dict_Insert(dp, rp);
    rp = makeRecord((short)1, "james_doe"); Dict_Insert(dp, rp);
    rp = makeRecord((short)1, "steve_johnson"); Dict_Insert(dp, rp);
    rp = makeRecord((short)2, "debbie_jones"); Dict_Insert(dp, rp);
    rp = makeRecord((short)0, "jacob_jacobson"); Dict_Insert(dp, rp);

    Dict_Print(dp, TAB_STOPS);
}

/*************************************************************************/
/***                             Main Loop                             ***/
/*************************************************************************/

void
main_dict ()
{
    Dictionary * pdict;
    Dictionary ** ppdict = &pdict;

    printf("getting a new dict\n");
    Dict_New_Dict(ppdict);
    printf("gotten a new dict in main_dict\n");
    TestLoop(pdict);
}

void _CRTAPI1
main(int argc, char *argv[])
{
    main_dict();
}


PLAY.H

/*************************************************************/
/**                                                         **/
/**                 Microsoft RPC Examples                  **/
/**                 Dictionary Application                  **/
/**        Copyright 1992 - 1998 Microsoft Corporation      **/
/**                                                         **/
/*************************************************************/

#include "rpc.h"
#include "rpcndr.h"

#define IN
#define OUT
#define STRING
#define CONTEXT_HANDLE

/*************************************************************************/
/***            Strongly typed tree nodes and dictionaries             ***/
/*************************************************************************/

/*
 ************************************************************************
 * Record type - previously imported from util1.idl
 * This is the type of items stored in the remote dictionary.
 ************************************************************************
*/

typedef struct _Record {
    short key;                      // RPC "generation"
    STRING char* name;              // contributor
} Record;

/*
 ************************************************************************
 * The following definitions (RDict, RecordTreeNode) are required
 * for marshalling a complete dictionary, binary tree, respectively.
 * All pointers are based on RPC-able types, replacing "void*"
 * pointers in the local dictionary (dict0) which are non-transmissible.
 ************************************************************************
*/

typedef struct _RecordTreeNode {
    struct _RecordTreeNode *left;   // left child pointer
    struct _RecordTreeNode *right;  // right child pointer
    Record *item;                   // pointer to a Record structure
} RecordTreeNode;

typedef struct _DictState {
    short ref_count;                // for shared dictionaries
    Record * curr_record;           // for global iterators
} DictState;

typedef struct _RDict {
    RecordTreeNode *root;           // pointer to the root of a SAT
    long size;                      // number of records in dictionary
    DictState * state;              // pointer to state info
} RDict;

/*
 * VDict is a "virtual dictionary" object.  It is used in the client
 * application as a handle on a dictionary maintained by a server
*/
typedef CONTEXT_HANDLE void * VDict;

typedef enum {
    DICT_SUCCESS,
    DICT_ITEM_ALREADY_PRESENT,
    DICT_ITEM_NOT_FOUND,
    DICT_FIRST_ITEM,
    DICT_LAST_ITEM,
    DICT_EMPTY_DICTIONARY,
    DICT_NULL_ITEM
} VDict_Status;

/*************************************************************************/
/***    Generic Dictionary Operations: (From dict0.h)                  ***/
/***                                                                   ***/
/***    Dictionary *Dict_New(Cmp_rec*, Splay*, print_rec*)             ***/
/***                                                                   ***/
/***    Dict_Status Dict_Find(Dictionary*, Item*)                      ***/
/***    Dict_Status Dict_Next(Dictionary*, Item*)                      ***/
/***    Dict_Status Dict_Prev(Dictionary*, Item*)                      ***/
/***    Dict_Status Dict_Insert(Dictionary*, Item*)                    ***/
/***    Dict_Status Dict_Delete(Dictionary*, Item**)                   ***/
/***                                                                   ***/
/***    Item* DICT_CURR_ITEM(Dict*)                                    ***/
/*************************************************************************/


CLIENT.C

/*************************************************************/
/**                                                         **/
/**                 Microsoft RPC Examples                  **/
/**                 Dictionary Application                  **/
/**        Copyright 1992 - 1998 Microsoft Corporation      **/
/**                                                         **/
/*************************************************************/

/*************************************************************************
 *                                                                       *
 * Remote dictionary example: client side                                *
 *                                                                       *
 * Description:                                                          *
 * This is the driver for the client side remote dictionary              *
 * (splay trees based) demo.  This works as follows:                     *
 *                                                                       *
 *  o in main we bind to the dict interface, and call main_dict;         *
 *                                                                       *
 *  o in dict_main we call VDict_New to initialize the dictionary        *
 *    on the server side, and then call TestLoop.                        *
 *                                                                       *
 *  o in TestLoop we loop responding to user input of the form:          *
 *    <one_letter_opcode> [<optional_arg>],                              *
 *    where <optional_arg> is a an integer constant followed by a        *
 *    string.  Type "?" for a detailed usage message.                    *
 *                                                                       *
 *************************************************************************
*/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>

#include "replay.h"  // header file generated by MIDL compiler
#include "dict0.h"
#include "util0.h"

#define TAB_STOPS 4

#define RDICT_CURR_RECORD(dict) (((DictState*)dict->state)->curr_record)

char * view = "normal";

void Usage()
{
  printf("Usage : client -s\n");
  printf("               -n <server_name>\n");
  printf("               -v <view_option>\n");
  exit(1);
}

/*************************************************************************/
/***                     Remote Dictionary Print                       ***/
/*************************************************************************/

void
RevPrinTree(
    int lmargin,             /* indentation of the root of the tree     */
    int indent,              /* indentation of subsequent levels        */
    TreeNode *np,            /* pointer to the root node                */
    PrintFun print)          /* short, one line, record print routine   */
{
    static char lspaces[] =
"                                                                                                                                                                                                                                                       ";

    if (np == NULL)
        return;

    RevPrinTree(lmargin+indent, indent, np->left, print);

    if (lmargin > sizeof(lspaces))
        lmargin = sizeof(lspaces);

    lspaces[lmargin] = 0;
    printf(lspaces);
    lspaces[lmargin] = ' ';

    (*print)(np->item);

    RevPrinTree(lmargin+indent, indent, np->right, print);
}

void
LinPrinTree(
    TreeNode *np,           /* pointer to the root node                */
    PrintFun print,         /* short, one line, record print routine   */
    Record * local,         /* local iterator point                    */
    Record * global         /* global iterator point                   */
    )
{
    if (np == NULL)
        return;

    LinPrinTree(np->left, print, local, global);

    if (comp(np->item, local) == 0) {
        if (comp(np->item, global) == 0)
            printf(" ==>> ");
        else
            printf("   >> ");
    }
    else if (comp(np->item, global) == 0)
        printf(" ==   ");
    else
        printf("      ");

    (*print)(np->item);

    LinPrinTree(np->right, print, local, global);
}

void
Clnt_Dict_Print(
    VDict * pvd,
    int indent,
    Record * local,
    Record * global
    )
{
    RDict DictT = {0, 0};
    RDict *prd = &DictT;

    // first: get a new copy a from the server
    VDict_Get_Dict(*pvd, &prd);

    if ( !strcmp(view, "normal") )
        prinTree(0,
                 TAB_STOPS,
                 (TreeNode *) prd->root,
                 printRecord);
    else if ( !strcmp(view, "reverse") || !strcmp(view, "rev") )
        RevPrinTree(0,
                    TAB_STOPS,
                    (TreeNode *) prd->root,
                    printRecord);
    else if ( !strcmp(view, "flat") )
        LinPrinTree((TreeNode *) prd->root,
                    printRecord,
                    local,
                    RDICT_CURR_RECORD(prd));

    RDict_Free_Dict(prd);
}

/*************************************************************************/
/***                     Remote Dictionary Test Loop                   ***/
/*************************************************************************/

void
Usage_Msg()
{
    printf("\nUsage: \nType a single character, followed by an optional key as follows:\n\n");
    printf("    i <key> :: Insert <key> into dictionary\n");
    printf("    d <key> :: Delete <key> from dictionary\n");
    printf("    f <key> :: Find <key> in dictionary\n");
    printf("    N       :: next of current item in dictionary\n");
    printf("    P       :: previous of current item in dictionary\n");
    printf("    n       :: Next of local current item in dictionary\n");
    printf("    p       :: Previous of local current item in dictionary\n");
    printf("    h       :: Head (first item) of dictionary\n");
    printf("    t       :: Tail (last item) of dictionary\n");
    printf("    ?       :: Print this message\n");
    printf("    q       :: Quit\n\n");
    printf("where <key> is <integer> <string>\n");
}

void
TestLoop( VDict * pvd )
{
    char currName[80];
    char name[80];
    char op = 0;
    char buffer[80];

    Record r, currRecord;
    Record *pcurrRecord = &currRecord;
    Record *pr = &r;
    Record * pNullRecord = NULL;

    Dict_Status status;

    pcurrRecord->name = currName;
    pr->name = name;

    VDict_Curr_Item(*pvd, &pcurrRecord);
    ItemCopy(pcurrRecord, pr);

    Clnt_Dict_Print(pvd, TAB_STOPS, pcurrRecord, pr);
    Usage_Msg();

    while ( op != 'q' ) {

        printf("\nnext op (i d x f n N p P h t ? q): ");
        gets(buffer);
        op = buffer[0];

        if (op == 'i' || op == 'd' || op == 'f' || op == 'I')
            sscanf(buffer+1, "%d %s", &pr->key, pr->name);

        switch (op) {
            case 'h':
                // get Head of list (first record);

                status = VDict_Next(*pvd, &pNullRecord);
                if (pNullRecord != NULL) {
                    ItemCopy(pNullRecord, pcurrRecord);
                    freeRecord(pNullRecord);
                    pNullRecord = NULL;
                }
                break;

            case 't':
                // get Tail of list (last record)

                status = VDict_Prev(*pvd, &pNullRecord);
                if (pNullRecord != NULL) {
                    ItemCopy(pNullRecord, pcurrRecord);
                    freeRecord(pNullRecord);
                    pNullRecord = NULL;
                }
                break;

            case 'n':
                // get next record (advance private (local) iterator)

                status = VDict_Next(*pvd, &pcurrRecord);
                break;

            case 'p':
                // get previous record (retreat private (local) iterator)

                status = VDict_Prev(*pvd, &pcurrRecord);
                break;

            case 'r':
                // Reset local iterator to global "current item"

                status = VDict_Curr_Item(*pvd, &pcurrRecord);
                break;

            case 'N':
                // get Next record (advance global iterator)

                status = VDict_Curr_Next(*pvd, &pr);
                pr = &r;
                break;

            case 'P':
                // get Previous record (retreat global iterator)

                status = VDict_Curr_Prev(*pvd, &pr);
                pr = &r;
                break;

            case 'f':
                // Find <key>

                status = VDict_Find(*pvd, &pr);
                pr = &r;
                break;

            case 'i':
                // Insert <key>

                status = VDict_Insert(*pvd, pr);
                break;

            case 'I':
                // Insert (<num'>,"") for all num' s.t. 3 < num' < num

                status = VDict_I_Dict(*pvd, pr->key);
                break;

            case 'd':
                // Delete <key>

                status = VDict_Delete(*pvd, &pr);
                if (status != ITEM_NOT_FOUND && status != EMPTY_DICTIONARY) {
                    pr = &r;
                }
                break;

            case 'x':
                // Delete DICT_CURR_ITEM

                status = VDict_Curr_Delete(*pvd, &pr);
                if (pr == NULL) {
                    pr = &r;
                }
                break;

            case 'X':
                // Empty the dictionary

                status = VDict_X_Dict(*pvd);
                break;

            case '?':
                Usage_Msg();
                break;
        }

        if (op != '?' && op != 'q')
            Clnt_Dict_Print(pvd, TAB_STOPS, pcurrRecord, pr);
    }
}

/*************************************************************************/
/***                             Main Loop                             ***/
/*************************************************************************/

void main_dict (short SharedDict)
{
    VDict v_dict = (VDict)0;
    VDict * pvdict;

    pvdict = &v_dict;

    printf ("Getting a new dict...");
    VDict_New( SharedDict, pvdict );

    printf ("Done.\n");
    TestLoop(pvdict);
}

int _CRTAPI1
main(int argc, char *argv[])
{
    RPC_STATUS status;
    unsigned char * pszUuid             = NULL;
    unsigned char * pszProtocolSequence = "ncacn_np";
    unsigned char * pszNetworkAddress   = NULL;
    unsigned char * pszEndpoint         = "\\pipe\\dict";
    unsigned char * pszOptions          = NULL;
    unsigned char * pszStringBinding    = NULL;
    short Shared_Dictionary = 0;  // Share an existing dictionary?
    int i;

    printf ("Microsoft RPC demo Client - Splay (Binary) Tree DataBase\n");

    for (i = 1; i < argc; i++) {
        if ((*argv[i] == '-') || (*argv[i] == '/')) {
            switch (tolower(*(argv[i]+1))) {
            case 'n':  // network address
                pszNetworkAddress = argv[++i];
                break;
            case 's':
                Shared_Dictionary = 1;
                break;
            case 'v':
                view = argv[++i];
                break;
            case 'h':
            case '?':
            default:
                Usage();
            }
        }
        else
            Usage();
    }

    /* The dict_ProtocolStack contains a partially initialized
     * protocol stack for the dict interface.  Fill in the
     * remaining fields:
     */

    status = RpcStringBindingCompose(pszUuid,
                                     pszProtocolSequence,
                                     pszNetworkAddress,
                                     pszEndpoint,
                                     pszOptions,
                                     &pszStringBinding);
    if (status) {
        printf("RpcStringBindingCompose returned 0x%x\n", status);
        return(status);
    }

    status = RpcBindingFromStringBinding(pszStringBinding,
                                         &dict_IfHandle);
    if (status) {
        printf("RpcBindingFromStringBinding returned 0x%x\n", status);
        return(status);
    }

    status = RpcStringFree(&pszStringBinding);  // remote calls done; unbind
    if (status) {
        printf("RpcStringFree returned 0x%x\n", status);
        return(status);
    }

    RpcTryExcept {
       main_dict(Shared_Dictionary);
    }
    RpcExcept(1) {
       printf("RPC runtime raised exception 0x%x\n", RpcExceptionCode());
    }
    RpcEndExcept

    status = RpcBindingFree(&dict_IfHandle);  // remote calls done; unbind
    if (status) {
        printf("RpcBindingFree returned 0x%x\n", status);
        return(status);
    }

    return(0);

}


REPLAY.ACF

/*************************************************************/
/**                                                         **/
/**                 Microsoft RPC Examples                  **/
/**                 Dictionary Application                  **/
/**          Copyright(c) Microsoft Corp. 1992-1995         **/
/**                                                         **/
/*************************************************************/

[ implicit_handle(handle_t dict_IfHandle) ]
interface dict
{
}


REPLAY.C

/*************************************************************/
/**                                                         **/
/**                 Microsoft RPC Examples                  **/
/**                 Dictionary Application                  **/
/**         Copyright 1992 - 1998 Microsoft Corporation     **/
/**                                                         **/
/*************************************************************/

/*************************************************************************/
/***                                                                   ***/
/***  Example dictionary using splay trees:                            ***/
/***                                                                   ***/
/*************************************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "replay.h"    // header file generated by MIDL compiler
#include "dict0.h"
#include "util0.h"

#define TAB_STOPS 3

/*************************************************************************/
/***                   Declarations (from replay.h)                    ***/
/*************************************************************************/

/*
typedef struct _Record {
    short key;                      // RPC "generation"
    [string] char* name;            // contributor
} Record;

typedef struct _RecordTreeNode RecordTreeNode;

typedef struct _RecordTreeNode {
    RecordTreeNode *left;           // left child pointer
    RecordTreeNode *right;          // right child pointer
    Record *item;                   // pointer to a Record structure
} RecordTreeNode;

typedef struct _RDict {
    RecordTreeNode *root;           // pointer to the root of a SAT
    long size;                      // number of records in dictionary
} RDict;

// RDict is used for marshalling a complete dictionary.

typedef enum {
    DICT_SUCCESS,
    DICT_ITEM_ALREADY_PRESENT,
    DICT_ITEM_NOT_FOUND,
    DICT_FIRST_ITEM,
    DICT_LAST_ITEM,
    DICT_EMPTY_DICTIONARY,
    DICT_NULL_ITEM
} VDict_Status;

typedef struct _DictState {
    short ref_count;               // for shared dictionaries
    Record * curr_record;          // for global iterators
} DictState;
*/

/*************************************************************************/
/***                   Internal state access methods                   ***/
/*************************************************************************/

#define RDICT_CURR_RECORD(dict) (((DictState*)dict->state)->curr_record)

#define RDICT_REF_COUNT(dict)   (((DictState*)dict->state)->ref_count)

#define RDICT_STATE(dict)       (((RDict*)dict)->state)

static int active_dictionary = 0;

/*************************************************************************/
/***    Generic Dictionary Operations: (From dict0.h)                  ***/
/***                                                                   ***/
/***    Dictionary *Dict_New(Cmp_rec*, Splay*, print_rec*)             ***/
/***                                                                   ***/
/***    Dict_Status Dict_Find(Dictionary*, Item*)                      ***/
/***    Dict_Status Dict_Next(Dictionary*, Item*)                      ***/
/***    Dict_Status Dict_Prev(Dictionary*, Item*)                      ***/
/***    Dict_Status Dict_Insert(Dictionary*, Item*)                    ***/
/***    Dict_Status Dict_Delete(Dictionary*, Item**)                   ***/
/***                                                                   ***/
/***    Item* DICT_CURR_ITEM(Dict*)                                    ***/
/*************************************************************************/

/*************************************************************************/
/***    Virtual Dictionary Operations: (From replay.h)                 ***/
/***                                                                   ***/
/***    VDict_Status VDict_New(OUT VDict **)                           ***/
/***                                                                   ***/
/***    VDict_Status VDict_Find(IN VDict*, IN OUT Record**)            ***/
/***    VDict_Status VDict_Next(IN VDict*, IN OUT Record**)            ***/
/***    VDict_Status VDict_Prev(IN VDict*, IN OUT Record**)            ***/
/***    VDict_Status VDict_Insert(IN VDict*, IN Record*)               ***/
/***    VDict_Status VDict_Delete(IN VDict*, IN OUT Record**)          ***/
/***                                                                   ***/
/***    VDict_Status VDict_Curr_Item(IN VDict*, OUT Record**);         ***/
/***    VDict_Status VDict_Curr_Delete(IN VDict*, OUT Record**);       ***/
/***    VDict_Status VDict_Curr_Next(IN VDict*, OUT Record**);         ***/
/***    VDict_Status VDict_Curr_Prev(IN VDict*, OUT Record**);         ***/
/***                                                                   ***/
/***    VDict_Status VDict_Get_Dict(IN VDict*, OUT RDict**)            ***/
/*************************************************************************/

/*************************************************************************/
/***                   context rundown routine                         ***/
/*************************************************************************/

void __RPC_USER VDict_rundown (VDict v_dict)
{
    Dictionary * pdict = (Dictionary*) v_dict;
    short count = --RDICT_REF_COUNT(pdict);

    printf("# of remaining users of context: %d\n\n",
           RDICT_REF_COUNT(pdict) );

    if (RDICT_REF_COUNT(pdict) <= 0) {
        printf("CLOSING CONTEXT\n\n");

        // free the splay tree
        RDict_Free_Dict(v_dict);

        // free the dictionary object
        free_state((DictState*) pdict->state);
        MIDL_user_free(v_dict);
        if (count == 0)
            active_dictionary = 0;
    }
}

//  For now: need to allocate a new item record prior to operation,
//  for all OUT and IN OUT arguments, as the callee stub will free
//  storage allocated for such pointer parameters.
//
//  Will be fixed by the introduction of [allocate(dont_free)]
//  ACF pointer type and parameter attribute.

/**************************************************************************
 * VDict_New initializes a new dictionary if (1) a non shared dictionary
 * was requested, or (2) a shared dictionary was requested, but no shared
 * dictionary is currently active.  There is at most one shared dictionary
 * at any point in time.
 *
 * BUGBUG: shared dictionaries are *NOT* locked (by semaphore, to
 * serialize access to them.  They *should be, though...
 **************************************************************************
*/

VDict_Status
VDict_New(
  IN short shared_dict,
  OUT VDict * v_dict
  )
{
    Dictionary * pdict0;
    static Dictionary * pdict;

    if (!active_dictionary  || !shared_dict) {
        // server side dictionary initialization
        pdict0 = Dict_New(comp, tdSplay, printRecord);

        // Initializes RDICT_CURR_RECORD(pdict0) to
        // "minus infinity" in the order
        ((RDict*)pdict0)->state = allocate_state();

        Init_dict(pdict0);
        *v_dict = (VDict)pdict0;

        if (shared_dict) {
            printf("OPENING SHARED DICTIONARY CONTEXT!\n\n");
            active_dictionary = 1;
            pdict = pdict0;
            RDICT_REF_COUNT(pdict)++;
        } // else RDICT_REF_COUNT(pdict0) = 0;
    }
    else {
        RDICT_REF_COUNT(pdict)++;
        printf("# of users of shared context: %d\n\n", RDICT_REF_COUNT(pdict));
        *v_dict = (VDict)pdict;
    }

    return(DICT_SUCCESS);
}

VDict_Status
VDict_Find(
    IN VDict  v_dict,
    IN OUT Record ** item
    )
{
    Dictionary * pdict = (Dictionary*) (v_dict);
    Dict_Status status;

    status = Dict_Find(pdict, *item);
    if ( (pdict == NULL) || DICT_EMPTY(pdict) ) {
        *item = NULL;
    }
    else {
        *item = DICT_CURR_ITEM(pdict);
        *item = ItemDuplicate(*item);
    }
    return( (VDict_Status)status );
}

VDict_Status
VDict_Next(
    IN VDict  v_dict,
    IN OUT Record ** item
    )
// get successor of *item, and update *item to point to it
{
    Dictionary * pdict = (Dictionary*) (v_dict);
    Dict_Status status;

    status = Dict_Next(pdict, *item);
    if ( (pdict == NULL) || DICT_EMPTY(pdict) ) {
        *item = NULL;
    }
    else {
        if (*item == NULL)
            ItemCopy(DICT_CURR_ITEM(pdict), RDICT_CURR_RECORD(pdict));
        *item = DICT_CURR_ITEM(pdict);
    }
    *item = ItemDuplicate(*item);
    return( (VDict_Status)status );
}

VDict_Status
VDict_Prev(
    IN VDict  v_dict,
    IN OUT Record ** item
    )
// get predecessor of *item, and update *item to point to it
{
    Dictionary * pdict = (Dictionary*) (v_dict);
    Dict_Status status;

    status = Dict_Prev(pdict, *item);
    if ( (pdict == NULL) || DICT_EMPTY(pdict) ) {
        *item = NULL;
    }
    else {
        if (*item == NULL)
            ItemCopy(DICT_CURR_ITEM(pdict), RDICT_CURR_RECORD(pdict));
        *item = DICT_CURR_ITEM(pdict);
    }
    *item = ItemDuplicate(*item);
    return( (VDict_Status)status );
}

VDict_Status
VDict_Curr_Next(
    IN VDict  v_dict,
    OUT Record ** item
    )
// get successor of RDICT_CURR_RECORD(v_dict),
// and update *item to point to it (global iterator prev)
{
    Dictionary * pdict = (Dictionary*) (v_dict);
    Dict_Status status;

    if ( (pdict == NULL) || DICT_EMPTY(pdict) ) {
        status = EMPTY_DICTIONARY;
        *item = NULL;
    }
    else {
        status = Dict_Next(pdict, RDICT_CURR_RECORD(pdict));
        ItemCopy(DICT_CURR_ITEM(pdict), RDICT_CURR_RECORD(pdict));
        *item = DICT_CURR_ITEM(pdict);
    }
    *item = ItemDuplicate(*item);
    return( (VDict_Status)status );
}

VDict_Status
VDict_Curr_Prev(
    IN VDict  v_dict,
    OUT Record ** item
    )
// get predecessor of RDICT_CURR_RECORD(v_dict),
// and update *item to point to it (global iterator prev)
{
    Dictionary * pdict = (Dictionary*) (v_dict);
    Dict_Status status;

    if ( (pdict == NULL) || DICT_EMPTY(pdict) ) {
        status = EMPTY_DICTIONARY;
        *item = NULL;
    }
    else {
        // update RDICT_CURR_RECORD(pdict)
        status = Dict_Prev(pdict, RDICT_CURR_RECORD(pdict));
        ItemCopy(DICT_CURR_ITEM(pdict), RDICT_CURR_RECORD(pdict));
        *item = DICT_CURR_ITEM(pdict);
    }
    *item = ItemDuplicate(*item);
    return( (VDict_Status)status );
}

VDict_Status
VDict_Insert(
    IN VDict  v_dict,
    IN Record * item
    )
{
    Dictionary * pdict = (Dictionary*) (v_dict);
    Dict_Status status;
    Record * rp = makeRecord(item->key, item->name);

    status = Dict_Insert(pdict, rp); // No return value required.
    return( (VDict_Status)status );
}

VDict_Status
VDict_Delete(
    IN VDict  v_dict,
    IN OUT Record ** item
    )
{
    Dictionary * pdict = (Dictionary*) (v_dict);
    Dict_Status status;

    status = Dict_Delete(pdict, (void **)item);
                                    // (*item) is returned by Dict_Delete!
    return( (VDict_Status)status );
}

VDict_Status
VDict_Curr_Item(
    IN VDict  v_dict,
    OUT Record ** item
    )
{
    Dictionary * pdict = (Dictionary*) (v_dict);
    Dict_Status status;

    if ( (pdict == NULL) || DICT_EMPTY(pdict) ) {
        status = EMPTY_DICTIONARY;
        *item = NULL;
    }
    else {
        status = SUCCESS;
        *item = DICT_CURR_ITEM(pdict);
    }
    *item = ItemDuplicate(*item);
    return( (VDict_Status)status );
}

VDict_Status
VDict_Curr_Delete(
    IN VDict  v_dict,
    OUT Record ** item
    )
{
    Dictionary * pdict = (Dictionary*) (v_dict);
    Dict_Status status;

    if ( (pdict == NULL) || DICT_EMPTY(pdict) ) {
        status = EMPTY_DICTIONARY;
        *item = NULL;
    }
    else {
        *item = DICT_CURR_ITEM(pdict);
        status = Dict_Delete( pdict, (void **)item );
    }
    return( (VDict_Status)status );
}

VDict_Status
VDict_Get_Dict(
    IN VDict  v_dict,
    OUT RDict ** r_dict
    )
//  used to transmit the dictionary back to the callee
//  (In this demo program - for printing on the client side)
{
    Dictionary * pdict = (Dictionary*) (v_dict);

    if (pdict == NULL) {
        return(DICT_EMPTY_DICTIONARY);
    }
    else {
        prinTree(0, 3, pdict->root, printRecord);

        *r_dict = RDict_Duplicate((RDict*)pdict);
            // Duplication is done to avoid freeing the tree by
            // the callee stub.  This is a temporary fix, until we
            // implement [allocate(dont_free)]!

        return(DICT_SUCCESS);
    }
}

/*************************************************************************/
/***                        Server Utility Functions                   ***/
/*************************************************************************/

void
Init_dict(Dictionary * dp)
{
    Record* rp;

    rp = makeRecord((short)0, "jack_smith"); Dict_Insert(dp, rp);
    rp = makeRecord((short)0, "john_doe"); Dict_Insert(dp, rp);
    rp = makeRecord((short)1, "steve_johnson"); Dict_Insert(dp, rp);
    rp = makeRecord((short)2, "debbie_jones"); Dict_Insert(dp, rp);
    rp = makeRecord((short)0, "mike_jacobs"); Dict_Insert(dp, rp);
    rp = makeRecord((short)2, "bill_jackson"); Dict_Insert(dp, rp);
    rp = makeRecord((short)0, "jane_doe"); Dict_Insert(dp, rp);
    rp = makeRecord((short)1, "james_doe"); Dict_Insert(dp, rp);
    rp = makeRecord((short)1, "jean_doe"); Dict_Insert(dp, rp);
    rp = makeRecord((short)0, "joana_smith"); Dict_Insert(dp, rp);
    rp = makeRecord((short)1, "michael_jones"); Dict_Insert(dp, rp);
    rp = makeRecord((short)0, "dianne_jackson"); Dict_Insert(dp, rp);
    rp = makeRecord((short)0, "jacob_jacobson"); Dict_Insert(dp, rp);

    Dict_Print(dp, TAB_STOPS);
}

/*************************************************************************/
/***                        Play oriented Functions ...                ***/
/*** (used to empty the tree and replace it by a tree of integers      ***/
/*************************************************************************/

VDict_Status
VDict_X_Dict(
    IN VDict  v_dict
    )
// Empty the dictionary
{
    RDict * prdict = (RDict*)v_dict;

    if (DICT_EMPTY(prdict))
        return(DICT_EMPTY_DICTIONARY);

    RecordTreeNodeFree(prdict->root);
    prdict->root = NULL;
    return(DICT_SUCCESS);
}

VDict_Status
VDict_I_Dict(
    IN VDict  v_dict,
    IN short  size
    )
// Insert integers from 3 to a specified upper bound into the tree
{
    RDict * prdict = (RDict*)v_dict;
    Dict_Status status;
    short i;

    // Insert (<num'>, "") for all num' s.t. 3 < num' < num
    for (i=3; i < size; i++) {
        status = VDict_Insert(prdict,
                              makeRecord(i, "<"));
    }
    return( (VDict_Status)status );
}


REPLAY.IDL

/*************************************************************/
/**                                                         **/
/**                 Microsoft RPC Examples                  **/
/**                 Dictionary Application                  **/
/**         Copyright 1992 - 1998 Microsoft Corporation     **/
/**                                                         **/
/*************************************************************/

[ uuid (12345678-1234-ABCD-EF00-0123456789AB),
  version(1.0),
//  endpoint("msc_np:[\\pipe\\splay]"),
  pointer_default(unique)
]
interface dict
{

/*************************************************************************/
/***            Strongly typed tree nodes and dictionaries             ***/
/*************************************************************************/

/*
 ************************************************************************
 * Record type - previously imported from util1.idl
 * This is the type of items stored in the remote dictionary.
 ************************************************************************
*/

typedef struct _Record {
    short key;                      // RPC "generation"
    [string] char* name;            // contributor
} Record;

typedef Record * PRecord;

/*
 ************************************************************************
 * The following definitions (RDict, RecordTreeNode) are required
 * for marshalling a complete dictionary, binary tree, respectively.
 * All pointers are based on RPC-able types, replacing "void*"
 * pointers in the local dictionary (dict0) which are non-transmissible.
 ************************************************************************
*/

typedef struct _RecordTreeNode {
    struct _RecordTreeNode *left;   // left child pointer
    struct _RecordTreeNode *right;  // right child pointer
    Record *item;                   // pointer to a Record structure
} RecordTreeNode;

typedef struct _DictState {
    short ref_count;                // for shared dictionaries
    Record * curr_record;           // for global iterators
} DictState;

typedef struct _RDict {
    RecordTreeNode *root;           // pointer to the root of a SAT
    long size;                      // number of records in dictionary
    DictState * state;              // pointer to state info
} RDict;

/*
 * VDict is a "virtual dictionary" object.  It is used in the client
 * application as a handle on a dictionary maintained by a server
*/
typedef [context_handle] void * VDict;

typedef enum {
    DICT_SUCCESS,
    DICT_ITEM_ALREADY_PRESENT,
    DICT_ITEM_NOT_FOUND,
    DICT_FIRST_ITEM,
    DICT_LAST_ITEM,
    DICT_EMPTY_DICTIONARY,
    DICT_NULL_ITEM
} VDict_Status;

/*************************************************************************/
/***    Generic Dictionary Operations: (From dict0.h)                  ***/
/***                                                                   ***/
/***    Dictionary *Dict_New(Cmp_rec*, Splay*, print_rec*)             ***/
/***                                                                   ***/
/***    Dict_Status Dict_Find(Dictionary*, Item*)                      ***/
/***    Dict_Status Dict_Next(Dictionary*, Item*)                      ***/
/***    Dict_Status Dict_Prev(Dictionary*, Item*)                      ***/
/***    Dict_Status Dict_Insert(Dictionary*, Item*)                    ***/
/***    Dict_Status Dict_Delete(Dictionary*, Item**)                   ***/
/***                                                                   ***/
/***    Item* DICT_CURR_ITEM(Dict*)                                    ***/
/*************************************************************************/

/*************************************************************************/
/***    Virtual Dictionary Operations (on remote dictionaries)         ***/
/***                                                                   ***/
/***    VDict_Status VDict_New(OUT VDict *)                            ***/
/***                                                                   ***/
/***    VDict_Status VDict_Find(IN VDict, IN OUT Record**)             ***/
/***    VDict_Status VDict_Next(IN VDict, IN OUT Record**)             ***/
/***    VDict_Status VDict_Prev(IN VDict, IN OUT Record**)             ***/
/***    VDict_Status VDict_Insert(IN VDict, IN Record*)                ***/
/***    VDict_Status VDict_Delete(IN VDict, IN OUT Record**)           ***/
/***                                                                   ***/
/***    VDict_Status VDict_Get_Dict(IN VDict, OUT RDict**)             ***/
/***    VDict_Status VDict_Curr_Item(IN VDict, OUT Record**);          ***/
/***    VDict_Status VDict_Delete_Curr(IN VDict, OUT Record**);        ***/
/***    VDict_Status VDict_Curr_Next(IN VDict, OUT Record**);          ***/
/***    VDict_Status VDict_Curr_Prev(IN VDict, OUT Record**);          ***/
/***                                                                   ***/
/*************************************************************************/

/*
 ************************************************************************
 * Most of the remote operations interfacing to a remote dictionary
 * are very close to operations on local dictionaries, with the
 * following noted exceptions.  To compansate for the fact that it is
 * possible to "peek" and get the current item of a local dictionary,
 * some interfaces had to be added, and others have to be changed to
 * closely match the capabilities of a local dictionaries by a remote
 * dictionary.  In particular the item (Record) argument became an OUT
 * or an IN OUT argument, returning the value of the "current_item"
 * following an operation (VDict_Find, VDict_Next, VDict_Prev).
 * The operations VDict_Curr_Item, VDict_Delete_Curr, VDict_Curr_Next,
 * and VDict_Curr_Prev were added to get functionality obtained in
 * local dictionaries by the DICT_CURR_ITEM macro, and by passing the
 * current item as an IN argument to Dict_Delete, Dict_Next
 * and Dict_Prev.  The basic return [IN] OUT parameter was changed
 * from (Item*) to (Record**), partly to further test the pointer
 * handling capabilities of the MIDL compiler.
 *************************************************************************
*/

/*
 *************************************************************************
 * In non-shared mode: Creates and initializes a new (private copy of
 * the) dictionary.
 *
 * In shared mode: If there is an existing shared dictionary, return
 * it, otherwise Creates and initializes a new (shared copy of
 * the) dictionary.
 *************************************************************************
*/

VDict_Status
VDict_New(
  [in] short shared_dict,
  [out] VDict * v_dict
  );

/*
 *************************************************************************
 * Find *item in the dictionary.  If *item was not present a "neighbor"
 * of *item will be returned instead
 *************************************************************************
*/

VDict_Status
VDict_Find(
    [in] VDict  v_dict,
    [in, out] Record ** item
    );

/*
 *************************************************************************
 * Get successor / predecessor of *item, and update *item to point to it
 *************************************************************************
*/

VDict_Status
VDict_Next(
    [in] VDict  v_dict,
    [in, out] Record ** item
    );

VDict_Status
VDict_Prev(
    [in] VDict  v_dict,
    [in, out] Record ** item
    );

/*
 *************************************************************************
 * Get successor / predecessor of RDICT_CURR_RECORD(v_dict),
 * and update *item to point to it (global iterator prev)
 *************************************************************************
*/

VDict_Status
VDict_Curr_Next(
    [in] VDict  v_dict,
    [out] Record ** item
    );

VDict_Status
VDict_Curr_Prev(
    [in] VDict  v_dict,
    [out] Record ** item
    );

/*
 *************************************************************************
 * Insert *item into the dictionary
 *************************************************************************
*/

VDict_Status
VDict_Insert(
    [in] VDict  v_dict,
    [in] Record * item
    );

/*
 *************************************************************************
 * Delete *item from the dictionary.  (It is the callers responsibility
 * to free the storage allocated for the returned record)
 *************************************************************************
*/

VDict_Status
VDict_Delete(
    [in] VDict  v_dict,
    [in, out] Record ** item
    );

/*
 *************************************************************************
 * Return a local copy of the whole dictionary
 *************************************************************************
*/

VDict_Status
VDict_Get_Dict(
    [in] VDict  v_dict,
    [out] RDict ** r_dict
    );

/*
 *************************************************************************
 * Return DICT_CURR_ITEM(v_dict)
 *************************************************************************
*/

VDict_Status
VDict_Curr_Item(
    [in] VDict  v_dict,
    [out] Record ** item
    );

/*
 *************************************************************************
 * Delete RDICT_CURR_RECORD(v_dict) from the dictionary.
 * (It is the callers responsibility to free the storage
 * allocated for the returned record)
 *************************************************************************
*/

VDict_Status
VDict_Curr_Delete(
    [in] VDict  v_dict,
    [out] Record ** item
    );

/*************************************************************************/
/***                        Play oriented Functions ...                ***/
/*************************************************************************/

VDict_Status
VDict_X_Dict(
    [in] VDict  v_dict
    );

VDict_Status
VDict_I_Dict(
    [in] VDict  v_dict,
    [in] short  size
    );

}


SERVER.C

/*************************************************************/
/**                                                         **/
/**                 Microsoft RPC Examples                  **/
/**                 Dictionary Application                  **/
/**        Copyright 1992 - 1998 Microsoft Corporation      **/
/**                                                         **/
/*************************************************************/

/*
 *************************************************************************
 *                                                                       *
 * Remote dictionary example: server side                                *
 *                                                                       *
 * Description:                                                          *
 * This is the driver for the server side remote dictionary              *
 * (splay trees based) demo.  This is a standard server driver,          *
 * and it works as follows:                                              *
 *                                                                       *
 *  o Call RpcCreateServer to initialize all data structures             *
 *                                                                       *
 *  o Initialize an appropriate protocol stack                           *
 *                                                                       *
 *  o Call RpcAddAddress to start listening on a transport address       *
 *    (a named pipe in our case).                                        *
 *                                                                       *
 *  o Call RpcAddInterface to initialize interface specific structures   *
 *    (such as dispatch table, etc.)                                     *
 *                                                                       *
 *  o Optionally advertise by calling RpcExport (not in this version)    *
 *                                                                       *
 *  o Loop forever...                                                    *
 *                                                                       *
 *************************************************************************
*/

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>

#include "replay.h"    // header file generated by MIDL compiler
#include "dict0.h"
#include "util0.h"

void Usage()
{
  printf("Usage : server -e <endpoint>\n");
  exit(1);
}

void _CRTAPI1
main(int argc, char *argv[])
{
    RPC_STATUS status;
    unsigned char * pszProtocolSequence = "ncacn_np";
    unsigned char * pszSecurity         = NULL;
    unsigned char * pszEndpoint         = "\\pipe\\dict";
    unsigned int    cMinCalls           = 1;
    unsigned int    cMaxCalls           = 20;
    unsigned int    fDontWait           = FALSE;
    int i;

    printf ("Microsoft RPC demo Server - Splay (Binary) Tree DataBase\n");

    /* allow the user to override settings with command line switches */
    for (i = 1; i < argc; i++) {
        if ((*argv[i] == '-') || (*argv[i] == '/')) {
            switch (tolower(*(argv[i]+1))) {
            case 'e':
                pszEndpoint = argv[++i];
                break;
            case 'h':
            case '?':
            default:
                Usage();
            }
        }
        else
            Usage();
    }

    status = RpcServerUseProtseqEp(pszProtocolSequence,
                                   cMaxCalls,
                                   pszEndpoint,
                                   pszSecurity);  // Security descriptor
    if (status) {
        printf("RpcServerUseProtseqEp returned 0x%x\n", status);
        exit(status);
    }

    status = RpcServerRegisterIf(dict_ServerIfHandle,  // interface to register
                                 NULL,   // MgrTypeUuid
                                 NULL);  // MgrEpv; null means use default
    if (status) {
        printf("RpcServerRegisterIf returned 0x%x\n", status);
        exit(status);
    }

    printf("Calling RpcServerListen\n");
    status = RpcServerListen(cMinCalls,
                             cMaxCalls,
                             fDontWait);
    if (status) {
        printf("RpcServerListen returned: 0x%x\n", status);
        exit(status);
    }

}


UTIL0.C

/*************************************************************/
/**                                                         **/
/**                 Microsoft RPC Examples                  **/
/**                 Dictionary Application                  **/
/**        Copyright 1992 - 1998 Microsoft Corporation      **/
/**                                                         **/
/*************************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>

#ifndef _LOCAL
#include "replay.h"
#else
#include "play.h"
#endif // _LOCAL

#include "dict0.h"
#include "util0.h"

/*************************************************************************/
/***                RecordNode / RecordTree free routines              ***/
/*************************************************************************/

void
RecordTreeNodeFree(
    IN RecordTreeNode * node
    )
{
    if (node == NULL)
        return;

    MIDL_user_free( node->item->name );
    MIDL_user_free( node->item );
    node->left = NULL;
    node->right = NULL;
    MIDL_user_free( node );
}

void
RecordTreeFree(
    IN RecordTreeNode * node
    )
{
    if (node == NULL)
        return;

    if (node->left != NULL) {
        RecordTreeFree(node->left);
    }
    if (node->right != NULL) {
        RecordTreeFree(node->right);
    }
    RecordTreeNodeFree( node );
}

VDict_Status
RDict_Free_Dict(
    IN OUT RDict * r_dict
    )
{
    RecordTreeFree( r_dict->root );

    return(DICT_SUCCESS);
}

/*************************************************************************/
/***                  State Allocate / Free routines                   ***/
/*************************************************************************/

DictState * allocate_state(void)
{
    DictState * pstate = (DictState*) MIDL_user_allocate(sizeof(DictState));

    pstate->curr_record = (Record*) MIDL_user_allocate(sizeof(Record));
    pstate->curr_record->name = (char*) MIDL_user_allocate(81 * sizeof(char));

    // initialize curr_record to "minus infinity" in the order
    pstate->curr_record->key = -1;
    strcpy(pstate->curr_record->name, "");
    pstate->ref_count = 0;

    return(pstate);
}

void free_state(DictState * state)
{
    if (state != NULL) {
        if (state->curr_record != NULL) {
            if (state->curr_record->name != NULL)
                MIDL_user_free(state->curr_record->name);
            MIDL_user_free(state->curr_record);
        }
        MIDL_user_free(state);
    }
}

/*************************************************************************/
/***                     Rdict Duplicate utilities                     ***/
/*************************************************************************/

RDict *
RDict_Duplicate(
    IN RDict * src
    )
{
    RDict * dst = (RDict*) MIDL_user_allocate(sizeof(RDict));

    dst->root = (RecordTreeNode*) Tree_Duplicate((TreeNode*)src->root);
    dst->size = src->size;
    dst->state = DictState_Duplicate(src->state);

    return(dst);
}

DictState *
DictState_Duplicate(
    IN DictState * src
    )
{
    DictState * dst = (DictState*) MIDL_user_allocate(sizeof(DictState));

    dst->curr_record = ItemDuplicate(src->curr_record);
    dst->ref_count = src->ref_count;

    return(dst);
}

TreeNode *
TreeNode_Duplicate(
    IN TreeNode * src
    )
{
    TreeNode * pnode = (TreeNode*) MIDL_user_allocate(sizeof(TreeNode));

    pnode->left = pnode->right = NULL;
    pnode->item = ItemDuplicate(src->item);

    return(pnode);
}

TreeNode *
Tree_Duplicate(
    IN TreeNode * src
    )
{
    TreeNode * dst;

    if (src == NULL)
        return((TreeNode*)NULL);

    dst = TreeNode_Duplicate(src);
    dst->left = Tree_Duplicate(src->left);
    dst->right = Tree_Duplicate(src->right);
    return(dst);
}

/*************************************************************************/
/***                MIDL_user_allocate / MIDL_user_free                ***/
/*************************************************************************/

void __RPC_FAR * __RPC_USER MIDL_user_allocate(size_t count)
{
    return(malloc(count));
}

void __RPC_USER MIDL_user_free(void __RPC_FAR * p)
{
    free(p);
}

/*************************************************************************/
/***                          Utility functions                        ***/
/*************************************************************************/

/*  In the most general case *cmp is a two argument function:
    (*cmp)(void *item0, void *item1) which compares two items,
    and returns:    -1 if item0 < item1;
                     0 if item0 == item1;
                    +1 if item0 > item1.
    The common case is: each item has a field named "key";
    item.key is of type long, or string.
*/

int
comp(void* x, void* y)
{
    int res = ((Record*)x)->key - ((Record*)y)->key;

    if (res == 0)
        return( strcmp( ((Record*)x)->name, ((Record*)y)->name ) );
    else
        return( res ) ;
}

Record *
ItemDuplicate(
    Record * item
    )
{
    if (item == NULL)
        return(NULL);

    return( makeRecord( item->key, item->name ) );
}

Record *
makeRecord(
    short key,
    char * name
    )
{
    Record * pr = (Record*) MIDL_user_allocate(sizeof(Record));

    if (!name)
        printf("makeRecord: NULL name\n");

    pr->name = (char*) MIDL_user_allocate(strlen(name)+1);
    strcpy(pr->name, name);
    pr->key = key;

    return(pr);
}

void
freeRecord(
    Record * pr
    )
{
    if (pr != NULL) {
        if (pr->name != NULL)
            MIDL_user_free(pr->name);
        MIDL_user_free(pr);
    }
}

void
ItemCopy(
    IN Record * src,
    OUT Record * dest
    )
{
    int i;

    dest->key = src->key;

    // copy name, trubcated to 80 characters
    for(i=0 ; (src->name[i] != '\0') && (i<80) ; i++)
        dest->name[i]=src->name[i];

    dest->name[i]='\0';
}

void
printRecord(void* rp)
{
    printf("%d : %s\n", ((Record*)rp)->key, ((Record*)rp)->name);
}

void
Dict_Print(             /* prints the binary tree (indented right subtree,
                           followed by the root, followed by the indented
                           right dubtree) */
    Dictionary * dp,
    int indent)         /* number of spaces to indent subsequent levels */
{
    prinTree(0, indent, dp->root, dp->print_rec);
}

char spaces[] =
"                                                                                                                                                                                                                                                       ";

void
prinTree(
    int lmargin,             /* indentation of the root of the tree     */
    int indent,              /* indentation of subsequent levels        */
    TreeNode *np,            /* pointer to the root node                */
    PrintFun print)          /* short, one line, record print routine   */
{
    if (np == NULL)
        return;

    prinTree(lmargin+indent, indent, np->right, print);

    if (lmargin > sizeof(spaces))
        lmargin = sizeof(spaces);;

    spaces[lmargin] = 0;
    printf(spaces);
    spaces[lmargin] = ' ';

    (*print)(np->item);

    prinTree(lmargin+indent, indent, np->left, print);

}

TreeNode*
makeNode(void * item)
{
    TreeNode* tp;

    tp = (TreeNode*)MIDL_user_allocate(sizeof(TreeNode));
    tp->item = item;
    tp->left = tp->right = NULL;

    return(tp);
}


UTIL0.H

/*************************************************************/
/**                                                         **/
/**                 Microsoft RPC Examples                  **/
/**                 Dictionary Application                  **/
/**           Copyright 1992 - 1998 Microsoft Corporation   **/
/**                                                         **/
/*************************************************************/

#include <rpc.h>

/*************************************************************************/
/***                RecordNode / RecordTree free routines              ***/
/*************************************************************************/

void
RecordTreeNodeFree(
    IN RecordTreeNode * node
    );

void
RecordTreeFree(
    IN RecordTreeNode * node
    );

VDict_Status
RDict_Free_Dict(
    IN OUT RDict * r_dict
    );

void
VDict_Print(
    VDict * pvd,
    int indent
    );

/*************************************************************************/
/***                  State Allocate / Free routines                   ***/
/*************************************************************************/

DictState * allocate_state(void);

void free_state(DictState * state);

/*************************************************************************/
/***                     Rdict Duplicate utilities                     ***/
/*************************************************************************/

RDict *
RDict_Duplicate(
    IN RDict * src
    );

DictState *
DictState_Duplicate(
    IN DictState * src
    );

TreeNode *
TreeNode_Duplicate(
    IN TreeNode * src
    );

TreeNode *
Tree_Duplicate(
    IN TreeNode * src
    );

/*************************************************************************/
/***                MIDL_user_allocate / MIDL_user_free                ***/
/*************************************************************************/

void __RPC_FAR * __RPC_USER MIDL_user_allocate(size_t count);

void __RPC_USER MIDL_user_free(void __RPC_FAR * p);

/*************************************************************************/
/***                    Comparison and Printing routines               ***/
/*************************************************************************/

Record *
makeRecord(
    short key,
    char * name
    );

void
freeRecord(
    Record * pr
    );

Record *
ItemDuplicate(
    Record * item
    );

void
ItemCopy(
    IN Record * src,
    OUT Record * dest
    );

int comp(void* x, void* y);

void printRecord(void* rp);

typedef void (*PrintFun) (void *);

void prinTree(
    int lmargin,
    int indent,
    TreeNode *np,
    PrintFun print);

void
Dict_Print(             /* prints the binary tree (indented right subtree,
                           followed by the root, followed by the indented
                           right dubtree) */
    Dictionary * dp,
    int indent);        /* number of spaces to indent subsequent levels */

TreeNode*
makeNode(void * item);

void
Init_dict(Dictionary * dp);


README.TXT

DOCTOR

The DOCTOR program is an RPC psychotherapy application that demonstrates
arrays, strings, and the size_is attribute.

FILES
=====

The directory \samples\rpc\handles\doctor contains the following files to
build the distributed application DOCTOR:

File          Description

README.TXT    Readme file for the DOCTOR sample
DOCTOR.IDL    Interface definition language file
DOCTOR.ACF    Attribute configuration file
DOCTORC.C     Client main program
DOCTORS.C     Server main program
DOCTORP.C     Remote procedures
MAKEFILE      Nmake file to build for NT
MAKEFILE.DOS  Nmake file to build for MS-DOS

-------------------------------------------
BUILDING CLIENT AND SERVER APPLICATIONS FOR
MICROSOFT WINDOWS NT OR WINDOWS 95
-------------------------------------------

The following environment variables should be already set for you:

  set CPU=i386
  set INCLUDE=%SDKROOT%\h
  set LIB=%SDKROOT%\lib
  set PATH=%SDKROOT%\system32;%SDKROOT%\bin

where %SDKROOT% is the root directory for the 32-bit Windows SDK.

For mips, set CPU=mips
For alpha, set CPU=alpha

Build the sample distributed application:

  nmake cleanall
  nmake

This builds the executable programs doctorc.exe
(client) and doctors.exe (server).

------------------------------------------
BUILDING THE CLIENT APPLICATION FOR MS-DOS
------------------------------------------

After installing the Microsoft Visual C/C++ version 1.50 development
environment and the 16-bit RPC SDK on a Windows NT or Windows 95
computer, you can build the sample client application from Windows NT
or Windows 95:

  nmake -f makefile.dos cleanall
  nmake -f makefile.dos

This builds the client application doctorc.exe.

You may also execute the Microsoft Visual C/C++ compiler under MS-DOS.
This requires a two-step build process.

  Step One: Compile the .IDL files under Windows NT or Windows 95:

     nmake -a -f makefile.dos doctor.h

  Step Two: Compile the C sources (stub and application) under MS-DOS:

     nmake -f makefile.dos

------------------------------------------
RUNNING THE CLIENT AND SERVER APPLICATIONS
------------------------------------------

On the server, enter:

  doctors

On the client, enter:

  net start workstation
  doctorc

Note: The client and server applications can run on
the same Microsoft Windows NT computer when you use
different screen groups.

Several command line switches are available to change
settings for this program. For a listing of the switches
available from the client program, enter:

  doctorc -?

For a listing of switches available from the server
program, enter:

  doctors -?


DOCTOR.IDL

[ uuid (C2557720-CA46-1067-B31C-00DD010662DA),
  version(1.0),
  pointer_default(unique)
]
interface doctor
{

const short STRSIZE = 500;

void Analyze([in, out, string, size_is(STRSIZE)] unsigned char * pszInOut);

void Shutdown(void);

}


DOCTORC.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                       Doctor Example

    FILE:       doctorc.c

    USAGE:      doctorc  -n network_address
                         -p protocol_sequence
                         -e endpoint
                         -o options

    PURPOSE:    Client side of RPC distributed application

    FUNCTIONS:  main() - binds to server and calls remote procedure

    COMMENTS:   This version of the distributed application prints
                strings from the Doctor server.  Doctor is an Eliza-like
                personal therapist program that works by simple
                pattern-matching.

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

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "doctor.h"    // header file generated by MIDL compiler

#define PURPOSE \
"This Microsoft RPC Version 2.0 sample program demonstrates\n\
the use of the [string] and [size_is] attributes. For more\n\
information about attributes and RPC API functions, see the\n\
RPC programming guide and reference.\n\n"

#define GREETING \
"The doctor is in.\n\
I am at your service; just tell me anything that troubles\n\
or concerns you. Please end your sentences with a period,\n\
a question mark, or an exclamation point, and then press\n\
the RETURN or ENTER key.  When you are ready to quit our\n\
session, just enter \"bye\" and press RETURN or ENTER.\n\n\
What is your name? >"

#define FAREWELL \
"I hope I have been of some service to you.\n\
Let's get together again some time.\n\n"

void Usage(char * pszProgramName)
{
    fprintf(stderr, "%s", PURPOSE);
    fprintf(stderr, "Usage:  %s\n", pszProgramName);
    fprintf(stderr, " -p protocol_sequence\n");
    fprintf(stderr, " -n network_address\n");
    fprintf(stderr, " -e endpoint\n");
    fprintf(stderr, " -o options\n");
    exit(1);
}

void _CRTAPI1 main(int argc, char **argv)
{
    RPC_STATUS status;                 // returned by RPC API function
    unsigned char pszName[STRSIZE];    // patient name
    unsigned char achIn[STRSIZE];      // patient input

    unsigned char * pszUuid             = NULL;
    unsigned char * pszProtocolSequence = "ncacn_np";
    unsigned char * pszNetworkAddress   = NULL;
    unsigned char * pszEndpoint         = "\\pipe\\doctor";
    unsigned char * pszOptions          = NULL;
    unsigned char * pszStringBinding    = NULL;
    int i;

    /* allow the user to override settings with command line switches */
    for (i = 1; i < argc; i++) {
        if ((*argv[i] == '-') || (*argv[i] == '/')) {
            switch (tolower(*(argv[i]+1))) {
            case 'p':  // protocol sequence
                pszProtocolSequence = argv[++i];
                break;
            case 'n':  // network address
                pszNetworkAddress = argv[++i];
                break;
            case 'e':
                pszEndpoint = argv[++i];
                break;
            case 'o':
                pszOptions = argv[++i];
                break;
            case 'h':
            case '?':
            default:
                Usage(argv[0]);
            }
        }
        else
            Usage(argv[0]);
    }

    /* Use a convenience function to concatenate the elements of  */
    /* the string binding into the proper sequence.               */
    status = RpcStringBindingCompose(pszUuid,
                                     pszProtocolSequence,
                                     pszNetworkAddress,
                                     pszEndpoint,
                                     pszOptions,
                                     &pszStringBinding);
    if (status) {
        printf("RpcStringBindingCompose returned 0x%x\n", status);
        printf("pszStringBinding = %s\n", pszStringBinding);
        exit(status);
    }

    /* Set the binding handle that will be used to bind to the server. */
    status = RpcBindingFromStringBinding(pszStringBinding,
                                         &doctor_IfHandle);
    if (status) {
        printf("RpcBindingFromStringBinding returned 0x%x\n", status);
        exit(status);
    }

    /* RPC is now initialized.  Call remote procedures as if        */
    /* they were local procedures.                                  */

    /* The doctor program consists of patient statements and doctor */
    /* responses.  The patient string is transmitted to the server, */
    /* and all processing is performed on the server.               */

    printf("%s", GREETING);
    gets(pszName);
    printf("\n%s>", pszName);

    while (gets(achIn)) {
        if (strncmp(achIn, "bye", 3) == 0)  // end of session?
            break;
        RpcTryExcept {
            Analyze(&achIn[0]);
        }
        RpcExcept(1) {
            printf("Runtime reported exception %ld\n", RpcExceptionCode() );
            break;
        }
        RpcEndExcept

        printf("%s%s>", achIn, pszName);    // no, continue
    }

    RpcTryExcept {
        Shutdown();                             // yes, shutdown the server
    }
    RpcExcept(1) {
        printf("Runtime reported exception %ld\n", RpcExceptionCode() );
    }
    RpcEndExcept

    /*  The calls to the remote procedure are complete.  */
    /*  Free the binding handle.                         */
    status = RpcBindingFree(&doctor_IfHandle);  // remote calls done; unbind
    if (status) {
       printf("RpcBindingFree returned 0x%x\n", status);
       exit(status);
    }

    printf("%s", FAREWELL);

    exit(0);

}  // end main()

/*********************************************************************/
/*                 MIDL allocate and free                            */
/*********************************************************************/

void __RPC_FAR * __RPC_USER midl_user_allocate(size_t len)
{
    return(malloc(len));
}

void __RPC_USER midl_user_free(void __RPC_FAR * ptr)
{
    free(ptr);
}

/* end file doctorc.c */


DOCTORP.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                        Doctor Example

    FILE:       Doctorp.c

    PURPOSE:    Procedures that can be linked with the client side
                to form a standalone application, or linked with
                the server side to form a distributed RPC application

    DATA:       Pattern   - array of strings; input patterns and responses
                Substring - substrings within user input

    FUNCTIONS:  Analyze(*pszInOut) - top level function
                Match(*string, *pattern) - compare input to patterns
                Respond(*string, *response) - replace substrings in response
                Synonyms(*ps1, *ps2) - replace synonyms in user input

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

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "doctor.h"     // header file generated by MIDL compiler

#define TRUE  1
#define FALSE 0

char *Pattern[] =
    {
    "0COMPUTER 1",
    "0COMPUTERS 1",
    "=DO 11S WORRY YOU?",    // string 11 = "computer" or "machine"
    "=WHY DO YOU MENTION 11S?",
    "=WHAT DO YOU THINK 11S HAVE TO DO WITH YOUR PROBLEM?",
    "=WHAT DO YOU THINK ABOUT 11S?",

    "0RPC0",
    "=WHY DO YOU MENTION RPC?",
    "=DO YOU MENTION RPC BECAUSE YOU ARE FEELING REMOTE RIGHT NOW?",
    "=WHAT FEELINGS DO YOU HAVE ABOUT RPC?",

    "0SORRY0",
    "=PLEASE DON'T APOLOGIZE.",
    "=APOLOGIES ARE NOT NECESSARY.",
    "=WHAT FEELINGS DO YOU HAVE WHEN YOU APOLOGIZE?",
    "=I'VE TOLD YOU THAT APOLOGIES ARE NOT REQUIRED.",
    "=APOLOGIES ARE NOT NECESSARY, PLEASE GO ON",

    "0I AM SAD1",
    "=I AM SORRY TO HEAR YOU ARE 14.",    // string 14 = "sad", "sick", etc.
    "=DO YOU THINK COMING HERE WILL HELP YOU NOT TO BE 14?",
    "=I'M SURE IT'S NOT PLEASANT TO BE 14.",
    "=CAN YOU EXPLAIN WHAT MADE YOU 14?",

    "0I AM HAPPY1",
    "=HOW HAVE I HELPED YOU TO BE 15?",    // string 15 = "happy", etc.
    "=HAS YOUR TREATMENT MADE YOU 15?",
    "=WHAT MAKES YOU 15 JUST NOW?",
    "=CAN YOU EXPLAIN WHY YOU ARE SUDDENLY 15?",

    "0NAME0",
    "=I AM NOT INTERESTED IN NAMES.",
    "=I'VE TOLD YOU BEFORE I DON'T CARE ABOUT NAMES.  PLEASE CONTINUE.",

    "0 FAMILY1",
    "=TELL ME MORE ABOUT YOUR 12.",    // string 12 = "mother", "brother", etc.
    "=WHO ELSE IN YOUR FAMILY?",
    "=YOUR 12?",
    "=WHAT ELSE COMES TO MIND WHEN YOU THINK OF YOUR 12?",

    "0I REMEMBER 1",
    "=DO YOU OFTEN THINK OF 1?",
    "=WHAT IN THE PRESENT SITUATION REMINDS YOU OF 1?",

    "0SAME0",
    "0ALIKE0",
    "0IS 0LIKE0",
    "=IN WHAT WAY?",
    "=WHAT RESEMBLANCE DO YOU SEE?",
    "=WHAT DOES THAT SIMILARITY SUGGEST TO YOU?",
    "=WHAT OTHER CONNECTIONS DO YOU SEE?",
    "=WHAT DO YOU SUPPOSE THAT RESEMBLANCE MEANS?",
    "=WHAT IS THE CONNECTION, DO YOU SUPPOSE?",
    "=COULD THERE REALLY BE SOME CONNECTION?",
    "=HOW?",

    "0I REMEMBER 1",
    "=DO YOU OFTEN THINK OF 1?",
    "=DOES THINKING OF 1 BRING ANYTHING ELSE TO MIND?",
    "=WHAT ELSE DO YOU REMEMBER?",
    "=WHY DO YOU REMEMBER 1 JUST NOW?",
    "=WHAT IN THE PRESENT SITUATION REMINDS YOU OF 1?",
    "=WHAT IS THE CONNECTION BETWEEN ME AND 1?",

    "0DO YOU REMEMBER 1",
    "=DID YOU THINK I WOULD FORGET 1?",
    "=WHY DO YOU THINK I SHOULD RECALL 1 NOW?",
    "=WHAT ABOUT 1?",
    "=YOU MENTIONED 1?",

    "1 IS MY PROBLEM0",
    "=1 13 YOUR PROBLEM?",    // string 13 = "is" or "are"
    "=ARE YOU SURE 13 1 YOUR PROBLEM?",
    "=PERHAPS 13 1 NOT YOUR REAL PROBLEM.",
    "=YOU THINK YOU HAVE PROBLEMS?",
    "=DO YOU OFTEN THINK ABOUT 1?",

    "0PROBLEM0",
    "=PLEASE CONTINUE, THIS MAY BE INTERESTING.",
    "=HAVE YOU ANY OTHER PROBLEMS YOU WISH TO DISCUSS?",
    "=PERHAPS YOU'D RATHER CHANGE THE SUBJECT.",
    "=YOU SEEM A BIT UNEASY.",

    "0I DREAMT 1",
    "0I DREAMED 1",
    "=REALLY 1?",
    "=HAVE YOU EVER FANTASIZED 1 WHILE YOU WERE AWAKE?",
    "=HAVE YOU DREAMT 1 BEFORE?",
    "=WHAT DOES THAT DREAM SUGGEST TO YOU?",
    "=DO YOU DREAM OFTEN?",
    "=WHAT PERSONS APPEAR IN YOUR DREAMS?",
    "=DON'T YOU BELIEVE THAT DREAM HAS SOMETHING TO DO WITH YOUR PROBLEM?",
    "=DO YOU EVER WISH YOU COULD FLEE FROM REALITY?",

    "0DREAM0",
    "=WHAT DOES THAT DREAM SUGGEST TO YOU?",
    "=DO YOU DREAM OFTEN?",
    "=WHAT PERSONS APPEAR IN YOUR DREAMS?",
    "=DON'T YOU BELIEVE THAT DREAM HAS SOMETHING TO DO WITH YOUR PROBLEM?",
    "=DO YOU EVER WISH YOU COULD FLEE FROM REALITY?",

    "0IF 1 HAD 2",
    "=DO YOU THINK IT'S LIKELY THAT 1 MIGHT HAVE 2?",
    "=DO YOU WISH THAT 1 HAD 2?",
    "=REALLY, IF 1 HAD 2?",

    "0IF 1",
    "=DO YOU THINK IT'S LIKELY THAT 1?",
    "=DO YOU WISH THAT 1?",
    "=WHAT DO YOU THINK ABOUT 1?",
    "=REALLY, IF 1?",

    "WAS I 1",
    "=WHAT IF YOU WERE 1?",
    "=DO YOU THINK YOU WERE 1?",
    "=WERE YOU 1?",
    "=WHAT WOULD IT MEAN IF YOU WERE 1?",
    "=WHAT DOES \"1\" SUGGEST TO YOU?",
    "=WHY DO YOU ASK?",
    "=DOES THAT QUESTION INTERST YOU?",
    "=WHAT IS IT YOU REALLY WANT TO KNOW?",
    "=ARE SUCH QUESTIONS MUCH ON YOUR MIND?",
    "=WHAT ANSWER WOULD PLEASE YOU MOST?",
    "=WHAT DO YOU THINK?",
    "=WHAT COMES TO YOUR MIND WHEN YOU ASK THAT?",
    "=HAVE YOU ASKED SUCH QUESTIONS BEFORE?",
    "=HAVE YOU ASKED ANYONE ELSE?",

    "0I WAS 1",
    "=WERE YOU REALLY?",
    "=WHY DO YOU TELL ME YOU WERE 1 NOW?",
    "=PERHAPS I ALREADY KNEW YOU WERE 1.",

    "WERE YOU 1",
    "=WOULD YOU LIKE TO BELIEVE THAT I WAS 1?",
    "=WHAT SUGGESTS THAT I WAS 1?",
    "=WHAT DO YOU THINK?",
    "=PERHAPS I WAS 1.",
    "=WHAT IF I HAD BEEN 1.",

    "0 MY 1",
    "MY 1",
    "=YOUR 1?",
    "=WHY DO YOU SAY YOUR 1?",
    "=DOES THAT SUGGEST ANYTHING ELSE WHICH BELONGS TO YOU?",
    "=IS IT IMPORTANT TO YOU THAT 1?",

    "0EVERYONE0",
    "0EVERYBODY0",
    "=REALLY, EVERYONE?",
    "=SURELY NOT EVERYONE.",
    "=CAN YOU THINK OF ANYONE IN PARTICULAR?",
    "=WHO, FOR EXAMPLE?",
    "=YOU ARE THINKING OF A VERY SPECIAL PERSON.",
    "=WHO, MAY I ASK?",
    "=SOMEONE SPECIAL, PERHAPS.",
    "=YOU HAVE A PARTICULAR PERSON IN MIND, DON'T YOU?",
    "=WHO DO YOU THINK YOU'RE TALKING ABOUT?",
    "=I SUSPECT YOU'RE EXAGGERATING A LITTLE.",

    "NO ONE0",
    "0 NO ONE0",
    "0NOBODY0",
    "=REALLY, NO ONE?",
    "=SURELY SOMEONE.",
    "=CAN YOU THINK OF ANYONE IN PARTICULAR?",
    "=WHO, FOR EXAMPLE?",
    "=YOU ARE THINKING OF A VERY SPECIAL PERSON.",
    "=WHO, MAY I ASK?",
    "=SOMEONE SPECIAL, PERHAPS.",
    "=YOU HAVE A PARTICULAR PERSON IN MIND, DON'T YOU?",
    "=WHO DO YOU THINK YOU'RE TALKING ABOUT?",
    "=I SUSPECT YOU'RE EXAGGERATING A LITTLE.",

    "0ALWAYS0",
    "=CAN YOU THINK OF A SPECIFIC EXAMPLE?",
    "=WHEN?",
    "=WHAT INCIDENT ARE YOU THINKING OF?",
    "=REALLY, ALWAYS?",
    "=WHAT IF THIS NEVER HAPPENED?",

    "0HOW 0",
    "0WHERE 0",
    "=WHY DO YOU ASK?",
    "=DOES THAT QUESTION INTERST YOU?",
    "=WHAT IS IT YOU REALLY WANT TO KNOW?",
    "=ARE SUCH QUESTIONS MUCH ON YOUR MIND?",
    "=WHAT ANWSER WOULD PLEASE YOU MOST?",
    "=WHAT DO YOU THINK?",
    "=WHAT COMES TO YOUR MIND WHEN YOU ASK THAT?",
    "=HAVE YOU ASKED SUCH QUESTIONS BEFORE?",
    "=HAVE YOU ASKED ANYONE ELSE?",

    /* "there is no" strings must precede "there is" strings */
    "0THERE IS NO 1",
    "0THERE IS NOT 1",
    "=WHAT IF THERE WERE 1",
    "=DID YOU THINK THERE MIGHT BE 1",
    "=HOW 13 1 RELATED TO YOU?",
    /* these must follow "there is no" strings */
    "0THERE IS 1",
    "=13 THERE REALLY 1?",
    "=HOW 13 1 RELATED TO YOU?",

    "0SEX 1",
    "=DO YOU REALLY WANT TO DISCUSS SEX?",
    "=DO YOU EVER DREAM ABOUT SEX?",
    "=WHY DO YOU MENTION SEX?",
    "=COULD SEX BE PART OF YOUR PROBLEM?",

    "0HECK1",    // checks for strong language
    "0DARN1",
    "=ARE SUCH OBSCENITIES FREQUENTLY ON YOUR MIND?",
    "=YOU ARE BEING A BIT CHILDISH.",
    "=REALLY NOW",
    "=DEAR ME",
    "=I REALLY SHOULDN'T TOLERATE SUCH LANGUAGE.",

    "0MY FRIEND 1",
    "=WHAT ELSE CAN YOU TELL ME ABOUT YOUR FRIEND?",
    "=WHAT MIGHT YOUR FRIEND HAVE TO DO WITH THE PROBLEM?",

    "0",    // last resort: match anything
    "=PLEASE GO ON.",
    "=I AM NOT SURE I UNDERSTAND YOU FULLY.",
    "=WHAT DOES THAT SUGGEST TO YOU?",
    "=DO YOU FEEL STRONGLY ABOUT DISCUSSING SUCH THINGS?",
    NULL    // special end of list character
};

int iLastIndex = 0;     // don't send same response twice in a row
                        // note: this requires 2 responses for each pattern

char Substring[20][STRSIZE] = {
  "", "", "", "", "", "", "", "", "", "",
  "REMEMBER",  /* 10 */
  "COMPUTER",  /* 11 */
  "FAMILY",    /* 12 */
  "IS",        /* 13 */
  "", "", "", "", "", ""        };

/* Compare user input to patterns */

int Match (char *str, char *pat)
{
    char * pStr;             // backup ptr to the user input string
    char * pPat;             // backup ptr to the pattern
    char *pSubstr = NULL;    // possible substrings within pattern
    int index = 0;           // index to substring

    while (TRUE) {  // exit routine from within this loop

        if (isdigit(*pat)) {  // replaceable pattern number
            index = atoi(pat);       // extract pattern number
            while (isdigit(*pat))    // skip number in pattern
                pat++;

            pSubstr = Substring[index]; // pointer to current substring

            if (*pat == '\0') {  // if end of pattern, everything will match
                strcpy(pSubstr, str);   // copy rest of input into substring
                return(TRUE);
            }

            else
                while (*pat != *str) {  // match all to specific char in pattern
                    *pSubstr++ = *str++; // fill substring
                    if (*str == '\0')    // stuff left to match, but no input
                        return(FALSE);
                    *pSubstr = '\0';     // can overwrite if there's more...
                }

            pStr = str;  // keep track; it may still be *
            pPat = pat;  // keep track; it may still be *
        }

        if (*str == *pat) {  // matches the specific pattern
            str++;  // skip to next character of input string
            pat++;  // skip to next character of pattern
            if ((*str == '\0') && (*pat == '\0'))  // both at end?
                return(TRUE);
        }

        else if (pSubstr != NULL) {  // didn't match specific, so still * substring
            pat = pPat;              // restore ptr to input pattern
            while (pStr < str)       // catch up to current input character
                *pSubstr++ = *pStr++;
            while (*pat != *str) {   // match to next possible match in pattern
                *pSubstr++ = *str++; // fill substring with more characters
                if (*str == '\0')    // stuff left to match, but no input
                    return(FALSE);
            }
            *pSubstr = '\0';         // can overwrite if there's more...
            pStr = str;              // keep track; it may still be *
            pPat = pat;              // keep track; it may still be *
        }

        else
            return(FALSE);           // no match

    }  // end while (TRUE) loop

}  // end function Match

/* Add possible substrings to the response string */

void Respond(char *str, char *finalstr)
{
    char copy[STRSIZE];  // copy of the substring to process token at a time
    char *token;
    char *word;
    int index;           // index to substring (string converted to number)
    int len = 0;         // index to output string for sprintf

    while (*str) {

        if (! isdigit(*str))
            len += sprintf(finalstr + len, "%c", *str++);

        else {  // check for pattern number

            index = atoi(str);     // extract pattern number
            while (isdigit(*str))  // skip number in pattern
                str++;

            strcpy(copy, Substring[index]);  // process substring
            token = strtok(copy, " ");       // skip blanks

            while (token) {
                word = token;

                if (! strcmp(token,"I") || ! strcmp(token,"ME"))
                    word = "YOU";
                else if (! strcmp(token,"YOU"))
                    word = "I";
                else if (! strcmp(token,"MY"))
                    word = "YOUR";
                else if (! strcmp(token,"YOUR"))
                    word = "MY";
                else if (! strcmp(token,"MINE"))
                    word = "YOURS";
                else if (! strcmp(token,"YOURS"))
                    word = "MINE";
                else if (! strcmp(token,"MYSELF"))
                    word = "YOURSELF";
                else if (! strcmp(token,"YOURSELF"))
                    word = "MYSELF";
                else if (! strcmp(token,"I'M"))
                    word = "YOU'RE";
                else if (! strcmp(token,"YOU'RE"))
                    word = "I'M";
                else if (! strcmp(token,"AM"))
                    word = "ARE";
                else if (! strcmp(token,"WAS"))
                    word = "WERE";

                len += sprintf(finalstr + len, "%s", word);

                // get next token from Substring
                if ((token = strtok(NULL, " ")) != NULL)
                    len += sprintf(finalstr + len, " ");

            }  // end while token

        }  // end else (if isdigit)

    }  // end while str

    len += sprintf(finalstr + len, "\n");

}  // end function Respond

/* Replace synonyms in user input */

void Synonyms(char *input, char *output)
{
    char *ps1 = input;
    char *ps2 = output;
    char *token;
    char *word;

    static char *synonym10[] = {"RECALL", "RECOLLECT", ""};
    static char *synonym11[] = {"MACHINE", ""};
    static char *synonym12[] = {"MOTHER", "MOMMY", "FATHER", "DADDY",
                                "SISTER", "BROTHER", "AUNT", "UNCLE", ""};
    static char *synonym13[] = {"ARE", "AM", ""};
    static char *synonym14[] = {"SAD", "UNHAPPY", "DEPRESSED", "SICK", ""};
    static char *synonym15[] = {"HAPPY", "ELATED", "GLAD", "BETTER", ""};

    short StrMember();

    // preprocessing...convert to uppercase and check for special chars

    while (*ps1) {
        *ps1 = toupper(*ps1);
        if (*ps1 == '.' || *ps1 == ',' || *ps1 == '!' ||
            *ps1 == '?' || *ps1 == ';' || *ps1 == ':' ||
            ! isprint(*ps1)) {
            *ps1 = '\0';
            break;
        }
        ps1++;
    }

    // ok, it's preprocessed now...let's check for pattern matches

    ps1 = input;               // back to the beginning...
    strcpy(ps2, "");
    token = strtok(ps1, " ");  // search for tokens deliminated by space

    while (token) {

        word = token;

        if (StrMember(token, synonym10)) {
            word = "REMEMBER";  // use this synonym for pattern-matching
        }
        else if (StrMember(token, synonym11)) {
            strcpy(Substring[11], token);  // 11 is hardcoded into response
            word = "COMPUTER";
        }
        else if (StrMember(token, synonym12)) {
            strcpy(Substring[12], token);  // 12
            word = "FAMILY";
        }
        else if (StrMember(token, synonym13)) {
            strcpy(Substring[13], token);  // 13
            word = "IS";
        }
        else if (StrMember(token, synonym14)) {
            strcpy(Substring[14], token);  // 14
            word = "SAD";
        }
        else if (StrMember(token, synonym15)) {
            strcpy(Substring[15], token);  // 15
            word = "HAPPY";
        }

        strcat(ps2, word);          // append the token to the output string
        strcat(ps2, " ");           // restore space
        token = strtok(NULL, " ");  // search for the next token

    }  // end while token

}  // end function Synonyms

short StrMember(char *str, char **list)
{
    while (strcmp(*list, "")) {    // not end of the list
        if (!strcmp(str, *list))
            return 1;
        else
            list++;
    }

    return 0;
}

/* Top-level routine */

void Analyze(unsigned char *str)
{
    int i = 0;      // index to entry within Pattern
    int j;          // index to first possible response string
    int index;      // index of the actual response string used
    int count = 0;  // count of all possible response strings (must be > 2)
    char *response;
    char synstr[STRSIZE];

    Synonyms(str, synstr);

    while (Pattern[i] != NULL) {  // check against all patterns

        if (Pattern[i][0] != '=') {  // if a patient pattern

            if (Match(synstr, Pattern[i]) == TRUE) {

                // skip past patient input to doctor responses
                while (Pattern[i] && Pattern[i][0] != '=')
                    i++;

                // count the number of possible responses, and
                // point index to beginning of doctor responses
                if (Pattern[i] && Pattern[i][0] == '=') {
                    count = 1;
                    j = i++;
                }

                // count all possible doctor responses
                while (Pattern[i] && Pattern[i][0] == '=') {
                    count++;
                    i++;
                }

                // pick a different doctor response from last time
                do {
                    index = j + (rand() % count);
                } while (index == iLastIndex);

                // process the doctor respose, and
                // keep track of the response used last
                response = &Pattern[index][1];
                iLastIndex = index;
                Respond(response, str);
                return;

            }  // end if match

        }  // end if patient pattern

        i++;  // next pattern

    }  // end while pattern

}  // end function Analyze

/* Shutdown the server by calling RPC API functions */

void Shutdown(void)
{
    RPC_STATUS status;

    printf("Calling RpcMgmtStopServerListening\n");
    status = RpcMgmtStopServerListening(NULL);
    printf("RpcMgmtStopServerListening returned: 0x%x\n", status);
    if (status) {
        exit(status);
    }

    printf("Calling RpcServerUnregisterIf\n");
    status = RpcServerUnregisterIf(NULL, NULL, FALSE);
    printf("RpcServerUnregisterIf returned 0x%x\n", status);
    if (status) {
        exit(status);
    }
}

/* end file doctorp.c */


DOCTORS.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                       Doctor Example

    FILE:       doctors.c

    USAGE:      doctors  -p protocol_sequence
                         -e endpoint
                         -m max calls
                         -n min calls
                         -f flag for RpcServerListen

    PURPOSE:    Server side of RPC distributed application Doctor

    FUNCTIONS:  main() - registers server as RPC server

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

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include "doctor.h"    // header file generated by MIDL compiler

#define PURPOSE \
"This Microsoft RPC Version 2.0 sample program demonstrates\n\
the use of the [string] and [size_is] attributes. For more\n\
information about attributes and RPC API functions, see the\n\
RPC programming guide and reference.\n\n"

void Usage(char * pszProgramName)
{
    fprintf(stderr, "%s", PURPOSE);
    fprintf(stderr, "Usage:  %s\n", pszProgramName);
    fprintf(stderr, " -p protocol_sequence\n");
    fprintf(stderr, " -e endpoint\n");
    fprintf(stderr, " -m maxcalls\n");
    fprintf(stderr, " -n mincalls\n");
    fprintf(stderr, " -f flag_wait_op\n");
    exit(1);
}

void _CRTAPI1 main(int argc, char * argv[])
{
    RPC_STATUS status;
    unsigned char * pszProtocolSequence = "ncacn_np";
    unsigned char * pszSecurity         = NULL;
    unsigned char * pszEndpoint         = "\\pipe\\doctor";
    unsigned int    cMinCalls           = 1;
    unsigned int    cMaxCalls           = 20;
    unsigned int    fDontWait           = FALSE;
    int i;

    /* allow the user to override settings with command line switches */
    for (i = 1; i < argc; i++) {
        if ((*argv[i] == '-') || (*argv[i] == '/')) {
            switch (tolower(*(argv[i]+1))) {
            case 'p':  // protocol sequence
                pszProtocolSequence = argv[++i];
                break;
            case 'e':
                pszEndpoint = argv[++i];
                break;
            case 'm':
                cMaxCalls = (unsigned int) atoi(argv[++i]);
                break;
            case 'n':
                cMinCalls = (unsigned int) atoi(argv[++i]);
                break;
            case 'f':
                fDontWait = (unsigned int) atoi(argv[++i]);
                break;
            case 'h':
            case '?':
            default:
                Usage(argv[0]);
            }
        }
        else
            Usage(argv[0]);
    }

    status = RpcServerUseProtseqEp(pszProtocolSequence,
                                   cMaxCalls,
                                   pszEndpoint,
                                   pszSecurity);  // Security descriptor
    printf("RpcServerUseProtseqEp returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    status = RpcServerRegisterIf(doctor_ServerIfHandle, // interface to register
                                 NULL,   // MgrTypeUuid
                                 NULL);  // MgrEpv; null means use default
    printf("RpcServerRegisterIf returned 0x%x\n", status);
    if (status) {
        exit(status);
    }
    printf("The doctor is in.\n");

    printf("Calling RpcServerListen\n");
    status = RpcServerListen(cMinCalls,
                             cMaxCalls,
                             fDontWait);
    printf("RpcServerListen returned: 0x%x\n", status);
    if (status) {
        exit(status);
    }

    if (fDontWait) {
        printf("Calling RpcMgmtWaitServerListen\n");
        status = RpcMgmtWaitServerListen();  // wait operation
        printf("RpcMgmtWaitServerListen returned: 0x%x\n", status);
        if (status) {
            exit(status);
        }
    }

}  // end main()

/*********************************************************************/
/*                 MIDL allocate and free                            */
/*********************************************************************/

void __RPC_FAR * __RPC_USER midl_user_allocate(size_t len)
{
    return(malloc(len));
}

void __RPC_USER midl_user_free(void __RPC_FAR * ptr)
{
    free(ptr);
}

/* end file doctors.c */


DOCTOR.ACF

[implicit_handle(handle_t doctor_IfHandle)]
interface doctor
{

}


README.TXT

DYNEPT

The DYNEPT program demonstrates a client application managing its connection
to the server through dynamic endpoints.

FILES
=====

The directory samples\rpc\dynept contains the following files for building
the distributed application DYNEPT:

File          Description

README.TXT     Readme file for the DYNEPT sample
DYNEPT.IDL     Interface definition language file
DYNEPT.ACF     Attribute configuration file
DYNEPTC.C      Client main program
DYNEPTS.C      Server main program
DYNEPTP.C      Remote procedures
MAKEFILE       Nmake file to build for Windows NT or Windows 95
MAKEFILE.DOS   Nmake file to build for MS-DOS

-------------------------------------------
BUILDING CLIENT AND SERVER APPLICATIONS FOR
MICROSOFT WINDOWS NT OR WINDOWS 95
-------------------------------------------

The following environment variables should be already set for you:

  set CPU=i386
  set INCLUDE=%SDKROOT%\h
  set LIB=%SDKROOT%\lib
  set PATH=%SDKROOT%\system32;%SDKROOT%\bin

where %SDKROOT% is the root directory for the 32-bit Windows SDK.

For mips, set CPU=mips
For alpha, set CPU=alpha

Build the sample distributed application:

 nmake cleanall
 nmake

This builds the executable programs dyneptc.exe
(client) and dynepts.exe (server).

------------------------------------------
BUILDING THE CLIENT APPLICATION FOR MS-DOS
------------------------------------------

After installing the Microsoft Visual C/C++ version 1.50 development
environment and the 16-bit RPC SDK on a Windows NT or Windows 95
computer, you can build the sample client application from Windows NT
or Windows 95:

  nmake -f makefile.dos cleanall
  nmake -f makefile.dos

This builds the client application dyneptc.exe.

You may also execute the Microsoft Visual C/C++ compiler under MS-DOS.
This requires a two-step build process.

  Step One: Compile the .IDL files under Windows NT or Windows 95:

     nmake -a -f makefile.dos dynept.h

  Step Two: Compile the C sources (stub and application) under MS-DOS:

     nmake -f makefile.dos

------------------------------------------
RUNNING THE CLIENT AND SERVER APPLICATIONS
------------------------------------------

On the server, enter:

  dynepts

On the client, enter:

  net start workstation
  dyneptc

Note:  The client and server applications can run on the same Microsoft
Windows NT computer when you use different screen groups.

Several command-line switches are available to change settings for this
program. For a listing of the switches available from the client program,
enter:

  dyneptc -?

For a listing of switches available from the server
program, enter:

  dynepts -?


DYNEPT.IDL

[ uuid (6238dc00-af17-11cd-b799-00aa003cd575),
  version(1.0),
  pointer_default(unique)
]
interface dynept
{

void HelloProc([in, string] unsigned char * pszString);

void Shutdown(void);

}


DYNEPTC.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                       Dynept Example

    FILE:       dyneptc.c

    USAGE:      dyneptc  -n network_address
                         -p protocol_sequence
                         -o options
                         -s string_displayed_on_server

    PURPOSE:    Client side of RPC distributed application

    FUNCTIONS:  main() - binds to server and calls remote procedure

    COMMENTS:   This version of the distributed application that
                prints "What a dynamic, world" (or other string) on
                the server features a client that manages its connection
                to the server. It uses the binding handle dynept_IfHandle,
                defined in the file dynept.h.

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

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include "dynept.h"    // header file generated by MIDL compiler

void Usage(char * pszProgramName)
{
    fprintf(stderr, "Usage:  %s\n", pszProgramName);
    fprintf(stderr, " -p protocol_sequence\n");
    fprintf(stderr, " -n network_address\n");
    fprintf(stderr, " -o options\n");
    fprintf(stderr, " -s string\n");
    exit(1);
}

void _CRTAPI1 main(int argc, char **argv)
{
    RPC_STATUS status;
    unsigned char * pszUuid             = NULL;
    unsigned char * pszProtocolSequence = "ncacn_np";
    unsigned char * pszNetworkAddress   = NULL;
    unsigned char * pszOptions          = NULL;
    unsigned char * pszStringBinding    = NULL;
    unsigned char * pszString           = "What a dynamic world";
    unsigned long ulCode;
    int i;

    /* allow the user to override settings with command line switches */
    for (i = 1; i < argc; i++) {
        if ((*argv[i] == '-') || (*argv[i] == '/')) {
            switch (tolower(*(argv[i]+1))) {
            case 'p':  // protocol sequence
                pszProtocolSequence = argv[++i];
                break;
            case 'n':  // network address
                pszNetworkAddress = argv[++i];
                break;
            case 'o':
                pszOptions = argv[++i];
                break;
            case 's':
                pszString = argv[++i];
                break;
            case 'h':
            case '?':
            default:
                Usage(argv[0]);
            }
        }
        else
            Usage(argv[0]);
    }

    /* Use a convenience function to concatenate the elements of */
    /* the string binding into the proper sequence.              */
    status = RpcStringBindingCompose(pszUuid,
                                     pszProtocolSequence,
                                     pszNetworkAddress,
                                     NULL,
                                     pszOptions,
                                     &pszStringBinding);
    printf("RpcStringBindingCompose returned 0x%x\n", status);
    printf("pszStringBinding = %s\n", pszStringBinding);
    if (status) {
        exit(status);
    }

    /* Set the binding handle that will be used to bind to the server. */
    status = RpcBindingFromStringBinding(pszStringBinding,
                                         &dynept_IfHandle);
    printf("RpcBindingFromStringBinding returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    printf("Calling the remote procedure 'HelloProc'\n");
    printf("Print the string '%s' on the server\n", pszString);

    RpcTryExcept {
        HelloProc(pszString);  // make call with user message
        printf("Calling the remote procedure 'Shutdown'\n");
        Shutdown();  // shut down the server side
    }
    RpcExcept(1) {
        ulCode = RpcExceptionCode();
        printf("Runtime reported exception 0x%lx = %ld\n", ulCode, ulCode);
    }
    RpcEndExcept

    /*  The calls to the remote procedures are complete. */
    /*  Free the string and the binding handle           */
    status = RpcStringFree(&pszStringBinding);  // remote calls done; unbind
    printf("RpcStringFree returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    status = RpcBindingFree(&dynept_IfHandle);  // remote calls done; unbind
    printf("RpcBindingFree returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    exit(0);

}  // end main()

/*********************************************************************/
/*                 MIDL allocate and free                            */
/*********************************************************************/

void  __RPC_FAR * __RPC_USER midl_user_allocate(size_t len)
{
    return(malloc(len));
}

void __RPC_USER midl_user_free(void __RPC_FAR * ptr)
{
    free(ptr);
}

/* end file dyneptc.c */


DYNEPTP.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                          DynEpt Example

    FILE:       dyneptp.c

    PURPOSE:    Remote procedures that are linked with the server
                side of RPC distributed application

    FUNCTIONS:  HelloProc() - prints "What a dynamic world" or other string
                sent by client to server

    COMMENTS:   This version of the distributed application that prints
                "What a dynamic world world" (or other string) on the
                server features a client that manages its connection to
                the server.  It uses the binding handle dynept_IfHandle,
                defined in the file dynept.h.

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

#include <stdlib.h>
#include <stdio.h>
#include "dynept.h"    // header file generated by MIDL compiler

void HelloProc(unsigned char * pszString)
{
    printf("%s\n", pszString);
}

void Shutdown(void)
{
    RPC_STATUS status;

    printf("Calling RpcMgmtStopServerListening\n");
    status = RpcMgmtStopServerListening(NULL);
    printf("RpcMgmtStopServerListening returned: 0x%x\n", status);
    if (status) {
       exit(status);
    }
}

/* end file dyneptp.c */


DYNEPTS.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                       dynept Example

    FILE:       dynepts.c

    USAGE:      dynepts  -p protocol_sequence
                         -m max calls
                         -n min calls
                         -f flag for RpcServerListen

    PURPOSE:    Server side of RPC distributed application dynept

    FUNCTIONS:  main() - registers server as RPC server

    COMMENTS:   This version of the distributed application that prints
                "What a dynamic world" (or other string) on the server
                features a client that manages its connection to the server.
                It uses the binding handle dynept_IfHandle, defined in
                the file dynept.h.

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

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include "dynept.h"    // header file generated by MIDL compiler

#define PURPOSE \
"This Microsoft RPC Version 2.0 sample program demonstrates\n\
the use of dynamic endpoints. For more information\n\
about the attributes and the RPC API functions, see the\n\
RPC programming guide and reference.\n\n"

void Usage(char * pszProgramName)
{
    fprintf(stderr, "%s", PURPOSE);
    fprintf(stderr, "Usage:  %s\n", pszProgramName);
    fprintf(stderr, " -p protocol_sequence\n");
    fprintf(stderr, " -m maxcalls\n");
    fprintf(stderr, " -n mincalls\n");
    fprintf(stderr, " -f flag_wait_op\n");
    exit(1);
}

/* main:  register the interface, start listening for clients */
void _CRTAPI1 main(int argc, char * argv[])
{
    RPC_STATUS status;
    RPC_BINDING_VECTOR * pBindingVector = NULL;
    unsigned char * pszProtocolSequence = "ncacn_np";
    unsigned char * pszSecurity         = NULL;
    unsigned int    cMinCalls           = 1;
    unsigned int    cMaxCalls           = 20;
    unsigned int    fDontWait           = FALSE;
    unsigned int    fRegistered         = 0;
    unsigned int    fEndpoint           = 0;
    int i;

    /* allow the user to override settings with command line switches */
    for (i = 1; i < argc; i++) {
        if ((*argv[i] == '-') || (*argv[i] == '/')) {
            switch (tolower(*(argv[i]+1))) {
            case 'p':  // protocol sequence
                pszProtocolSequence = argv[++i];
                break;
            case 'm':
                cMaxCalls = (unsigned int) atoi(argv[++i]);
                break;
            case 'n':
                cMinCalls = (unsigned int) atoi(argv[++i]);
                break;
            case 'f':
                fDontWait = (unsigned int) atoi(argv[++i]);
                break;
            case 'h':
            case '?':
            default:
                Usage(argv[0]);
            }
        }
        else
            Usage(argv[0]);
    }

    status = RpcServerUseProtseq(pszProtocolSequence,
                                 cMaxCalls,
                                 pszSecurity);  // Security descriptor
    printf("RpcServerUseProtseq returned 0x%x\n", status);
    if (status) {
        goto cleanup;
    }

    status = RpcServerRegisterIf(dynept_ServerIfHandle,  // interface to register
                                 NULL,   // MgrTypeUuid
                                 NULL);  // MgrEpv; null means use default
    printf("RpcServerRegisterIf returned 0x%x\n", status);
    if (status) {
        goto cleanup;
    }
    else
        fRegistered = 1;

    status = RpcServerInqBindings(&pBindingVector);
    printf("RpcServerInqBindings returned 0x%x\n", status);
    if (status) {
        goto cleanup;
    }

    status = RpcEpRegister( dynept_ServerIfHandle,
                            pBindingVector,
                            NULL,
                            "" );
    printf("RpcEpRegister returned: 0x%x\n", status);
    if (status) {
        goto cleanup;
    }
    else
        fEndpoint = 1;

    printf("Calling RpcServerListen\n");
    status = RpcServerListen(cMinCalls,
                             cMaxCalls,
                             fDontWait);
    printf("RpcServerListen returned: 0x%x\n", status);
    if (status) {
        goto cleanup;
    }

    if (fDontWait) {
        printf("Calling RpcMgmtWaitServerListen\n");
        status = RpcMgmtWaitServerListen();  // wait operation
        printf("RpcMgmtWaitServerListen returned: 0x%x\n", status);
    }

  cleanup:

    if ( fEndpoint )
    {
        status = RpcEpUnregister(dynept_ServerIfHandle,
                                 pBindingVector,
                                 NULL);
        printf( "RpcEpUnregister returned 0x%x\n", status);
    }

    if ( pBindingVector )
    {
        status = RpcBindingVectorFree(&pBindingVector);
        printf( "RpcBindingVectorFree returned 0x%x\n", status);
    }

    if ( fRegistered )
    {
        printf("Calling RpcServerUnregisterIf\n");
        status = RpcServerUnregisterIf(NULL, NULL, FALSE);
        printf("RpcServerUnregisterIf returned 0x%x\n", status);
    }

    exit (status);

}  // end main()

/*********************************************************************/
/*                MIDL allocate and free                             */
/*********************************************************************/

void __RPC_FAR * __RPC_USER midl_user_allocate(size_t len)
{
    return(malloc(len));
}

void __RPC_USER midl_user_free(void __RPC_FAR * ptr)
{
    free(ptr);
}

/* end file dynepts.c */


DYNEPT.ACF

[implicit_handle(handle_t dynept_IfHandle)]
interface dynept
{

}


README.TXT

DYNOUT

The DYNOUT sample demonstrates how to allocate memory at a server for an n-byte
object and pass it back to the client as an [out]-only parameter. The client
then frees the memory. This technique allows the stub to call the server
without knowing in advance how much data will be returned.

FILES
=====

The directory samples\rpc\dynout contains the following files for
building the sample distributed application DYNOUT:

File          Description
-------------------------

README.TXT    Readme file for the dynout sample
DYNOUT.IDL    Interface definition language file
DYNOUT.ACF    Attribute configuration file
DYNOUTC.C     Client main program
DYNOUTS.C     Server main program
DYNOUTP.C     Remote procedure
XX_MIDL.BAT   separate MIDL build file
MAKEFILE      nmake file to build 32-bit client and server applications.

NMAKE builds the executable programs DYNOUT.EXE (client) and
DYNOUTS.EXE (server).


DYNOUT.IDL

[
uuid(8b4c8b20-fec6-11ce-b4eb-0080c779fc83),
version(1.0),
pointer_default(unique)
]

interface MemStuff
{
short GrabChunk([out] long *lSize, [out,size_is(,*lSize)] char **ppData);
}


DYNOUTC.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1995 - 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.
\******************************************************************************/
//
// Sample code to show server-side
// memory allocation for an [out] parameter
//
//
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <assert.h>
#include <malloc.h>
#include "dynout.h"

void main(int argc,char **argv)
{
unsigned char *pr = NULL;
long lSize;
int i;
int bOK;

RpcTryExcept
{
for(;;)
{
pr = NULL;// This is *REALLY* important!

GrabChunk(&lSize,&pr);

// quick sanity check
for (i=0, bOK=TRUE; (i < lSize) && bOK; i++)
{
if (*pr != pr[i])
{
puts("***Error***");
bOK=FALSE;
break;
}
}

printf("Returned %d bytes of '%c'\n",lSize,*pr);

midl_user_free(pr);
}
}
RpcExcept(1)
{
printf("Err: %u\n",RpcExceptionCode());
}
RpcEndExcept
}

void  __RPC_FAR * __RPC_API midl_user_allocate(size_t len)
{
    return malloc(len);
}

void __RPC_API midl_user_free(void __RPC_FAR * ptr)
{

    free(ptr);
}


DYNOUTP.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1995 - 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.
\******************************************************************************/
//
// Sample code to show server-side
// memory allocation for an [out] parameter
//
//

#include <assert.h>
#include "stdio.h"
#include "string.h"
#include "dynout.h"

short GrabChunk(long *lSize, unsigned char **ppData)
{
int cbChunk = rand() % 10000;// how much memory to grab
int cChar = 33 + rand()%30;// what to fill it with

printf("GrabChunk() allocating %d bytes of '%c'\n",cbChunk,cChar);

*lSize = (long)cbChunk;
*ppData = midl_user_allocate(cbChunk);
memset(*ppData,cChar,cbChunk);

return 0;
}


DYNOUTS.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1995 - 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.
\******************************************************************************/
//
// Copyright 1996 - 1998 Microsoft Corporation.  All Rights Reserved.
//
//
// Sample code to show server-side
// memory allocation for an [out] parameter
//
//

#include <stdio.h>
#include <assert.h>
#include <malloc.h>
#include "dynout.h"

#ifdef NODEBUG
#define CHECK_STATUS(x)
#define TRACE(s)
#else
#define CHECK_STATUS(x){if (x!=RPC_S_OK) { printf("RPC Err: %d\nfile = %s, line=%d.",x,__FILE__,__LINE__); exit(x);}}
#define TRACE(s)puts(s)
#endif

void main(int argc,char **argv)
{
RPC_STATUSrpcStatus;
RPC_BINDING_VECTOR  *vectBinding;
unsigned char *szEntryName;

TRACE("RpcServerRegisterIf");
rpcStatus=RpcServerRegisterIf(MemStuff_v1_0_s_ifspec,0,0);
CHECK_STATUS(rpcStatus);

TRACE("RpcServerUseAllProtseqs");
rpcStatus=RpcServerUseAllProtseqs(RPC_C_PROTSEQ_MAX_REQS_DEFAULT,NULL);
CHECK_STATUS(rpcStatus);

TRACE("RpcServerInqBindings");
rpcStatus=RpcServerInqBindings(&vectBinding);
CHECK_STATUS(rpcStatus);

TRACE("RpcNsBindingExport");
szEntryName="/.:/MyServer";
rpcStatus=RpcNsBindingExport(RPC_C_NS_SYNTAX_DEFAULT,szEntryName,MemStuff_v1_0_s_ifspec,vectBinding,NULL);
CHECK_STATUS(rpcStatus);

TRACE("RpcEpRegister");
rpcStatus=RpcEpRegister(MemStuff_v1_0_s_ifspec,
vectBinding,
NULL,NULL);
CHECK_STATUS(rpcStatus);

TRACE("RpcBindingVectorFree");
rpcStatus=RpcBindingVectorFree(&vectBinding);
CHECK_STATUS(rpcStatus);

puts("\n\n");
puts("(C) Microsoft New Zealand Limited. Written by Michael Howard, " __DATE__);
puts("\nListening....");

rpcStatus=RpcServerListen(1,RPC_C_LISTEN_MAX_CALLS_DEFAULT,FALSE);
CHECK_STATUS(rpcStatus);
}

void  __RPC_FAR * __RPC_API midl_user_allocate(size_t len)
{
    return malloc(len);
}

void __RPC_API midl_user_free(void __RPC_FAR * ptr)
{
    free(ptr);
}


DYNOUT.ACF

[auto_handle]

interface MemStuff
{
}


README.TXT

AUTO

The AUTO program demonstrates the use of the ACF attribute auto_handle in
a distributed application that generates a time stamp.

The directory samples\rpc\handles\auto contains the following files to
build the distributed application AUTO:

File          Description

README.TXT    Readme file for the AUTO sample
AUTO.IDL      Interface definition language file
AUTO.ACF      Attribute configuration file
AUTOC.C       Client main program
AUTOS.C       Server main program
AUTOP.C       Remote procedures
MAKEFILE      Nmake file to build for Windows NT or Windows 95
MAKEFILE.DOS  Nmake file to build for MS-DOS

-------------------------------------------
BUILDING CLIENT AND SERVER APPLICATIONS FOR
MICROSOFT WINDOWS NT OR WINDOWS 95
-------------------------------------------

The following environment variables should be already set for you:

  set CPU=i386
  set INCLUDE=%SDKROOT%\h
  set LIB=%SDKROOT%\lib
  set PATH=%SDKROOT%\system32;%SDKROOT%\bin

where %SDKROOT% is the root directory for the 32-bit Windows SDK.

For mips, set CPU=mips
For alpha, set CPU=alpha

Build the sample distributed application:

  nmake cleanall
  nmake

This builds the executable programs autoc.exe (client) and
autos.exe (server).

-----------------------------------------------------------------------
BUILDING THE CLIENT APPLICATION FOR MS-DOS
-----------------------------------------------------------------------

After installing the Microsoft Visual C/C++ version 1.50 development
environment and the 16-bit RPC SDK on a Windows NT or Windows 95
computer, you can build the sample client application from Windows NT
or Windows 95:

  nmake -f makefile.dos cleanall
  nmake -f makefile.dos

This builds the client application callc.exe.

You may also execute the Microsoft Visual C/C++ compiler under MS-DOS.
This requires a two-step build process.

  Step One: Compile the .IDL files under Windows NT or Windows 95:

     nmake -a -f makefile.dos auto.h

  Step Two: Compile the C sources (stub and application) under MS-DOS:

     nmake -f makefile.dos

------------------------------------------
RUNNING THE CLIENT AND SERVER APPLICATIONS
------------------------------------------

On the server, enter:

  net start rpclocator
  autos

(You can also start the RPC Locator by running the
Services program in Control Panel.)

On the client, enter:

  net start workstation
  autoc

Note: The client and server applications can run on
the same Microsoft Windows NT computer when you use
different screen groups.

Several command line switches are available to change
settings for this program. For a listing of the switches
available from the client program, enter:

  autoc -?

For a listing of switches available from the server
program, enter:

  autos -?

---------------------
RUNNING ON WINDOWS 95
---------------------

If you wish to run the server on Windows 95, you will need to use
the LRPC protocol.

To start the server, enter:

  autos -p ncalrpc

To start the client (from the same machine), enter:

  autoc -p ncalrpc


AUTO.IDL

[ uuid (6B29FC40-CA47-1067-B31D-00DD010662DA),
  version(1.0),
  pointer_default(unique)
]
interface autoh
{

void GetTime([out] long * time);

void Shutdown(void);

}


AUTOC.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                         Auto Example

    FILE:       autoc.c

    USAGE:      autoc

    PURPOSE:    Client side of RPC distributed application

    FUNCTIONS:  main() - binds to server and calls remote procedure

    COMMENTS:   This distributed application (time stamp) is implemented
                using an auto handle.  The client side of the application
                does not care which server provides the time stamp; it
                merely wants to get the time from any available server.

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

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <time.h>
#include "auto.h"    // header file generated by MIDL compiler

#define PURPOSE \
"This Microsoft RPC Version 2.0 sample program demonstrates\n\
the use of the [auto_handle] attribute. For more information\n\
about attributes and RPC API functions, see the RPC programming\n\
guide and reference.\n\n"

void Usage(char * pszProgramName)
{
    fprintf(stderr, "%s", PURPOSE);
    fprintf(stderr, "Usage:  %s\n", pszProgramName);
    exit(1);
}

void _CRTAPI1 main(int argc, char **argv)
{
    time_t t1;
    time_t t2;
    char * pszTime;
    int i;

    /* allow the user to override settings with command line switches */
    for (i = 1; i < argc; i++) {
        if ((*argv[i] == '-') || (*argv[i] == '/')) {
            switch (tolower(*(argv[i]+1))) {
            case 'h':
            case '?':
            default:
                Usage(argv[0]);
            }
        }
        else
            Usage(argv[0]);
    }

    RpcTryExcept {
        GetTime(&t1);  // GetTime is a remote procedure
        GetTime(&t2);

        pszTime = ctime(&t1);
        printf("time 1= %s\n", pszTime);

        pszTime = ctime(&t2);
        printf("time 2= %s\n", pszTime);

        Shutdown();    // Shutdown is a remote procedure
    }
    RpcExcept(1) {
        printf("The RPC runtime library raised an exception.\n");
        printf("Please verify that the server application and \n");
        printf("the locator service have been started.");
    }
    RpcEndExcept

    exit(0);

}  // end main()

/*********************************************************************/
/*                 MIDL allocate and free                            */
/*********************************************************************/

void __RPC_FAR * __RPC_USER midl_user_allocate(size_t len)
{
    return(malloc(len));
}

void __RPC_USER midl_user_free(void __RPC_FAR * ptr)
{
    free(ptr);
}

/* end file autoc.c */


AUTOP.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                        Auto Example

    FILE:       autop.c

    PURPOSE:    Remote procedures that are linked with the server
                side of RPC distributed application

    FUNCTIONS:  TimeProc() - obtains the time from the server

    COMMENTS:   This distributed application uses an auto handle.

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

#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include "auto.h"    // header file generated by MIDL compiler

void GetTime(time_t * pTime)
{
    time(pTime);
    return;
}

void Shutdown(void)
{
    RPC_STATUS status;

    printf("Calling RpcMgmtStopServerListening\n");
    status = RpcMgmtStopServerListening(NULL);
    printf("RpcMgmtStopServerListening returned: 0x%x\n", status);
    if (status) {
        exit(status);
    }

    printf("Calling RpcServerUnregisterIf\n");
    status = RpcServerUnregisterIf(NULL, NULL, FALSE);
    printf("RpcServerUnregisterIf returned 0x%x\n", status);
    if (status) {
        exit(status);
    }
}

/* end file autop.c */


AUTOS.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                          Auto Example

    FILE:       autos.c

    USAGE:      autos   -p protocol_sequence
                        -e endpoint
                        -m maxcalls
                        -n mincalls
                        -f flag for RpcServerListen wait
                        -a auto_sample_nsi_entry_name
                        -t name_syntax_type

    PURPOSE:    Server side of RPC distributed application Auto

    FUNCTIONS:  main() - registers server as RPC server

    COMMENTS:   This distributed application (time stamp) is implemented
                using an auto handle.  The server side of the application
                must export its binding information and make it available
                to the clients.  The auto handle requires a location
                service running on a server that is accessible to the client.

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

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include "auto.h"    // header file generated by MIDL compiler

void Usage(char * pszProgramName)
{
    fprintf(stderr, "Usage:  %s\n", pszProgramName);
    fprintf(stderr, " -p protocol_sequence\n");
    fprintf(stderr, " -e endpoint\n");
    fprintf(stderr, " -m maxcalls\n");
    fprintf(stderr, " -n mincalls\n");
    fprintf(stderr, " -f flag for RpcServerListen wait\n");
    fprintf(stderr, " -a auto_sample_nsi_entry_name\n");
    fprintf(stderr, " -t name_syntax_type\n");
    exit(1);
}

void _CRTAPI1 main(int argc, char * argv[])
{
    RPC_STATUS status;
    RPC_BINDING_VECTOR * pBindingVector;

    unsigned char * pszAutoEntryName    = "/.:/Autohandle_sample";
    unsigned char * pszEndpoint         = "\\pipe\\auto";
    unsigned char * pszProtocolSequence = "ncacn_np";
    unsigned char * pszSecurity         = NULL;
    unsigned int    cMinCalls           = 1;
    unsigned int    cMaxCalls           = 20;
    unsigned int    fDontWait           = FALSE;
    unsigned int    fNameSyntaxType     = RPC_C_NS_SYNTAX_DEFAULT;
    int i;

    /* allow the user to override settings with command line switches */
    for (i = 1; i < argc; i++) {
        if ((*argv[i] == '-') || (*argv[i] == '/')) {
            switch (tolower(*(argv[i]+1))) {
            case 'p':  // protocol sequence
                pszProtocolSequence = argv[++i];
                break;
            case 'e':
                pszEndpoint = argv[++i];
                break;
            case 'm':
                cMaxCalls = (unsigned int) atoi(argv[++i]);
                break;
            case 'n':
                cMinCalls = (unsigned int) atoi(argv[++i]);
                break;
            case 'f':
                fDontWait = (unsigned int) atoi(argv[++i]);
                break;
            case 'a':
                pszAutoEntryName = argv[++i];
                break;
            case 't':
                fNameSyntaxType = (unsigned int) atoi(argv[++i]);
                break;
            case 'h':
            case '?':
            default:
                Usage(argv[0]);
            }
        }
        else
            Usage(argv[0]);
    }

    status = RpcServerUseProtseqEp(pszProtocolSequence,
                                   cMaxCalls,
                                   pszEndpoint,
                                   pszSecurity);  // Security descriptor
    printf("RpcServerUseProtseqEp returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    status = RpcServerRegisterIf(autoh_ServerIfHandle,  // interface to register
                                 NULL,   // MgrTypeUuid
                                 NULL);  // MgrEpv; null means use default
    printf("RpcServerRegisterIf returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    status = RpcServerInqBindings(&pBindingVector);
    printf("RpcServerInqBindings returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    status = RpcNsBindingExport(fNameSyntaxType,   // name syntax type
                                pszAutoEntryName,  // nsi entry name
                                autoh_ServerIfHandle,
                                pBindingVector,    // set in previous call
                                NULL);             // UUID vector
    printf("RpcNsBindingExport returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    printf("Calling RpcServerListen\n");
    status = RpcServerListen(cMinCalls,
                             cMaxCalls,
                             fDontWait);  // wait flag
    printf("RpcServerListen returned: 0x%x\n", status);
    if (status) {
        exit(status);
    }

    if (fDontWait) {
        printf("Calling RpcMgmtWaitServerListen\n");
        status = RpcMgmtWaitServerListen();  //  wait operation
        printf("RpcMgmtWaitServerListen returned: 0x%x\n", status);
        if (status) {
            exit(status);
        }
    }

}  // end main()

/*********************************************************************/
/*                 MIDL allocate and free                            */
/*********************************************************************/

void __RPC_FAR * __RPC_USER midl_user_allocate(size_t len)
{
    return(malloc(len));
}

void __RPC_USER midl_user_free(void __RPC_FAR * ptr)
{
    free(ptr);
}

/* end file autos.c */


AUTO.ACF

[auto_handle]
interface autoh
{
}


README.TXT

CXHNDL

The CXHNDL program demonstrates the use of the context_handle attribute.

The directory samples\rpc\handles\cxhndl contains the following files for
building the sample distributed application CXHNDL:

File          Description

README.TXT    Readme file for the CXHNDL sample
CXHNDL.IDL    Interface definition language file
CXHNDL.ACF    Attribute configuration file
CXHNDLC.C     Client main program
CXHNDLS.C     Server main program
CXHNDLP.C     Remote procedures
MAKEFILE      Nmake file to build for Windows NT or Windows 95
MAKEFILE.DOS  Nmake file to build for MS-DOS

-------------------------------------------
BUILDING CLIENT AND SERVER APPLICATIONS FOR
MICROSOFT WINDOWS NT OR WINDOWS 95
-------------------------------------------

The following environment variables should be already set for you:

  set CPU=i386
  set INCLUDE=%SDKROOT%\h
  set LIB=%SDKROOT%\lib
  set PATH=%SDKROOT%\system32;%SDKROOT%\bin

where %SDKROOT% is the root directory for the 32-bit Windows SDK.

For mips, set CPU=mips
For alpha, set CPU=alpha

Build the sample distributed application:

  nmake cleanall
  nmake

This builds the executable programs cxhndlc.exe
(client) and cxhndls.exe (server).

-----------------------------------------------------------------------
BUILDING THE CLIENT APPLICATION FOR MS-DOS
-----------------------------------------------------------------------

After installing the Microsoft Visual C/C++ version 1.50 development
environment and the 16-bit RPC SDK on a Windows NT or Windows 95
computer, you can build the sample client application from Windows NT
or Windows 95:

  nmake -f makefile.dos cleanall
  nmake -f makefile.dos

This builds the client application callc.exe.

You may also execute the Microsoft Visual C/C++ compiler under MS-DOS.
This requires a two-step build process.

  Step One: Compile the .IDL files under Windows NT or Windows 95:

     nmake -a -f makefile.dos cxhndl.h

  Step Two: Compile the C sources (stub and application) under MS-DOS:

     nmake -f makefile.dos

------------------------------------------
RUNNING THE CLIENT AND SERVER APPLICATIONS
------------------------------------------

On the server, enter:

  cxhndls

On the client, enter:

  net start workstation
  cxhndlc

Note:  The client and server applications can run on the same Microsoft
Windows NT computer when you use different screen groups.

Several command-line switches are available to change settings for this
program. For a listing of the switches available from the client program,
enter:

  cxhndlc -?

For a listing of switches available from the server program, enter:

  cxhndls -?


CXHNDL.IDL

[ uuid (A4F1DB00-CA47-1067-B31F-00DD010662DA),
  version(1.0),
  pointer_default(unique)
]
interface cxhndl
{

const short BUFSIZE = 1024;

typedef [context_handle] void *PCONTEXT_HANDLE_TYPE;

short RemoteOpen( [out] PCONTEXT_HANDLE_TYPE *pphContext,
                  [in, string] unsigned char *pszFile
                );

short RemoteRead( [in]  PCONTEXT_HANDLE_TYPE phContext,
                  [out] unsigned char achBuf[BUFSIZE],
                  [out] short *pcbBuf
                );

short RemoteClose( [in, out] PCONTEXT_HANDLE_TYPE *pphContext );

void Shutdown(void);

}


CXHNDLC.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                      cxhndl Example

    FILE:       cxhndlc.c

    USAGE:      cxhndlc  -n network_address
                         -p protocol_sequence
                         -e endpoint
                         -o options
                         -f filename

    PURPOSE:    Client side of RPC distributed application

    FUNCTIONS:  main() - binds to server and calls remote procedure

    COMMENTS:   This distributed application uses a context handle.

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

#include <stdlib.h>
#include <stdio.h>
#include "cxhndl.h"    // header file generated by MIDL compiler

#define PURPOSE \
"This Microsoft RPC Version 2.0 sample program demonstrates\n\
the use of the [context_handle] attribute. For more information\n\
about attributes and RPC API functions, see the RPC programming\n\
guide and reference.\n\n"

void Usage(char * pszProgramName)
{
    fprintf(stderr, "%s", PURPOSE);
    fprintf(stderr, "Usage:  %s\n", pszProgramName);
    fprintf(stderr, " -p protocol_sequence\n");
    fprintf(stderr, " -n network_address\n");
    fprintf(stderr, " -e endpoint\n");
    fprintf(stderr, " -o options\n");
    fprintf(stderr, " -f filename\n");
    exit(1);
}

void _CRTAPI1 main(int argc, char **argv)
{
    RPC_STATUS status;
    PCONTEXT_HANDLE_TYPE phContext = NULL;

    unsigned char * pbBuf = NULL;
    short cbRead;    // count of bytes read

    unsigned char * pszUuid             = NULL;
    unsigned char * pszProtocolSequence = "ncacn_np";
    unsigned char * pszNetworkAddress   = NULL;
    unsigned char * pszEndpoint         = "\\pipe\\cxhndl";
    unsigned char * pszOptions          = NULL;
    unsigned char * pszStringBinding    = NULL;
    unsigned char * pszFileName         = "readme.txt";
    int i;

    /* allow the user to override settings with command line switches */
    for (i = 1; i < argc; i++) {
        if ((*argv[i] == '-') || (*argv[i] == '/')) {
            switch (tolower(*(argv[i]+1))) {
            case 'p':  // protocol sequence
                pszProtocolSequence = argv[++i];
                break;
            case 'n':  // network address
                pszNetworkAddress = argv[++i];
                break;
            case 'e':
                pszEndpoint = argv[++i];
                break;
            case 'o':
                pszOptions = argv[++i];
                break;
            case 'f':
                pszFileName = argv[++i];
                break;
            case 'h':
            case '?':
            default:
                Usage(argv[0]);
            }
        }
        else
            Usage(argv[0]);
    }

    pbBuf = (unsigned char *)
            midl_user_allocate(BUFSIZE * sizeof(unsigned char));

    /* Use a convenience function to concatenate the elements of  */
    /* the string binding into the proper sequence.               */
    status = RpcStringBindingCompose(pszUuid,
                                     pszProtocolSequence,
                                     pszNetworkAddress,
                                     pszEndpoint,
                                     pszOptions,
                                     &pszStringBinding);
    printf("RpcStringBindingCompose returned 0x%x\n", status);
    printf("pszStringBinding = %s\n", pszStringBinding);
    if (status) {
        exit(status);
    }

    /* Set the binding handle that will be used to bind to the server. */
    status = RpcBindingFromStringBinding(pszStringBinding,
                                         &hStarter);
    printf("RpcBindingFromStringBinding returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    printf("Calling the remote procedure RemoteOpen\n");
    if (RemoteOpen(&phContext, pszFileName) < 0) {
        printf("Unable to open %s\n", pszFileName);
        Shutdown();
        exit(2);
    }

    /* Now the context handle also manages the binding. */
    status = RpcBindingFree(&hStarter);
    printf("RpcBindingFree returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    /*  Free the string binding */
    status = RpcStringFree(&pszStringBinding);
    printf("RpcStringFree returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    printf("Calling the remote procedure RemoteRead\n");
    while (RemoteRead(phContext, pbBuf, &cbRead) > 0) {
        for (i = 0; i < cbRead; i++)
            putchar(*(pbBuf+i));
    }

    printf("Calling the remote procedure RemoteClose\n");
    if (RemoteClose(&phContext) < 0 ) {
        printf("Close failed on %s\n", pszFileName);
        exit(2);
    }

    exit(0);

}  // end main()

/*********************************************************************/
/*                 MIDL allocate and free                            */
/*********************************************************************/

void __RPC_FAR * __RPC_USER midl_user_allocate(size_t len)
{
    return(malloc(len));
}

void __RPC_USER midl_user_free(void __RPC_FAR * ptr)
{
    free(ptr);
}

/* end cxhndlc.c */


CXHNDLP.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                        cxhndl Example

    FILE:       cxhndlp.c

    PURPOSE:    Remote procedures used in server application cxhndls

    FUNCTIONS:  RemoteOpen() - Open the file
                RemoteRead() - Read a buffer's worth of the file
                RemoteClose() - Close the file and shutdown server
                Shutdown() - Shutdown the server

    COMMENTS:   This distributed application uses a context handle.

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

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "cxhndl.h"    // header file generated by MIDL compiler

typedef struct {
    FILE *hFile;
    char achFile[256];
} FILE_CONTEXT_TYPE;

/* This remote procedure opens a file on the server. */
short RemoteOpen(PCONTEXT_HANDLE_TYPE  *pphContext,
                 unsigned char         *pszFileName
    )
{
    FILE *hFile;
    FILE_CONTEXT_TYPE *pFileContext;

    if ((hFile = fopen(pszFileName, "r")) == NULL) {
        *pphContext = (PCONTEXT_HANDLE_TYPE) NULL;
        return(-1);
    }
    else {
        pFileContext = (FILE_CONTEXT_TYPE *)
                       midl_user_allocate(sizeof(FILE_CONTEXT_TYPE));
        pFileContext->hFile = hFile;
        strcpy(pFileContext->achFile, pszFileName);
        *pphContext = (PCONTEXT_HANDLE_TYPE) pFileContext;
        return(0);
    }
}

/* This remote procedure reads a file on the server. */
short RemoteRead(PCONTEXT_HANDLE_TYPE  phContext,
                 unsigned char         *pbBuf,
                 short                 *pcbBuf)
{
    FILE_CONTEXT_TYPE *pFileContext;

    printf("in RemoteRead\n");

    pFileContext = (FILE_CONTEXT_TYPE *) phContext;
    *pcbBuf = (short) fread(pbBuf,
                            sizeof(char),
                            BUFSIZE,
                            pFileContext->hFile);
    return(*pcbBuf);
}

/* This remote procedure closes a file on the server. */
short RemoteClose(PCONTEXT_HANDLE_TYPE *pphContext)
{
    FILE_CONTEXT_TYPE *pFileContext;

    printf("in RemoteClose\n");

    pFileContext = (FILE_CONTEXT_TYPE *) *pphContext;

    if ( fclose( pFileContext->hFile ) == 0)
        {
        midl_user_free( *pphContext );
        *pphContext = NULL;
        return(0);
        }
    else
        /* Context Rundown routine will attempt to close it again */
        return(-1);
}

/* The Shutdown procedure tells the server to stop listening */
/* for client requests.                                      */
void Shutdown(void)
{
    RPC_STATUS status;

    printf("Calling RpcMgmtStopServerListening\n");
    status = RpcMgmtStopServerListening(NULL);
    printf("RpcMgmtStopServerListening returned: 0x%x\n", status);
    if (status) {
        exit(status);
    }

    printf("Calling RpcServerUnregisterIf\n");
    status = RpcServerUnregisterIf(NULL, NULL, FALSE);
    printf("RpcServerUnregisterIf returned 0x%x\n", status);
    if (status) {
        exit(status);
    }
}

/* The rundown routine is associated with the context handle type. */
void __RPC_USER PCONTEXT_HANDLE_TYPE_rundown(PCONTEXT_HANDLE_TYPE phContext)
{
    FILE_CONTEXT_TYPE *pFileContext;

    printf("Context rundown routine\n");

    if ( phContext )
    {
        pFileContext = (FILE_CONTEXT_TYPE *) phContext;
        if (pFileContext->hFile != NULL)
            fclose(pFileContext->hFile);

        midl_user_free( phContext );
    }

}

/* end file cxhndlp.c */


CXHNDLS.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                        cxhndl Example

    FILE:       cxhndls.c

    USAGE:      cxhndls  -p protocol_sequence
                         -e endpoint
                         -m max calls
                         -n min calls
                         -f flag for RpcServerListen

    PURPOSE:    Server side of RPC distributed application cxhndl

    FUNCTIONS:  main() - registers server as RPC server

    COMMENTS:   This distributed application uses a context handle.

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

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include "cxhndl.h"    // header file generated by MIDL compiler

#define PURPOSE \
"This Microsoft RPC Version 2.0 sample program demonstrates\n\
the [context_handle] attribute. For more information\n\
about this attribute and the RPC API functions, see the\n\
RPC programming guide and reference.\n\n"

void Usage(char * pszProgramName)
{
    fprintf(stderr, "%s", PURPOSE);
    fprintf(stderr, "Usage:  %s\n", pszProgramName);
    fprintf(stderr, " -p protocol_sequence\n");
    fprintf(stderr, " -e endpoint\n");
    fprintf(stderr, " -m maxcalls\n");
    fprintf(stderr, " -n mincalls\n");
    fprintf(stderr, " -f flag_wait_op\n");
    exit(1);
}

void _CRTAPI1 main(int argc, char * argv[])
{
    RPC_STATUS status;
    unsigned char * pszProtocolSequence = "ncacn_np";
    unsigned char * pszSecurity         = NULL;
    unsigned char * pszEndpoint         = "\\pipe\\cxhndl";
    unsigned int    cMinCalls           = 1;
    unsigned int    cMaxCalls           = 20;
    unsigned int    fDontWait           = FALSE;
    int i;

    /* allow the user to override settings with command line switches */
    for (i = 1; i < argc; i++) {
        if ((*argv[i] == '-') || (*argv[i] == '/')) {
            switch (tolower(*(argv[i]+1))) {
            case 'p':  // protocol sequence
                pszProtocolSequence = argv[++i];
                break;
            case 'e':
                pszEndpoint = argv[++i];
                break;
            case 'm':
                cMaxCalls = (unsigned int) atoi(argv[++i]);
                break;
            case 'n':
                cMinCalls = (unsigned int) atoi(argv[++i]);
                break;
            case 'f':
                fDontWait = (unsigned int) atoi(argv[++i]);
                break;
            case 'h':
            case '?':
            default:
                Usage(argv[0]);
            }
        }
        else
            Usage(argv[0]);
    }

    status = RpcServerUseProtseqEp(pszProtocolSequence,
                                   cMaxCalls,
                                   pszEndpoint,
                                   pszSecurity);  // Security descriptor
    printf("RpcServerUseProtseqEp returned 0x%x\n", status);
    if (status) {
         exit(status);
    }

    status = RpcServerRegisterIf(cxhndl_ServerIfHandle, // interface to register
                                 NULL,   // MgrTypeUuid
                                 NULL);  // MgrEpv; null means use default
    printf("RpcServerRegisterIf returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    printf("Calling RpcServerListen\n");
    status = RpcServerListen(cMinCalls,
                             cMaxCalls,
                             fDontWait);
    printf("RpcServerListen returned: 0x%x\n", status);
    if (status) {
        exit(status);
    }

    if (fDontWait) {
        printf("Calling RpcMgmtWaitServerListen\n");
        status = RpcMgmtWaitServerListen();  //  wait operation
        printf("RpcMgmtWaitServerListen returned: 0x%x\n", status);
        if (status) {
            exit(status);
        }
    }

} // end main()

/*********************************************************************/
/*                 MIDL allocate and free                            */
/*********************************************************************/

void __RPC_FAR * __RPC_USER midl_user_allocate(size_t len)
{
    return(malloc(len));
}

void __RPC_USER midl_user_free(void __RPC_FAR * ptr)
{
    free(ptr);
}

/* end file cxhndls.c */


CXHNDL.ACF

[implicit_handle(handle_t hStarter)]
interface cxhndl
{
}


README.TXT

USRDEF

The USRDEF program demonstrates the use of user-defined handles.

The directory samples\rpc\handles\usrdef contains the following files
for building the sample distributed application USRDEF:

File          Description

README.TXT    Readme file for the USRDEF sample
USRDEF.IDL    Interface definition language file
USRDEF.ACF    Attribute configuration file
USRDEFC.C     Client main program
USRDEFS.C     Server main program
USRDEFP.C     Remote procedures
MAKEFILE      Nmake file to build for Windows NT or Windows 95
MAKEFILE.DOS  Nmake file to build for MS-DOS

-------------------------------------------
BUILDING CLIENT AND SERVER APPLICATIONS FOR
MICROSOFT WINDOWS NT OR WINDOWS 95:
-------------------------------------------

The following environment variables should be set for you already.

  set CPU=i386
  set INCLUDE=%SDKROOT%\h
  set LIB=%SDKROOT%\lib
  set PATH=%SDKROOT%\system32;%SDKROOT%\bin;

Where %SDKROOT% is the root directory for the 32-bit Windows SDK.

For mips, set CPU=mips
For alpha, set CPU=alpha

Build the sample distributed application:
  nmake cleanall
  nmake

This builds the executable programs usrdefc.exe
(client) and usrdefs.exe (server).

-----------------------------------------------------------------------
BUILDING THE CLIENT APPLICATION FOR MS-DOS
-----------------------------------------------------------------------

After installing the Microsoft Visual C/C++ version 1.50 development
environment and the 16-bit RPC SDK on a Windows NT or Windows 95
computer, you can build the sample client application from Windows NT
or Windows 95.

  nmake -f makefile.dos cleanall
  nmake -f makefile.dos

This builds the client application usrdefc.exe.

You may also execute the Microsoft Visual C/C++ compiler under MS-DOS.
This requires a two step build process.

  Step One: Compile the .IDL files under Windows NT or Windows 95
     nmake -a -f makefile.dos usrdef.h

  Step Two: Compile the C sources (stub and application) under MS-DOS.
     nmake -f makefile.dos

------------------------------------------
RUNNING THE CLIENT AND SERVER APPLICATIONS
------------------------------------------

On the server, enter

  usrdefs

On the client, enter

  net start workstation
  usrdefc

Note:  The client and server applications can run on
the same Microsoft Windows NT computer when you use
different screen groups.

Several command line switches are available to change
settings for this program. For a listing of the switches
available from the client program, enter

  usrdefc -?

For a listing of switches available from the server
program, enter

  usrdefs -?


USRDEF.ACF

[implicit_handle(handle_t hUnused)]

interface usrdef
{

}


USRDEF.IDL

[ uuid (85D7D1C0-CA47-1067-B31E-00DD010662DA),
  version(1.0),
  pointer_default(unique)
]
interface usrdef
{

typedef struct _DATA_TYPE {
    unsigned char * pszUuid;
    unsigned char * pszProtocolSequence;
    unsigned char * pszNetworkAddress;
    unsigned char * pszEndpoint;
    unsigned char * pszOptions;
} DATA_TYPE;

typedef [handle] DATA_TYPE *DATA_HANDLE_TYPE;

void UsrdefProc([in]     DATA_HANDLE_TYPE  hBinding,
[in, string] unsigned char     *pszString);

void Shutdown([in] DATA_HANDLE_TYPE  hBinding);

}


USRDEFC.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                        usrdef Example

    FILE:       usrdefc.c

    USAGE:      usrdefc  -n network_address
                         -p protocol_sequence
                         -e endpoint
                         -o options

    PURPOSE:    Client side of RPC distributed application

    FUNCTIONS:  main() - binds to server and calls remote procedure

    COMMENTS:   This distributed application uses a user-defined handle.

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

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include "usrdef.h"    // header file generated by MIDL compiler

void Usage(char * pszProgramName)
{
    fprintf(stderr, "Usage:  %s\n", pszProgramName);
    fprintf(stderr, " -p protocol_sequence\n");
    fprintf(stderr, " -n network_address\n");
    fprintf(stderr, " -e endpoint\n");
    fprintf(stderr, " -o options\n");
    fprintf(stderr, " -s string\n");
    exit(1);
}

void _CRTAPI1 main(int argc, char **argv)
{
    int i;
    DATA_HANDLE_TYPE dhBinding;
    unsigned char * pszString = "hello, world";

    dhBinding = (DATA_HANDLE_TYPE) midl_user_allocate(sizeof(DATA_TYPE));
    dhBinding->pszProtocolSequence = "ncacn_np";
    dhBinding->pszUuid             = NULL;
    dhBinding->pszEndpoint         = "\\pipe\\usrdef";
    dhBinding->pszNetworkAddress   = NULL;
    dhBinding->pszOptions          = NULL;

    /* allow the user to override settings with command line switches */
    for (i = 1; i < argc; i++) {
        if ((*argv[i] == '-') || (*argv[i] == '/')) {
            switch (tolower(*(argv[i]+1))) {
            case 'p':  // protocol sequence
                dhBinding->pszProtocolSequence = argv[++i];
                break;
            case 'n':  // network address
                dhBinding->pszNetworkAddress = argv[++i];
                break;
            case 'e':
                dhBinding->pszEndpoint = argv[++i];
                break;
            case 'o':
                dhBinding->pszOptions = argv[++i];
                break;
            case 's':
                pszString = argv[++i];
                break;
            case 'h':
            case '?':
            default:
                Usage(argv[0]);
            }
        }
        else
            Usage(argv[0]);
    }

    printf("Calling the remote procedure 'UsrdefProc'\n");
    UsrdefProc(dhBinding, pszString);  // call the remote procedure

    printf("Calling the remote procedure 'Shutdown'\n");
    Shutdown(dhBinding);  // shut down the server side

    exit(0);

}  // end main()

/* This _bind routine is called by the client stub immediately */
/* before each remote procedure call.                          */
RPC_BINDING_HANDLE __RPC_USER DATA_HANDLE_TYPE_bind(DATA_HANDLE_TYPE dh1)
{
    RPC_STATUS status;    // returned by RPC API functions
    RPC_BINDING_HANDLE hBinding;
    unsigned char * pszStringBinding;

    printf("Within DATA_HANDLE_TYPE_bind function:\n");
    status = RpcStringBindingCompose(dh1->pszUuid,
                                     dh1->pszProtocolSequence,
                                     dh1->pszNetworkAddress,
                                     dh1->pszEndpoint,
                                     dh1->pszOptions,
                                     &pszStringBinding);
    printf("RpcStringBindingCompose returned 0x%x\n", status);
    printf("pszStringBinding = %s\n", pszStringBinding);
    if (status) {
        exit(status);
    }

    status = RpcBindingFromStringBinding(pszStringBinding,
                                         &hBinding);
    printf("RpcBindingFromStringBinding returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    status = RpcStringFree(&pszStringBinding);  // unbind
    printf("RpcStringFree returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    return(hBinding);
}

/* This _unbind routine is called by the client stub immediately */
/* after each remote procedure call.                             */
void __RPC_USER DATA_HANDLE_TYPE_unbind(DATA_HANDLE_TYPE dh1,
                                       RPC_BINDING_HANDLE h1)
{
     RPC_STATUS status;    // returned by RPC API functions

     printf("Within DATA_HANDLE_TYPE_unbind function:\n");
     printf("Unbinding handle for %s\n", dh1->pszEndpoint);

     status = RpcBindingFree(&h1);  // unbind
     printf("RpcBindingFree returned 0x%x\n", status);
}

/*********************************************************************/
/*                 MIDL allocate and free                            */
/*********************************************************************/

void __RPC_FAR * __RPC_USER midl_user_allocate(size_t len)
{
    return(malloc(len));
}

void __RPC_USER midl_user_free(void __RPC_FAR * ptr)
{
    free(ptr);
}

/* end file usrdefc.c */


USRDEFP.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                        usrdef Example

    FILE:       usrdefp.c

    PURPOSE:    Remote procedures that are linked with the server
                side of RPC distributed application

    FUNCTIONS:  UsrdefProc() -

    COMMENTS:   This distributed application uses a user-defined handle.

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

#include <stdlib.h>
#include <stdio.h>
#include "usrdef.h"    // header file generated by MIDL compiler

void UsrdefProc(DATA_HANDLE_TYPE dh, unsigned char * pszString)
{
    printf("%s\n", pszString);
}

void Shutdown(DATA_HANDLE_TYPE dh)
{
    RPC_STATUS status;

    printf("Calling RpcMgmtStopServerListening\n");
    status = RpcMgmtStopServerListening(NULL);
    printf("RpcMgmtStopServerListening returned: 0x%x\n", status);
    if (status) {
        exit(status);
    }

    printf("Calling RpcServerUnregisterIf\n");
    status = RpcServerUnregisterIf(NULL, NULL, FALSE);
    printf("RpcServerUnregisterIf returned 0x%x\n", status);
    if (status) {
        exit(status);
    }
}

/* end file usrdefp.c */


USRDEFS.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                       usrdef Example

    FILE:       usrdefs.c

    USAGE:      usrdefs  -p protocol_sequence
                         -e endpoint
                         -m max calls
                         -n min calls
                         -f flag for RpcServerListen

    PURPOSE:    Server side of RPC distributed application usrdef

    FUNCTIONS:  main() - registers server as RPC server

    COMMENTS:   This distributed application uses a user-defined handle.

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

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include "usrdef.h"    // header file generated by MIDL compiler

#define PURPOSE \
"This Microsoft RPC Version 2.0 sample program demonstrates\n\
the use of user-defined handles. For more information\n\
about the [handle] attribute and RPC API functions, see the\n\
RPC programming guide and reference.\n\n"

void Usage(char * pszProgramName)
{
    fprintf(stderr, "%s", PURPOSE);
    fprintf(stderr, "Usage:  %s\n", pszProgramName);
    fprintf(stderr, " -p protocol_sequence\n");
    fprintf(stderr, " -e endpoint\n");
    fprintf(stderr, " -m maxcalls\n");
    fprintf(stderr, " -n mincalls\n");
    fprintf(stderr, " -f flag_wait_op\n");
    exit(1);
}

void _CRTAPI1 main(int argc, char * argv[])
{
    RPC_STATUS status;
    unsigned char * pszProtocolSequence = "ncacn_np";
    unsigned char * pszSecurity         = NULL;
    unsigned char * pszEndpoint         = "\\pipe\\usrdef";
    unsigned int    cMinCalls           = 1;
    unsigned int    cMaxCalls           = 20;
    unsigned int    fDontWait           = FALSE;
    int i;

    /* allow the user to override settings with command line switches */
    for (i = 1; i < argc; i++) {
        if ((*argv[i] == '-') || (*argv[i] == '/')) {
            switch (tolower(*(argv[i]+1))) {
            case 'p':  // protocol sequence
                pszProtocolSequence = argv[++i];
                break;
            case 'e':
                pszEndpoint = argv[++i];
                break;
            case 'm':
                cMaxCalls = (unsigned int) atoi(argv[++i]);
                break;
            case 'n':
                cMinCalls = (unsigned int) atoi(argv[++i]);
                break;
            case 'f':
                fDontWait = (unsigned int) atoi(argv[++i]);
                break;
            case 'h':
            case '?':
            default:
                Usage(argv[0]);
            }
        }
        else
            Usage(argv[0]);
    }

    status = RpcServerUseProtseqEp(pszProtocolSequence,
                                   cMaxCalls,
                                   pszEndpoint,
                                   pszSecurity);  // Security descriptor
    printf("RpcServerUseProtseqEp returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    status = RpcServerRegisterIf(usrdef_ServerIfHandle, // interface to register
                                 NULL,   // MgrTypeUuid
                                 NULL);  // MgrEpv; null means use default
    printf("RpcServerRegisterIf returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    printf("Calling RpcServerListen\n");
    status = RpcServerListen(cMinCalls,
                             cMaxCalls,
                             fDontWait);
    printf("RpcServerListen returned: 0x%x\n", status);
    if (status) {
        exit(status);
    }

    if (fDontWait) {
        printf("Calling RpcMgmtWaitServerListen\n");
        status = RpcMgmtWaitServerListen();  //  wait operation
        printf("RpcMgmtWaitServerListen returned: 0x%x\n", status);
        if (status) {
            exit(status);
        }
    }

}  // end main()

/*********************************************************************/
/*                 MIDL allocate and free                            */
/*********************************************************************/

void __RPC_FAR * __RPC_USER midl_user_allocate(size_t len)
{
    return(malloc(len));
}

void __RPC_USER midl_user_free(void __RPC_FAR * ptr)
{
    free(ptr);
}

/* end file usrdefs.c */


README.TXT

HELLO

The HELLO program is a client/server implementation of the classic "Hello,
world" program.

FILES
=====

The directory samples\rpc\hello contains the following files for
building the sample distributed application HELLO:

File          Description

README.TXT    Readme file for the HELLO sample
HELLO.IDL     Interface definition language file
HELLO.ACF     Attribute configuration file
HELLOC.C      Client main program
HELLOS.C      Server main program
HELLOP.C      Remote procedures
MAKEFILE      Nmake file to build for Windows NT or Windows 95
MAKEFILE.DOS  Nmake file to build for MS-DOS

-------------------------------------------
BUILDING CLIENT AND SERVER APPLICATIONS FOR
MICROSOFT WINDOWS NT OR WINDOWS 95
-------------------------------------------

The following environment variables should be already set for you:

  set CPU=i386
  set INCLUDE=%SDKROOT%\h
  set LIB=%SDKROOT%\lib
  set PATH=%SDKROOT%\system32;%SDKROOT%\bin

where %SDKROOT% is the root directory for the 32-bit Windows SDK.

For mips, set CPU=mips
For alpha, set CPU=alpha

Build the sample distributed application:

  nmake cleanall
  nmake

This builds the executable programs helloc.exe (client) and
hellos.exe (server).

------------------------------------------
BUILDING THE CLIENT APPLICATION FOR MS-DOS
------------------------------------------

After installing the Microsoft Visual C/C++ version 1.50 development
environment and the 16-bit RPC SDK on a Windows NT or Windows 95
computer, you can build the sample client application from Windows NT
or Windows 95:

  nmake -f makefile.dos cleanall
  nmake -f makefile.dos

This builds the client application helloc.exe.

You may also execute the Microsoft Visual C/C++ compiler under
MS-DOS. This requires a two-step build process.

  Step One: Compile the .IDL files under Windows NT or Windows 95:

     nmake -a -f makefile.dos hello.h

  Step Two: Compile the C sources (stub and application) under
    MS-DOS:

     nmake -f makefile.dos

------------------------------------------
RUNNING THE CLIENT AND SERVER APPLICATIONS
------------------------------------------

On the server, enter:

  hellos

On the client, enter:

  net start workstation
  helloc

Note: The client and server applications can run on the same
Microsoft Windows NT computer when you use different screen groups.

Several command-line switches are available to change settings for
this program. For a listing of the switches available from the client
program, enter:

  helloc -?

For a listing of switches available from the server
program, enter:

  hellos -?


HELLO.IDL

[ uuid (906B0CE0-C70B-1067-B317-00DD010662DA),
  version(1.0),
  pointer_default(unique)
]
interface hello
{

void HelloProc([in, string] unsigned char * pszString);

void Shutdown(void);

}


HELLOC.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                       Hello Example

    FILE:       helloc.c

    USAGE:      helloc  -n network_address
                        -p protocol_sequence
                        -e endpoint
                        -o options
                        -s string_displayed_on_server

    PURPOSE:    Client side of RPC distributed application

    FUNCTIONS:  main() - binds to server and calls remote procedure

    COMMENTS:   This version of the distributed application that
                prints "hello, world" (or other string) on the server
                features a client that manages its connection to the
                server. It uses the binding handle hello_IfHandle,
                defined in the file hello.h.

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

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include "hello.h"    // header file generated by MIDL compiler

void Usage(char * pszProgramName)
{
    fprintf(stderr, "Usage:  %s\n", pszProgramName);
    fprintf(stderr, " -p protocol_sequence\n");
    fprintf(stderr, " -n network_address\n");
    fprintf(stderr, " -e endpoint\n");
    fprintf(stderr, " -o options\n");
    fprintf(stderr, " -s string\n");
    exit(1);
}

void _CRTAPI1 main(int argc, char **argv)
{
    RPC_STATUS status;
    unsigned char * pszUuid             = NULL;
    unsigned char * pszProtocolSequence = "ncacn_np";
    unsigned char * pszNetworkAddress   = NULL;
    unsigned char * pszEndpoint         = "\\pipe\\hello";
    unsigned char * pszOptions          = NULL;
    unsigned char * pszStringBinding    = NULL;
    unsigned char * pszString           = "hello, world";
    unsigned long ulCode;
    int i;

    /* allow the user to override settings with command line switches */
    for (i = 1; i < argc; i++) {
        if ((*argv[i] == '-') || (*argv[i] == '/')) {
            switch (tolower(*(argv[i]+1))) {
            case 'p':  // protocol sequence
                pszProtocolSequence = argv[++i];
                break;
            case 'n':  // network address
                pszNetworkAddress = argv[++i];
                break;
            case 'e':  // endpoint
                pszEndpoint = argv[++i];
                break;
            case 'o':
                pszOptions = argv[++i];
                break;
            case 's':
                pszString = argv[++i];
                break;
            case 'h':
            case '?':
            default:
                Usage(argv[0]);
            }
        }
        else
            Usage(argv[0]);
    }

    /* Use a convenience function to concatenate the elements of */
    /* the string binding into the proper sequence.              */
    status = RpcStringBindingCompose(pszUuid,
                                     pszProtocolSequence,
                                     pszNetworkAddress,
                                     pszEndpoint,
                                     pszOptions,
                                     &pszStringBinding);
    printf("RpcStringBindingCompose returned 0x%x\n", status);
    printf("pszStringBinding = %s\n", pszStringBinding);
    if (status) {
        exit(status);
    }

    /* Set the binding handle that will be used to bind to the server. */
    status = RpcBindingFromStringBinding(pszStringBinding,
                                         &hello_IfHandle);
    printf("RpcBindingFromStringBinding returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    printf("Calling the remote procedure 'HelloProc'\n");
    printf("Print the string '%s' on the server\n", pszString);

    RpcTryExcept {
        HelloProc(pszString);  // make call with user message
        printf("Calling the remote procedure 'Shutdown'\n");
        Shutdown();  // shut down the server side
    }
    RpcExcept(1) {
        ulCode = RpcExceptionCode();
        printf("Runtime reported exception 0x%lx = %ld\n", ulCode, ulCode);
    }
    RpcEndExcept

    /*  The calls to the remote procedures are complete. */
    /*  Free the string and the binding handle           */
    status = RpcStringFree(&pszStringBinding);  // remote calls done; unbind
    printf("RpcStringFree returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    status = RpcBindingFree(&hello_IfHandle);  // remote calls done; unbind
    printf("RpcBindingFree returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    exit(0);

}  // end main()

/*********************************************************************/
/*                 MIDL allocate and free                            */
/*********************************************************************/

void  __RPC_FAR * __RPC_USER midl_user_allocate(size_t len)
{
    return(malloc(len));
}

void __RPC_USER midl_user_free(void __RPC_FAR * ptr)
{
    free(ptr);
}

/* end file helloc.c */


HELLOP.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                        Hello Example

    FILE:       hellop.c

    PURPOSE:    Remote procedures that are linked with the server
                side of RPC distributed application

    FUNCTIONS:  HelloProc() - prints "hello, world" or other string
                sent by client to server

    COMMENTS:   This version of the distributed application that prints
                "hello, world" (or other string) on the server features
                a client that manages its connection to the server.
                It uses the binding handle hello_IfHandle, defined in
                the file hello.h.

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

#include <stdlib.h>
#include <stdio.h>
#include "hello.h"    // header file generated by MIDL compiler

void HelloProc(unsigned char * pszString)
{
    printf("%s\n", pszString);
}

void Shutdown(void)
{
    RPC_STATUS status;

    printf("Calling RpcMgmtStopServerListening\n");
    status = RpcMgmtStopServerListening(NULL);
    printf("RpcMgmtStopServerListening returned: 0x%x\n", status);
    if (status) {
       exit(status);
    }

    printf("Calling RpcServerUnregisterIf\n");
    status = RpcServerUnregisterIf(NULL, NULL, FALSE);
    printf("RpcServerUnregisterIf returned 0x%x\n", status);
    if (status) {
       exit(status);
    }
}

/* end file hellop.c */


HELLOS.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                        hello Example

    FILE:       hellos.c

    USAGE:      hellos  -p protocol_sequence
                        -e endpoint
                        -m max calls
                        -n min calls
                        -f flag for RpcServerListen

    PURPOSE:    Server side of RPC distributed application hello

    FUNCTIONS:  main() - registers server as RPC server

    COMMENTS:   This version of the distributed application that prints
                "hello, world" (or other string) on the server features
                a client that manages its connection to the server.
                It uses the binding handle hello_IfHandle, defined in
                the file hello.h.

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

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include "hello.h"    // header file generated by MIDL compiler

#define PURPOSE \
"This Microsoft RPC Version 1.0 sample program demonstrates\n\
the use of the [string] attribute. For more information\n\
about the attributes and the RPC API functions, see the\n\
RPC programming guide and reference.\n\n"

void Usage(char * pszProgramName)
{
    fprintf(stderr, "%s", PURPOSE);
    fprintf(stderr, "Usage:  %s\n", pszProgramName);
    fprintf(stderr, " -p protocol_sequence\n");
    fprintf(stderr, " -e endpoint\n");
    fprintf(stderr, " -m maxcalls\n");
    fprintf(stderr, " -n mincalls\n");
    fprintf(stderr, " -f flag_wait_op\n");
    exit(1);
}

/* main:  register the interface, start listening for clients */
void _CRTAPI1 main(int argc, char * argv[])
{
    RPC_STATUS status;
    unsigned char * pszProtocolSequence = "ncacn_np";
    unsigned char * pszSecurity         = NULL;
    unsigned char * pszEndpoint         = "\\pipe\\hello";
    unsigned int    cMinCalls           = 1;
    unsigned int    cMaxCalls           = 20;
    unsigned int    fDontWait           = FALSE;
    int i;

    /* allow the user to override settings with command line switches */
    for (i = 1; i < argc; i++) {
        if ((*argv[i] == '-') || (*argv[i] == '/')) {
            switch (tolower(*(argv[i]+1))) {
            case 'p':  // protocol sequence
                pszProtocolSequence = argv[++i];
                break;
            case 'e':
                pszEndpoint = argv[++i];
                break;
            case 'm':
                cMaxCalls = (unsigned int) atoi(argv[++i]);
                break;
            case 'n':
                cMinCalls = (unsigned int) atoi(argv[++i]);
                break;
            case 'f':
                fDontWait = (unsigned int) atoi(argv[++i]);
                break;
            case 'h':
            case '?':
            default:
                Usage(argv[0]);
            }
        }
        else
            Usage(argv[0]);
    }

    status = RpcServerUseProtseqEp(pszProtocolSequence,
                                   cMaxCalls,
                                   pszEndpoint,
                                   pszSecurity);  // Security descriptor
    printf("RpcServerUseProtseqEp returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    status = RpcServerRegisterIf(hello_ServerIfHandle,  // interface to register
                                 NULL,   // MgrTypeUuid
                                 NULL);  // MgrEpv; null means use default
    printf("RpcServerRegisterIf returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    printf("Calling RpcServerListen\n");
    status = RpcServerListen(cMinCalls,
                             cMaxCalls,
                             fDontWait);
    printf("RpcServerListen returned: 0x%x\n", status);
    if (status) {
        exit(status);
    }

    if (fDontWait) {
        printf("Calling RpcMgmtWaitServerListen\n");
        status = RpcMgmtWaitServerListen();  // wait operation
        printf("RpcMgmtWaitServerListen returned: 0x%x\n", status);
        if (status) {
            exit(status);
        }
    }

}  // end main()

/*********************************************************************/
/*                MIDL allocate and free                             */
/*********************************************************************/

void __RPC_FAR * __RPC_USER midl_user_allocate(size_t len)
{
    return(malloc(len));
}

void __RPC_USER midl_user_free(void __RPC_FAR * ptr)
{
    free(ptr);
}

/* end file hellos.c */


HELLO.ACF

[implicit_handle(handle_t hello_IfHandle)]
interface hello
{

}


README.TXT

INTEROP

The INTEROP program demonstrates portability between OSF DCE RPC and
Microsoft RPC.

SUMMARY
=======

The INTEROP program sends simple messages (strings) from the client to the
server. The most important part of INTEROP is the header file dceport.h.
This header file maps OSF DCE RPC APIs and data structures to the
Microsoft RPC equivalents.

FILES
=====

The directory samples\rpc\interop contains the following files for building
the distributed application INTEROP:

File          Description

README.TXT    Readme file for the INTEROP sample
MSG.IDL       Interface definition language file
MSG.ACF       Attribute configuration file
DCEPORT.H     Maps OSF APIs and structures to Microsoft RPC equivalents
CLIENT.C      Client main program
SERVER.C      Server main program
MIDLUSER.C    Memory management routines for Microsoft RPC
MANAGER.C     Client-side utility routines
MAKEFILE      Nmake file to build for Windows NT or Windows 95
MAKEFILE.DOS  Nmake file to build for MS-DOS
MAKEFILE.DOS  Makefile to build for DCE platform

------------------------------------
Building on Windows NT or Windows 95
------------------------------------

The following environment variables should be already set for you:

  set CPU=i386
  set INCLUDE=%SDKROOT%\h
  set LIB=%SDKROOT%\lib
  set PATH=%SDKROOT%\system32;%SDKROOT%\bin

where %SDKROOT% is the root directory for the 32-bit Windows SDK.

For mips, set CPU=mips
For alpha, set CPU=alpha

Build the sample distributed application:

  nmake cleanall
  nmake

--------------------------
Building on MS-DOS Systems
--------------------------

After installing the Microsoft Visual C/C++ version 1.50 development
environment and the 16-bit RPC SDK on a Windows NT or Windows 95
computer, you can build the sample client application from Windows NT
or Windows 95:

  nmake -f makefile.dos cleanall
  nmake -f makefile.dos

This builds the client application callc.exe.

You may also execute the Microsoft Visual C/C++ compiler under MS-DOS.
This requires a two-step build process.

  Step One: Compile the .IDL files under Windows NT or Windows 95:

     nmake -a -f makefile.dos msg.h

  Step Two: Compile the C sources (stub and application):

     nmake -f makefile.dos

Building on DCE Systems
-----------------------

You need to copy the following files to the DCE machine:

client.c
server.c
manager.c
msg.idl
msg.acf
makefile.dce

  make -f makefile.dce cleanall all

Note: You will probably need to change the CFLAGS and LIBS
variables in makefile.dce to match your platform.

Using the program:
------------------

The basic example:

Run:
  server
on the server machine.

Run:
  client -n <server name> -s "Hi, I'm a client"
on the client machine to send the message.

Run:
  client -n <server name> -s "Okay, stop this example" -x
on the client machine to send the message and cause the server to stop.

You can use fixed endpoints by adding the -e switch:

server -e 3452
client -e 3452 -n <server name> -s "Hi, I'm a client"
client -e 3452 -n <server name> -s "Okay, stop this example" -x

You can run the demo over a different protocol by adding a -t switch
to both the client and server:

server -t ncacn_np
client -t ncacn_np -n <server name> -s "Hi, I'm a client"
client -t ncacn_np -n <server name> -s "Okay, stop this example" -x

Options:
--------

The -h switch displays a usage message.

The -s <message> switch is used to change with message sent from the client
    to the server.  Without it the message "Hello World" is sent.

The -n <server_name> switch is used for specifying a server machine.
    Without it the server is assumed to run on the same machine.

The -e <endpoint> switch is used to specify a fixed endpoint to be used.
    Without it a dynamic endpoint will be used and registered with the
    endpoint mapper.

The -t <protseq> switch is used to specify which protocol to use.  Without
    it the protocol sequence "ncacn_ip_tcp" will be used.

----------------------
RUNNING ON WINDOWS 95:
----------------------

If you wish to run the server on Windows 95, you will need to use
the LRPC protocol.

To start the server, enter:

 server -t ncalrpc

To start the client (from the same machine), enter:

 client -t ncalrpc


DCEPORT.H

/*++

Copyright 1993 - 1998 Microsoft Corporation

Module Name:

    dceport.h

Abstract:

    Include file defining types and macros which map DCE RPC APIs to
    Microsoft RPC APIs.  Useful when porting DCE RPC applications to MS RPC.

--*/

#ifndef DCEPORT_H
#define DCEPORT_H

#ifdef __cplusplus
extern "C" {
#endif

/*
** Define various idl types
*/
#define idl_char                unsigned char
#define idl_boolean             unsigned char
#define idl_byte                unsigned char
#define idl_small_int           char
#define idl_usmall_int          unsigned char
#define idl_short_int           signed short
#define idl_ushort_int          unsigned short
#define idl_long_int            long
#define idl_ulong_int           unsigned long
#define boolean32               unsigned long
#define unsigned32              unsigned long
#define unsigned16              unsigned short
#define idl_true                1
#define idl_false               0
#define unsigned_char_t         unsigned char
typedef unsigned char __RPC_FAR *unsigned_char_p_t;
typedef void __RPC_FAR          *idl_void_p_t;

#ifndef _ERROR_STATUS_T_DEFINED
typedef unsigned long error_status_t;
#define _ERROR_STATUS_T_DEFINED
#endif

/*
** Define various DCE RPC types
*/
#define rpc_if_handle_t             RPC_IF_HANDLE
#define rpc_ns_handle_t             RPC_NS_HANDLE
#define rpc_authz_handle_t          RPC_AUTHZ_HANDLE
#define rpc_auth_identity_handle_t  RPC_AUTH_IDENTITY_HANDLE
#define rpc_sm_thread_handle_t      RPC_SS_THREAD_HANDLE
#define rpc_mgr_epv_t               RPC_MGR_EPV __RPC_FAR *
#define rpc_object_inq_fn_t         RPC_OBJECT_INQ_FN __RPC_FAR *
#define rpc_auth_key_retrieval_fn_t RPC_AUTH_KEY_RETRIEVAL_FN
#define rpc_mgmt_authorization_fn_t RPC_MGMT_AUTHORIZATION_FN

/*
** Define rpc_binding_vector_t to match DCE
*/
#ifdef rpc_binding_vector_t
#undef rpc_binding_vector_t
#endif

typedef struct
{
     unsigned long           count;
     handle_t                binding_h[1];
} rpc_binding_vector_t, __RPC_FAR *rpc_binding_vector_p_t;

/*
** Define rpc_protseq_vector_t to match DCE
*/

typedef struct
{
    unsigned long           count;
    unsigned char *         protseq[1];
} rpc_protseq_vector_t, __RPC_FAR *rpc_protseq_vector_p_t;

/*
** Define rpc_stats_vector_t to match DCE
*/

typedef struct
{
    unsigned long           count;
    unsigned long           stats[1];
} rpc_stats_vector_t, __RPC_FAR *rpc_stats_vector_p_t;

/*
** Define uuid_t to match DCE
*/
#ifdef uuid_t
#undef uuid_t
#endif

typedef struct
{
    unsigned long            time_low;
    unsigned short           time_mid;
    unsigned short           time_hi_and_version;
    unsigned char            clock_seq_hi_and_reserved;
    unsigned char            clock_seq_low;
    unsigned char            node[6];
} uuid_t, __RPC_FAR *uuid_p_t;

/*
** Define uuid_vector_t to match DCE
*/
#ifdef uuid_vector_t
#undef uuid_vector_t
#endif

typedef struct
{
     unsigned long           count;
     uuid_p_t                uuid[1];
} uuid_vector_t, __RPC_FAR *uuid_vector_p_t;

/*
** Define rpc_if_id_t and rpc_if_id_p_t to match DCE
*/

typedef struct
{
    uuid_t                   uuid;
    unsigned short           vers_major;
    unsigned short           vers_minor;
} rpc_if_id_t, __RPC_FAR *rpc_if_id_p_t;

/*
** Define rpc_if_id_vector_t to match DCE
*/

typedef struct
{
    unsigned long           count;
    rpc_if_id_p_t           if_id[1];
} rpc_if_id_vector_t, __RPC_FAR *rpc_if_id_vector_p_t;

/*
** The MinThreads parameters to RpcServerListen()
** is not part of the DCE API rpc_server_listen().
** This is the default value.
*/

#define rpc_c_listen_min_threads_default 1

/*
** Define various constants
*/
#define rpc_c_ns_syntax_default            RPC_C_NS_SYNTAX_DEFAULT
#define rpc_c_ns_syntax_dce                RPC_C_SYNTAX_DCE
#define rpc_c_ns_default_exp_age           RPC_C_DEFAULT_EXP_AGE
#define rpc_c_protseq_max_reqs_default     RPC_C_PROTSEQ_MAX_REQS_DEFAULT
#define rpc_c_protseq_max_calls_default    RPC_C_PROTSEQ_MAX_REQS_DEFAULT
#define rpc_c_listen_max_calls_default     RPC_C_LISTEN_MAX_CALLS_DEFAULT
#define rpc_c_ep_all_elts                  RPC_C_EP_ALL_ELTS
#define rpc_c_ep_match_by_if               RPC_C_EP_MATCH_BY_IF
#define rpc_c_ep_match_by_obj              RPC_C_EP_MATCH_BY_OBJ
#define rpc_c_ep_match_by_both             RPC_C_EP_MATCH_BY_BOTH
#define rpc_c_vers_all                     RPC_C_VERS_ALL
#define rpc_c_vers_compatible              RPC_C_VERS_COMPATIBLE
#define rpc_c_vers_exact                   RPC_C_VERS_EXACT
#define rpc_c_vers_major_only              RPC_C_VERS_MAJOR_ONLY
#define rpc_c_vers_upto                    RPC_C_VERS_UPTO
#define rpc_c_profile_default_elt          RPC_C_PROFILE_DEFAULT_ELT
#define rpc_c_profile_all_elts             RPC_C_PROFILE_ALL_ELTS
#define rpc_c_profile_match_by_if          RPC_C_PROFILE_MATCH_BY_IF
#define rpc_c_profile_match_by_mbr         RPC_C_PROFILE_MATCH_BY_MBR
#define rpc_c_profile_match_by_both        RPC_C_PROFILE_MATCH_BY_BOTH
#define rpc_c_binding_min_timeout          RPC_C_BINDING_MIN_TIMEOUT
#define rpc_c_binding_default_timeout      RPC_C_BINDING_DEFAULT_TIMEOUT
#define rpc_c_binding_max_timeout          RPC_C_BINDING_MAX_TIMEOUT
#define rpc_c_binding_infinite_timeout     RPC_C_BINDING_INFINITE_TIMEOUT
#define rpc_c_stats_calls_in               RPC_C_STATS_CALLS_IN
#define rpc_c_stats_calls_out              RPC_C_STATS_CALLS_OUT
#define rpc_c_stats_pkts_in                RPC_C_STATS_PKTS_IN
#define rpc_c_stats_pkts_out               RPC_C_STATS_PKTS_OUT
#define rpc_c_mgmt_inq_if_ids              RPC_C_MGMT_INQ_IF_IDS
#define rpc_c_mgmt_inq_princ_name          RPC_C_MGMT_INQ_PRINC_NAME
#define rpc_c_mgmt_inq_stats               RPC_C_MGMT_INQ_STATS
#define rpc_c_mgmt_inq_server_listen       RPC_C_MGMT_INQ_SERVER_LISTEN
#define rpc_c_mgmt_stop_server_listen      RPC_C_MGMT_STOP_SERVER_LISTEN
#define rpc_c_mgmt_cancel_infinite_timeout RPC_C_CANCEL_INFINITE_TIMEOUT

/*
** Define DCE API equivalents
*/
#define rpc_binding_copy(source,dest,status) \
                *(status) = RpcBindingCopy(source,dest)

#define rpc_binding_free(binding,status) *(status) = RpcBindingFree(binding)

#define rpc_binding_from_string_binding(string_binding,binding,status) \
                *(status) = RpcBindingFromStringBinding(string_binding,binding)

#define rpc_binding_inq_auth_client(binding,privs,princ_name,protect_level, \
                authn_svc,authz_svc,status) \
                *(status) = RpcBindingInqAuthClient(binding,privs,princ_name, \
                protect_level,authn_svc,authz_svc)

#define rpc_binding_inq_auth_info(binding,princ_name,protect_level,\
                authn_svc,auth_identity,authz_svc,status) \
                *(status) = RpcBindingInqAuthInfo(binding,princ_name, \
                protect_level,authn_svc,auth_identity,authz_svc)

#define rpc_binding_inq_object(binding,object_uuid,status) \
                *(status) = RpcBindingInqObject(binding,\
                (UUID __RPC_FAR *)object_uuid)

#define rpc_binding_reset(binding,status) *(status) = RpcBindingReset(binding)

#define rpc_binding_server_from_client(cbinding,sbinding,status) \
                *(status) = RpcBindingServerFromClient(cbinding,sbinding)

#define rpc_binding_set_auth_info(binding,princ_name,protect_level,\
                authn_svc,auth_identity,authz_svc,status) \
                *(status) = RpcBindingSetAuthInfo(binding,princ_name,\
                protect_level,authn_svc,auth_identity,authz_svc)

#define rpc_binding_set_object(binding,object_uuid,status) \
                *(status) = RpcBindingSetObject(binding,\
                (UUID __RPC_FAR *)object_uuid)

#define rpc_binding_to_string_binding(binding,string_binding,status) \
                *(status) = RpcBindingToStringBinding(binding,string_binding)

#define rpc_binding_vector_free(binding_vector,status) \
                *(status) = RpcBindingVectorFree(\
                (RPC_BINDING_VECTOR __RPC_FAR * __RPC_FAR *)binding_vector)

#define rpc_ep_register(if_spec,binding_vec,object_uuid_vec,annotation,\
                status)\
                *(status) = RpcEpRegister(if_spec,\
                (RPC_BINDING_VECTOR __RPC_FAR *)binding_vec, \
                (UUID_VECTOR __RPC_FAR *)object_uuid_vec, annotation)

#define rpc_ep_register_no_replace(if_spec,binding_vec,object_uuid_vec,\
                annotation,status) \
                *(status) = RpcEpRegisterNoReplace(if_spec,\
                (RPC_BINDING_VECTOR __RPC_FAR *)binding_vec,\
                (UUID_VECTOR __RPC_FAR *)object_uuid_vec,annotation)

#define rpc_ep_resolve_binding(binding_h,if_spec,status) \
                *(status) = RpcEpResolveBinding(binding_h,if_spec)

#define rpc_ep_unregister(if_spec,binding_vec,object_uuid_vec,status) \
                *(status) = RpcEpUnregister(if_spec,\
                (RPC_BINDING_VECTOR __RPC_FAR *)binding_vec,\
                (UUID_VECTOR __RPC_FAR *)object_uuid_vec)

#define rpc_if_id_vector_free(if_id_vector,status) \
                *(status) = RpcIfIdVectorFree(\
                (RPC_IF_ID_VECTOR __RPC_FAR * __RPC_FAR *)if_id_vector)

#define rpc_if_inq_id(if_spec,if_id,status) \
                *(status) = RpcIfInqId(if_spec,(RPC_IF_ID __RPC_FAR *)if_id)

#define rpc_if_register_auth_info(if_spec,princ_name,protect_level,\
                authn_svc,auth_identity,authz_svc,status) \
                *(status) = RpcIfRegisterAuthInfo(if_spec,princ_name,\
                protect_level,authn_svc,auth_identity,authz_svc)

#define rpc_mgmt_ep_elt_inq_begin(ep_binding,inquiry_type,if_id,vers_option,\
                object_uuid,inquiry_context,status) \
                *(status) = RpcMgmtEpEltInqBegin(ep_binding,inquiry_type,if_id,\
                vers_option,object_uuid,inquiry_context)

#define rpc_mgmt_ep_elt_inq_done(inquiry_context,status) \
                *(status) = RpcMgmtEpEltInqDone(inquiry_context)

#define rpc_mgmt_ep_elt_inq_next(inquiry_context,if_id,binding,object_uuid,\
                annotation,status) \
                *(status) = RpcMgmtEpEltInqNext(inquiry_context,if_id,binding,\
                object_uuid,annotation)

#define rpc_mgmt_ep_unregister(ep_binding,if_id,binding,object_uuid,status) \
                *(status) = RpcMgmtEpUnregister(ep_binding,if_id,binding,\
                object_uuid)

#define rpc_mgmt_inq_com_timeout(binding,timeout,status) \
                *(status) = RpcMgmtInqComTimeout(binding,timeout)

#define rpc_mgmt_inq_dflt_protect_level(authn_svc,level,status) \
                *(status) = RpcMgmtInqDefaultProtectLevel(authn_svc,level)

#define rpc_mgmt_inq_if_ids(binding,if_id_vector,status) \
                *(status) = RpcMgmtInqIfIds((bindings),\
                (RPC_IF_ID_VECTOR __RPC_FAR * __RPC_FAR *)(if_id_vector))

#define rpc_mgmt_inq_server_princ_name(binding,authn_svc,princ_name,status) \
                *(status) = RpcMgmtInqServerPrincName(binding,authn_svc,\
                princ_name)

#define rpc_mgmt_inq_stats(binding,statistics,status) \
                *(status) = RpcMgmtInqStats(binding,\
                (RPC_STATS_VECTOR __RPC_FAR * __RPC_FAR *)statistics)

#define rpc_mgmt_is_server_listening(binding,status) \
                ( ((*(status) = RpcMgmtIsServerListening(binding)) == RPC_S_OK) \
                ? (1) : (*(status) == RPC_S_NOT_LISTENING) \
                ? (*(status) = RPS_S_OK, 0) : (0) )

#define rpc_mgmt_set_authorization_fn(authz_fn,status) \
                *(status) = RpcMgmtSetAuthorizathionFn(authz_fn)

#define rpc_mgmt_set_cancel_timeout(seconds,status) \
                *(status) = RpcMgmtSetCancelTimeout(seconds)

#define rpc_mgmt_set_com_timeout(binding,timeout,status) \
                *(status) = RpcMgmtSetComTimeout(binding,timeout)

#define rpc_mgmt_set_server_stack_size(size,status) \
                *(status) = RpcMgmtSetServerStackSize(size)

#define rpc_mgmt_stats_vector_free(stats,status) \
                *(status) = RpcMgmtStatsVectorFree(\
                (RPC_STATS_VECTOR __RPC_FAR * __RPC_FAR *)stats)

#define rpc_mgmt_stop_server_listening(binding,status) \
                *(status) = RpcMgmtStopServerListening(binding)

#define rpc_network_inq_protseqs(prots,status) \
                *(status) = RpcNetworkInqProtseqs(\
                (RPC_PROTSEQ_VECTOR __RPC_FAR * __RPC_FAR *)prots)

#define rpc_network_is_protseq_valid(protseq,status) \
                *(status) = RpcNetworkIsProtseqValid(protseq)

/*
** Define NSI equivalents
*/
#define rpc_ns_binding_export(name_syntax,entry_name,if_spec,\
                binding_vector, uuid_vector,status) \
                *(status) = RpcNsBindingExport(name_syntax,entry_name,\
                if_spec, (RPC_BINDING_VECTOR *)binding_vector,\
                (UUID_VECTOR __RPC_FAR *)uuid_vector)

#define rpc_ns_binding_import_begin(name_syntax,entry_name,if_spec,\
                object_uuid,import_context,status) \
                *(status) = RpcNsBindingImportBegin(name_syntax,entry_name,\
                if_spec,(UUID __RPC_FAR *)object_uuid,import_context)

#define rpc_ns_binding_import_done(import_context,status) \
                *(status) = RpcNsBindingImportDone(import_context)

#define rpc_ns_binding_import_next(import_context,binding,status) \
                *(status) = RpcNsBindingImportNext(import_context,binding)

#define rpc_ns_binding_inq_entry_name(binding,name_syntax,entry_name,status)\
                *(status) = RpcNsBindingInqEntryName(binding,name_syntax,\
                entry_name)

#define rpc_ns_binding_lookup_begin(name_syntax,entry_name,if_spec,\
                object_uuid,max_count,lookup_context,status) \
                *(status) = RpcNsBindingLookupBegin(name_syntax,entry_name,\
                if_spec,(UUID __RPC_FAR *)object_uuid,max_count,lookup_context)

#define rpc_ns_binding_lookup_done(lookup_context,status) \
                *(status) = RpcNsBindingLookupDone(lookup_context)

#define rpc_ns_binding_lookup_next(lookup_context,binding_vector,status) \
                *(status) = RpcNsBindingLookupNext(lookup_context, \
                (RPC_BINDING_VECTOR __RPC_FAR * __RPC_FAR *)binding_vector)

#define rpc_ns_binding_select(binding_vector,binding,status) \
                *(status) = RpcNsBindingSelect(\
                (RPC_BINDING_VECTOR __RPC_FAR *)binding_vector,binding)

#define rpc_ns_binding_unexport(name_syntax,entry_name,if_spec,\
                uuid_vector,status) \
                *(status) = RpcNsBindingUnexport(name_syntax,entry_name,\
                if_spec, (UUID_VECTOR __RPC_FAR *)uuid_vector)

#define rpc_ns_entry_expand_name(name_syntax,entry_name,expanded_name,\
                status)\
                *(status) = RpcNsEntryExpandName(name_syntax,entry_name,\
                expanded_name)

#define rpc_ns_entry_object_inq_begin(name_syntax,entry_name,\
                inquiry_context,status)\
                *(status) = RpcNsEntryObjectInqBegin(name_syntax,\
                entry_name,inquiry_context)

#define rpc_ns_entry_object_inq_done(inquiry_context,status) \
                *(status) = RpcNsEntryObjectInqDone(inquiry_context)

#define rpc_ns_entry_object_inq_next(inquiry_context,object_uuid,status) \
                *(status) = RpcNsEntryObjectInqNext(inquiry_context,\
                (UUID __RPC_FAR *)object_uuid)

#define rpc_ns_group_delete(name_syntax,group_name,status) \
                *(status) = RpcNsGroupDelete(name_syntax,group_name)

#define rpc_ns_group_mbr_add(name_syntax,group_name,member_name_syntax,\
                member_name,status) \
                *(status) = RpcNsGroupMbrAdd(name_syntax,group_name,\
                member_name_syntax,member_name)

#define rpc_ns_group_mbr_inq_begin(name_syntax,group_name,member_name_syntax,\
                inquiry_context,status) \
                *(status) = RpcNsGroupMbrInqBegin(name_syntax,group_name,\
                member_name_syntax,inquiry_context)

#define rpc_ns_group_mbr_inq_done(inquiry_context,status) \
                *(status) = RpcNsGroupMbrInqDone(inquiry_context)

#define rpc_ns_group_mbr_inq_next(inquiry_context,member_name,status) \
                *(status) = RpcNsGroupMbrInqNext(inquiry_context,member_name)

#define rpc_ns_group_mbr_remove(name_syntax,group_name,member_name_syntax,\
                member_name,status) \
                *(status) = RpcNsGroupMbrRemove(name_syntax,group_name,\
                member_name_syntax,member_name)

#define rpc_ns_mgmt_binding_unexport(name_syntax,entry_name,if_id,vers_option,\
                uuid_vector,status) \
                *(status) = RpcNsMgmtBindingUnexport(name_syntax,entry_name,\
                (RPC_IF_ID __RPC_FAR *)if_id,vers_option,\
                (UUID_VECTOR __RPC_FAR *)uuid_vector)

#define rpc_ns_mgmt_entry_create(name_syntax,entry_name,status) \
                *(status) = RpcNsMgmtEntryCreate(name_syntax,entry_name)

#define rpc_ns_mgmt_entry_delete(name_syntax,entry_name,status) \
                *(status) = RpcNsMgmtEntryDelete(name_syntax,entry_name)

#define rpc_ns_mgmt_entry_inq_if_ids(name_syntax,entry_name,if_id_vector,\
                status) \
                *(status) = RpcNsMgmtEntryInqIfIds(name_syntax,entry_name,\
                (RPC_IF_ID_VECTOR __RPC_FAR * __RPC_FAR *)if_id_vector)

#define rpc_ns_mgmt_handle_set_exp_age(ns_handle,expiration_age,status) \
                *(status) = RpcNsMgmtHandleSetExpAge(ns_handle,expiration_age)

#define rpc_ns_mgmt_inq_exp_age(expiration_age,status) \
                *(status) = RpcNsMgmtInqExpAge(expiration_age)

#define rpc_ns_mgmt_set_exp_age(expiration_age,status) \
                *(status) = RpcNsMgmtSetExpAge(expiration_age)

#define rpc_ns_profile_delete(name_syntax,profile_name,status) \
                *(status) = RpcNsProfileDelete(name_syntax,profile_name)

#define rpc_ns_profile_elt_add(name_syntax,profile_name,if_id,\
                member_name_syntax,member_name,priority,annotation,status) \
                *(status) = RpcNsProfileEltAdd(name_syntax,profile_name,\
                (RPC_IF_ID __RPC_FAR *)if_id,member_name_syntax,member_name,\
                priority,annotation)

#define rpc_ns_profile_elt_inq_begin(name_syntax,profile_name,inquiry_type,\
                if_id,if_vers_option,member_name_syntax,\
                member_name,inquiry_context,status) \
                *(status) = RpcNsProfileEltInqBegin(name_syntax,profile_name,\
                inquiry_type,(RPC_IF_ID __RPC_FAR *)if_id,if_vers_option,\
                member_name_syntax,member_name,inquiry_context)

#define rpc_ns_profile_elt_inq_done(inquiry_context,status) \
                *(status) = RpcNsProfileEltInqDone(inquiry_context)

#define rpc_ns_profile_elt_inq_next(inquiry_context,if_id,member_name,\
                priority,annotation,status) \
                *(status) = RpcNsProfileEltInqNext(inquiry_context,\
                (RPC_IF_ID __RPC_FAR *)if_id,member_name,priority,annotation)

#define rpc_ns_profile_elt_remove(name_syntax,profile_name,if_id,\
                member_name_syntax,member_name,status) \
                *(status) = RpcNsProfileEltRemove(name_syntax,profile_name,\
                (RPC_IF_ID __RPC_FAR *)if_id,member_name_syntax,member_name)

#define rpc_object_inq_type(object_uuid,type_uuid,status) \
                *(status) = RpcObjectInqType((UUID __RPC_FAR *)object_uuid,\
                (UUID __RPC_FAR *)type_uuid)

#define rpc_object_set_inq_fn(inq_fn,status) \
                *(status) = RpcObjectSetInqFn(inq_fn)

#define rpc_object_set_type(object_uuid,type_uuid,status) \
                *(status) = RpcObjectSetType((UUID __RPC_FAR *)object_uuid,\
                (UUID __RPC_FAR *)type_uuid)

#define rpc_protseq_vector_free(protseq_vector,status) \
                *(status) = RpcProtseqVectorFree( \
                (RPC_PROTSEQ_VECTOR __RPC_FAR * __RPC_FAR *)protseq_vector)

#define rpc_server_inq_bindings(binding_vector,status) \
                *(status) = RpcServerInqBindings(\
                (RPC_BINDING_VECTOR __RPC_FAR * __RPC_FAR *)binding_vector)

#define rpc_server_inq_if(if_spec,type_uuid,mgr_epv,status) \
                *(status) = RpcServerInqIf(if_spec,(UUID __RPC_FAR *)type_uuid,\
                (RPC_MGR_EPV __RPC_FAR *)mgr_epv)

#define rpc_server_listen(max_calls,status) \
                *(status) = RpcServerListen(rpc_c_listen_min_threads_default,\
                max_calls,0)

#define rpc_server_register_auth_info(princ_name,auth_svc,get_key_func,arg,\
                status) \
                *(status) = RpcServerRegisterAuthInfo(princ_name,auth_svc,\
                get_key_func,arg)

#define rpc_server_register_if(if_spec,type_uuid,mgr_epv,status) \
                *(status) = RpcServerRegisterIf(if_spec,\
                (UUID __RPC_FAR *)type_uuid,(RPC_MGR_EPV __RPC_FAR *)mgr_epv)

#define rpc_server_unregister_if(if_spec,type_uuid,status) \
                *(status) = RpcServerUnregisterIf(if_spec,(UUID *)type_uuid,0)

#define rpc_server_use_all_protseqs(max_call_requests,status) \
                *(status) = RpcServerUseAllProtseqs(max_call_requests,0)

#define rpc_server_use_all_protseqs_if(max_call_requests,if_spec,status) \
                *(status) = RpcServerUseAllProtseqsIf(max_call_requests,\
                if_spec,0)

#define rpc_server_use_protseq(protseq,max_call_requests,status) \
                *(status) = RpcServerUseProtseq(protseq,max_call_requests,0)

#define rpc_server_use_protseq_ep(protseq,max_call_requests,endpoint,status)\
                *(status) = RpcServerUseProtseqEp(protseq,max_call_requests,\
                endpoint,0)

#define rpc_server_use_protseq_if(protseq,max_call_requests,if_spec,status) \
                *(status) = RpcServerUseProtseqIf(protseq,max_call_requests,\
                if_spec,0)

#define rpc_sm_alloce(size,status) *(status) = RpcSmAllocate(size)

#define rpc_sm_client_free(ptr,status) *(status) = RpcSmClientFree(ptr)

#define rpc_sm_destroy_client_context(context,status) \
                *(status) = RpcSmDestroyClientContext(context)

#define rpc_sm_disable_allocate(status) *(status) = RpcSmDisableAllocate()

#define rpc_sm_enable_allocate(status) *(status) = RpcSmEnableAllocate()

#define rpc_sm_free(ptr,status) *(status) = RpcSmFree(ptr)

#define rpc_sm_get_thread_handle(status) RpcSmGetThreadHandle(status)

#define rpc_sm_set_client_alloc_free(alloc,free,status) \
                *(status) = RpcSmSetClientAllocFree(alloc,free)

#define rpc_sm_set_thread_handle(id,status) \
                *(status) = RpcSmSetThreadHandle(id)

#define rpc_sm_swap_client_alloc_free(alloc,free,old_alloc,old_free,status) \
                *(status) = RpcSmSwapClientAllocFree(alloc,free \
                old_alloc, old_free)

#define rpc_string_binding_compose(object_uuid,protseq,netaddr,endpoint,\
                options,binding,status) \
                *(status) = RpcStringBindingCompose(object_uuid,protseq,\
                netaddr,endpoint,options,binding)

#define rpc_string_binding_parse(string_binding,object_uuid,protseq,netaddr,\
                endpoint,options,status) \
                *(status) = RpcStringBindingParse(string_binding,\
                object_uuid,protseq,netaddr,endpoint,options)

#define rpc_string_free(string,status) *(status) = RpcStringFree(string)

#define uuid_compare(uuid1,uuid2,status) \
                UuidCompare((UUID __RPC_FAR *)(uuid1),\
                            (UUID __RPC_FAR *)(uuid2),(status))

#define uuid_create(uuid,status) \
                *(status) = UuidCreate((UUID __RPC_FAR *)uuid)

#define uuid_create_nil(uuid,status) \
                *(status) = UuidCreateNil((UUID __RPC_FAR *)uuid)

#define uuid_equal(uuid1,uuid2,status) \
                UuidEqual((UUID __RPC_FAR *)(uuid1), \
                          (UUID __RPC_FAR *)(uuid2), (status))

#define uuid_from_string(string,uuid,status) \
                *(status) = UuidFromString(string,(UUID __RPC_FAR *)uuid)

#define uuid_hash(uuid,status) \
                UuidHash((UUID __RPC_FAR *)(uuid),(status))

#define uuid_is_nil(uuid,status) \
                UuidIsNil((UUID __RPC_FAR *)(uuid), (status))

#define uuid_to_string(uuid,string,status)\
                *(status) = UuidToString((UUID __RPC_FAR *)uuid,string)

#define true  1
#define false 0

/*
** Define exception handling equivalents
**
*/
#if defined (__RPC_WIN16__) || defined (__RPC_DOS__)

#define TRY                                       \
    {                                                \
    int _exception_mode_finally;                     \
    int _exception_code;                             \
    ExceptionBuff exception;                         \
    _exception_code = RpcSetException(&exception);   \
    if (!_exception_code)                            \
    {

#define CATCH_ALL                                 \
        _exception_mode_finally = false;             \
        RpcLeaveException();                         \
        }                                            \
        else                                         \
        {
/*
 * #define CATCH(X)                               \
 *   }else if ((unsigned long)RpcExceptionCode()==(unsigned long)X) {
 */
#define FINALLY                                   \
        _exception_mode_finally = true;              \
        RpcLeaveException();                         \
        } {
#define ENDTRY                                    \
          }                                               \
        if (_exception_mode_finally && _exception_code)  \
            RpcRaiseException(_exception_code);           \
        }

#endif /* WIN16 or DOS */

#if defined (__RPC_WIN32__)
#define TRY             try {
/*
 * #define CATCH(X)        \
 *                      } except (GetExceptionCode() == X ? \
 *                        EXCEPTION_EXECUTE_HANDLER : \
 *                        EXCEPTION_CONTINUE_SEARCH) {
 */
#define CATCH_ALL       } except (EXCEPTION_EXECUTE_HANDLER) {
#define FINALLY         } finally {
#define ENDTRY          }
#endif /* WIN32 */

#define RAISE(v)   RpcRaiseException(v)
#define RERAISE    RpcRaiseException(RpcExceptionCode())
#define THIS_CATCH RpcExceptionCode()

/*
** DCE Status code mappings
*/
#ifndef rpc_s_ok
#define rpc_s_ok                        RPC_S_OK
#endif
#ifndef error_status_ok
#define error_status_ok                 RPC_S_OK
#endif
#define ept_s_cant_perform_op           EPT_S_CANT_PERFORM_OP
#define ept_s_invalid_entry             EPT_S_INVALID_ENTRY
#define ept_s_not_registered            EPT_S_NOT_REGISTERED
#define rpc_s_already_listening         RPC_S_ALREADY_LISTENING
#define rpc_s_already_registered        RPC_S_ALREADY_REGISTERED
#define rpc_s_binding_has_no_auth       RPC_S_BINDING_HAS_NO_AUTH
#define rpc_s_binding_imcomplete        RPC_S_BINDING_IMCOMPLETE
#define rpc_s_call_cancelled            RPC_S_CALL_CANCELLED
#define rpc_s_call_failed               RPC_S_CALL_FAILED
#define rpc_s_cant_bind_socket          RPC_S_CANNOT_BIND
#define rpc_s_cant_create_socket        RPC_S_CANT_CREATE_ENDPOINT
#define rpc_s_comm_failure              RPC_S_COMM_FAILURE
#define rpc_s_connect_no_resources      RPC_S_OUT_OF_RESOURCES
#define rpc_s_cthread_create_failed     RPC_S_OUT_OF_THREADS
#define rpc_s_endpoint_not_found        RPC_S_NO_ENDPOINT_FOUND
#define rpc_s_entry_already_exists      RPC_S_ENTRY_ALREADY_EXISTS
#define rpc_s_entry_not_found           RPC_S_ENTRY_NOT_FOUND
#define rpc_s_fault_addr_error          RPC_S_ADDRESS_ERROR
#define rpc_s_fault_fp_div_by_zero      RPC_S_FP_DIV_ZERO
#define rpc_s_fault_fp_overflow         RPC_S_FP_OVERFLOW
#define rpc_s_fault_fp_underflow        RPC_S_FP_UNDERFLOW
#define rpc_s_fault_int_div_by_zero     RPC_S_ZERO_DIVIDE
#define rpc_s_fault_invalid_bound       RPC_S_INVALID_BOUND
#define rpc_s_fault_invalid_tag         RPC_S_INVALID_TAG
#define rpc_s_fault_remote_no_memory    RPC_S_SERVER_OUT_OF_MEMORY
#define rpc_s_fault_unspec              RPC_S_CALL_FAILED
#define rpc_s_incomplete_name           RPC_S_INCOMPLETE_NAME
#define rpc_s_interface_not_found       RPC_S_INTERFACE_NOT_FOUND
#define rpc_s_internal_error            RPC_S_INTERNAL_ERROR
#define rpc_s_inval_net_addr            RPC_S_INVALID_NET_ADDR
#define rpc_s_invalid_arg               RPC_S_INVALID_ARG
#define rpc_s_invalid_binding           RPC_S_INVALID_BINDING
#define rpc_s_invalid_endpoint_format   RPC_S_INVALID_ENDPOINT_FORMAT
#define rpc_s_invalid_naf_id            RPC_S_INVALID_NAF_IF
#define rpc_s_invalid_name_syntax       RPC_S_INVALID_NAME_SYNTAX
#define rpc_s_invalid_rpc_protseq       RPC_S_INVALID_RPC_PROTSEQ
#define rpc_s_invalid_string_binding    RPC_S_INVALID_STRING_BINDING
#define rpc_s_invalid_timeout           RPC_S_INVALID_TIMEOUT
#define rpc_s_invalid_vers_option       RPC_S_INVALID_VERS_OPTION
#define rpc_s_max_calls_too_small       RPC_S_MAX_CALLS_TOO_SMALL

#define rpc_s_mgmt_op_disallowed        RPC_S_ACCESS_DENIED
#define rpc_s_name_service_unavailable  RPC_S_NAME_SERVICE_UNAVAILABLE
#define rpc_s_no_bindings               RPC_S_NO_BINDINGS
#define rpc_s_no_entry_name             RPC_S_NO_ENTRY_NAME
#define rpc_s_no_interfaces             RPC_S_NO_INTERFACES
#define rpc_s_no_interfaces_exported    RPC_S_NO_INTERFACES_EXPORTED
#define rpc_s_no_memory                 RPC_S_OUT_OF_MEMORY
#define rpc_s_no_more_elements          RPC_X_NO_MORE_ENTRIES
#define rpc_s_no_more_bindings          RPC_S_NO_MORE_BINDINGS
#define rpc_s_no_more_members           RPC_S_NO_MORE_MEMBERS
#define rpc_s_no_ns_permission          RPC_S_ACCESS_DENIED
#define rpc_s_no_princ_name             RPC_S_NO_PRINC_NAME
#define rpc_s_no_protseqs               RPC_S_NO_PROTSEQS
#define rpc_s_no_protseqs_registered    RPC_S_NO_PROTSEQS_REGISTERED
#define rpc_s_not_rpc_tower             RPC_S_CANNOT_SUPPORT
#define rpc_s_not_supported             RPC_S_CANNOT_SUPPORT
#define rpc_s_not_authorized            RPC_S_ACCESS_DENIED
#define rpc_s_nothing_to_unexport       RPC_S_NOTHING_TO_UNEXPORT
#define rpc_s_object_not_found          RPC_S_OBJECT_NOT_FOUND
#define rpc_s_protocol_error            RPC_S_PROTOCOL_ERROR
#define rpc_s_protseq_not_supported     RPC_S_PROTSEQ_NOT_SUPPORTED
#define rpc_s_server_too_busy           RPC_S_SERVER_TOO_BUSY
#define rpc_s_string_too_long           RPC_S_STRING_TOO_LONG
#define rpc_s_type_already_registered   RPC_S_TYPE_ALREADY_REGISTERED
#define rpc_s_unknown_authn_service     RPC_S_UNKNOWN_AUTHN_SERVICE
#define rpc_s_unknown_authz_service     RPC_S_UNKNOWN_AUTHZ_SERVICE
#define rpc_s_unknown_if                RPC_S_UNKNOWN_IF
#define rpc_s_unknown_mgr_type          RPC_S_UNKNOWN_MGR_TYPE
#define rpc_s_unknown_reject            RPC_S_CALL_FAILED_DNE
#define rpc_s_unsupported_name_syntax   RPC_S_UNSUPPORTED_NAME_SYNTAX
#define rpc_s_unsupported_type          RPC_S_UNSUPPORTED_TYPE
#define rpc_s_wrong_boot_time           RPC_S_CALL_FAILED_DNE
#define rpc_s_wrong_kind_of_binding     RPC_S_WRONG_KIND_OF_BINDING
#define uuid_s_ok                       RPC_S_OK
#define uuid_s_internal_error           RPC_S_INTERNAL_ERROR
#define uuid_s_invalid_string_uuid      RPC_S_INVALID_STRING_UUID
#define uuid_s_no_address               RPC_S_UUID_NO_ADDRESS

/*
** DCE Exception mappings
*/

#define rpc_x_comm_failure              RPC_S_COMM_FAILURE
#define rpc_x_connect_no_resources      RPC_S_OUT_OF_RESOURCES
#define rpc_x_entry_not_found           RPC_S_ENTRY_NOT_FOUND
#define rpc_x_incomplete_name           RPC_S_INCOMPLETE_NAME
#define rpc_x_invalid_arg               RPC_S_INVALID_ARG
#define rpc_x_invalid_binding           RPC_S_INVALID_BINDING
#define rpc_x_invalid_bound             RPC_X_INVALID_BOUND
#define rpc_x_invalid_endpoint_format   RPC_S_INVALID_ENDPOINT_FORMAT
#define rpc_x_invalid_naf_id            RPC_S_INVALID_NAF_IF
#define rpc_x_invalid_name_syntax       RPC_S_INVALID_NAME_SYNTAX
#define rpc_x_invalid_rpc_protseq       RPC_S_INVALID_RPC_PROTSEQ
#define rpc_x_invalid_tag               RPC_X_INVALID_TAG
#define rpc_x_invalid_timeout           RPC_S_INVALID_TIMEOUT
#define rpc_x_no_memory                 RPC_X_NO_MEMORY
#define rpc_x_object_not_found          RPC_S_OBJECT_NOT_FOUND
#define rpc_x_protocol_error            RPC_S_PROTOCOL_ERROR
#define rpc_x_protseq_not_supported     RPC_S_PROTSEQ_NOT_SUPPORTED
#define rpc_x_server_too_busy           RPC_S_SERVER_TOO_BUSY
#define rpc_x_ss_char_trans_open_fail   RPC_X_SS_CHAR_TRANS_OPEN_FAIL
#define rpc_x_ss_char_trans_short_file  RPC_X_SS_CHAR_TRANS_SHORT_FILE
#define rpc_x_ss_context_damaged        RPC_X_SS_CONTEXT_DAMAGED
#define rpc_x_ss_context_mismatch       RPC_X_SS_CONTEXT_MISMATCH
#define rpc_x_ss_in_null_context        RPC_X_SS_IN_NULL_CONTEXT
#define rpc_x_string_too_long           RPC_S_STRING_TOO_LONG
#define rpc_x_unknown_if                RPC_S_UNKNOWN_IF
#define rpc_x_unknown_mgr_type          RPC_S_UNKNOWN_MGR_TYPE
#define rpc_x_unsupported_name_syntax   RPC_S_UNSUPPORTED_NAME_SYNTAX
#define rpc_x_unsupported_type          RPC_S_UNSUPPORTED_TYPE
#define rpc_x_wrong_boot_time           RPC_S_CALL_FAILED_DNE
#define rpc_x_wrong_kind_of_binding     RPC_S_WRONG_KIND_OF_BINDING
#define uuid_x_internal_error           RPC_S_INTERNAL_ERROR

#ifdef __cplusplus
}
#endif

#endif /* DCEPORT_H */


MANAGER.C

/*************************************************************/
/**                                                         **/
/**                 Microsoft RPC Examples                  **/
/**                 OSF DCE Interop Application             **/
/**         Copyright 1993 - 1998 Microsoft Corporation     **/
/**                                                         **/
/*************************************************************/

#include <stdio.h>
#include "msg.h"    /* header file generated by M/IDL compiler */

#if defined(__RPC_WIN32__) || defined(__RPC_DOS__)
#include "dceport.h"
#endif

/*
 * Print out client messages
 */

void ClientMessage(unsigned char *message)
{
    printf("%s\n", message);
    return;
}

/*
 * The client can stop the server by calling this operation.
 */
void ShutdownServer()
{
    unsigned32 status;
    rpc_mgmt_stop_server_listening(0, &status);
    if (status)
        {
        printf("rpc_mgmt_stop_server_listening returns 0x%x\n", status);
        }
    return;
}


MIDLUSER.C

/*************************************************************/
/**                                                         **/
/**                 Microsoft RPC Examples                  **/
/**            OSF DCE Interop Sample Application           **/
/**         Copyright 1993 - 1998 Microsoft Corporation     **/
/**                                                         **/
/*************************************************************/

#include <stdlib.h>
#include "msg.h"

/*
 * These are used in Microsoft clients and servers for stub memory allocation
 */

void __RPC_FAR * __RPC_USER MIDL_user_allocate(size_t size)
{
    return malloc(size);
}

void __RPC_USER MIDL_user_free(void __RPC_FAR *p)
{
    free(p);
}


MSG.ACF

/*************************************************************/
/**                                                         **/
/**                 Microsoft RPC Examples                  **/
/**                 OSF DCE Interop Application             **/
/**          Copyright(c) Microsoft Corp. 1992-1995         **/
/**                                                         **/
/*************************************************************/

[ implicit_handle(handle_t interop_binding_handle) ]
interface interop
{
}


MSG.IDL

/*************************************************************/
/**                                                         **/
/**                 Microsoft RPC Examples                  **/
/**            OSF DCE Interop Sample Application           **/
/**           Copyright 1993 - 1998 Microsoft Corporation-1995        **/
/**                                                         **/
/*************************************************************/

[
uuid(0E8BDF58-B3BF-101A-A116-08002B2B8CD3),
version(1.0)
]
interface interop
{

void ClientMessage([in, string, ref] unsigned char *pszMessage);

void ShutdownServer(void);

}


CLIENT.C

/*************************************************************/
/**                                                         **/
/**                 Microsoft RPC Examples                  **/
/**                 OSF DCE Sample Application              **/
/**         Copyright 1992 - 1998 Microsoft Corporation     **/
/**                                                         **/
/*************************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>

#include "msg.h"        /* header file generated by M/IDL compiler */

#if defined(__RPC_WIN32__) || defined(__RPC_DOS__)

/*
 On MS platforms we must include the dceport.h header file
 which maps OSF DCE style APIs to MS style APIs.
*/

#include "dceport.h"

#else
#include <pthread.h>
#endif

#ifndef _CRTAPI1
#define _CRTAPI1
#endif

void Usage()
{
  printf("Usage : client -n <server_name>\n");
  printf("               -t <transport>   - optional, default ncacn_ip_tcp\n");
  printf("               -e <endpoint>    - optional, should match server\n");
  printf("               -s <message>     - optional, send a different message\n");
  printf("               -x               - use to stop the server\n");
  exit(1);
}

int _CRTAPI1
main(int argc, char *argv[])
{
    unsigned32 status;
    unsigned char * pszProtocolSequence = (unsigned char *)"ncacn_ip_tcp";
    unsigned char * pszNetworkAddress   = NULL;
    unsigned char * pszEndpoint         = NULL;
    unsigned char * pszStringBinding    = NULL;
    unsigned char * pszMessage          = (unsigned char *)"Hello World";
    int fStopServer = 0;
    int i;

    printf ("Microsoft RPC Demo - OSF DCE Interop Message Client\n");

    for (i = 1; i < argc; i++) {
        if ((*argv[i] == '-') || (*argv[i] == '/')) {
            switch (tolower(*(argv[i]+1))) {
            case 'n':  /* network address */
                pszNetworkAddress = (unsigned char *)argv[++i];
                break;
            case 't':  /* protocol sequence */
                pszProtocolSequence = (unsigned char *)argv[++i];
                break;
            case 'e':  /* network endpoint */
                pszEndpoint = (unsigned char *)argv[++i];
                break;
            case 's':  /* update message */
                pszMessage = (unsigned char *)argv[++i];
                break;
            case 'x':  /* stop the server */
                fStopServer = 1;
                break;
            case 'h':
            case '?':
            default:
                Usage();
            }
        }
        else
            Usage();
    }

    rpc_string_binding_compose(0,   /* no object uuid */
                               pszProtocolSequence,
                               pszNetworkAddress,
                               pszEndpoint,
                               0,  /* no options */
                               &pszStringBinding,
                               &status);
    if (status) {
        printf("rpc_string_binding_compose returned 0x%x\n", status);
        return(status);
    }

    rpc_binding_from_string_binding(pszStringBinding,
                                    &interop_binding_handle,
                                    &status);
    if (status) {
        printf("rpc_binding_from_string_binding returned 0x%x\n", status);
        return(status);
    }

    rpc_string_free(&pszStringBinding,&status);
    if (status) {
        printf("rpc_string_free returned 0x%x\n", status);
        return(status);
    }

    TRY {
       ClientMessage(pszMessage);

       printf("Message sent okay\n");
       if (fStopServer)
           {
           ShutdownServer();
           printf("Server shutdown\n");
           }
    }
    CATCH_ALL {
       printf("RPC raised exception 0x%x\n", THIS_CATCH);
    }
    ENDTRY

    rpc_binding_free(&interop_binding_handle, &status);
    if (status) {
        printf("rpc_binding_free returned 0x%x\n", status);
        return(status);
    }

    return(0);
}


SERVER.C

/*************************************************************/
/**                                                         **/
/**                 Microsoft RPC Examples                  **/
/**                 OSF DCE Interop Application             **/
/**         Copyright 1993 - 1998 Microsoft Corporation     **/
/**                                                         **/
/*************************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>

#include "msg.h"    /* header file generated by M/IDL compiler */

#if defined(__RPC_WIN32__) || defined(__RPC_DOS__)

/*
 On MS platforms we must include the dceport.h header file
 which maps OSF DCE style APIs to MS style APIs.

 Currently, we must also define a common interface handle name.
*/

#include "dceport.h"

#else
#include <pthread.h>
#endif

#ifndef _CRTAPI1
#define _CRTAPI1
#endif

void Usage()
{
  printf("Usage : server -e <endpoint>   - optional endpoint\n");
  printf("               -t <transport>  - optional, default ncacn_ip_tcp\n");
  exit(1);
}

int _CRTAPI1
main(int argc, char *argv[])
{
    unsigned32 status;
    unsigned char * pszProtocolSequence = (unsigned char *)"ncacn_ip_tcp";
    unsigned char * pszEndpoint         = NULL;
    unsigned int    cMaxCalls           = 20;
    rpc_binding_vector_p_t pbvBindings  = NULL;
    int i;

    printf ("Microsoft RPC Demo - OSF DCE Interop Message Server\n");

    for (i = 1; i < argc; i++) {
        if ((*argv[i] == '-') || (*argv[i] == '/')) {
            switch (tolower(*(argv[i]+1))) {
            case 'e':
                pszEndpoint = (unsigned char *)argv[++i];
                break;
            case 't':
                pszProtocolSequence = (unsigned char *)argv[++i];
                break;
            case 'h':
            case '?':
            default:
                Usage();
            }
        }
        else
            Usage();
    }

    if (pszEndpoint != NULL)
        {
        /*
         Since we have an explict endpoint, use it and
         wait for client requests.
        */
        rpc_server_use_protseq_ep(pszProtocolSequence,
                                   cMaxCalls,
                                   pszEndpoint,
                                   &status);
        if (status) {
            printf("rpc_server_use_protseq_ep returned 0x%x\n", status);
            return status;
            }
        }
    else
        {
        /*
         No explict endpoint, use the protocol sequence and register
         the endpoint with the endpoint mapper.
        */
        rpc_server_use_protseq(pszProtocolSequence,
                               cMaxCalls,
                               &status);
        if (status) {
            printf("rpc_server_use_protseq returned 0x%x\n", status);
            return status;
            }

        rpc_server_inq_bindings(&pbvBindings, &status);
        if (status) {
            printf("rpc_server_inq_bindings returned 0x%x\n", status);
            return status;
            }

        rpc_ep_register(interop_v1_0_s_ifspec,
                        pbvBindings,
                        0,
                        0,
                        &status);
        if (status) {
            printf("rpc_ep_register returned 0x%x\n", status);
            return status;
            }
        }

    rpc_server_register_if(interop_v1_0_s_ifspec,
                           0,
                           0,
                           &status);
    if (status) {
        printf("rpc_server_register_if returned 0x%x\n", status);
        return status;
    }

    printf("RPC server ready\n");
    rpc_server_listen(cMaxCalls,&status);

    if (status) {
        printf("rpc_server_listen returned: 0x%x\n", status);
        return status;
    }

    rpc_server_unregister_if(interop_v1_0_s_ifspec,
                             0,
                             &status);
    if (status) {
        printf("rpc_server_unregister_if returned 0x%x\n", status);
        return status;
    }

    if (pszEndpoint == NULL)
        {
        /*
         Unregister from endpoint mapper
         */
        rpc_ep_unregister(interop_v1_0_s_ifspec,
                          pbvBindings,
                          0,
                          &status);
        if (status) {
            printf("rpc_ep_unregister returned 0x%x\n", status);
            return status;
            }

        rpc_binding_vector_free(&pbvBindings, &status);
        if (status) {
            printf("rpc_binding_vector_free returned 0x%x\n", status);
            return status;
            }
        }
}


README.TXT

MANDEL

The MANDEL program is a distributed fractal drawing application that uses
reference pointers, the implicit_handle attribute and handl_t primitive types.

FILES
=====

The directory samples\rpc\mandel contains the following files for the
sample distributed application MANDEL:

File          Description

README.TXT    Readme file for the MANDEL sample
MDLRPC.IDL    Interface definition language file
MDLRPC.ACF    Attribute configuration file
MANDEL.C      Client main program
MANDEL.H      Client global data
REMOTE.C      Client code that calls remote procedures
RPC.ICO       Client icon
MANDEL.DEF    Client module definition file
MANDEL.RC     Client resource script file
SERVER.C      Server main program
CALC.C        Remote procedures
MAKEFILE      nmake utility for Windows NT or Windows 95
MAKEFILE.WIN  nmake utility for Win 3.x

-------------------------------------------
BUILDING CLIENT AND SERVER APPLICATIONS FOR
MICROSOFT WINDOWS NT OR WINDOWS 95
-------------------------------------------

The following environment variables should be already set for you:

  set CPU=i386
  set INCLUDE=%SDKROOT%\h
  set LIB=%SDKROOT%\lib
  set PATH=%SDKROOT%\system32;%SDKROOT%\bin

where %SDKROOT% is the root directory for the 32-bit Windows SDK.

For mips, set CPU=mips
For alpha, set CPU=alpha

Build the sample distributed application:

  nmake cleanall
  nmake

This builds the executable programs client.exe and server.exe for Microsoft
Windows NT or Windows 95.

--------------------------------------------
BUILDING THE CLIENT APPLICATION FOR WIN 3.X
--------------------------------------------

After installing the Microsoft Visual C/C++ version 1.50 development
environment and the 16-bit RPC SDK on a Windows NT or Windows 95
computer, you can build the sample client application from Windows NT
or Windows 95:

  nmake -f makefile.win cleanall
  nmake -f makefile.win

This builds the client application client.exe.

You may also execute the Microsoft Visual C/C++ compiler under MS-DOS.
This requires a two-step build process.

  Step One: Compile the .IDL files under Windows NT or Windows 95:

     nmake -a -f makefile.win yield.h

  Step Two: Compile the C sources (stub and application) under MS-DOS:

     nmake -f makefile.win

------------------------------------------
RUNNING THE CLIENT AND SERVER APPLICATIONS
------------------------------------------

On the server, enter:

  server

On the client, enter:

  net start workstation
  client

Note:  The client and server applications can run on the same Microsoft
Windows NT computer when you use different screen groups. If you run the
client on the Microsoft MS-DOS and Windows computer, choose the Run
command from the File menu in the Microsoft Windows 3.x Program Manager and
enter client.exe.

Several command line switches are available to change settings for the
server program. For a listing of switches available from the server program,
enter:

  server -?


MANDEL.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                        mandel Example

    FILE:       mandel.c

    PURPOSE:    Client side of the RPC distributed application

    COMMENTS:   Main code for the Windows Mandelbrot Set distributed
                drawing program.

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

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <time.h>
#include <windows.h>      // Required for all Windows applications
#include <windowsx.h>     // Allow portability from Win16, Win32

#ifdef RPC
#include "mdlrpc.h"       // header file generated by the MIDL compiler
#endif
#include "mandel.h"

/* data structures */

#ifdef RPC
char szTitle[] = "Mandelbrot RPC";
#else
char szTitle[] = "Mandelbrot Standalone";
#endif

CPOINT      cptUL = { (double) -2.05, (double) 1.4 };
double      dPrec = (double) .01;

HANDLE      hInst;         // current instance
HWND        hWND;          // Main window handle

svr_table   SvrTable;
int         iLines = LINES;

int         fContinueZoom = TRUE;
int         fZoomIn       = TRUE;

// split current picture into 16 regions
// zoom on most complex region;  region with most colors represented
int         Histogram[4][4][NCOLORS+1] = {0};
int         ColorCount[4][4] = {0};
int         Max[4][4] = {0};

int         iHistMaxI = 2;
int         iHistMaxJ = 3;

RECT        rcZoom;
BOOL        fRectDefined = FALSE;

#ifdef RPC
int             fBound = FALSE;     // flag indicates whether bound to svr
unsigned char * pszUuid             = NULL;
unsigned char pszProtocolSequence[MAXPROTSEQ] = "ncacn_np";
unsigned char pszEndpoint[PATHLEN]            = "\\pipe\\mandel";
unsigned char * pszOptions          = NULL;
unsigned char * pszStringBinding;
unsigned char   pszNetworkAddress[UNCLEN+1] = {'\0'};
#endif

/* function prototypes */

void     DoSomeWork(HWND, BOOL);
void     InitHistogram(void);
void     CalcHistogram(int, int, DWORD, DWORD);
void     PaintLine(HWND, svr_table *, HDC, int);
void     DrawRect(HWND, PRECT, BOOL, HDC);
COLORREF MapColor(DWORD, DWORD);

/*
 *  FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, 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,            /* current instance         */
    HINSTANCE hPrevInstance,        /* previous instance        */
    LPSTR lpCmdLine,                /* command line             */
    int nCmdShow)                   /* show-window type (open/icon) */
{

    MSG msg;

    UNREFERENCED_PARAMETER(lpCmdLine);

    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              */
                      (HWND)NULL,  /* 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   */
    }

    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(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 = (WNDPROC)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, "RPC_ICON");
    wc.hCursor = LoadCursor(0, IDC_ARROW);
    wc.hbrBackground = GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName =  "MandelMenu";    /* Name of menu resource in .RC file. */
    wc.lpszClassName = "MandelClass";   /* 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(HANDLE   hInstance,   /* Current instance identifier.       */
                  int      nCmdShow)    /* Param for first ShowWindow() call. */
{
    HMENU           hMenu;
    RECT            rc;

    /* 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.  */
    hWND = CreateWindow(
               "MandelClass",        /* See RegisterClass() call.          */
               szTitle,              /* Text for window title bar.         */
               WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_BORDER | WS_MINIMIZEBOX,
               CW_USEDEFAULT,        /* Default horizontal position.       */
               CW_USEDEFAULT,        /* Default vertical position.         */
               WIDTH,                /* Default width.                     */
               HEIGHT,               /* Default height.                    */
               (HWND) NULL,          /* Overlapped windows have no parent. */
               (HMENU) NULL,         /* Use the window class menu.         */
               hInstance,            /* This instance owns this window.    */
               (LPVOID) NULL         /* Pointer not needed.                */
           );

    /* If window could not be created, return "failure" */
    if (!hWND)
        return(FALSE);

    /* Make the window visible; update its client area; and return "success" */
    ShowWindow(hWND, nCmdShow);  /* Show the window                */
    UpdateWindow(hWND);          /* Sends WM_PAINT message         */
    rc.top = rc.left = 0;
    rc.bottom = HEIGHT-1;
    rc.right = WIDTH-1;

    SetNewCalc(cptUL, dPrec, rc);
    hMenu = GetMenu(hWND);

#ifndef RPC
    EnableMenuItem(hMenu, IDM_SERVER, MF_GRAYED);  /* disable option */
#endif

    return(TRUE);               /* Returns the value from PostQuitMessage */
}

/*
 *  FUNCTION: MainWndProc(HWND, unsigned, WORD, LONG)
 *
 *  PURPOSE:  Processes messages
 *
 *  MESSAGES:
 *      WM_COMMAND    - application menu
 *      WM_DESTROY    - destroy window
 *
 *  COMMENTS:
 */

LONG APIENTRY MainWndProc(
    HWND hWnd,               /* window handle               */
    UINT message,            /* type of message             */
    UINT wParam,             /* additional information      */
    LONG lParam)             /* additional information      */
{
    DLGPROC lpProc;          /* pointer to the dialog box function */
    PAINTSTRUCT ps;
    HDC hdc;
    static HDC     hdcMem;
    static HBITMAP hbmMem;
    static int     width;
    static int     height;
    RECT           rc;
    static BOOL    fButtonDown = FALSE;
    static POINT   pSelected;
    POINT          pMove;
    int            iWidthNew;
    int            iHeightNew;
    static int     miOldLines;
    double         scaling;

    switch (message) {

    case WM_CREATE:

#ifdef WIN16
        RpcWinSetYieldInfo (hWnd, FALSE, 0, 0L); // To make TCP/IP happy
#else
        PostMessage(hWnd, WM_COMMAND, IDM_BIND, 0L);    // bind to server
#endif

        if (!InitRemote(hWnd))
            return(FALSE);

        InitHistogram();

        hdc = GetDC(hWnd);
        hdcMem = CreateCompatibleDC(hdc);
        GetWindowRect(hWnd, &rc);
        width = rc.right - rc.left;
        height = rc.bottom - rc.top;
        hbmMem = CreateCompatibleBitmap(hdc, width, height);
        SelectObject(hdcMem, hbmMem);

        ReleaseDC(hWnd,hdc);

        rc.left = rc.top = 0;
        rc.right = width+1;
        rc.bottom = height + 1;
        FillRect(hdcMem, &rc, GetStockObject(WHITE_BRUSH));

        CheckMenuItem(GetMenu(hWnd), IDM_4LINES, MF_CHECKED);
        CheckMenuItem(GetMenu(hWnd), IDM_CONTINUOUS, MF_CHECKED);
        miOldLines = IDM_4LINES;  // save to uncheck
        break;

    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
        BitBlt(hdc,
               ps.rcPaint.left,
               ps.rcPaint.top,
               ps.rcPaint.right - ps.rcPaint.left,
               ps.rcPaint.bottom - ps.rcPaint.top,
               hdcMem,
               ps.rcPaint.left,
               ps.rcPaint.top,
               SRCCOPY);
        EndPaint(hWnd, &ps);
        break;

    case WM_COMMAND:  // message: command from application menu
        switch(wParam) {

        case IDM_BIND:

#ifdef RPC
            if (Bind(hWnd) != RPC_S_OK)
                PostMessage(hWnd, WM_DESTROY, 0, 0L);
#endif
            break;

        case IDM_ABOUT:
            lpProc = MakeProcInstance(About, hInst);

            DialogBox(hInst,       // current instance
                      "AboutBox",  // resource to use
                      hWnd,        // parent handle
                      lpProc);     // About() instance address

            FreeProcInstance(lpProc);
            break;

        case IDM_ZOOMOUT:
            if (dPrec > (double)MAXPREC)  // don't allow the zoom out
                break;

            rcZoom.left = WIDTH/4 + (WIDTH/8);  // center square
            rcZoom.top   = HEIGHT/4 + (HEIGHT/8);
            rcZoom.right = rcZoom.left + (WIDTH/4);
            rcZoom.bottom = rcZoom.top + (HEIGHT/4);

            cptUL.real -= (rcZoom.left * dPrec); // inverse of zoom in
            cptUL.imag += (rcZoom.top * dPrec);
            iWidthNew = (rcZoom.right - rcZoom.left + 1);
            iHeightNew = (rcZoom.bottom - rcZoom.top + 1);
            scaling = ((double) ((iWidthNew > iHeightNew) ? iWidthNew : iHeightNew) / (double) width);
            dPrec /= scaling;

            rc.left = rc.top = 0;
            rc.bottom = height - 1;
            rc.right = width - 1;

            SetNewCalc(cptUL, dPrec, rc);
            fRectDefined = FALSE;
            DoSomeWork(hWnd, FALSE);
            break;

        case IDM_ZOOMIN:  // zoom in on selected rectangle
            // if no rectangle, don't zoom in
            if (!fRectDefined)
                break;

            if (dPrec < (double)MINPREC)  // don't allow zoom in
                break;

            DrawRect(hWnd, &rcZoom, TRUE, hdcMem);  // draw new rect

            // calculate new upper-left
            cptUL.real += (rcZoom.left * dPrec);
            cptUL.imag -= (rcZoom.top * dPrec);

            iWidthNew = (rcZoom.right - rcZoom.left + 1);
            iHeightNew = (rcZoom.bottom - rcZoom.top + 1);
            scaling = ((double) ((iWidthNew > iHeightNew) ? iWidthNew : iHeightNew) / (double) width);

            dPrec *= scaling;

            rc.left = rc.top = 0;
            rc.bottom = height - 1;
            rc.right = width - 1;

            SetNewCalc(cptUL, dPrec, rc);
            IncPictureID();

            fRectDefined = FALSE;
            DoSomeWork(hWnd, FALSE);
            break;

        case IDM_CONTINUOUS:  // continuous zoom in
            if (fContinueZoom == TRUE) {
                CheckMenuItem(GetMenu(hWnd), IDM_CONTINUOUS, MF_UNCHECKED);
                fContinueZoom = FALSE;
            }
            else {
                CheckMenuItem(GetMenu(hWnd), IDM_CONTINUOUS, MF_CHECKED);
                fContinueZoom = TRUE;
            }
            break;

        case IDM_REDRAW:
            if (fContinueZoom == TRUE)
                InitHistogram();

            rc.left = rc.top = 0;
            rc.right = width+1;
            rc.bottom = height + 1;
            FillRect(hdcMem, &rc, GetStockObject(WHITE_BRUSH));
            InvalidateRect(hWnd, NULL, TRUE);

            rc.left = rc.top = 0;
            rc.bottom = height - 1;
            rc.right = width - 1;
            SetNewCalc( cptUL, dPrec, rc);

            fRectDefined = FALSE;
            DoSomeWork(hWnd, FALSE);
            break;

        case IDM_EXIT:
            DestroyWindow(hWnd);
            FreeDrawBuffer();
            break;

        case IDM_TOP:
            cptUL.real = (double) -2.05;
            cptUL.imag = (double) 1.4;
            dPrec = .01;

            rc.left = rc.top = 0;
            rc.bottom = height - 1;
            rc.right = width - 1;

            SetNewCalc(cptUL, dPrec, rc);
            ResetPictureID();  // incremented past original

            fRectDefined = FALSE;
            DoSomeWork(hWnd, FALSE);
            break;

        case IDM_1LINE:

        case IDM_2LINES:

        case IDM_4LINES:

            CheckMenuItem(GetMenu(hWnd), miOldLines, MF_UNCHECKED);
            miOldLines = wParam;
            switch(wParam) {

            case IDM_1LINE:
                iLines = 1;
                break;
            case IDM_2LINES:
                iLines = 2;
                break;
            case IDM_4LINES:
                iLines = 4;
                break;
            }

            CheckMenuItem(GetMenu(hWnd), miOldLines, MF_CHECKED);
            break;

#ifdef RPC
        case IDM_PROTSEQ:
            lpProc = MakeProcInstance(Protseq, hInst);
            DialogBox(hInst,        // current instance
                      "ProtseqBox", // resource to use
                      hWnd,         // parent handle
                      lpProc);      // Server instance address
            FreeProcInstance(lpProc);
            break;

        case IDM_SERVER:
            lpProc = MakeProcInstance(Server, hInst);
            DialogBox(hInst,         // current instance
                      "ServerBox",   // resource to use
                      hWnd,          // parent handle
                      lpProc);       // Server  instance address
            FreeProcInstance(lpProc);
            break;

        case IDM_ENDPOINT:
            lpProc = MakeProcInstance(Endpoint, hInst);
            DialogBox(hInst,        // current instance
                      "EndpointBox",// resource to use
                      hWnd,         // parent handle
                      lpProc);      // Server instance address
            FreeProcInstance(lpProc);
            break;
#endif
        case IDM_GO:
                SetTimer(hWnd, 1, POLL_TIME, NULL);  // set timer for polls
            EnableMenuItem(GetMenu(hWnd), IDM_GO, MF_GRAYED);   // disable GO
            break;

        default:  // Lets Windows process it
            return(DefWindowProc(hWnd, message, wParam, lParam));

        }

        break;

    case WM_DESTROY:  // message: window being destroyed
        PostQuitMessage(0);
        DeleteDC(hdcMem);
        DeleteObject(hbmMem);
        break;

    case WM_DOSOMEWORK:  // do another slice of calculation work
        DoSomeWork(hWnd, FALSE);
        break;

    case WM_PAINTLINE:  // The shared buffer contains a line of data; draw it
        PaintLine(hWnd,
                  &SvrTable,
                  hdcMem,
                  height);
        break;

    case WM_TIMER:  // timer means we should do another slice of work
        DoSomeWork(hWnd, TRUE);
        break;

    case WM_LBUTTONDOWN:  // left button down; start to define a zoom rectangle
        if (fRectDefined)
            DrawRect(hWnd, &rcZoom, FALSE, hdcMem);  // undraw old rectangle

        // initialize rectangle
        rcZoom.left = rcZoom.right = pSelected.x = LOWORD(lParam);
        rcZoom.top = rcZoom.bottom = pSelected.y = HIWORD(lParam);

        // draw the new rectangle
        DrawRect(hWnd, &rcZoom, TRUE, hdcMem);

        fRectDefined = TRUE;
        fButtonDown = TRUE;
        SetCapture(hWnd);  // capture all mouse events
        break;

    case WM_MOUSEMOVE:  // mouse move
        // if the button is down, change the rect
        if (!fButtonDown)
            break;

        DrawRect(hWnd, &rcZoom, FALSE, hdcMem);  // undraw old rect

        pMove.x = LOWORD(lParam);
        pMove.y = HIWORD(lParam);

        // update the selection rectangle
        if (pMove.x <= pSelected.x)
            rcZoom.left = pMove.x;
        if (pMove.x >= pSelected.x)
            rcZoom.right = pMove.x;
        if (pMove.y <= pSelected.y)
            rcZoom.top = pMove.y;
        if (pMove.y >= pSelected.y)
            rcZoom.bottom = pMove.y;

        DrawRect(hWnd, &rcZoom, TRUE, hdcMem);  // draw new rect
        break;

    case WM_LBUTTONUP:  // button up; end selection
        fButtonDown = FALSE;
        ReleaseCapture();
        break;

    default:  // Passes it on if unproccessed
        return(DefWindowProc(hWnd, message, wParam, lParam));

    }

    return(0L);
}

/*
 *  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(
    HWND hDlg,              /* window handle of the dialog box */
    UINT message,           /* type of message                 */
    UINT wParam,            /* message-specific information    */
    LONG lParam)
{
    UNREFERENCED_PARAMETER(lParam);

    switch (message) {

    case WM_INITDIALOG:     /* message: initialize dialog box  */
        return(TRUE);

    case WM_COMMAND:        /* message: received a command     */
        if (wParam == IDOK || wParam == IDCANCEL)
        {
            EndDialog(hDlg, TRUE);    /* Exits the dialog box  */
            return(TRUE);
        }
        break;
    }

    return(FALSE);          /* Didn't process a message        */
}

/*
 *  FUNCTION: Protseq(HWND, unsigned, WORD, LONG)
 *
 *  PURPOSE:  Processes messages for "Protseq" 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 Protseq(
    HWND hDlg,               /* window handle of the dialog box */
    UINT message,            /* type of message             */
    UINT wParam,             /* message-specific information    */
    LONG lParam)
{

    UNREFERENCED_PARAMETER(lParam);

#ifdef RPC

    switch (message) {

    case WM_INITDIALOG:    // message: initialize dialog box
        SetDlgItemText((HANDLE)hDlg, IDD_PROTSEQNAME, pszProtocolSequence);
        return(TRUE);

    case WM_COMMAND:       // message: received a command
        switch(wParam) {

        case IDCANCEL:     // System menu close command?
            EndDialog(hDlg, FALSE);
            return(TRUE);

        case IDOK:         // "OK" box selected?
            GetDlgItemText(hDlg, IDD_PROTSEQNAME, pszProtocolSequence, MAXPROTSEQ);

            if (Bind(hDlg) != RPC_S_OK) {
                EndDialog(hDlg, FALSE);
                return(FALSE);
            }
                KillTimer(hWND, 1);  // stop timer for polls
            EnableMenuItem(GetMenu(hWND), IDM_GO, MF_ENABLED);  // enable GO

            EndDialog(hDlg, TRUE);
            return(TRUE);

        }

    }

#endif

    return(FALSE);  // Didn't process a message
}

/*
 *  FUNCTION: Server(HWND, unsigned, WORD, LONG)
 *
 *  PURPOSE:  Processes messages for "Server" 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 Server(
    HWND hDlg,               /* window handle of the dialog box */
    UINT message,            /* type of message             */
    UINT wParam,             /* message-specific information    */
    LONG lParam)
{

    UNREFERENCED_PARAMETER(lParam);

#ifdef RPC

    switch (message) {

    case WM_INITDIALOG:      /* message: initialize dialog box */
        SetDlgItemText(hDlg, IDD_SERVERNAME, pszNetworkAddress);
        return(TRUE);

    case WM_COMMAND:         /* message: received a command    */
        switch(wParam) {

        case IDCANCEL:       /* System menu close command?     */
            EndDialog(hDlg, FALSE);
            return(TRUE);

        case IDOK:            /* "OK" box selected?            */
            GetDlgItemText( hDlg, IDD_SERVERNAME, pszNetworkAddress, UNCLEN);

            if (Bind(hDlg) != RPC_S_OK) {
                EndDialog(hDlg, FALSE);
                return(FALSE);
            }
                KillTimer(hWND, 1);  // stop timer for polls
            EnableMenuItem(GetMenu(hWND), IDM_GO, MF_ENABLED);  // enable GO

            EndDialog(hDlg, TRUE);
            return(TRUE);
        }

    }

#endif

    return(FALSE);           /* Didn't process a message      */

}

/*
 *  FUNCTION: Endpoint(HWND, unsigned, WORD, LONG)
 *
 *  PURPOSE:  Processes messages for "Endpoint" 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 Endpoint(
    HWND hDlg,               /* window handle of the dialog box */
    UINT message,            /* type of message             */
    UINT wParam,             /* message-specific information    */
    LONG lParam)
{

    UNREFERENCED_PARAMETER(lParam);

#ifdef RPC

    switch (message) {

    case WM_INITDIALOG:    // message: initialize dialog box
        SetDlgItemText(hDlg, IDD_ENDPOINTNAME, pszEndpoint);
        return(TRUE);

    case WM_COMMAND:       // message: received a command
        switch(wParam) {

        case IDCANCEL:
            EndDialog(hDlg, FALSE);
            return(TRUE);

        case IDOK:
            GetDlgItemText(hDlg, IDD_ENDPOINTNAME, pszEndpoint, PATHLEN);

            if (Bind(hDlg) != RPC_S_OK) {
                EndDialog(hDlg, FALSE);
                return(FALSE);
            }
                KillTimer(hWND, 1);  // stop timer for polls
            EnableMenuItem(GetMenu(hWND), IDM_GO, MF_ENABLED);  // enable GO

            EndDialog(hDlg, TRUE);
            return(TRUE);

        }

    }

#endif

    return(FALSE);  // Didn't process a message
}

/*
 *  DoSomeWork --
 *
 *  This function does our work for us. It does it in little pieces, and
 *  will schedule itself as it sees fit.
 */

void
DoSomeWork(HWND    hwnd,
           BOOL    fTimer)
{
    static WORD   wIteration = 0;

    if (fTimer) {
        wIteration++;

        // on every nth tick, we send out a poll
        if (wIteration == 120) {  // tune this?
            wIteration = 0;
            return;
        }

        // on the half-poll, we check for responses
        if ((wIteration == 2) || (wIteration == 10)) {
            return;
        }
    }

    if (CheckDrawStatus(hwnd))
        SendMessage(hwnd, WM_DOSOMEWORK, 0, 0L);

    return;
}

/*
 *  DrawRect --
 *
 *  This function draws (or undraws) the zoom rectangle.
 */

void
DrawRect(HWND      hwnd,
         PRECT     prc,
         BOOL      fDrawIt,
         HDC       hdcBM)
{
    HDC     hdc;
    DWORD   dwRop;

    hdc = GetDC(hwnd);

    if (fDrawIt)
        dwRop = NOTSRCCOPY;
    else
        dwRop = SRCCOPY;

    // top side
    BitBlt(hdc, prc->left, prc->top, (prc->right - prc->left) + 1,
           1, hdcBM, prc->left, prc->top, dwRop);

    // bottom side
    BitBlt(hdc, prc->left, prc->bottom, (prc->right - prc->left) + 1,
           1, hdcBM, prc->left, prc->bottom, dwRop);

    // left side
    BitBlt(hdc,prc->left, prc->top, 1, (prc->bottom - prc->top) + 1,
           hdcBM, prc->left, prc->top, dwRop);

    // right side
    BitBlt(hdc,prc->right, prc->top, 1, (prc->bottom - prc->top) + 1,
           hdcBM, prc->right, prc->top, dwRop);

    ReleaseDC(hwnd, hdc);
}

/*
 *  PaintLine --
 *
 *  This function paints a buffer of data into the bitmap.
 */

void
PaintLine(HWND        hwnd,
          svr_table * pst,
          HDC         hdcBM,
          int         cHeight)
{
    LPWORD  pwDrawData;
    int     y;
    int     x;
    DWORD   dwThreshold;
    RECT    rc;
    WORD    lines;

    lines  = (WORD) pst->cLines;

    // picture ID had better match, or else we skip it
    if (CheckDrawingID(pst->cPicture))
    {
        // figure out our threshold
        dwThreshold = QueryThreshold();

        // get a pointer to the draw buffer
        pwDrawData = (LPWORD) LockDrawBuffer();
        if (pwDrawData == NULL) {
            ReturnDrawBuffer();
            return;
        }

        // starting x coordinate
        x = (int) pst->dwLine;

        // now loop through the rectangle
        while (lines-- > 0)
        {
            // bottom to top, since that's the order of the data in the buffer
            y = (int) cHeight-1;

            while (y >= 0)
            {
                // draw a pixel
                SetPixel(hdcBM, x,y, MapColor(*pwDrawData, dwThreshold));

                if (fContinueZoom == TRUE)
                    CalcHistogram(x, y, *pwDrawData, dwThreshold);

                // now increment buffer pointer and y coord

y--;
                pwDrawData++;
            }

            // increment X coordinate
            x++;
        }

        // figure out the rectangle to invalidate
        rc.top = 0;
        rc.bottom = cHeight;
        rc.left = (int)(pst->dwLine);
        rc.right = (int)(pst->dwLine) + pst->cLines;

        UnlockDrawBuffer();

        // and invalidate it on the screen so we redraw it
        InvalidateRect(hwnd, &rc, FALSE);
    }

    // free this for someone else to use
    ReturnDrawBuffer();

    // and change the pipe state, if necessary
    if (pst->iStatus == SS_PAINTING)
        pst->iStatus = SS_IDLE;

}

#define CLR_BLACK       RGB(0,0,0)
#define CLR_DARKBLUE    RGB(0,0,127)
#define CLR_BLUE        RGB(0,0,255)
#define CLR_CYAN        RGB(0,255,255)
#define CLR_DARKGREEN   RGB(0,127,0)
#define CLR_GREEN       RGB(0,255,0)
#define CLR_YELLOW      RGB(255,255,0)
#define CLR_RED         RGB(255,0,0)
#define CLR_DARKRED     RGB(127,0,0)
#define CLR_WHITE       RGB(255,255,255)
#define CLR_PALEGRAY    RGB(194,194,194)
#define CLR_DARKGRAY    RGB(127,127,127)

static COLORREF ColorMapTable[] = {     // size = NCOLORS
    CLR_DARKBLUE,
    CLR_BLUE,
    CLR_CYAN,
    CLR_DARKGREEN,
    CLR_GREEN,
    CLR_YELLOW,
    CLR_RED,
    CLR_DARKRED,
    CLR_WHITE,
    CLR_PALEGRAY,
    CLR_DARKGRAY};

/*
 *  MapColor --
 *
 *  This function maps an iteration count into a corresponding RGB color.
 */

COLORREF
MapColor(DWORD  dwIter,
         DWORD  dwThreshold)
{

    /* if it's beyond the threshold, call it black */
    if (dwIter >= dwThreshold) {
        return(CLR_BLACK);
    }

    /* get a modulus based on the number of colors */
    dwIter = (dwIter / 3) % NCOLORS; // 11;

    /* and return the appropriate color */
    return(ColorMapTable[dwIter]);

}

/*
 * CalcHistogram --
 *
 * This function is used to select the region that is the
 * most complex and will be used to zoom in for the next picture;
 * it contains the most colors.  The number of colors are counted.
 */

void
CalcHistogram(int    x,
              int    y,
              DWORD  dwIter,
              DWORD  dwThreshold)
{

    /* if it's beyond the threshold, call it black */
    if (dwIter >= dwThreshold) {
        Histogram[x/(WIDTH/4)][y/(HEIGHT/4)][NCOLORS]++;
        return;
    }

    /* get a modulus based on the number of colors */
    dwIter = (dwIter / 3) % NCOLORS; // 11;

    /* and bump the count for the appropriate color */
    Histogram[x/(WIDTH/4)][y/(HEIGHT/4)][dwIter]++;  // region of map

    return;

}

/*
 * InitHistogram --
 *
 * This function initializes the histogram data structures.
 */

void InitHistogram(void)
{
    int i, j, k;

    for (i = 0; i < 4; i++)
        for (j = 0; j < 4; j++)
            for (k = 0; k <= NCOLORS; k++)
                Histogram[i][j][k] = 0;  // count of colors
}

/*
 * CountHistogram --
 *
 * This function determines the number of colors represented
 * within a region.  The region with the most colors is
 * selected using the maxi and maxj values.  X and Y coordinates
 * corresponding to these regions are stored in the HistRegion
 * table and are used for the next picture.
 */

void CountHistogram(void)
{

    int i, j, k;

    /* count the number of colors in each region */
    /* find the color that dominates each region */
    for (i = 0; i < 4; i++) {
        for (j = 0; j < 4; j++) {
            ColorCount[i][j] = 0;
            Max[i][j] = 0;
            for (k = 0; k <= NCOLORS; k++) {
                if (Histogram[i][j][k] > Max[i][j])
                    Max[i][j] = Histogram[i][j][k];
                if (Histogram[i][j][k] != 0)  // count of colors
                    ColorCount[i][j]++;
            }
        }
    }

    iHistMaxI = 0;
    iHistMaxJ = 0;

    /* if several regions have the same number of colors,        */
    /* select the region with the most variety: the smallest max */
    for (i = 0; i < 4; i++) {
        for (j = 0; j < 4; j++) {
            if (   (ColorCount[i][j] >= ColorCount[iHistMaxI][iHistMaxJ])
                && (Max[i][j] < Max[iHistMaxI][iHistMaxJ]) ) {
                iHistMaxI = i;
                iHistMaxJ = j;
            }
        }
    }

    InitHistogram();  // initialize for next time

}

#ifdef RPC

void __RPC_FAR * __RPC_API midl_user_allocate(size_t len)
{
    UNREFERENCED_PARAMETER(len);
    return(NULL);
}

void __RPC_API midl_user_free(void __RPC_FAR * ptr)
{
    UNREFERENCED_PARAMETER(ptr);
    return;
}

/*
 *  FUNCTION: Bind(HWND)
 *
 *  PURPOSE:  Make RPC API calls to bind to the server application
 *
 *  COMMENTS:
 *
 *      The binding calls are made from InitInstance() and whenever
 *      the user changes the server name or endpoint. If the bind
 *      operation is successful, the global flag fBound is set to TRUE.
 *
 *      The global flag fBound is used to determine whether to call
 *      the RPC API function RpcBindingFree.
 */

RPC_STATUS Bind(HWND hWnd)
{
    RPC_STATUS status;
    char pszFail[MSGLEN];

    if (fBound == TRUE) {  // unbind only if bound
        status = RpcStringFree(&pszStringBinding);
        if (status) {
            sprintf(pszFail, "RpcStringFree failed 0x%x", status);
            MessageBox(hWnd,
                       pszFail,
                       "RPC Sample Application",
                       MB_ICONSTOP);
            return(status);
        }

        status = RpcBindingFree(&hMandel);
        if (status) {
            sprintf(pszFail, "RpcBindingFree failed 0x%x", status);
            MessageBox(hWnd,
                       pszFail,
                       "RPC Sample Application",
                       MB_ICONSTOP);
            return(status);
        }

        fBound = FALSE;  // unbind successful; reset flag
    }

    status = RpcStringBindingCompose(pszUuid,
                                     pszProtocolSequence,
                                     pszNetworkAddress,
                                     pszEndpoint,
                                     pszOptions,
                                     &pszStringBinding);
    if (status) {
        sprintf(pszFail, "RpcStringBindingCompose returned: (0x%x)\nNetwork Address = %s\n",
              status, pszNetworkAddress);
        MessageBox(hWnd, pszFail, "RPC Sample Application", MB_ICONINFORMATION);
        return(status);
    }

    status = RpcBindingFromStringBinding(pszStringBinding,
                                         &hMandel);
    if (status) {
        sprintf(pszFail, "RpcBindingFromStringBinding returned: (0x%x)\nString = %s\n",
             status, pszStringBinding);
        MessageBox(hWnd, pszFail, "RPC Sample Application", MB_ICONINFORMATION);
        return(status);
    }

    fBound = TRUE;  // bind successful; reset flag

    return(status);
}

#endif

/* end mandel.c */


MANDEL.H

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

    MANDEL.H -- Constants and function definitions for MANDEL.C

    Copyright 1990 - 1998 Microsoft Corporation

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

/* Constants */

#ifdef WIN16
#define APIENTRY        PASCAL
#define UNREFERENCED_PARAMETER
#endif

#define IDM_ABOUT        100
#define IDM_ZOOMIN       101
#define IDM_ZOOMOUT      105
#define IDM_TOP          106
#define IDM_REDRAW       107
#define IDM_EXIT         108
#define IDM_CONTINUOUS   109
#define IDM_PROTSEQ      110
#define IDD_PROTSEQNAME  111
#define IDM_SERVER       112
#define IDD_SERVERNAME   113
#define IDM_ENDPOINT     114
#define IDD_ENDPOINTNAME 115
#define IDM_BIND         116
#define IDM_GO           117
#define IDM_1LINE        200
#define IDM_2LINES       201
#define IDM_4LINES       202

#define WIDTH            300
#define HEIGHT           300
#define LINES              4
#define BUFSIZE         1200     // (HEIGHT * LINES)
#define MAX_BUFSIZE     4800     // (BUFSIZE * sizeof(short))

#define POLL_TIME        2000

#define CNLEN             25     // computer name length
#define UNCLEN       CNLEN+2     // \\computername
#define PATHLEN           260    // Path
#define MSGLEN            300    // arbitrary large number for message size
#define MAXPROTSEQ         20    // protocol sequence

#define NCOLORS           11

#define SVR_TABLE_SZ      20

// Status of connection to server
#define SS_DISCONN         0
#define SS_IDLE            1
#define SS_READPENDING     2
#define SS_PAINTING        3
#define SS_LOCAL           4

#define MINPREC       5.0E-9
#define MAXPREC       5.0E-3

#define WM_DOSOMEWORK   (WM_USER+0)
#define WM_PAINTLINE    (WM_USER+1)

#define EXCEPT_MSG      "The remote procedure raised an exception.\n\
Check your connection settings."

/* Data Structures */

typedef struct _svr_table {
    char    name[UNCLEN];       // name of remote server
    int     hfPipe;             // RPC handle
    int     iStatus;            // status of connection
    int     cPicture;           // picture id for this line
    DWORD   dwLine;             // line we're drawing
    int     cLines;             // lines in this chunk
} svr_table;

#ifndef RPC                     // If RPC, the following data would be
                                // defined in the IDL file
typedef struct _cpoint {
    double    real;
    double    imag;
} CPOINT;

typedef CPOINT * PCPOINT;

typedef struct _LONGRECT {
    long    xLeft;
    long    yBottom;
    long    xRight;
    long    yTop;
} LONGRECT;

typedef LONGRECT *PLONGRECT;

typedef unsigned short LINEBUF[BUFSIZE];

#endif

typedef struct _calcbuf {
    LONGRECT  rclDraw;
    double    dblPrecision;
    DWORD     dwThreshold;
    CPOINT    cptLL;
} CALCBUF;

/* Function Prototypes */

int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int);
BOOL InitApplication(HANDLE);
BOOL InitInstance(HANDLE, int);

LONG APIENTRY MainWndProc(HWND, UINT, UINT, LONG);
BOOL APIENTRY About(HWND, UINT, UINT, LONG);
BOOL APIENTRY Protseq(HWND, UINT, UINT, LONG);
BOOL APIENTRY Server(HWND, UINT, UINT, LONG);
BOOL APIENTRY Endpoint(HWND, UINT, UINT, LONG);

#ifdef RPC
RPC_STATUS Bind(HWND);
#endif

void CountHistogram(void);

BOOL InitRemote(HWND);
BOOL CheckDrawStatus(HWND);
void SetNewCalc(CPOINT, double, RECT);
void IncPictureID(void);
void ResetPictureID(void);
BOOL CheckDrawingID(int);
DWORD QueryThreshold(void);

// buffer routines
BOOL TakeDrawBuffer(void);
LPVOID LockDrawBuffer(void);
void UnlockDrawBuffer(void);
void ReturnDrawBuffer(void);
void FreeDrawBuffer(void);

#ifndef RPC                     // If RPC, MandelCalc() would be
                                // defined in the IDL file
void MandelCalc(PCPOINT   pcptLL,
                PLONGRECT prcDraw,
                double    precision,
                DWORD     ulThreshold,
                LINEBUF * pbBuf);
#endif


MDLRPC.ACF

[implicit_handle(handle_t hMandel)]
interface mdlrpc
{

}


MDLRPC.IDL

[ uuid (5630EAA0-CA48-1067-B320-00DD010662DA),
  version (1.0),
  pointer_default(unique)
]
interface mdlrpc
{

#define BUFSIZE   1200     // also defined in mandel.h

typedef struct _cpoint {
    double  real;
    double  imag;
} CPOINT;

typedef CPOINT * PCPOINT;

typedef struct _LONGRECT {
    long    xLeft;
    long    yBottom;
    long    xRight;
    long    yTop;
} LONGRECT;

typedef LONGRECT *PLONGRECT;

typedef unsigned short LINEBUF[BUFSIZE];

void MandelCalc(
    [in]       PCPOINT        pcptLL,
    [in]       PLONGRECT      prcDraw,
    [in]       double         precision,
    [in]       unsigned long  ulThreshold,
    [out, ref] LINEBUF *      pbBuf
    );

}


CALC.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                        mandel Example

    FILE:       calc.c

    PURPOSE:    Server side of the RPC distributed application Mandel

    FUNCTIONS:  MandelCalc() - Do the calculations for the Windows
                               Mandelbrot Set distributed drawing program.

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

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

#ifdef RPC
#include "mdlrpc.h"
#endif
#include "mandel.h"

short calcmand(double, double, short);

void MandelCalc(PCPOINT    pcptLL,
                PLONGRECT  prcDraw,
                double     precision,
                DWORD      ulThreshold,
                LINEBUF *  pbBuf)
{
    DWORD   h, height, width;
    double  dreal, dimag, dimag2;
    short   maxit = 0;
    short * pbPtr;

    pbPtr = *pbBuf;   // LINEBUF is an array of shorts

    dreal = pcptLL->real + ((double)prcDraw->xLeft * precision);
    dimag = pcptLL->imag + ((double)prcDraw->yBottom * precision);

    maxit = (short) ulThreshold;

    height = (prcDraw->yTop - prcDraw->yBottom) + 1;
    width = (prcDraw->xRight - prcDraw->xLeft) + 1;

    for ( ; width > 0; --width, dreal += precision) {
        for (dimag2 = dimag, h = height; h > 0; --h, dimag2 += precision) {
            if ((dreal > 4.0) || (dreal < -4.0) ||
                (dimag2 > 4.0) || (dimag2 < -4.0))
                *(pbPtr++) = 0;
            else
                *(pbPtr++) = calcmand(dreal, dimag2, maxit);
        }
    }
}

/* C version of the assembly language program */
short calcmand(double dreal,
               double dimag,
               short  maxit)
{
    double x, y, xsq, ysq;
    short k;

    k = maxit;
    x = dreal;
    y = dimag;

    while (1) {
        xsq = x * x;
        ysq = y * y;
        y = 2.0 * x * y + dimag;
        x = (xsq - ysq) + dreal;
        if (--k == 0)
            return((short) (maxit - k));
        if ((xsq + ysq) > 4.0)
            return((short) (maxit - k));
    }
}


REMOTE.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                        mandel Example

    FILE:      remote.c

    PURPOSE:   Client side of the RPC distributed application Mandel

    COMMENTS:  Code to do the remote calculations for the Windows
               Mandelbrot Set distributed drawing program.

               Information coming into this module (via API calls) is
               based on upper-left being (0,0) (the Windows standard).
               We translate that to lower-left is (0,0) before we ship
               it out onto the net, and we do reverse translations
               accordingly.

               The iteration data is passed back to the main window
               procedure (by means of a WM_PAINTLINE message) which
               draws the picture.

               A word about the shared buffer: multiple buffers could
               be used, but a single one is used. The buffer is requested
               in this code, and then released after the data has been
               drawn (in PaintLine() in mandel.c). So long as the painting
               is done quickly, this is efficient.

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

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <windows.h>

#ifdef RPC
#include "mdlrpc.h"
#endif
#include "mandel.h"

/*
 * External variables
 */
extern int          fBound;
extern svr_table    SvrTable;    // the server table
extern int          iLines;
extern double       dPrec;
extern int          fContinueZoom;
extern int          fZoomIn;
extern int          iHistMaxI;
extern int          iHistMaxJ;
extern RECT         rcZoom;
extern BOOL         fRectDefined;

/*
 * Picture information
 */
int             cPictureID = 0;    // picture id, in case we reset in the middle
static CPOINT   cptLL;             // upper-left
static double   dPrecision;        // precision of draw
static LONGRECT rclPicture;        // rectangle defining client window
static DWORD    dwCurrentLine;     // next line to be drawn
static DWORD    dwThreshold;       // threshold for iterations

/*
 * Function prototypes for local procs
 */
DWORD CalcThreshold(double);

/*
 *  InitRemote --
 *
 *  This function initializes everything for our remote connections.
 *  It gets the local wksta name (making sure the wksta is started)
 *  and it creates the mailslot with which to collect replies to our poll.
 *
 *  RETURNS
 *      TRUE    - initialization succeeded
 *      FALSE   - initialization failed, can't go on
 */

BOOL InitRemote(HWND hWnd)
{

#ifndef RPC
    UNREFERENCED_PARAMETER(hWnd);
#endif

    // set up our local entry
    strcpy(SvrTable.name, "Local machine");
    SvrTable.iStatus = SS_LOCAL;

    // good, we succeeded
    return(TRUE);
}

/*
 *  CheckDrawStatus --
 *
 *  This function does a check of all buffers being drawn.
 *
 *  If it finds an idle pipe, and there is work to be done, it assigns
 *      a line, and writes out the request.
 *  If it finds a read-pending pipe, it checks if the read has completed.
 *      If it has, it is read and a message is sent so the read data can
 *      be processed.
 *
 *  RETURNS
 *      TRUE    - we did a piece of work
 *      FALSE   - we could not find any work to do.
 */

BOOL CheckDrawStatus(HWND hwnd)
{
    CALCBUF     cb;
    LPVOID      pbBuf;

    while(TRUE) {

        // Check the status
        switch(SvrTable.iStatus) {

        case SS_PAINTING:
            break;

        case SS_IDLE:
            break;

        case SS_LOCAL:
            // Do a chunk of work locally

#ifdef RPC
            if (fBound == FALSE)
                break;
#endif

            if ((long) dwCurrentLine > rclPicture.xRight) {
                if (fContinueZoom == TRUE) {
                    if ((fZoomIn == TRUE) && (dPrec < (double)MINPREC))
                        fZoomIn = FALSE;  // start zooming out
                    if ((fZoomIn == FALSE) && (dPrec > (double)MAXPREC))
                        fZoomIn = TRUE;
                    if (fZoomIn) {
                        CountHistogram();
                        rcZoom.top    = iHistMaxJ * (WIDTH/4);
                        rcZoom.bottom = rcZoom.top + (WIDTH/4) - 1;
                        rcZoom.left   = iHistMaxI * (HEIGHT/4);
                        rcZoom.right  = rcZoom.left + (HEIGHT/4) - 1;
                        fRectDefined = TRUE;
                        PostMessage(hwnd, WM_COMMAND, IDM_ZOOMIN, 0L);
                    }
                    else
                        PostMessage(hwnd, WM_COMMAND, IDM_ZOOMOUT, 0L);
                }
                break;
            }

            if (TakeDrawBuffer() == FALSE)
                break;

            pbBuf = LockDrawBuffer();

            cb.rclDraw.xLeft = dwCurrentLine;
            cb.rclDraw.xRight = dwCurrentLine + iLines - 1;
            cb.rclDraw.yTop = rclPicture.yTop;
            cb.rclDraw.yBottom = rclPicture.yBottom;

            RpcTryExcept {
                MandelCalc(&cptLL,
                           &(cb.rclDraw),
                           dPrecision,
                           dwThreshold,
                           (LINEBUF *) pbBuf);
            }
            RpcExcept(1) {
                char szFail[MSGLEN];

                sprintf (szFail, "%s (0x%x)\n", EXCEPT_MSG, RpcExceptionCode());
                MessageBox(hwnd,
                           szFail,
                           "Remote Procedure Call",
                           MB_ICONINFORMATION);
                        KillTimer(hwnd, 1);  // stop timer for polls
                    EnableMenuItem(GetMenu(hwnd), IDM_GO, MF_ENABLED);  // enable GO
                    UnlockDrawBuffer();

                            ReturnDrawBuffer();
                        return(FALSE);
            }
            RpcEndExcept

            UnlockDrawBuffer();

            SvrTable.cPicture = cPictureID;
            SvrTable.dwLine = dwCurrentLine;
            SvrTable.cLines = iLines;

            PostMessage(hwnd, WM_PAINTLINE, 0, 0L);
            dwCurrentLine += iLines;

            return(TRUE);
        }

        return(FALSE);
    }
}

/*
 *  SetNewCalc --
 *
 *  This sets up new information for a drawing and
 *  updates the drawing ID so any calculations in progress will not
 *  be mixed in.
 */

void SetNewCalc(CPOINT cptUL, double dPrec, RECT rc)
{
    // First, translate from upper left to lower left
    cptLL.real = cptUL.real;
    cptLL.imag = cptUL.imag - (dPrec * (rc.bottom - rc.top));

    // Now the precision
    dPrecision = dPrec;

    // The rectangle. Once again, translate.
    rclPicture.xLeft = (long) rc.left;
    rclPicture.xRight = (long) rc.right;
    rclPicture.yBottom = (long) rc.top;
    rclPicture.yTop = (long) rc.bottom;

    // Current line, start of drawing
    dwCurrentLine = rclPicture.xLeft;

    dwThreshold = CalcThreshold(dPrecision);
}

void IncPictureID(void)
{
    cPictureID++;
}

void ResetPictureID(void)
{
    cPictureID = 0;
}

/*
 *  CheckDrawing --
 *
 *  Just a sanity check here -- a function to check to make sure that we're
 *  on the right drawing
 */

BOOL CheckDrawingID(int id)
{
    return((id == cPictureID) ? TRUE : FALSE);
}

/*
 *  TakeDrawBuffer ensures only one pipe read at a time.
 *  LockDrawBuffer locks the handle and returns a pointer.
 *  UnlockDrawBuffer unlocks the handle.
 *  ReturnDrawBuffer lets another pipe read go.
 *  FreeDrawBuffer ensures the allocated buffer is freed upon exit.
 */

static BOOL fBufferTaken = FALSE;
static HANDLE hSharedBuf = (HANDLE) NULL;

BOOL TakeDrawBuffer(void)
{
    if (fBufferTaken) {
        return(FALSE);
    }

    if (hSharedBuf == (HANDLE) NULL) {
        hSharedBuf = LocalAlloc(LMEM_MOVEABLE, MAX_BUFSIZE);
        if (hSharedBuf == (HANDLE) NULL)
            return(FALSE);
    }

    fBufferTaken = TRUE;
    return(TRUE);
}

LPVOID LockDrawBuffer(void)
{
    if (hSharedBuf == (HANDLE) NULL)
        return(NULL);

    return(LocalLock(hSharedBuf));
}

void UnlockDrawBuffer(void)
{
    LocalUnlock(hSharedBuf);
}

void ReturnDrawBuffer(void)
{
    fBufferTaken = FALSE;
}

void FreeDrawBuffer(void)
{
    if (hSharedBuf != (HANDLE) NULL)
        LocalFree(hSharedBuf);
}

/*
 *  CalcThreshold --
 *
 *  We need an iteration threshold beyond which we give up. We want it to
 *  increase the farther we zoom in. This code generates a threshold value
 *  based on the precision of drawing.
 *
 *  RETURNS
 *      threshold calculated based on precision
 */

DWORD CalcThreshold(double precision)
{
    DWORD   thres = 25;
    double  multiplier = (double) 100.0;

    /* for every 100, multiply by 2 */
    while ((precision *= multiplier) < (double)1.0)
        thres *= 2;

    return(thres);
}

/*
 *  QueryThreshold --
 *
 *  Callback for finding out what the current drawing's threshold is.
 */

DWORD QueryThreshold(void)
{
    return(dwThreshold);
}


SERVER.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                         mandel Example

    FILE:       server.c

    USAGE:      server  -p protocol_sequence
                        -e endpoint
                        -m max calls
                        -n min calls
                        -f flag for RpcServerListen

    PURPOSE:    Server side of RPC distributed application mandel

    FUNCTIONS:  main() - registers interface and listen for clients

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

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include "mdlrpc.h"    // header file generated by MIDL compiler

void Usage(char * pszProgramName)
{
    fprintf(stderr, "Usage:  %s\n", pszProgramName);
    fprintf(stderr, " -p protocol_sequence\n");
    fprintf(stderr, " -e endpoint\n");
    fprintf(stderr, " -m maxcalls\n");
    fprintf(stderr, " -n mincalls\n");
    fprintf(stderr, " -f flag_wait_op\n");
    exit(1);
}

void _CRTAPI1 main(int argc, char * argv[])
{
    RPC_STATUS status;
    unsigned char * pszProtocolSequence = "ncacn_np";
    unsigned char * pszSecurity         = NULL;
    unsigned char * pszEndpoint         = "\\pipe\\mandel";
    unsigned int    cMinCalls           = 1;
    unsigned int    cMaxCalls           = 20;
    unsigned int    fDontWait           = FALSE;
    int i;

    /* allow the user to override settings with command line switches */
    for (i = 1; i < argc; i++) {
        if ((*argv[i] == '-') || (*argv[i] == '/')) {
            switch (tolower(*(argv[i]+1))) {
            case 'p':  // protocol sequence
                pszProtocolSequence = argv[++i];
                break;
            case 'e':
                pszEndpoint = argv[++i];
                break;
            case 'm':
                cMaxCalls = (unsigned int) atoi(argv[++i]);
                break;
            case 'n':
                cMinCalls = (unsigned int) atoi(argv[++i]);
                break;
            case 'f':
                fDontWait = (unsigned int) atoi(argv[++i]);
                break;
            case 'h':
            case '?':
            default:
                Usage(argv[0]);
            }
        }
        else
            Usage(argv[0]);
    }

    status = RpcServerUseProtseqEp(pszProtocolSequence,
                                   cMaxCalls,
                                   pszEndpoint,
                                   pszSecurity);  // Security descriptor
    printf("RpcServerUseProtseqEp returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    status = RpcServerRegisterIf(mdlrpc_ServerIfHandle, // interface to register
                                 NULL,   // MgrTypeUuid
                                 NULL);  // MgrEpv; null means use default
    printf("RpcServerRegisterIf returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    printf("Calling RpcServerListen\n");
    status = RpcServerListen(cMinCalls,
                             cMaxCalls,
                             fDontWait);
    printf("RpcServerListen returned: 0x%x\n", status);
    if (status) {
        exit(status);
    }

    if (fDontWait) {
        printf("Calling RpcMgmtWaitServerListen\n");
        status = RpcMgmtWaitServerListen();  //  wait operation
        printf("RpcMgmtWaitServerListen returned: 0x%x\n", status);
        if (status) {
            exit(status);
        }
    }

} // end main()

/*********************************************************************/
/*                MIDL allocate and free                             */
/*********************************************************************/

void __RPC_FAR * __RPC_API midl_user_allocate(size_t len)
{
    return(malloc(len));
}

void __RPC_API midl_user_free(void __RPC_FAR * ptr)
{
    free(ptr);
}

/* end server.c */


README.TXT

3-D Maze Game

SUMMARY
=======

The Mazelord sample is a 3-D maze game. The objective is to get as high a
score as possible by zapping other players, and by destroying automated
drones which wander around the maze. When there are no other players,
zapping drones still makes an interesting game in itself.

Mazelord is a networked or non-networked game, depending upon how it is
invoked. In both versions, the player is placed in a room of sanctuary in a
palace. In this room, players may not shoot, and drones cannot hurt players.
Once outside of its 3x3 confines, however, anything goes. Your task is to
hunt down drones and other players and 'zap' them. Zapped players are
teleported back to the sanctuary, and zapped drones are teleported to a
random location in the maze.

MORE INFORMATION
================

Drones appear as gray robots. The number and speed of drones is configurable
under the Options. A suggestion is 20 drones, speed 2.

The controls to Mazelord are simple:

  - UpArrow:    Move one square forward.
  - LeftArrow:  Turn left, stay in same square.
  - RightArrow: Turn right, stay in same square.
  - DownArrow:  Move one square backwards.
  - Space:      Zap whatever is in line of sight.
  - X:          Exit the game.
  - W:          Whisper something. Anyone within the specified # of squares
                of you can hear your message.
  - S:          Shout something. Same as whisper, except people who are
                farther away can hear your message.

To play, ideally you should be running on a 486 or MIPS with lots of memory
and sound enabled, although the game should work on other systems. Note that
if you don't have sound, you can still play. The important sounds are
changed to speaker beeps.

Upon invoking Maze, you have several options:

File Menu

    NewGame:   Starts a new game. Make sure you have configured the game
               under the OPTIONS menu unless you want the defaults and want
               to play Locally rather than over the network.
    PauseGame: Pauses the movement of all drones, but you will be a sitting
               duck if someone shoots at you.
    StopGame:  Resets the game.
    Exit:      Exits the game.

Options Menu

    Networked/Local: Determines whether you will be playing against other
               people on your network domain or not. It's fine to run
               NETWORK'd when there is no one else playing. Default is
               LOCAL.
    PlayerSettings: Brings up a dialog with two list boxes. The first shows
               a list of bitmaps you can choose from to be your character.
               Currently, there is a fish and a smiley face, but the number
               of choices will be increasing. You can also choose a home
               maze. This isn't used right now, but soon your home maze will
               be added to a random part of the existing maze, and you will
               be able to teleport there at will.
    Drones:    Brings up the drones configuration dialog box. Select the
               number of drones you want and the speed you want them to go,
               (# of moves per second). Move algorithm is currently ignored,
               as all drones use the same semi-random algorithm.

The display consists of the following:

  - A 3-D view of the maze. There are drawing bugs in terms of correctness,
    but unless you're watching for them, they generally don't make a
    difference. If you have doubts about a wall, you can verify its
    existence on the overhead view.

  - Overhead view: This view is two squares in each direction from where
    you are located. You can't see other players on the overhead map, but
    you can see a small triangle in the center, which indicates which way
    you are facing. This window is an ABSOLUTE view; up is ALWAYS north
    on the window, regardless of which direction you're facing in the maze.

  - Text window: This gives information about what is happening in the game,
    such as who you zap or who zaps you, and notification when players enter
    or leave the game. You will also see messages whispered or shouted by
    other players if they are close enough.

  - Score window. This shows all players active in your domain if you're
    networked. The display includes the bitmap being used by the player, their
    score, and a directional indicator to show the ABSOLUTE direction you
    need to go to reach the square that player is in. These are updated
    every time they change.

Have fun with the game!


CRCTABLE.H

/*
        CRCtable.h
*/

unsigned long CRCtable[] = {
    0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F,
    0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
    0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2,
    0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
    0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9,
    0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
    0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C,
    0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
    0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423,
    0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
    0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106,
    0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
    0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D,
    0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
    0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950,
    0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
    0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7,
    0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
    0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA,
    0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
    0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81,
    0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
    0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84,
    0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
    0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB,
    0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
    0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E,
    0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
    0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55,
    0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
    0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28,
    0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
    0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F,
    0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
    0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242,
    0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
    0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69,
    0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
    0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC,
    0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
    0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693,
    0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
    0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D };


DRAW.C

/**********************************************************************
File:   InitMaze.c

Abstract:

    This module contains drawing routines for MazeLords.

Contents:

    DelHole() --
    NewHole() --
    DrawRect() --
    NewPlayerDraw() --
    DelPlayerDraw() --
    CheckForPlayers() --
    DrawFoundPlayers() --
    DrawMaze() --
    DrawTopView() --
    DrawPlayers() --

Revision History:

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

#include "winmaze.h"
#include "mazproto.h"

LPPOINT pDraw;
LPBYTE pType;
int PointCount;

/**********************************************************************
 *                                                                    *
 * FUNCTION:  PolyDraw95(HDC, LPPOINT, LPBYTE, int)                   *
 *                                                                    *
 * PURPOSE:   Draws the points returned from a call to GetPath()      *
 *            to an HDC                                               *
 *                                                                    *
 * NOTES:     This function is similar to the Windows NT PolyDraw     *
 *            function, which draws a set of line segments and Bezier *
 *            curves. Because PolyDraw is not supported in Windows 95 *
 *            this PolyDraw95 function is used instead.               *
 *                                                                    *
 * MODS:      Added this function in order to provide support for     *
 *            Windows 95.                                             *
 *                                                                    *
 *********************************************************************/
BOOL PolyDraw95(HDC  hdc,              // handle of a device context
                CONST LPPOINT lppt,      // array of points
                CONST LPBYTE lpbTypes, // line and curve identifiers
                int  cCount)             // count of points
{
  int i;

  for (i=0; i<cCount; i++)
    switch (lpbTypes[i]) {
      case PT_MOVETO :
         MoveToEx(hdc, lppt[i].x, lppt[i].y, NULL);
         break;

      case PT_LINETO | PT_CLOSEFIGURE:
      case PT_LINETO :
         LineTo(hdc, lppt[i].x, lppt[i].y);
         break;

      case PT_BEZIERTO | PT_CLOSEFIGURE:
      case PT_BEZIERTO :
        PolyBezierTo(hdc, &lppt[i], 3);
       i+=2;
         break;
    }

   return TRUE;
}

//
// convert an offset to an increment to add.
//
#define ABS2OFFX(x,y,Facing) (((Facing)&NORTH) ? (x) : \
                              ((Facing)&SOUTH) ? -(x) : \
                              ((Facing)&EAST) ? (y) : -(y))
#define ABS2OFFY(x,y,Facing) (((Facing)&NORTH) ? -(y) : \
                              ((Facing)&SOUTH) ? (y) : \
                              ((Facing)&EAST) ? (x) : -(x))

/*=====================================================================
Function: DelHole()

Inputs: Pointer to HoleType record to delete

Outputs: Returns ->next field of deleted record

Abstract:
    Deletes a record of type HoleType, returns ->next pointer to
    facilitate list maintenance
======================================================================*/

HoleType FAR *DelHole(
    HoleType FAR *h
    )
{
    HoleType FAR *t;
    HANDLE hMem;

    if (h == NULL) {
        return((HoleType FAR *) NULL);
        }
    else {
        hMem = (HGLOBAL) GlobalHandle(SELECTOROF( h));
        t = h->next;
        GlobalUnlock(hMem);
        GlobalFree(hMem);
        return(t);
        }
}

/*=====================================================================
Function: NewHole()

Inputs: HoleType element values

Outputs:Returns a pointer to a new initialized HoleType record

Abstract:
    No explanation needed
======================================================================*/

HoleType FAR *NewHole(
    int x1,
    int x2,
    HoleType FAR *next
    )

{
    HoleType FAR *h;
    HANDLE hMem;

    hMem =  GlobalAlloc(GHND,sizeof(HoleType));
    h = (HoleType FAR *) GlobalLock(hMem);
    if (h == (HoleType FAR *) NULL) {
        MessageBox((HANDLE) NULL,GetStringRes(IDS_MALLOCQUIT), NULL ,MB_ICONEXCLAMATION);
        exit(0);
        }
    h->x[0] = x1;
    h->x[1] = x2;
    h->next = next;

    return(h);
}

/*=====================================================================
Function: DrawRect()

Inputs: Hole List (determining what is visible),
    relative x and y coordinates of wall to draw,
    relative facing of wall to draw

Outputs: Modifies hole list, returns a pointer to where hole processing
    should resume from

Abstract:
    DrawRect expects to draw a wall for the coordinate (iRelX,iRelY) relative
    facing bRelDir.

    For clipping, suppose we have

          xr
          |
      p1|---
        |   ---    xs
        |      --- |
        |         --- p4
        |            |
        |            |
        |            |
        |         --- p3
        |      ---
        |   ---
      p2|---

    where (p1 to p4) are known endpoints for a panel, and xr and xs are
    vertical clipping lines (fortunately we're restricted to these.
    The new dimensions for p1-p4 are easy to calculate, since, it is a matter
    of similar triangles:

        |         ---. p3
        |      --- ; .
        |   ---    ; .
      p2|---..........
                   ;
                   xs

    We need to recalculate p3, since it is past the clipping boundary. By
    similar triangles, we know  (p3.y-p2.y)/(p3.x-p2.x) = (xs.y-p2.y)/(xs.x-p2.x).
    This in turn gives us xs.y = (p3.y-p2.y)*(xs.x-p2.x)/(p3.x-p2.x) + p2.y. This
    can be applied to all four endpoints.
======================================================================*/

HoleType FAR *DrawRect(
    HoleType FAR *htTrav,
    int iRelX,
    int iRelY,
    BYTE bRelDir
    )
{
    POINT p[4];
    int i,rb;
    int ihLeft,ihRight;
    BOOL bIsLeftSide;

    bIsLeftSide = (iRelX < 0);

    if ((htTrav == NULL)||(htTrav->next == NULL)) {
        return(NULL);
        }

    ihLeft = htTrav->next->x[0];
    ihRight= htTrav->next->x[1];

    if (ihLeft >= ihRight) {
        return(htTrav->next);
        }

    //
    //p1=p[0],p2=p[1],p4=p[2],p3=p[3] from diagram above.
    //
    switch(bRelDir) {

        //
        // Wall furthest away from us
        //
        case NORTH:
            for(i=0;i<2;i++) {
                p[i] = pPost[iRelX+MAX_DRAW_DIST+1][iRelY+1][i];
                p[i+2] = pPost[iRelX+MAX_DRAW_DIST+2][iRelY+1][i];
                }
            break;

        //
        // Wall closest to us
        //
        case SOUTH:
            for(i=0;i<2;i++) {
                p[i] = pPost[iRelX+MAX_DRAW_DIST+1][iRelY][i];
                p[i+2] = pPost[iRelX+MAX_DRAW_DIST+2][iRelY][i];
                }
            break;

        //
        // West ==> we're working on the left side of the screen
        //
        case WEST:
            for(i=0;i<2;i++) {
                p[i] = pPost[iRelX+MAX_DRAW_DIST+1][iRelY][i];
                p[i+2] = pPost[iRelX+MAX_DRAW_DIST+1][iRelY+1][i];
                }
            break;

        //
        // East ==> we're working on the right side of the screen
        //
        case EAST:
            for(i=0;i<2;i++) {
                p[i] = pPost[iRelX+MAX_DRAW_DIST+2][iRelY+1][i];
                p[i+2] = pPost[iRelX+MAX_DRAW_DIST+2][iRelY][i];
                }
            break;
        }

    //
    // If we can see any of the panel through the hole,
    // go ahead and draw it!
    //
    if ((p[0].x < ihRight)&&(p[2].x > ihLeft)) {
        for(i=0;i<2;i++) {
            //
            // Clip the left side, if needed.
            //
            if (p[i].x < ihLeft ) {
                p[i].y = ((p[i].y-p[i+2].y)*(ihLeft-p[i+2].x)/
                            (p[i].x-p[i+2].x)) + p[i+2].y;
                p[i].x = ihLeft;
                }

            //
            // and the right side...
            //
            if (p[3-i].x > ihRight) {
                p[3-i].y = ((p[3-i].y-p[1-i].y)*(ihRight-p[1-i].x)/
                            (p[3-i].x-p[1-i].x)) + p[1-i].y;
                p[3-i].x = ihRight;
                }
            }

        //
        // Now we need to add these points into the draw list.
        //
        pType[PointCount]=PT_MOVETO;
        pDraw[PointCount++]=p[0];
        pType[PointCount]=PT_LINETO;
        pDraw[PointCount++]=p[1];
        pType[PointCount]=PT_LINETO;
        pDraw[PointCount++]=p[3];
        pType[PointCount]=PT_LINETO;
        pDraw[PointCount++]=p[2];
        pType[PointCount]=PT_LINETO;
        pDraw[PointCount++]=p[0];

        if (p[0].x == ihLeft) {
            if (p[2].x == ihRight) {
                //
                // If left and right match exactly, delete hole.
                //
                htTrav->next = DelHole(htTrav->next);
                }
            else {
                //
                // if left matches, but right doesn't, set hole to rt,rt
                //
                htTrav->next->x[0] = p[2].x;
                }
            }
        else {
            //
            // if the left doesn't match, at the very least we need
            // to keep the left to the current spot.
            //
            rb = htTrav->next->x[1];
            htTrav->next->x[1] = p[0].x;

            if (p[2].x != rb) {
                //
                // Also, if the right side didn't match up, we need to
                // create a new hole.

                //
                // need to make sure the hole won't get skipped as we
                // continue processing inside to outside...
                //
                if (bIsLeftSide) {
                    htTrav->next = NewHole(p[2].x,rb,htTrav->next);
                    }
                else {
                    htTrav->next->next = NewHole(p[2].x,rb,htTrav->next->next);
                    }
                }
            }
        }

    return(htTrav);
}

/*=====================================================================
Function: NewPlayerDraw()

Inputs: PlayerDrawType record element values

Outputs: Pointer to a new, initialized record of type PlayerDrawType

Abstract:
    No explanation needed.
======================================================================*/

PlayerDrawType FAR *NewPlayerDraw(
    PlayerType FAR *p,
    LPRECT rDraw,
    LPRECT rClip,
    int iRelx,
    int iRely,
    PlayerDrawType FAR *next
    )
{
    PlayerDrawType FAR *pdtRet;
    HGLOBAL hMem;

    hMem = GlobalAlloc(GHND,(sizeof(PlayerDrawType)));
    pdtRet = (PlayerDrawType FAR *) GlobalLock(hMem);
    if (pdtRet == NULL) {
        MessageBox((HWND) NULL,GetStringRes(IDS_MALLOCFAIL),"NewPlayerDraw",
                   MB_APPLMODAL|MB_ICONEXCLAMATION);
        }
    else {
        pdtRet->p = p;
        pdtRet->rDraw = *rDraw;
        pdtRet->rClip = *rClip;
        pdtRet->iRelx = iRelx;
        pdtRet->iRely = iRely;
        pdtRet->next = next;
        }

    return(pdtRet);
}

/*=====================================================================
Function: DelPlayerDraw()

Inputs: Pointer to PlayerDrawType record to delete

Outputs: Pointer to ->next field value of deleted record

Abstract:
    returns the ->next field for linked list maintenance
======================================================================*/

PlayerDrawType FAR *DelPlayerDraw(
    PlayerDrawType FAR *pdtP
    )
{
    PlayerDrawType FAR *pdtRet;
    HGLOBAL hMem;

    pdtRet = NULL;
    if (pdtP != NULL) {
        pdtRet = pdtP->next;
        hMem = (HGLOBAL) GlobalHandle(SELECTOROF(pdtP));
        GlobalFree(hMem);
        }

    return(pdtRet);
}

/*=====================================================================
Function: CheckForPlayers()

Inputs: List of players, holes in view, positions to check

Outputs:

Abstract:
    This routine checks for players on a single square, given the relative
    and absolute coordinates of that square. If a player IS on that square,
    then we check the hole we're looking through to see if any of the
    destination rectangle is visible. If it is, we make a note of the player
    by adding them to the pdtTD = 'Players To Draw' list.
======================================================================*/

void CheckForPlayers(
    PlayerDrawType FAR *pdtTD,
    PlayerType FAR *ptObj,
    HoleType FAR *htHole,
    int iRelx,
    int iRely,
    int iAbsx,
    int iAbsy
    )
{
    PlayerType FAR *ptTrav;
    RECT rDraw,rClip;
    float x,y,z;
    int xSize,ySize;
    POINT pCenter;

    if (htHole == NULL) {
        return;
        }

    GetClientRect(hWndMaze,&rMaze);

    pCenter.x = rMaze.left + (rMaze.right-rMaze.left)/2;
    pCenter.y = rMaze.top + (rMaze.bottom-rMaze.top)/2;

    //
    // Set x/y/z to screen coordinate upper left corner of panel
    //
    x = (float) ((iRelx*PANEL_WIDTH) - PANEL_WIDTH/2);
    y = (float) (-PANEL_HEIGHT/2);
#if ( _ALPHA_ == 1 )
    z = (float) iRely*PANEL_WIDTH - (PANEL_WIDTH/2);
#else
    z = (float) (iRely-1)*PANEL_WIDTH;
#endif

    //
    // Calculate the physical width and depth of a panel placed at the bitmap
    // location, since this is the maximum possible size for a player bitmap.
    // PIC_X and PIC_Y are the pixel values for this.
    //
    xSize = (int) MC_TO_SC(PIC_X,z);
    ySize = (int) MC_TO_SC(PIC_Y,z);

    //
    // Calculate the physical rectangle to contain the player bitmap
    //
    rDraw.left = pCenter.x + (int) MC_TO_SC(x,z);
    rDraw.top  = pCenter.y + (int) MC_TO_SC(y,z);
    rDraw.right = rDraw.left + xSize-1;
    rDraw.bottom = rDraw.top + ySize-1;

    //
    // Set the clipping rectangle to the visible screen
    //
    rClip.left = GREATEROF(rDraw.left,0);
    rClip.top = GREATEROF(rDraw.top,0);
    rClip.right = LESSEROF(rDraw.right,rMaze.right);
    rClip.bottom = LESSEROF(rDraw.bottom,rMaze.bottom);

    //
    // Adjust the clipping rectangle's left and right by the hole
    // which we're constrained by (Holes are horizontal coordinates only)
    //
    if (htHole != (HoleType FAR *) -1) {
        rClip.left = GREATEROF(rClip.left,htHole->x[0]);
        rClip.right = LESSEROF(rClip.right,htHole->x[1]);
        }

    //
    // If The whole picture is clipped, don't bother drawing it.
    //
    if (rClip.left >= rClip.right) {
        return;
        }

    ptTrav=ptObj;
    while (ptTrav->next != NULL) {
        ptTrav = ptTrav->next;
        if ((ptTrav->Pos.ix == iAbsx)&&(ptTrav->Pos.iy == iAbsy)) {
            //
            // We need to adjust the drawing coordinates according to this
            // player's picture.
            //
            pdtTD->next = NewPlayerDraw(ptTrav,&rDraw,&rClip,iRelx,iRely,pdtTD->next);
            }
        }
}

/*=====================================================================
Function: DrawFoundPlayers()

Inputs: List of players

Outputs: modifies rBounds to contain rectangle saying where player is.

Abstract:
    BUGBUG -- needs abstract
======================================================================*/

void DrawFoundPlayers(
    HDC hDC,
    PlayerDrawType FAR *pdtTrav,
    LPRECT rBounds
    )
{
    BYTE dBackward,b1,b2;

    dBackward = BACK_TO_ABS(ptSelf.Pos.Facing);

    while (pdtTrav->next != NULL) {
        b1 = BACK;
        b2 = ptSelf.Pos.Facing;
        while (b2 != pdtTrav->next->p->Pos.Facing) {
            b1 = (BYTE) ((b1+1)%4);
            b2 = RIGHT_TO_ABS(b2);
            }

        pdtTrav->next->p->rDrawn = pdtTrav->next->rDraw;
        if (rBounds != NULL) {
            rBounds->left = LESSEROF(rBounds->left,
                               GREATEROF(pdtTrav->next->rDraw.left,
                                         pdtTrav->next->rClip.left));
            rBounds->top = LESSEROF(rBounds->top,
                               GREATEROF(pdtTrav->next->rDraw.top,
                                         pdtTrav->next->rClip.top));
            rBounds->right = GREATEROF(rBounds->right,
                               LESSEROF(pdtTrav->next->rDraw.right,
                                         pdtTrav->next->rClip.right));
            rBounds->bottom = GREATEROF(rBounds->bottom,
                               LESSEROF(pdtTrav->next->rDraw.bottom,
                                         pdtTrav->next->rClip.bottom));
            }

        DrawClippedPic(pdtTrav->next->p->iPicNum,b1,hDC,
                       &pdtTrav->next->p->rDrawn,
                       &pdtTrav->next->rClip,
                       &pdtTrav->next->p->rFrom,
                       pdtTrav->next->iRelx,
                       pdtTrav->next->iRely);
        pdtTrav->next->p->Drawn = TRUE;
        pdtTrav->next->p->rDrawn.right++;
        pdtTrav->next->p->rDrawn.bottom++;

        pdtTrav->next = DelPlayerDraw(pdtTrav->next);
        }

    return;
}

/*=====================================================================
Function: DrawMaze()

Inputs: DC, rectangle that needs to be redrawn

Outputs: none

Abstract:
    This entrypoint takes care of drawing the 3-d maze. It loops from
    front to back, inside to outside, Checking for panels. It keeps a list
    of all the 'holes' in panels in terms of physical coordinates. For
    instance, if I've found the wall in front of me, but no others, and
    I can see to the left and to the right of the wall, I would have two
    holes in my hole-list. The first would be from the left of the view
    window to the left side of the wall, and the second would be from the
    right side of the wall to the right side of the view window.
======================================================================*/

void DrawMaze(HDC hDC,LPRECT rUpd)
{
    int i,j,tx,ty,iEndl,iEndr;
    BYTE dLeft,dRight,dForward,dBackward,b1,b2,b3;
    HoleType htLeft,htRight,FAR *htTrav, FAR *htHold;
    BOOL bFound;
    PlayerDrawType pdtToDraw;
    HBRUSH hOldBrush,hWhiteBrush;
    HPEN hOldPen,hWhitePen,hBlackPen;
    HGLOBAL hMem;

    if ((rUpd->left >= rUpd->right)||(rUpd->top >= rUpd->bottom)) {
        return;
        }

    hMem = GlobalAlloc(GHND,10000*sizeof(POINT));
    pDraw = (LPPOINT) GlobalLock(hMem);
    hMem = GlobalAlloc(GHND,10000*sizeof(BYTE));
    pType = (LPBYTE) GlobalLock(hMem);

    GetClientRect(hWndMaze,&rMaze);
    pdtToDraw.next = NULL;

    //
    // Clear out the space to be drawn into
    //
    hWhiteBrush = GetStockObject(WHITE_BRUSH);
    hWhitePen = GetStockObject(WHITE_PEN);
    hBlackPen = GetStockObject(BLACK_PEN);
    hOldBrush = SelectObject(hDC,hWhiteBrush);
    hOldPen = SelectObject(hDC,hWhitePen);

    PointCount = 0;

    dForward = ptSelf.Pos.Facing;
    dBackward = BACK_TO_ABS(ptSelf.Pos.Facing);
    dLeft = LEFT_TO_ABS(ptSelf.Pos.Facing);
    dRight = RIGHT_TO_ABS(ptSelf.Pos.Facing);

    tx = ptSelf.Pos.ix;
    ty = ptSelf.Pos.iy;

    Rectangle(hDC,rUpd->left,rUpd->top,rUpd->right,rUpd->bottom);

    SelectObject(hDC,hOldBrush);
    SelectObject(hDC,hBlackPen);

    //
    // Find the End which lies straight before us, else default
    // it to MAX_DRAW_DIST.
    //
    b2 = bMaze[tx][ty];
    bFound = FALSE;
    for(j=0,ViewEnd=MAX_DRAW_DIST-1;j<MAX_DRAW_DIST;j++) {
        b1 = b2;
        tx = ADJ_X(tx,ptSelf.Pos.Facing);
        ty = ADJ_Y(ty,ptSelf.Pos.Facing);
        b2 = bMaze[tx][ty];
        if ((b1&dForward)||(b2&dBackward)) {
            ViewEnd = j;
            bFound = TRUE;
            break;
            }
        }

    PointCount = 0;
    iEndl = iEndr = (rMaze.right - rMaze.left) / 2;

    //
    // Draw the end rectangle.
    //
    if (bFound) {
        //
        // Only bother with the draw if the end is to be updated
        //
        iEndl = pPost[MAX_DRAW_DIST+1][ViewEnd+1][0].x;
        iEndr = pPost[MAX_DRAW_DIST+2][ViewEnd+1][0].x;

        //
        // Left Post
        //
        pDraw[0].x = pPost[MAX_DRAW_DIST+1][ViewEnd+1][0].x;
        pDraw[0].y = pPost[MAX_DRAW_DIST+1][ViewEnd+1][0].y;
        pType[0] = PT_MOVETO;
        pDraw[1].x = pPost[MAX_DRAW_DIST+1][ViewEnd+1][1].x;
        pDraw[1].y = pPost[MAX_DRAW_DIST+1][ViewEnd+1][1].y;
        pType[1] = PT_LINETO;

        //
        // Right Post
        //
        pDraw[2].x = pPost[MAX_DRAW_DIST+2][ViewEnd+1][1].x;
        pDraw[2].y = pPost[MAX_DRAW_DIST+2][ViewEnd+1][1].y;
        pType[2] = PT_LINETO;
        pDraw[3].x = pPost[MAX_DRAW_DIST+2][ViewEnd+1][0].x;
        pDraw[3].y = pPost[MAX_DRAW_DIST+2][ViewEnd+1][0].y;
        pType[3] = PT_LINETO;
        pDraw[4].x = pPost[MAX_DRAW_DIST+1][ViewEnd+1][0].x;
        pDraw[4].y = pPost[MAX_DRAW_DIST+1][ViewEnd+1][0].y;
        pType[4] = PT_LINETO;
        PointCount = 5;
        }

    //
    // Make LeftHoles and RightHoles accordingly
    //
    htLeft.next = NewHole(0,iEndl,NULL);
    htRight.next = NewHole(iEndr,rMaze.right-rMaze.left,NULL);

    //
    // For the left side, loop from front to back, right to
    // left, checking the rEAST and rNORTH walls, in that
    // order. Eventually we'll need to put the check for players
    // in between the two. As a wall is found, draw it.
    //

    //
    // j is the relative y-offset into the maze.
    //
    for(j=0;j<MAX_DRAW_DIST;j++) {
        //
        // Check for players/drones directly in front of us,
        // make note of them to draw them later.
        //
        if (j < ViewEnd+1) {
            CheckForPlayers(&pdtToDraw,&ptPlayers,(HoleType FAR *) -1,0,j,
                      ptSelf.Pos.ix + ABS2OFFX(0,j,dForward),
                      ptSelf.Pos.iy + ABS2OFFY(0,j,dForward));
            CheckForPlayers(&pdtToDraw,&ptDrones,(HoleType FAR *) -1,0,j,
                      ptSelf.Pos.ix + ABS2OFFX(0,j,dForward),
                      ptSelf.Pos.iy + ABS2OFFY(0,j,dForward));
            }

        //
        // b1 is the square we're in, b2 the square we want to
        // see if there's a wall EAST/WEST between. b3 is the
        // square we want to see if there is a NORTH/SOUTH wall
        // from b2 to.  Then we move to b2 and try it again.
        //
        htTrav=&htLeft;

        while((htTrav != (HoleType FAR *) NULL)&&(htTrav->next != (HoleType FAR *)NULL)) {
            // i will loop from the 0th relative column to the far left.
            // This will be done for each and every hole, or even some
            // holes more than once, depending upon how they are filled.
            // set (tx,ty) to spot in our x-pos with relative y j.
            tx = ptSelf.Pos.ix + ABS2OFFX(0,j,dForward);
            ty = ptSelf.Pos.iy + ABS2OFFY(0,j,dForward);
            b2 = bMaze[tx][ty];

            htHold = htTrav->next;
            for (i=0;(i>= -MAX_DRAW_DIST)&&(htTrav != (HoleType FAR *)NULL)&&(htTrav->next != (HoleType FAR *)NULL);i--) {
                //
                // if we're not in the hole yet, continue
                //
                if (htTrav->next->x[1] < pPost[MAX_DRAW_DIST+i][j+1][0].x) {
                   continue;
                   }

                //
                // if we're past the outside of the hole, go to next hole.
                //
                if (htTrav->next->x[0] > pPost[MAX_DRAW_DIST+1+i][j+1][0].x) {
                    break;
                    }

                //
                // b1, b2, and b3 correspond to the square to the inside of the one
                // we're checking, the square we're checking, and the square
                // forward past the square we're checking.
                //
                b1 = b2;

                //
                // set (tx,ty) one square to the 'left'.
                //
                tx = ADJ_X(tx,dLeft);
                ty = ADJ_Y(ty,dLeft);

                b2 = bMaze[tx][ty];

                b3 = bMaze[ADJ_X(tx,dForward)][ADJ_Y(ty,dForward)];

                if ((b1&dLeft)||(b2&dRight)) {
                    htTrav = DrawRect(htTrav,i,j,WEST);
                    }

                CheckForPlayers(&pdtToDraw,&ptPlayers,htTrav->next,i-1,j,tx,ty);
                CheckForPlayers(&pdtToDraw,&ptDrones,htTrav->next,i-1,j,tx,ty);

                if ((b2&dForward)||(b3&dBackward)) {
                    htTrav = DrawRect(htTrav,i-1,j,NORTH);
                    }
                }

            if ((htTrav!= (HoleType FAR *)NULL)&&(htHold == htTrav->next)) {
                htTrav = htTrav->next;
                }
            }
         }

    //
    // Same as above, except this time for the RIGHT side.
    //
    for(j=0;j<MAX_DRAW_DIST;j++) {
        htTrav=&htRight;

        while((htTrav != (HoleType FAR *)NULL)&&(htTrav->next != (HoleType FAR *)NULL)) {
            tx = ptSelf.Pos.ix + ABS2OFFX(0,j,dForward);
            ty = ptSelf.Pos.iy + ABS2OFFY(0,j,dForward);

            b2 = bMaze[tx][ty];

            htHold = htTrav->next;
            for (i=0;(i<MAX_DRAW_DIST)&&(htTrav != (HoleType FAR *)NULL)&&(htTrav->next != (HoleType FAR *)NULL);i++) {
                //
                // if we're before the hole, continue
                //
                if (htTrav->next->x[0] > pPost[MAX_DRAW_DIST+i+3][j+1][0].x) {
                    continue;
                    }

                //
                // if we're outside the hole, go to next hole.
                //
                if (htTrav->next->x[1] < pPost[MAX_DRAW_DIST+i+2][j+1][0].x) {
                    break;
                    }

                b1 = b2;

                //
                // set (tx,ty) one square to the 'right'.
                //
                tx = ADJ_X(tx,dRight);
                ty = ADJ_Y(ty,dRight);

                b2 = bMaze[tx][ty];

                b3 = bMaze[ADJ_X(tx,dForward)][ADJ_Y(ty,dForward)];

                if ((b1&dRight)||(b2&dLeft)) {
                    htTrav = DrawRect(htTrav,i,j,EAST);
                    }

                CheckForPlayers(&pdtToDraw,&ptPlayers,htTrav->next,i+1,j,tx,ty);
                CheckForPlayers(&pdtToDraw,&ptDrones,htTrav->next,i+1,j,tx,ty);

                if ((b2&dForward)||(b3&dBackward)) {
                    htTrav = DrawRect(htTrav,i+1,j,NORTH);
                    }
                }

            if ((htTrav != (HoleType FAR *)NULL)&&(htHold == htTrav->next)) {
                htTrav = htTrav->next;
                }
            }
        }

    //
    // Draw the maze itself
    //

    PolyDraw95(hDC,pDraw,pType,PointCount);

    //
    // We were looking for players as we went along. Draw all the ones we
    // found
    //
    DrawFoundPlayers(hDC,&pdtToDraw,rUpd);

    //
    // Clean up.
    //

    htTrav = &htLeft;
    while (htTrav->next != NULL) {
        htTrav->next = DelHole(htTrav->next);
    }

    htTrav = &htRight;
    while (htTrav->next != NULL) {
        htTrav->next = DelHole(htTrav->next);
    }

    DeleteObject(hWhiteBrush);
    DeleteObject(hWhitePen);
    DeleteObject(hBlackPen);
    hMem = (HGLOBAL) GlobalHandle(SELECTOROF(pType));
    GlobalUnlock(hMem);
    GlobalFree(hMem);
    hMem = (HGLOBAL) GlobalHandle(SELECTOROF(pDraw));
    GlobalUnlock(hMem);
    GlobalFree(hMem);
    SelectObject(hDC,hOldPen);

    return;
}

/*=====================================================================
Function: DrawTopView()

Inputs: DC, whether or not it's a total redraw

Outputs: none

Abstract:
    This routine takes care of the top maze view window. It draws white
    lines over black and vice versa to get the window drawn in the shortest
    possible time. A triangle is drawn in the middle of the window to
    represent the player and his current facing.
======================================================================*/
void DrawTopView(
    HDC hDC,
    BOOL bRedraw
    )
{
    int i,j,Step;
//
//BUGBUG -- these can be fixed after we have square pens
//
    // POINT p[5*5*4*2+5];
    // BYTE bType[5*5*4*2+5];
    LPPOINT p;
    //[3*5*5*4*2+5];
    LPBYTE bType;
    //[3*5*5*4*2+5];
    int cPoint,iPenWidth;
    int x,y;
    HPEN hBlackPen,hWhitePen,hPenOld;
    HGLOBAL hPMem,hBMem;

    hPMem = GlobalAlloc(GHND,(3*5*5*4*2 + 5)*sizeof(POINT));

p = (LPPOINT) GlobalLock(hPMem);
    hBMem = GlobalAlloc(GHND,(3*5*5*4*2 + 5)*sizeof(BYTE));
    bType = (LPBYTE) GlobalLock(hBMem);

    cPoint = 0;

    Step = (rTopView.right - rTopView.left)/5;

    iPenWidth = Step/10+1;
    hBlackPen = CreatePen(PS_SOLID,iPenWidth,0);

    hWhitePen = CreatePen(PS_SOLID,iPenWidth,0x00FFFFFF);

    //
    // First, we need to draw black lines for all the missing lines in
    // the current map
    //
    hPenOld = SelectObject(hDC,hBlackPen);

    for (i =-3;i<=2; i++) {
        for (j= -3; j<=2; j++) {
            if (bRedraw ||
                (!((bMaze[ptLastPos.ix + i][ptLastPos.iy + j]&SOUTH)||
                (bMaze[ptLastPos.ix + i][ptLastPos.iy + j+1]&NORTH)))
               ) {

                if ((bMaze[ptSelf.Pos.ix + i][ptSelf.Pos.iy + j]&SOUTH)||
                    (bMaze[ptSelf.Pos.ix + i][ptSelf.Pos.iy + j+1]&NORTH)
                   ) {

                    p[cPoint].x = (i+2)*Step;
                    p[cPoint].y = (j+3)*Step;
                    bType[cPoint++] = PT_MOVETO;
                    p[cPoint].x = (i+3)*Step;
                    p[cPoint].y = (j+3)*Step;
                    bType[cPoint++] = PT_LINETO;
                    }
                }
            if (bRedraw ||
                (!((bMaze[ptLastPos.ix + i][ptLastPos.iy + j]&EAST)||
                 (bMaze[ptLastPos.ix + i+1][ptLastPos.iy + j]&WEST)))
               ) {

                if ((bMaze[ptSelf.Pos.ix + i][ptSelf.Pos.iy + j]&EAST)||
                    (bMaze[ptSelf.Pos.ix + i+1][ptSelf.Pos.iy + j]&WEST)
                   ) {

                    p[cPoint].x = (i+3)*Step;
                    p[cPoint].y = (j+2)*Step;
                    bType[cPoint++] = PT_MOVETO;
                    p[cPoint].x = (i+3)*Step;
                    p[cPoint].y = (j+3)*Step;
                    bType[cPoint++] = PT_LINETO;
                    }
                }
            }
        }

    PolyDraw95(hDC,p,bType,cPoint);

    //
    // next, we draw white lines over BLACK lines drawn but no longer
    // needed.
    //
    SelectObject(hDC,hWhitePen);
    cPoint = 0;

    for (i =-3;i<=2; i++) {
        for (j= -3; j<=2; j++) {
            if ((!bRedraw) &&
               ((bMaze[ptLastPos.ix + i][ptLastPos.iy + j]&SOUTH)||
               (bMaze[ptLastPos.ix + i][ptLastPos.iy + j+1]&NORTH))
              ) {

                if (!((bMaze[ptSelf.Pos.ix + i][ptSelf.Pos.iy + j]&SOUTH)||
                   (bMaze[ptSelf.Pos.ix + i][ptSelf.Pos.iy + j+1]&NORTH))
                  ) {

                    p[cPoint].x = (i+2)*Step+iPenWidth;
                    p[cPoint].y = (j+3)*Step;
                    bType[cPoint++] = PT_MOVETO;
                    p[cPoint].x = (i+3)*Step-iPenWidth;
                    p[cPoint].y = (j+3)*Step;
                    bType[cPoint++] = PT_LINETO;
                    //
                    // We also need to kludge to clean up until we get a square pen.
                    //
                    p[cPoint].x = (i+2)*Step+iPenWidth;
                    p[cPoint].y = (j+3)*Step-iPenWidth;
                    bType[cPoint++] = PT_MOVETO;
                    p[cPoint].x = (i+2)*Step+iPenWidth;
                    p[cPoint].y = (j+3)*Step+iPenWidth;
                    bType[cPoint++] = PT_LINETO;
                    p[cPoint].x = (i+3)*Step-iPenWidth;
                    p[cPoint].y = (j+3)*Step-iPenWidth;
                    bType[cPoint++] = PT_MOVETO;
                    p[cPoint].x = (i+3)*Step-iPenWidth;
                    p[cPoint].y = (j+3)*Step+iPenWidth;
                    bType[cPoint++] = PT_LINETO;
                    //
                    // End kludge BUGBUG
                    //
                    }
                }
            if ((!bRedraw) &&
                ((bMaze[ptLastPos.ix + i][ptLastPos.iy + j]&EAST)||
                (bMaze[ptLastPos.ix + i+1][ptLastPos.iy + j]&WEST))
               ) {

                if (!((bMaze[ptSelf.Pos.ix + i][ptSelf.Pos.iy + j]&EAST)||
                    (bMaze[ptSelf.Pos.ix + i+1][ptSelf.Pos.iy + j]&WEST))
                   ) {

                    p[cPoint].x = (i+3)*Step;
                    p[cPoint].y = (j+2)*Step+iPenWidth;
                    bType[cPoint++] = PT_MOVETO;
                    p[cPoint].x = (i+3)*Step;
                    p[cPoint].y = (j+3)*Step-iPenWidth;
                    bType[cPoint++] = PT_LINETO;
                    //
                    // We also need to kludge to clean up until we get a square pen.
                    //
                    p[cPoint].x = (i+3)*Step-iPenWidth;
                    p[cPoint].y = (j+2)*Step+iPenWidth;
                    bType[cPoint++] = PT_MOVETO;
                    p[cPoint].x = (i+3)*Step+iPenWidth;
                    p[cPoint].y = (j+2)*Step+iPenWidth;
                    bType[cPoint++] = PT_LINETO;
                    p[cPoint].x = (i+3)*Step-iPenWidth;
                    p[cPoint].y = (j+3)*Step-iPenWidth;
                    bType[cPoint++] = PT_MOVETO;
                    p[cPoint].x = (i+3)*Step+iPenWidth;
                    p[cPoint].y = (j+3)*Step-iPenWidth;
                    bType[cPoint++] = PT_LINETO;
                    //
                    // End kludge BUGBUG
                    //
                    }
                }
            };
        }

    PolyDraw95(hDC,p,bType,cPoint);

    SelectObject(hDC,hPenOld);
    DeleteObject(hBlackPen);
    DeleteObject(hWhitePen);

    //
    // And drawn an arrow for our ptSelf.Pos.Facing
    //
    x = y = (Step * 10) / 4;    // the center
    Step /= 4;

    if (ptLastPos.Facing != ptSelf.Pos.Facing) {
        if (hWhitePen != NULL) {
            DeleteObject(hWhitePen);
        }
        hWhitePen = CreatePen(PS_SOLID,1,0x00FFFFFF);
        hPenOld = SelectObject(hDC,hWhitePen);
        cPoint = 0;

        p[cPoint].x = p[cPoint+4].x = x;
        p[cPoint].y = p[cPoint+4].y = y;
        bType[cPoint+4] = PT_LINETO;
        bType[cPoint++] = PT_MOVETO;

        switch (ptLastPos.Facing) {
            case NORTH:
                p[cPoint].x = x+Step;
                p[cPoint].y = y;
                bType[cPoint++] = PT_LINETO;
                p[cPoint].x = x;
                p[cPoint].y = y-Step;
                bType[cPoint++] = PT_LINETO;
                p[cPoint].x = x-Step;
                p[cPoint].y = y;
                bType[cPoint++] = PT_LINETO;
                break;
            case SOUTH:
                p[cPoint].x = x+Step;
                p[cPoint].y = y;
                bType[cPoint++] = PT_LINETO;
                p[cPoint].x = x;
                p[cPoint].y = y+Step;
                bType[cPoint++] = PT_LINETO;
                p[cPoint].x = x-Step;
                p[cPoint].y = y;
                bType[cPoint++] = PT_LINETO;
                break;
            case EAST:
                p[cPoint].x = x;
                p[cPoint].y = y+Step;
                bType[cPoint++] = PT_LINETO;
                p[cPoint].x = x+Step;
                p[cPoint].y = y;
                bType[cPoint++] = PT_LINETO;
                p[cPoint].x = x;
                p[cPoint].y = y-Step;
                bType[cPoint++] = PT_LINETO;
                break;
            case WEST:
                p[cPoint].x = x;
                p[cPoint].y = y+Step;
                bType[cPoint++] = PT_LINETO;
                p[cPoint].x = x-Step;
                p[cPoint].y = y;
                bType[cPoint++] = PT_LINETO;
                p[cPoint].x = x;
                p[cPoint].y = y-Step;
                bType[cPoint++] = PT_LINETO;
                break;
            }

        cPoint++;
        PolyDraw95(hDC,p,bType,cPoint);
        SelectObject(hDC,hPenOld);
        DeleteObject(hWhitePen);
        }

    cPoint = 0;

    p[cPoint].x = p[cPoint+4].x = x;
    p[cPoint].y = p[cPoint+4].y = y;
    bType[cPoint+4] = PT_LINETO;
    bType[cPoint++] = PT_MOVETO;

    switch (ptSelf.Pos.Facing) {
        case NORTH:
            p[cPoint].x = x+Step;
            p[cPoint].y = y;
            bType[cPoint++] = PT_LINETO;
            p[cPoint].x = x;
            p[cPoint].y = y-Step;
            bType[cPoint++] = PT_LINETO;
            p[cPoint].x = x-Step;
            p[cPoint].y = y;
            bType[cPoint++] = PT_LINETO;
            break;
        case SOUTH:
            p[cPoint].x = x+Step;
            p[cPoint].y = y;
            bType[cPoint++] = PT_LINETO;
            p[cPoint].x = x;
            p[cPoint].y = y+Step;
            bType[cPoint++] = PT_LINETO;
            p[cPoint].x = x-Step;
            p[cPoint].y = y;
            bType[cPoint++] = PT_LINETO;
            break;
        case EAST:
            p[cPoint].x = x;
            p[cPoint].y = y+Step;
            bType[cPoint++] = PT_LINETO;
            p[cPoint].x = x+Step;
            p[cPoint].y = y;
            bType[cPoint++] = PT_LINETO;
            p[cPoint].x = x;
            p[cPoint].y = y-Step;
            bType[cPoint++] = PT_LINETO;
            break;
        case WEST:
            p[cPoint].x = x;
            p[cPoint].y = y+Step;
            bType[cPoint++] = PT_LINETO;
            p[cPoint].x = x-Step;
            p[cPoint].y = y;
            bType[cPoint++] = PT_LINETO;
            p[cPoint].x = x;
            p[cPoint].y = y-Step;
            bType[cPoint++] = PT_LINETO;
            break;
        }

    cPoint++;
    PolyDraw95(hDC,p,bType,cPoint);

    GlobalUnlock(hBMem);
    GlobalUnlock(hPMem);
    GlobalFree(hBMem);
    GlobalFree(hPMem);
}

/*=====================================================================
Function: DrawPlayers()

Inputs: DC, pointer to list of players, rectangle being updated on scrn

Outputs: none

Abstract:
    BUGBUG -- make an abstract for this.
======================================================================*/

void DrawPlayers(
    HDC hDC,
    PlayerType FAR *ptPlyr,
    LPRECT rUpd
    )
{
    LPRECT rHld;
    PlayerType FAR *ptTrav;
    HGLOBAL hMem;

    if (rUpd == NULL) {
        hMem = GlobalAlloc(GHND,sizeof(RECT));
        rHld = (LPRECT) GlobalLock(hMem);
        if (rHld == NULL) {
            MessageBox((HWND) NULL,GetStringRes(IDS_RECTALLOCFAIL),"DrawPlayers",MB_APPLMODAL);
            }
/******
        rHld->right = rMaze.left;
        rHld->left = rMaze.right;
        rHld->top = rMaze.bottom;
        rHld->bottom = rMaze.top;
******/
*rHld = rMaze;
        }
    else {
        rHld = rUpd;
        }

    ptTrav = ptPlyr;
    while (ptTrav->next != NULL) {
        ptTrav = ptTrav->next;
        if (ptTrav->Drawn) {
            rHld->left = LESSEROF(rHld->left,ptTrav->rDrawn.left);
            rHld->right = GREATEROF(rHld->right,ptTrav->rDrawn.right);
            rHld->top = LESSEROF(rHld->top,ptTrav->rDrawn.top);
            rHld->bottom = GREATEROF(rHld->bottom,ptTrav->rDrawn.bottom);
            }
        }

    PostMessage(hWndMaze,WM_COMMAND,IDM_REDRAW,(DWORD) rHld);
}


DRONES.C

/***********************************************************************
File:   Drones.c

Abstract:

    This file contains the functions which are associated with moving and
    drawing drones.

Contents:

    DeletePlayer() -- Free a record of type PlayerType
    NewPlayer() -- Create & initialize a record of type PlayerType
    RandRange() -- return a random number in a given range
    InitDrones() -- Add/remove drone entries from the list of drones
    MoveDrone() -- Timer callback to change drone positions in data struct

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

#include <stdlib.h>
#include "winmaze.h"
#include "mazproto.h"
#include <mmsystem.h>

/*=====================================================================
Function: DeletePlayer()

Inputs: Pointer to record to delete

Outputs: Returns pointer to next record in list

Abstract:
    Returns pointer to next element to facilitate maintenance of
    linked lists
======================================================================*/

PlayerType FAR *DeletePlayer(
    PlayerType FAR *ptTrav
    )
{
    PlayerType FAR *ptHold;
    HGLOBAL hMem;

    ptHold = NULL;
    if (ptTrav != NULL) {
        ptHold = ptTrav->next;
        hMem = (HGLOBAL) GlobalHandle(SELECTOROF( ptTrav));
        GlobalUnlock(hMem);
        GlobalFree(hMem);
        }

    return(ptHold);
}

/*=====================================================================
Function: NewPlayer()

Inputs: Data elements of PlayerType element to be created

Outputs: Pointer to newly created & initialize PlayerType record

Abstract:
    Needs no explanation
======================================================================*/

PlayerType FAR *NewPlayer(
    unsigned long ulID,     // Unique identifier for this player struct
    DWORD dwPID,            // process id of window
    LPSTR cUserName,        // Name of the player
    LPSTR cComputerName,    // Name of the computer player is on
    int ix,                 // Maze X- coordinate of player
    int iy,                 // Maze Y- coordinate of player
    BYTE Facing,            // Facing of player in maze
    int iScore,             // Points player is reporting
    int iPicNum,            // Picture number to use for player
    int iGridNum,           // Home grid #
    int iGridxPos,          // Home grid x-position (in grid coordinates
    int iGridyPos,          // Home grid y-position (in grid coordinates
    PlayerType FAR *next    // for linked list maintenance
    )
{
    PlayerType FAR *ptRet;
    HGLOBAL hMem;

    hMem = GlobalAlloc(GHND,sizeof(PlayerType));
    ptRet = (PlayerType FAR *) GlobalLock(hMem);

    if (ptRet == NULL) {
        MessageBox((HWND) NULL,GetStringRes(IDS_MALLOCFAILABT),"NewPlayer",
               MB_APPLMODAL|MB_ICONEXCLAMATION);
        PostMessage(hWndMain,WM_CLOSE,0,0);
        }
    else {
        ptRet->ulID = ulID;
        ptRet->dwPID = dwPID;
        lstrcpy(ptRet->cUserName,cUserName);
        lstrcpy(ptRet->cComputerName,cComputerName);
        ptRet->Pos.ix = ix;
        ptRet->Pos.iy = iy;
        ptRet->Pos.Facing = Facing;
        ptRet->Drawn = FALSE;
        ptRet->iScore = iScore;
        ptRet->iPicNum = iPicNum;
        ptRet->iGridNum = iGridNum;
        ptRet->pGridLoc.x = iGridxPos;
        ptRet->pGridLoc.y = iGridyPos;

        ptRet->next = next;
        }

    return(ptRet);
}

/*=====================================================================
Function: RandRange()

Inputs: Low, high end of range return value should be in

Outputs: returns number between iLow and iHigh inclusive

Abstract:
    Since this is using rand() it is not so random for a new process.
======================================================================*/

int RandRange(
    int iLow,
    int iHigh
    )
{
    float f;

    f = ((float) rand())/((float) RAND_MAX);

    return((int) (iLow+f*(iHigh - iLow + 1)));
}

/*=====================================================================
Function: InitDrones()

Inputs: Globals only

Outputs: none

Abstract:
    Performs PART of drone initialization whenever # of drones or their
    speed is changed. This will add or delete drones from the drone list,
    as required.
======================================================================*/

void InitDrones(
    )
{
    int i,iCurNumDrones;
    BYTE b;
    PlayerType FAR *ptTrav;
    char buff[20];

    b = NORTH;

    //
    // If the number of drones has changed, but drones were
    // already active, we need to take a different course of
    // action, ie add or delete drones from active drones,
    // rather than creating completely new ones.
    //

    //
    // Find number of drones currently active.
    //
    iCurNumDrones = 0;
    ptTrav = &ptDrones;
    while (ptTrav->next != NULL) {
        iCurNumDrones++;
        ptTrav = ptTrav->next;
        }

    //
    // read in/ remove the drone bitmaps, as necessary
    //
    if (iNumDrones > 0) {
        AddPic(PIC_DRONE);
        }
    else {
        DelPic(PIC_DRONE);
        }

    //
    // If more drones have been added, we need to create them
    //
    for(i=iCurNumDrones;i<iNumDrones;i++) {
        sprintf(buff,GetStringRes(IDS_FMT_DRONE),i+1);
        ptTrav->next = NewPlayer(i,i,buff,ptSelf.cComputerName,
                    RandRange(5*X_CELLS_PER_SUBGRID,8*X_CELLS_PER_SUBGRID-1),
                    RandRange(7*Y_CELLS_PER_SUBGRID,8*Y_CELLS_PER_SUBGRID-1),
                    b,0,PIC_DRONE,0,0,0,ptTrav->next);
        b = (BYTE) ((b*2 > 0x8) ? 0x1 : b*2);
        }

    //
    // Or, delete drones as needed
    //
    for(i=iNumDrones; i < iCurNumDrones;i++) {
        ptDrones.next = DeletePlayer(ptDrones.next);
        }

    return;
}

/*=====================================================================
Function: MoveDrone()

Inputs: Ignored except for hWnd, handle to current window

Outputs:returns success/failure

Abstract:
    This is a timer callback function which modifies the position of the
    drones in their data structures. After their positions have been changed
    in their record, a message is sent to the 3-d window telling it that
    drones need to be redrawn.
======================================================================*/

LRESULT CALLBACK MoveDrone(
    HWND hWnd,
    UINT wMsg,
    UINT idTimer,
    DWORD dwTime
    )
{
    int x,y;
    BYTE b1,b2,dForward,dBack,dLeft,dRight;
    BOOL blocked;
    PlayerType FAR *ptTrav;
    PositionType ptPos;
    char cBuff[132];
    LPRECT rUpd;
    HANDLE hMem;
    static DWORD dwCallCount=0;

    dwCallCount++;

    //
    // If we're in demo mode, use this callback to move US!
    //
    if (bDemoMode) {

        x=ptSelf.Pos.ix;
        y=ptSelf.Pos.iy;
        dForward = ptSelf.Pos.Facing;
        dBack = BACK_TO_ABS(dForward);
        dLeft = LEFT_TO_ABS(dForward);
        dRight = RIGHT_TO_ABS(dForward);
        b1 = bMaze[x][y];
        b2 = bMaze[ADJ_X(x,dForward)][ADJ_Y(y,dForward)];
        blocked = FALSE;
        if ((b1&dForward)||(b2&dBack)) {
            blocked = TRUE;
            b2 = bMaze[ADJ_X(x,dRight)][ADJ_Y(y,dRight)];
            if ((b1&dLeft)||(b2&dRight)) {
                b2 = bMaze[ADJ_X(x,dLeft)][ADJ_Y(y,dLeft)];
                if ((b1&dLeft)||(b2&dRight)) {
                    x = RIGHT;
                    }
                else {
                    x = LEFT;
                    }
                }
            else {
                x = RIGHT;
                }
            }
        else {
            x = FORWARD;
            }

        y = RandRange(1,11);

        switch (y) {
            case 8:
                x = LEFT;
                break;
            case 9:
                x = RIGHT;
                break;
            case 10:
                x = FORWARD;
                break;
            case 11:
                x = BACK;
                break;
            }

        switch (x) {
            case LEFT:
                PostMessage(hWndMaze,WM_KEYDOWN,VK_LEFT,0);
                break;
            case RIGHT:
                PostMessage(hWndMaze,WM_KEYDOWN,VK_RIGHT,0);
                break;
            case FORWARD:
                PostMessage(hWndMaze,WM_KEYDOWN,VK_UP,0);
                break;
            }

        if ((RandRange(1,3) == 3)&&(!blocked)) {
            PostMessage(hWndMaze,WM_KEYDOWN,VK_SPACE,0);
            }
        }

    hMem = GlobalAlloc(GHND,sizeof(RECT));
    rUpd = (LPRECT) GlobalLock(hMem);
    if (rUpd == NULL) {
        MessageBox((HWND)NULL,GetStringRes(IDS_RECTALLOCFAIL),"MoveDrone",
               MB_APPLMODAL);
        PostMessage(hWndMain,WM_CLOSE,0,0);
        }

    rUpd->right = rMaze.left;
    rUpd->left = rMaze.right;
    rUpd->top = rMaze.bottom;
    rUpd->bottom = rMaze.top;

    ptTrav = &ptDrones;
    while ((ptTrav->next != NULL)&&(!GamePaused)) {
        //
        // Only use every 2nd drone move in demo mode
        //
        if (bDemoMode && (dwCallCount % 2)) {
            break;
            }

        ptTrav = ptTrav->next;

        if (ptTrav->Drawn) {
            rUpd->right = (rUpd->right > ptTrav->rDrawn.right) ? rUpd->right : ptTrav->rDrawn.right;
            rUpd->left = (rUpd->left < ptTrav->rDrawn.left) ? rUpd->left : ptTrav->rDrawn.left;
            rUpd->top = (rUpd->top < ptTrav->rDrawn.top) ? rUpd->top : ptTrav->rDrawn.top;
            rUpd->bottom = (rUpd->bottom > ptTrav->rDrawn.bottom) ? rUpd->bottom : ptTrav->rDrawn.bottom;
            ptTrav->Drawn=FALSE;
            }
        x=ptTrav->Pos.ix;
        y=ptTrav->Pos.iy;
        dForward = ptTrav->Pos.Facing;
        dBack = BACK_TO_ABS(dForward);
        dLeft = LEFT_TO_ABS(dForward);
        dRight = RIGHT_TO_ABS(dForward);
        ptPos.ix = x;
        ptPos.iy = y;
        ptPos.Facing = dForward;
        b1 = bMaze[x][y];
        b2 = bMaze[ADJ_X(x,dForward)][ADJ_Y(y,dForward)];
        if ((b1&dForward)||(b2&dBack)) {
            b2 = bMaze[ADJ_X(x,dLeft)][ADJ_Y(y,dLeft)];
            if ((b1&dLeft)||(b2&dRight)) {
                b2 = bMaze[ADJ_X(x,dRight)][ADJ_Y(y,dRight)];
                if ((b1&dRight)||(b2&dLeft)) {
                    ptPos.Facing = LEFT_TO_ABS(ptTrav->Pos.Facing);
                    }
                else {
                    ptPos.Facing = RIGHT_TO_ABS(ptTrav->Pos.Facing);
                    }
                }
            else {
                ptPos.Facing = LEFT_TO_ABS(ptTrav->Pos.Facing);
                }
            }
        else {
            if (RandRange(1,10) == 1) {
                ptPos.Facing = LEFT_TO_ABS(ptTrav->Pos.Facing);
                }
            else {
                if (RandRange(1,10) == 1) {
                    ptPos.Facing = RIGHT_TO_ABS(ptTrav->Pos.Facing);
                    }
                else {
                    ptPos.ix = ADJ_X(ptTrav->Pos.ix,dForward);
                    ptPos.iy = ADJ_Y(ptTrav->Pos.iy,dForward);
                    }
                }
            }

        ptTrav->Pos = ptPos;

        //
        // If the drone has homed in on us, we are dead!
        //
        if ((!InSanctuary(&ptSelf.Pos))&&
            (ptSelf.Pos.ix == ptPos.ix)&&(ptSelf.Pos.iy == ptPos.iy)
           ) {
            sprintf(cBuff,GetStringRes(IDS_FMT_RUNDOWN),ptTrav->cUserName);
            PrintTextLine(cBuff);
            ptSelf.iScore -=(iDroneSpeed) ? 36/iDroneSpeed/iDroneSpeed : 72;
            KillSelf();
            SendNetMessage(0,0,NULL,NP_SCORE);
            iKilledByDrones++;
            break;
            }
        }

    PostMessage(hWndMaze,WM_COMMAND,IDM_DRAWDRONES,(DWORD) rUpd);

    return(0);
}


INITMAZE.C

/***********************************************************************
File:   InitMaze.c

Abstract:

    This module contains code to initialize the full maze, as well as the
    data structures associated with it.

Contents:

    InitMaze() -- Inserts subgrids into the Maze array, sets player position
    Calc3DMaze() -- Calculates posts used for drawing 3-d view
    InsertSubGrid() -- copies a sub-grid into the maze

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

#include "winmaze.h"
#include "mazproto.h"
#include "math.h"

/*=====================================================================
Function: InitMaze()

Inputs: none

Outputs: none

Abstract:
    InitMaze is responsible for initializing the full maze to blocked
    characters, and setting up the temple space.
======================================================================*/

void InitMaze(
    void
    )
{
    int i,j;
    BYTE b;

    //
    // Initialize the entire maze to being filled already.
    // we'll leave the outer subgrid ring as a buffer.
    //
    b = NORTH | WEST | SOUTH | EAST;

    for(i=0;i<X_SIZE;i++) {
        for (j=0;j<Y_SIZE;j++) {
            bMaze[i][j] = b;
            }
        }

    InsertSubGrid(NUM_PLAYER_SUBGRIDS,6,6);
    InsertSubGrid(NUM_PLAYER_SUBGRIDS+1,7,6);
    InsertSubGrid(NUM_PLAYER_SUBGRIDS+2,5,7);
    InsertSubGrid(NUM_PLAYER_SUBGRIDS+3,6,7);
    InsertSubGrid(NUM_PLAYER_SUBGRIDS+4,7,7);
    InsertSubGrid(NUM_PLAYER_SUBGRIDS+5,6,8);
    InsertSubGrid(NUM_PLAYER_SUBGRIDS+6,7,8);

    //
    // Kludge to start new players out in the sanctuary
    //
    ptSelf.Pos.ix = 6*X_CELLS_PER_SUBGRID+1;
    ptSelf.Pos.iy = 7*Y_CELLS_PER_SUBGRID+1;
    ptSelf.Pos.Facing = NORTH;
    ptLastPos = ptSelf.Pos;

    return;
}

/*=====================================================================
Function: Calc3DMaze()

Inputs: none

Outputs: none

Abstract:
    Calc maze is responsible for recalculating the maze drawing info.
    3-d transformations are done only when the window is resized. The
    results are stored in a table (giving the polygons for each panel, from
    left to right in 2-d coordinates.
======================================================================*/

void Calc3DMaze(
     )
{
    int i,j,k,width,depth;
    float pw, ph,x[3],y[3], dx, dyh, dyl, dz, dist;
    POINT pCenter;
    float m,b;

    SetCursor(LoadCursor((HINSTANCE)NULL,IDC_WAIT));

    pw = (float) PANEL_WIDTH;
    ph = (float) PANEL_HEIGHT;

    //
    // establish the scale. Should be sufficient to allow the end of the panel
    // we're in to show.  A full panel displayed 1/2 panel away should be 2/3
    // of the screen wide.
    //
    width = (rMaze.right - rMaze.left);
    depth = (rMaze.bottom - rMaze.top);

    //
    // Since scale is used in MC_TO_SC, we need to nullify its effect to set it
    //
    scale = (float) 1.0;
    scale = (float) (.9 * depth/((float)MC_TO_SC(ph,-pw/2)));

    pCenter.x = rMaze.left + width/2;
    pCenter.y = rMaze.top + depth/2;

    //
    // x and y center
    //
    dyl = ph /2;
    dyh = - dyl;

    for(i=0;i<(MAX_DRAW_DIST*2+2);i++) {
        for (j=1;j<(MAX_DRAW_DIST+2);j++) {

            //
            // Calculate post in left position
            //

            dx = (i - (MAX_DRAW_DIST+1)) * pw - pw /2;
            dz = (j-1)*pw - pw/2;

            //
            // left top
            //
            dist = dz;

            pPost[i][j][0].x = pCenter.x + (int) MC_TO_SC(dx,dist);
            pPost[i][j][0].y = pCenter.y + (int) MC_TO_SC(dyh,dist);

            //
            // left bottom
            //
            dist = dz;
            pPost[i][j][1].x = pCenter.x + (int) MC_TO_SC(dx,dist);
            pPost[i][j][1].y = pCenter.y + (int) MC_TO_SC(dyl,dist);

            }
        }

    //
    // Clip as necessary, using line equation: y=mx + b.
    //
    for (i=0,j=0;i<(MAX_DRAW_DIST*2+2);i++) {
        for (k=0;k<2;k++) {
            x[1] = (float) pPost[i][1][k].x;
            x[2] = (float) pPost[i][2][k].x;
            y[1] = (float) pPost[i][1][k].y;
            y[2] = (float) pPost[i][2][k].y;
            m = (y[2] - y[1])/(x[2]-x[1]);
            b = y[1] - m*x[1];
            if (i < MAX_DRAW_DIST+2) {
                pPost[i][0][k].x = rMaze.left;
                }
            else {
                pPost[i][0][k].x = rMaze.right;
                }
            pPost[i][0][k].y = (int)(m*((float)pPost[i][0][k].x) + b);
            }
        }

    SetCursor(LoadCursor((HINSTANCE)NULL,IDC_ARROW));

    return;
}

/*=====================================================================
Function: InsertSubGrid()

Inputs: # of subgrid, x and y position in maze to insert the subgrid

Outputs: none

Abstract:
    Insert Subgrid will copy the specified subgrid into bMaze, with its upper
    left corner at iXPos*X_CELLS_PER_SUBGRID, iYPos*Y_CELLS_PER_SUBGRID.
======================================================================*/

void InsertSubGrid(
    int SubGridNo,
    int iXPos,
    int iYPos
    )
{
    int i,j;

    iXPos = iXPos*X_CELLS_PER_SUBGRID;
    iYPos = iYPos*Y_CELLS_PER_SUBGRID;

    for (i=0;i<X_CELLS_PER_SUBGRID;i++) {
        for (j=0;j<Y_CELLS_PER_SUBGRID;j++) {
            bMaze[i+iXPos][j+iYPos] = SubGrids[SubGridNo].Cell[i][j];
            }
        }

    return;
}


MAZE.C

/***********************************************************************
File:   Maze.c

Abstract:

    This is the main module for MazeLords. It Contains WinMain, as well as the
    WndProcs for the main modules.

Contents:

    InSanctuary() -- determines whether a maze position is in the Sanctuary
    WinMain() -- Main windows entrypoint
    WndProc() -- Main window processing entrypoint
    RegisterClasses() -- Register different window classes
    OneTimeInit() -- Initialization done first time window created
    EachTimeInit() -- Initialization done each new game
    OneTimeShutdown() -- cleanup done last time window destroyed
    EachTimeShutdown() -- cleanup done each time the game is STOPped.

Revision History:

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

#define FULL_REDRAW 200
#define IN_MAIN
#include "winmaze.h"
#include "mazproto.h"
#include "net.h"

int ngCmdShow;
HDC hHoldDC;

/*=====================================================================
Function: InSanctuary()

Inputs: Position

Outputs: TRUE if position in Sanctuary, else FALSE

Abstract:
    This function is used to determine if a given position is inside
    the sanctuary. If it is, then combat isn't allowed from/to that
    position, with the exception of shooting drones from outside the
    sanctuary.
======================================================================*/

BOOL InSanctuary(
    PositionType FAR *Pos
    )
{
    BOOL bRet;
    int iLeft,iTop;

    iLeft = 6*X_CELLS_PER_SUBGRID+1;
    iTop = 7*Y_CELLS_PER_SUBGRID+1;

    bRet = ((Pos->ix >= iLeft)&&(Pos->ix <= iLeft+2));
    bRet = bRet&&((Pos->iy >= iTop)&&(Pos->iy <= iTop+2));

    return(bRet);
}

/*=====================================================================
Function: WinMain()

Inputs: Standard winmain inputs

Outputs:success

Abstract:
    This is the WinMain for MazeLords. It is responsible for, among
    other things, registering the windows classes, and allocating some
    of the larger dynamic data structures.
======================================================================*/

int PASCAL WinMain(
    HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPSTR lpszCmdLine,
    int nCmdShow
    )
{
    MSG     msg;
    int     i,j,nRc;
    HGLOBAL hMem;

    //
    // DYNAMIC ALLOCATION OF GLOBAL VARS
    //
    hMem = GlobalAlloc(GHND,(MAX_DRAW_DIST*2+2+1) * sizeof(LPPOINT FAR *));
    pPost = (LPPOINT FAR * FAR *) GlobalLock(hMem);
    for(i=0;i<(MAX_DRAW_DIST*2+2+1);i++) {
        hMem = GlobalAlloc(GHND,(MAX_DRAW_DIST+2+1)*sizeof(LPPOINT));
        pPost[i] = (LPPOINT FAR *) GlobalLock(hMem);
        for(j=0;j<(MAX_DRAW_DIST+2+1);j++) {
            hMem = GlobalAlloc(GHND,2*sizeof(POINT));
            pPost[i][j] = (LPPOINT) GlobalLock(hMem);
            }
        }

    hMem = GlobalAlloc(GHND,NUM_PICS * sizeof(FullPicType FAR *));
    PreStretch = (FullPicType FAR * FAR *) GlobalLock(hMem);
    for(i=0;i<NUM_PICS;i++) {
        hMem = GlobalAlloc(GHND,MAX_DRAW_DIST * sizeof(FullPicType));
        PreStretch[i] = (FullPicType FAR *) GlobalLock(hMem);
        }

    hMem = GlobalAlloc(GHND,X_SIZE * sizeof(BYTE FAR *));
    bMaze = (BYTE FAR * FAR *) GlobalLock(hMem);
    for(i=0;i<X_SIZE;i++) {
        hMem = GlobalAlloc(GHND,Y_SIZE * sizeof(BYTE));
        bMaze[i] = (LPBYTE) GlobalLock(hMem);
        }

    hMem = GlobalAlloc(GHND,X_SUBGRIDS_PER_GRID * sizeof(SubGridType FAR *));
    Grid = (SubGridType FAR * FAR *) GlobalLock(hMem);
    for(i=0;i<X_SUBGRIDS_PER_GRID;i++) {
        hMem = GlobalAlloc(GHND,Y_SUBGRIDS_PER_GRID * sizeof(SubGridType));
        Grid[i] = (SubGridType FAR *) GlobalLock(hMem);
        }

    hMem = GlobalAlloc(GHND,NUM_SUBGRIDS * sizeof(SubGridCellsType));
    SubGrids = (SubGridCellsType FAR *) GlobalLock(hMem);

    lstrcpy(szAppName, "MAZE");
    hInst = hInstance;

    //
    // We need to register the different windows and child
    // windows we'll be using if this is the first instance
    // of MazeLords.
    //
    if(!hPrevInstance) {
      if ((nRc = RegisterClasses()) == -1) {
        LoadString(hInst, IDS_ERR_REGISTER_CLASS, szString, sizeof(szString));
        MessageBox((HWND)NULL, szString, (LPSTR)NULL, MB_ICONEXCLAMATION);
        return nRc;
        }
      }

    //
    // Display the Main window for MazeLords
    //
    hWndMain = CreateWindow(
                  szAppName,
                  "Maze Lords",
                  WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX|WS_MAXIMIZEBOX |
                             WS_THICKFRAME|WS_CLIPCHILDREN|WS_OVERLAPPED,
                  CW_USEDEFAULT, 0,
                  CW_USEDEFAULT, 0,
                  (HWND)NULL,
                  (HMENU)NULL,
                  hInst,
                  NULL);

    OneTimeInit();

    if(hWndMain == (HWND)NULL){
        LoadString(hInst, IDS_ERR_CREATE_WINDOW, szString, sizeof(szString));
        MessageBox((HWND)NULL, szString, "WinMain", MB_ICONEXCLAMATION);
        return IDS_ERR_CREATE_WINDOW;
        }

    ShowWindow(hWndMain, nCmdShow);
    ngCmdShow = nCmdShow;

    while(GetMessage(&msg, (HWND)NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
        }

    UnRegisterClasses();
    return msg.wParam;
}

/*=====================================================================
Function: Wndproc()

Inputs: Standard windows message processing entrypoint parms

Outputs:returns Success

Abstract:
    This is the window procedure for the main window. It takes care of
    spawning and destroying child windows, interpreting menu-item
    command messages, and most of the none-movement keystrokes.
======================================================================*/

LONG FAR PASCAL WndProc(
    HWND hWnd,
    UINT Message,
    WPARAM wParam,
    LPARAM lParam
    )
{
    HMENU           hMenu=0;
    HBITMAP         hBitmap=0;
    HDC             hDC,hBMDC;
    PAINTSTRUCT     ps;
    int             nRc=0;
    INT             i,j,MazexDim,MazeyDim,TopxDim,TopyDim;
    UINT            uCmdId,uCmdCmd;
    HWND            hCmd;

    switch (Message) {

        case WM_TIMER:
            if ((!UserIntCount)&&(!bDemoMode)&&(bDemoEnable)) {
                StartDemo();
                }
            UserIntCount = 0;
            break;

        case WM_INITMENU:
            if (bDemoMode) {
                StopDemo();
                }
            UserIntCount++;
            return DefWindowProc(hWnd, Message, wParam, lParam);

        case WM_COMMAND:
            uCmdId = GET_WM_COMMAND_ID(wParam,lParam);
            uCmdCmd = GET_WM_COMMAND_CMD(wParam,lParam);
            hCmd = GET_WM_COMMAND_HWND(wParam,lParam);
            hMenu = GetMenu(hWnd);

            switch (uCmdId) {

                case IDT_NEWMAIL:
                    ProcessNewMail();
                    break;

                case IDM_F_HIGHSCORES:
                    bDemoEnable = FALSE;
                    MessageBox((HWND)NULL,GetStringRes(IDS_HSCONOTIMPL),
GetStringRes2(IDS_HSCO),  MB_ICONHAND | MB_APPLMODAL);
                    bDemoEnable = TRUE;
                    break;

                case IDM_F_NEWGAME:

                    bDemoEnable = FALSE;

                    SetCursor(LoadCursor((HINSTANCE)NULL,IDC_WAIT));

                    EachTimeInit();

                    GetClientRect(hWndMain,&rMain);

                    MazexDim = (rMain.right - rMain.left)/2;
                    MazeyDim = (rMain.bottom - rMain.top)*2/3;
                    MazexDim = MazeyDim = (MazexDim < MazeyDim) ? MazexDim : MazeyDim;

                    TopxDim = (rMain.right - rMain.left)/4;
                    TopyDim = (rMain.bottom - rMain.top)/3;
                    TopxDim = TopyDim = (TopxDim < TopyDim) ? TopxDim : TopyDim;
                    rTopView.right = rMain.right - 10;
                    rTopView.bottom = rMain.bottom - 10;
                    rTopView.left = rTopView.right - TopxDim;
                    rTopView.top = rTopView.bottom - TopyDim;

                    rText.left = rMain.left + 10;
                    rText.right = rMain.right - 10 - TopxDim - 10;
                    rText.top = rMain.top + 10 + MazeyDim + 10;
                    rText.bottom = rMain.bottom-10;

                    rScore.left = rMain.left + 10 + MazexDim +10;
                    rScore.right = rMain.right - 10;
                    rScore.top = rMain.top + 10;
                    rScore.bottom = rMain.bottom - 10 - TopyDim - 10;

                    //
                    // create the 3-d view window
                    //
                    rMaze.left = 0;rMaze.right = 0;
                    rMaze.right = MazexDim;
                    rMaze.bottom = MazeyDim;

                    InitMaze();
                    Calc3DMaze();
                    InitNetwork();

                    rMaze.left = rMain.left+10;
                    rMaze.top = rMain.top +10;
                    rMaze.right = rMaze.left + MazexDim;
                    rMaze.bottom = rMaze.top + MazeyDim;

                    hWndMaze = CreateWindow((LPSTR) "MazeDrawWindow",
                                            (LPSTR) "",
                                            WS_CHILD | WS_DLGFRAME,
                                            rMaze.left,rMaze.top,
                                            rMaze.right - rMaze.left,
                                            rMaze.bottom - rMaze.top,
                                            hWndMain,
                                            (HMENU) NULL,
                                            hInst,
                                            NULL);

                    if(hWndMaze == (HWND)NULL) {
                        return IDS_ERR_CREATE_WINDOW;
                        }

                    StretchBitmaps();

                    GetClientRect(hWndMaze,&rMaze);
                    ShowWindow(hWndMaze, ngCmdShow);

                    //
                    // Create the top-view window
                    //
                    hWndTopView = CreateWindow((LPSTR) "TopViewWindow",
                                               NULL,
                                               WS_CHILD | WS_DLGFRAME,
                                               rTopView.left,rTopView.top,
                                               rTopView.right - rTopView.left,
                                               rTopView.bottom - rTopView.top,
                                               hWndMain,
                                               (HMENU)NULL,
                                               hInst,
                                               NULL);

                    if(hWndTopView == (HWND)NULL) {
                        LoadString(hInst, IDS_ERR_CREATE_WINDOW, szString, sizeof(szString));
                        MessageBox((HWND)NULL, szString, "Main", MB_ICONEXCLAMATION);
                        return IDS_ERR_CREATE_WINDOW;
                        }

                    GetClientRect(hWndTopView,&rTopView);
                    ShowWindow(hWndTopView, ngCmdShow);

                    //
                    // Create the Text window
                    //
                    hWndText = CreateWindow((LPSTR) "TextWindow",
                                            NULL,
                                            WS_CHILD | WS_DLGFRAME,
                                            rText.left,rText.top,
                                            rText.right - rText.left,
                                            rText.bottom - rText.top,
                                            hWndMain,
                                            (HMENU)NULL,
                                            hInst,
                                            NULL);

                    if(hWndText == (HWND)NULL) {
                        LoadString(hInst, IDS_ERR_CREATE_WINDOW, szString, sizeof(szString));
                        MessageBox((HWND)NULL, szString, "Main", MB_ICONEXCLAMATION);
                        return IDS_ERR_CREATE_WINDOW;
                        }

                    GetClientRect(hWndText,&rText);
                    ShowWindow(hWndText, ngCmdShow);
                    PrintTextLine(GetStringRes(IDS_SANCTUARY));
                    PrintTextLine(GetStringRes(IDS_SAFEZONE));

                    //
                    // Create the Score window
                    //
                    hWndScore = CreateWindow((LPSTR) "ScoreWindow",
                                             NULL,
                                             WS_CHILD | WS_DLGFRAME,
                                             rScore.left,rScore.top,
                                             rScore.right - rScore.left,
                                             rScore.bottom - rScore.top,
                                             hWndMain,
                                             (HMENU) NULL,
                                             hInst,
                                             NULL);

                    if(hWndScore == (HWND)NULL) {
                        LoadString(hInst, IDS_ERR_CREATE_WINDOW, szString, sizeof(szString));
                        MessageBox((HWND)NULL, szString, "Main", MB_ICONEXCLAMATION);
                        return IDS_ERR_CREATE_WINDOW;
                        }

                    GetClientRect(hWndScore,&rScore);
                    ShowWindow(hWndScore, ngCmdShow);

                    //
                    // The game is started. reset the cursor as well.
                    //
                    GameStarted = TRUE;
                    SetCursor(LoadCursor((HINSTANCE)NULL,IDC_ARROW));
                    break;

                case IDM_F_EXIT:
                    SendMessage(hWnd,WM_CLOSE,0,0);
                    break;

                case IDM_O_NET:
                    bNetworked = TRUE;
                    CheckMenuItem(hMenu,IDM_O_NET,MF_CHECKED);
                    CheckMenuItem(hMenu,IDM_O_LOCAL,MF_UNCHECKED);
                    break;

                case IDM_O_LOCAL:
                    bNetworked = FALSE;
                    CheckMenuItem(hMenu,IDM_O_NET,MF_UNCHECKED);
                    CheckMenuItem(hMenu,IDM_O_LOCAL,MF_CHECKED);
                    break;

                case IDM_O_PLAYERSTRETCH:
                    bPlayerPrestretch = !bPlayerPrestretch;
                    CheckMenuItem(hMenu,IDM_O_PLAYERSTRETCH,
                    bPlayerPrestretch?MF_CHECKED:MF_UNCHECKED);
                    if (bPlayerPrestretch) {
                        bDronePrestretch = TRUE;
                        CheckMenuItem(hMenu,IDM_O_DRONESTRETCH,MF_CHECKED);
                        }
                    if (GameStarted) {
                        StretchBitmaps();
                        }
                    break;

                case IDM_O_DRONESTRETCH:
                    bDronePrestretch = !bDronePrestretch;
                    CheckMenuItem(hMenu,IDM_O_DRONESTRETCH,
                              bDronePrestretch?MF_CHECKED:MF_UNCHECKED);
                    if (bPlayerPrestretch) {
                        bDronePrestretch = TRUE;
                        CheckMenuItem(hMenu,IDM_O_DRONESTRETCH,MF_CHECKED);
                        }
                    if (GameStarted) {
                        StretchBitmaps();
                        }
                    break;

                case IDM_O_BITMAP:
                    if (bBitmapDraw) {
                        CheckMenuItem(hMenu,IDM_O_BITMAP,MF_UNCHECKED);
                        bBitmapDraw = FALSE;
                        hDC = hHoldDC;
                        hHoldDC = hMazeDC;
                        hMazeDC = hDC;
                        }
                    else {
                        CheckMenuItem(hMenu,IDM_O_BITMAP,MF_CHECKED);
                        bBitmapDraw = TRUE;
                        if ((((HWND)hMaze3DBM) == (HWND)NULL)&&(GameStarted)) {
                            hHoldDC = hMazeDC;
                            GetClientRect(hWnd,&rMaze);
                            hDC = GetDC(hWnd);
                            if (hMazeDC != NULL) {
                                DeleteDC(hMazeDC);
                            }
                            hMazeDC = CreateCompatibleDC(hDC);
                            if (hMaze3DBM != NULL) {
                                DeleteObject(hMaze3DBM);
                            }
                            hMaze3DBM = CreateCompatibleBitmap(hDC,rMaze.right-rMaze.left,
                                                               rMaze.bottom-rMaze.top);
                            SelectObject(hMazeDC,hMaze3DBM);
                            ReleaseDC(hWnd,hDC);
                            }
                        else {
                            hDC = hHoldDC;
                            hHoldDC = hMazeDC;
                            hMazeDC = hDC;
                            }
                        }
                    break;

                case IDM_O_PLAYERSET:
                    bDemoEnable = FALSE;
                    nRc = DialogBox(hInst,"PLAY_CONF_DLG",hWnd,PlayerDlg);
                    bDemoEnable = TRUE;
                    break;

                case IDM_O_DRONES:
                    bDemoEnable = FALSE;
                    nRc = DialogBox(hInst,"DRONE_DLG", hWnd, DroneDlg);
                    if (GameStarted) {
                        if (uiTimer != (UINT) NULL) {
                            KillTimer((HWND)NULL,uiTimer);
                            }

                        if ((iNumDrones)&&(iDroneSpeed != 0)) {
                            if (! (uiTimer = SetTimer((HWND)NULL,0,ONE_SECOND/iDroneSpeed,MoveDrone))) {
                                MessageBox((HWND)NULL,GetStringRes(IDS_CRETMRFAIL),GetStringRes(IDS_FATALERR),
                                           MB_ICONEXCLAMATION|MB_APPLMODAL);
                                SendMessage(hWndMain,WM_CLOSE,0,0);
                                }
                            }

                        InitDrones();
                        }
                    bDemoEnable = TRUE;
                    break;

                case IDM_O_PAUSE:
                    GamePaused = ! GamePaused;
                    break;

                case IDM_F_STOP:
                    if (GameStarted) {
                        nRc = MessageBox((HWND)NULL, GetStringRes(IDS_ASKSTOP),
                                         GetStringRes(IDS_STOP), MB_YESNO);
                        //
                        // If they really want to stop the game, do it...
                        //
                        if (nRc == IDYES) {
                            SendMessage(hWndScore,WM_CLOSE,0,0);
                            SendMessage(hWndText,WM_CLOSE,0,0);
                            SendMessage(hWndTopView,WM_CLOSE,0,0);
                            SendMessage(hWndMaze,WM_CLOSE,0,0);
                            EachTimeShutdown();
                            bDemoEnable = TRUE;
                            }
                        }

                    break;

                case IDM_ABOUT:
                    bDemoEnable = FALSE;
                    nRc = DialogBox(hInst,"MDLG_ABOUT", hWnd, AboutDlg);
                    bDemoEnable = TRUE;
                    break;

                default:
                    return DefWindowProc(hWnd, Message, wParam, lParam);
                }
            break;

        case WM_KEYDOWN:

            if (bDemoMode) {
                StopDemo();
                }
            UserIntCount++;

            switch(wParam) {

                //
                // X = Quit the game
                //
                case 'X':
                    if (!SendNetMessage(0,0,NULL,NP_LEAVINGGAME)) {
                        MessageBox((HWND)NULL,GetStringRes(IDS_SNDPCKTFAIL),"WndProc",
                                     MB_ICONEXCLAMATION|MB_APPLMODAL);
                        }

                    SendMessage(hWndMain,WM_CLOSE,0,0);
                    return(0);
                    break;

                //
                // movement messages go to the MazeWindow
                //
                case VK_SPACE:
                case VK_UP:
                case VK_LEFT:
                case VK_RIGHT:
                case VK_DOWN:
                case 'w':
                case 'W':
                case 's':
                case 'S':
                    SendMessage(hWndMaze,WM_KEYDOWN,wParam,lParam);
                    break;
                }
            break;

        case WM_CREATE:
            //
            // Set timer for 30-second intervals to go into demo-mode.
            //
            SetTimer(hWnd,(UINT) NULL, (UINT) (30*1000),(TIMERPROC) NULL);
            //
            // We decide on the basis of user interaction whether to
            // start demo-mode or not.
            //
            UserIntCount = 0;
            bDemoMode = FALSE;
            CheckMenuItem(hMenu,IDM_O_NET,MF_CHECKED);
            CheckMenuItem(hMenu,IDM_O_LOCAL,MF_UNCHECKED);
            bNetworked = TRUE;
            break;

        case WM_MOVE:
            break;

        case WM_SIZE:
            GetClientRect(hWndMain,&rMain);

            MazexDim = (rMain.right - rMain.left)/2;
            MazeyDim = (rMain.bottom - rMain.top)*2/3;
            MazexDim = MazeyDim = (MazexDim < MazeyDim) ? MazexDim : MazeyDim;

            rMaze.left = rMain.left+10;
            rMaze.top = rMain.top +10;
            rMaze.right = rMaze.left + MazexDim;
            rMaze.bottom = rMaze.top + MazeyDim;
            MoveWindow(hWndMaze,rMaze.left,rMaze.top,
                       rMaze.right-rMaze.left,rMaze.bottom-rMaze.top,TRUE);

            TopxDim = (rMain.right - rMain.left)/4;
            TopyDim = (rMain.bottom - rMain.top)/3;
            TopxDim = TopyDim = (TopxDim < TopyDim) ? TopxDim : TopyDim;
            rTopView.right = rMain.right - 10;
            rTopView.bottom = rMain.bottom - 10;
            rTopView.left = rTopView.right - TopxDim;
            rTopView.top = rTopView.bottom - TopyDim;
            MoveWindow(hWndTopView,rTopView.left,rTopView.top,
                       rTopView.right - rTopView.left,
                       rTopView.bottom - rTopView.top,TRUE);

            rText.left = rMain.left + 10;
            rText.right = rMain.right - 10 - TopxDim - 10;
            rText.top = rMain.top + 10 + MazeyDim + 10;
            rText.bottom = rMain.bottom-10;

            MoveWindow(hWndText,rText.left,rText.top,
                       rText.right - rText.left,
                       rText.bottom - rText.top,TRUE);

            rScore.left = rMain.left + 10 + MazexDim +10;
            rScore.right = rMain.right - 10;
            rScore.top = rMain.top + 10;
            rScore.bottom = rMain.bottom - 10 - TopyDim - 10;

            MoveWindow(hWndScore,rScore.left,rScore.top,
                    rScore.right - rScore.left,
                    rScore.bottom - rScore.top,TRUE);
            break;

        case WM_PAINT:
            memset(&ps, 0x00, sizeof(PAINTSTRUCT));
            hDC = BeginPaint(hWnd, &ps);

            SetBkMode(hDC, TRANSPARENT);
            hBitmap = LoadBitmap(hInst,"MAZE");
            hBMDC = CreateCompatibleDC(hDC);
            SelectObject(hBMDC,hBitmap);
            GetClientRect(hWndMain,&rMain);
            //
            // Fill the destination with the bitmap
            //
            for(i=rMain.left;i<rMain.right;i+=54) {
                for(j=rMain.top;j<rMain.bottom;j+=42) {
                    BitBlt(hDC,i,j,54,42,
                           hBMDC,0,0,
                           SRCCOPY
                          );
                    }
                }
            DeleteDC(hBMDC);
            DeleteObject(hBitmap);

            EndPaint(hWnd, &ps);
            break;

        case WM_CLOSE:
            DestroyWindow(hWnd);
            if (hWnd == hWndMain) {
                PostQuitMessage(0);
                EachTimeShutdown();
                OneTimeShutdown();
                }
            break;

        default:
            return DefWindowProc(hWnd, Message, wParam, lParam);
        }

    return(0);
}

/*=====================================================================
Function:RegisterClasses()

Inputs: None

Outputs:returns Success/failure

Abstract:
    This is responsible for registering the main window class and the
    child window classes as well.
======================================================================*/

int RegisterClasses(
    void
    )
{
    WNDCLASS   wndclass;

    memset(&wndclass, 0x00, sizeof(WNDCLASS));
    wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_BYTEALIGNWINDOW;
    wndclass.lpfnWndProc = WndProc;
    wndclass.cbClsExtra = 0;
    wndclass.cbWndExtra = 0;
    wndclass.hInstance = hInst;
    wndclass.hIcon = LoadIcon(hInst, "MAZE");
    wndclass.hCursor = LoadCursor((HINSTANCE)NULL, IDC_ARROW);
    wndclass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
    wndclass.lpszMenuName = szAppName;
    wndclass.lpszClassName = szAppName;

    if(!RegisterClass(&wndclass)) {
        return(-1);
        }

    //
    // Register the 3-d window class
    //
    wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_NOCLOSE | CS_OWNDC | CS_BYTEALIGNWINDOW;
    wndclass.lpfnWndProc = MazeWndProc;
    wndclass.hIcon = (HICON)NULL;
    wndclass.lpszMenuName = NULL;
    wndclass.lpszClassName = (LPSTR) "MazeDrawWindow";

    if(!RegisterClass(&wndclass)) {
        return(-1);
        }

    //
    // Register the top view window class
    //
    wndclass.lpfnWndProc = TopViewWndProc;
    wndclass.lpszClassName = (LPSTR) "TopViewWindow";

    if(!RegisterClass(&wndclass)) {
        return(-1);
        }

    //
    // Register the text window class
    //
    wndclass.lpfnWndProc = TextWndProc;
    wndclass.lpszClassName = (LPSTR) "TextWindow";

    if(!RegisterClass(&wndclass)) {
        return(-1);
        }

    //
    // Register the Score window class
    //
    wndclass.lpfnWndProc = ScoreWndProc;
    wndclass.lpszClassName = (LPSTR) "ScoreWindow";

    if(!RegisterClass(&wndclass)) {
        return(-1);
        }

    return(0);
}

/*=====================================================================
Function: UnRegisterClasses

Inputs: none

Outputs:none

Abstract:
    This deletes the windows classes we registered for MazeLords.
======================================================================*/

void UnRegisterClasses(
    void
    )
{
    WNDCLASS   wndclass;
    memset(&wndclass, 0x00, sizeof(WNDCLASS));

    UnregisterClass(szAppName, hInst);
    UnregisterClass((LPSTR) "MazeDrawWindow",hInst);
    UnregisterClass((LPSTR) "TopViewWindow",hInst);
    UnregisterClass((LPSTR) "TextWindow",hInst);
    UnregisterClass((LPSTR) "ScoreWindow",hInst);
}

/*=====================================================================
Function: OneTimeInit()

Inputs: none

Outputs:none

Abstract:
    This initialization routine is called the first time a game is started.
    It reads in the subgrids, initializes the network, makes a mailslot,
    and sets many of the global variables.
======================================================================*/

void OneTimeInit()
{
    DWORD dwScratch,dwSize,dwType;
    HMENU hMenu;
    BOOL bRet;
    int i,j,k;
#ifdef WIN32
    HKEY hKey;
#endif

    if (!ReadSubGrids()) {
        MessageBox((HWND)NULL,GetStringRes(IDS_RDSGRDTXTFLFAIL),
GetStringRes2(IDS_INITFAIL), MB_ICONEXCLAMATION);
        }

    dwScratch = MAX_USERNAME_LENGTH;
    strcpy(ptSelf.cUserName,"<A Player>");
    strcpy(ptSelf.cComputerName,"MachineName");
    dwSize =MAX_COMPUTERNAME_LENGTH;
    ptSelf.ulID = 0;
    ptSelf.iPicNum = PIC_DEFAULT;
    ptSelf.iGridNum = SUBGRID_DEFAULT;

    hMenu = GetMenu(hWndMain);
    EnableMenuItem(hMenu,IDM_F_STOP,MF_GRAYED);
    EnableMenuItem(hMenu,IDM_O_PAUSE,MF_GRAYED);

#ifdef WIN32
    if (!(bRet = GetUserName(ptSelf.cUserName,&dwScratch))) {
        MessageBox((HWND)NULL,GetStringRes(IDS_GETUSRNMFAIL),
GetStringRes2(IDS_NETINIT), MB_ICONEXCLAMATION|MB_APPLMODAL);
        }
    dwType = REG_SZ;
    RegOpenKeyEx(HKEY_LOCAL_MACHINE,
                 "SYSTEM\\CurrentControlSet\\Control\\ComputerName\\ActiveComputerName",
                 0,
                 KEY_READ,
                 &hKey);
    RegQueryValueEx(hKey,"ComputerName",NULL,&dwType,

ptSelf.cComputerName,&dwSize);
    ptSelf.dwPID = GetCurrentProcessId();
#endif
    ptSelf.ulID = MemCRC((LPSTR) &ptSelf.ulID,(int) sizeof(ptSelf));
    bDemoMode = FALSE;
    bDemoEnable = TRUE;
    iNumDrones = 10;
    iDroneSpeed = 2;
    iDroneMoveAlg = 1;
    bBitmapDraw = FALSE;
    bPlayerPrestretch = FALSE;
    bDronePrestretch = FALSE;
    for(i=0;i<NUM_PICS;i++) {
        for (j=0;j<MAX_DRAW_DIST;j++) {
            for(k=0;k<4;k++) {
                PreStretch[i][j].P[k].hBitmap = (HBITMAP)NULL;
                PreStretch[i][j].M[k].hBitmap = (HBITMAP)NULL;
                }
            }
        }

    return;
}

/*=====================================================================
Function: EachTimeInit()

Inputs: none

Outputs:none

Abstract:
    This function is called each time a new game is started (initially,
    and any time after a STOP selection from the menu) This loads some
    bitmaps, changes the state on menu-items whose validity is changed by
    a game in progress, and resets some global variables.
======================================================================*/

void EachTimeInit(
    void
    )
{
    HMENU hMenu;

    hMenu = GetMenu(hWndMain);

    EnableMenuItem(hMenu,IDM_O_NET,MF_GRAYED);
    EnableMenuItem(hMenu,IDM_O_LOCAL,MF_GRAYED);
    EnableMenuItem(hMenu,IDM_O_PLAYERSET,MF_GRAYED);
    EnableMenuItem(hMenu,IDM_F_NEWGAME,MF_GRAYED);
    EnableMenuItem(hMenu,IDM_F_STOP,MF_ENABLED);
    EnableMenuItem(hMenu,IDM_O_PAUSE,MF_ENABLED);
    uiTimer = (UINT) NULL;
    ptDrones.next = NULL;
    fptPic.next = NULL;
    bSelfInSanct = TRUE;

    hShotBM[0]=LoadBitmap(hInst,"SHOT1");
    hShotBM[1]=LoadBitmap(hInst,"SHOT2");
    hFadeBM[0]=LoadBitmap(hInst,"FADE1");
    hFadeBM[1]=LoadBitmap(hInst,"FADE2");

    hMaze3DBM = (HBITMAP)NULL;
    hMazeDC = (HDC)NULL;
    iWhisperDist = 2;
    iShoutDist = 10;
    iPlayersKilled = iTimesKilled = iDronesKilled = 0;
    ptSelf.iScore = iKilledByDrones = 0;
    GamePaused = GameStarted = FALSE;
    Scores.next = NULL;

    return;
}

/*=====================================================================
Function: OneTimeShutdown()

Inputs: none

Outputs:none

Abstract:
    This function does NOTHING right now. It'll be called at the very
    end of everything.
======================================================================*/

void OneTimeShutdown(
    void
    )
{
    return;
}

/*=====================================================================
Function: EachTimeShutdown()

Inputs: none

Outputs:none

Abstract:
    This function is called each time a STOP command is selected and
    approved. It changes menu items to the correct state, changes some
    global state variables, and notifies the network that we're quitting
    the game.
======================================================================*/

void EachTimeShutdown(
    void
    )
{
    HMENU hMenu;

    hMenu = GetMenu(hWndMain);

    DeleteObject(hShotBM[0]);
    DeleteObject(hShotBM[1]);
    DeleteObject(hFadeBM[0]);
    DeleteObject(hFadeBM[1]);
    DeleteObject(hMaze3DBM);
    DeleteDC(hMazeDC);

    EnableMenuItem(hMenu,IDM_O_NET,MF_ENABLED);
    EnableMenuItem(hMenu,IDM_O_LOCAL,MF_ENABLED);
    EnableMenuItem(hMenu,IDM_O_PLAYERSET,MF_ENABLED);
    EnableMenuItem(hMenu,IDM_F_STOP,MF_GRAYED);
    EnableMenuItem(hMenu,IDM_O_PAUSE,MF_GRAYED);
    EnableMenuItem(hMenu,IDM_F_NEWGAME,MF_ENABLED);

    if (!SendNetMessage(0,0,NULL,NP_LEAVINGGAME)) {
        MessageBox((HWND)NULL,GetStringRes(IDS_SNDQUITPCKTFAIL),"EachTimeShutDown",
                   MB_ICONEXCLAMATION|MB_APPLMODAL);
        }

    bNetworked = TRUE;
    GameStarted = FALSE;

    iNumDrones = 0;
    iDroneSpeed = 0;
    iDroneMoveAlg = 1;
    bBitmapDraw = FALSE;

    return;
}

/*=====================================================================
Function: StartDemo()

Inputs: none

Outputs:none

Abstract:
    This function is responsible for causing MazeLords to enter Demo
    mode.
======================================================================*/

void StartDemo(
    void
    )
{
    bDemoMode = TRUE;
    SetWindowText(hWndMain,GetStringRes(IDS_DEMOMODE));
    SendMessage(hWndMain,WM_COMMAND,MAKELONG(IDM_F_NEWGAME,0),(LPARAM)NULL);
    return;
}

/*=====================================================================
Function: StopDemo()

Inputs: none

Outputs:none

Abstract:
    This function is responsible for ending the mazelords demo and
    putting it into a state that the user can interact with.
======================================================================*/

void StopDemo(
    void
    )
{
    bDemoMode = FALSE;
    SendMessage(hWndScore,WM_CLOSE,0,0);
    SendMessage(hWndText,WM_CLOSE,0,0);
    SendMessage(hWndTopView,WM_CLOSE,0,0);
    SendMessage(hWndMaze,WM_CLOSE,0,0);
    EachTimeShutdown();
    iNumDrones  = 10;
    iDroneSpeed = 2;
    bDemoEnable = TRUE;
    UserIntCount++;
    SetWindowText(hWndMain,"Maze Lords");
    return;
}

/******************************************************************************\
*
*  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;
}

LPTSTR   GetStringRes2 (int id)
{
  static TCHAR buffer[MAX_PATH];

  buffer[0]=0;
  LoadString (GetModuleHandle (NULL), id, buffer, MAX_PATH);
  return buffer;
}


MAZEDLG.C

/***********************************************************************
File:   MazeDlg.c

Abstract:

    This module contains dialog boxes for Maze Lords.

Contents:

    DroneDlg() -- Drone Dialog Box winproc
    AboutDlg() -- About Box
    IntoneDlg() -- Dialog box for spoken messages
    PlayerDlg() -- Dialog box for selecting player picture/maze
    cwCenter() -- Center a given dialog box in the window

Revision History:

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

#include "winmaze.h"
#include "mazproto.h"
#include "mazedlg.h"

int iCurPic,iCurMaze;

/*=====================================================================
Function:   DroneDlg()

Inputs:     Standard Dialog inputs

Outputs:    Returns close status

Abstract:
    This dialog box allows the user to input # of drones and their movement
    speed.
======================================================================*/

BOOL FAR PASCAL DroneDlg(
    HWND hWndDlg,
    UINT Message,
    WPARAM wParam,
    LPARAM lParam
    )
{
    BOOL bRet;  // holding variable only.
    UINT uCmdId,uCmdCmd;
    HWND hCmd;
    static INT iOldNumDrones,iOldDroneSpeed;
    int i;
    char c[132];

    switch(Message) {
        case WM_INITDIALOG:
            cwCenter(hWndDlg, 0);
            iOldNumDrones = iNumDrones;
            iOldDroneSpeed = iDroneSpeed;
            SetDlgItemInt(hWndDlg,DLG_NUMDRONES,iNumDrones,TRUE);
            SetDlgItemInt(hWndDlg,DLG_MOVESPEED,iDroneSpeed,TRUE);
            break;

        case WM_CLOSE:
            PostMessage(hWndDlg, WM_COMMAND, IDCANCEL, 0L);
            break;

        case WM_COMMAND:
            uCmdId = GET_WM_COMMAND_ID(wParam,lParam);
            uCmdCmd = GET_WM_COMMAND_CMD(wParam,lParam);
            hCmd = GET_WM_COMMAND_HWND(wParam,lParam);
            switch(uCmdId) {
                case DLG_NUMDRONES:
                    if (uCmdCmd == EN_CHANGE) {
                        i = GetDlgItemInt(hWndDlg,DLG_NUMDRONES,&bRet,TRUE);
                        if ((i < 0)||(i > MAX_DRONES)) {
                            MessageBeep(MB_ICONHAND);
                            sprintf(c,GetStringRes(IDS_FMT_BADNUMDRONES),
                                    MAX_DRONES);
                            MessageBox(hWndDlg,c,GetStringRes(IDS_ERRINPUT),MB_APPLMODAL|MB_ICONHAND);
                            SetDlgItemInt(hWndDlg,DLG_NUMDRONES,iOldNumDrones,TRUE);
                        }

                    }
                    break;

                case DLG_MOVESPEED:
                    if (uCmdCmd == EN_CHANGE) {
                        i = GetDlgItemInt(hWndDlg,DLG_MOVESPEED,&bRet,TRUE);
                        if ((i < 0)||(i > MAX_DRONE_SPEED)) {
                            MessageBeep(MB_ICONHAND);
                            sprintf(c,GetStringRes(IDS_FMT_BADMOVESPERSEC),
                                    MAX_DRONE_SPEED);
                            MessageBox(hWndDlg,c,GetStringRes(IDS_ERRINPUT),MB_APPLMODAL|MB_ICONHAND);
                            SetDlgItemInt(hWndDlg,DLG_MOVESPEED,iOldDroneSpeed,TRUE);
                        }

                    }
                    break;

                case IDOK:
                    iNumDrones = GetDlgItemInt(hWndDlg,DLG_NUMDRONES,&bRet,TRUE);
                    iDroneSpeed = GetDlgItemInt(hWndDlg,DLG_MOVESPEED,&bRet,TRUE);
                    if ((iOldNumDrones != iNumDrones)&&GameStarted) {
                        PostMessage(hWndMaze,WM_COMMAND,IDM_REDRAW,(DWORD)NULL);
                        }

                    EndDialog(hWndDlg, TRUE);
                    break;

                case IDCANCEL:
                    EndDialog(hWndDlg, FALSE);
                    break;
                }
            break;

        default:
            return FALSE;
        }

    return(TRUE);
}

/*=====================================================================
Function:   AboutDlg()

Inputs:     Standard Dialog box parms

Outputs:    Returns close status

Abstract:
    This procedure displays the About dialog box.
======================================================================*/

BOOL FAR PASCAL AboutDlg(
    HWND hWndDlg,
    UINT Message,
    WPARAM wParam,
    LPARAM lParam
    )
{
    UINT uCmdId,uCmdCmd;
    HWND hCmd;

    switch(Message) {

        case WM_INITDIALOG:
            cwCenter(hWndDlg, 0);
            break;

        case WM_CLOSE:
            PostMessage(hWndDlg, WM_COMMAND, IDCANCEL, 0L);
            break;

        case WM_COMMAND:
            uCmdId = GET_WM_COMMAND_ID(wParam,lParam);
            uCmdCmd = GET_WM_COMMAND_CMD(wParam,lParam);
            hCmd = GET_WM_COMMAND_HWND(wParam,lParam);
            switch(uCmdId) {
                case IDOK:
                    EndDialog(hWndDlg, TRUE);
                    break;
                }
            break;

        default:
            return FALSE;
        }

    return(TRUE);
}

/*=====================================================================
Function:   IntoneDlg()

Inputs:     Standard Dialog Box Parms

Outputs:    Returns dialog close conditions

Abstract:
    This dialog allows the user to type a message to whisper or shout.
    It is called by both commands, with the loudness having been set
    before the dialog was invoked by the method it was invoked, either
    whispering or shouting.
======================================================================*/

BOOL FAR PASCAL IntoneDlg(
    HWND hWndDlg,
    UINT Message,
    WPARAM wParam,
    LPARAM lParam
    )
{
    IntoneType itTemp;
    BOOL bScratch;
    UINT uCmdId;

    switch(Message) {

        case WM_INITDIALOG:
            cwCenter(hWndDlg, 0);
            SetDlgItemInt(hWndDlg,DLG_INT_LOUDNESS,iLoudness,TRUE);
            SetDlgItemText(hWndDlg,DLG_INT_SAY,"");
            break;

        case WM_CLOSE:
            PostMessage(hWndDlg, WM_COMMAND, IDCANCEL, 0L);
            break;

        case WM_COMMAND:
            uCmdId = GET_WM_COMMAND_ID(wParam,lParam);
            switch(uCmdId) {

                case DLG_INT_LOUDNESS:
                    break;

                case DLG_INT_SAY:
                    break;

                case IDOK:
                    GetDlgItemText(hWndDlg,DLG_INT_SAY,itTemp.cBuff,131);
                    itTemp.dwLen=lstrlen(itTemp.cBuff);
                    iLoudness = GetDlgItemInt(hWndDlg,DLG_INT_LOUDNESS,&bScratch,FALSE);
                    itTemp.iLoudness= iLoudness;
                    itTemp.Pos = ptSelf.Pos;
                    SendNetMessage(0,0,&itTemp,NP_INTONE);
                    EndDialog(hWndDlg, TRUE);
                    break;

                case IDCANCEL:
                    EndDialog(hWndDlg, FALSE);
                    break;
                }
            break;

        default:
            return FALSE;
        }

    return(TRUE);
}

/*=====================================================================
Function:   PlayerDlg()

Inputs:     Standard Dialog box parms

Outputs:    returns dialog termination condition

Abstract:
    This dialog box uses two custom list-boxes to allow the player to
    select options. The first contains all possible player bitmaps. The
    second contains all possible player sub-grids to choose from.
======================================================================*/

BOOL FAR PASCAL PlayerDlg(
    HWND hWndDlg,
    UINT Message,
    WPARAM wParam,
    LPARAM lParam
    )
{
    int i,j,k,x,y;
    MEASUREITEMSTRUCT FAR *miInfo;
    DRAWITEMSTRUCT FAR *diInfo;
    HDC hDC,hPicDC;
    FullPicType FAR *fptTrav;
    BOOL bFound;
    LPPOINT p;
//[5*5*5*2+2];
    LPBYTE bType;
//[5*5*5*2+2],
    BYTE b;
    int iNumPts,Step;
    HGLOBAL hPMem,hBMem;
    float fAspect;

    switch(Message) {

        case WM_INITDIALOG:
            cwCenter(hWndDlg, 0);

            iCurPic = iCurMaze = 0;
            if (PIC_DRONE == 0) {
                iCurPic = 1;
                }
            for (i=0;i<NUM_PICS;i++) {
                if (i != PIC_DRONE) {
                    AddPic(i);
                    SendDlgItemMessage(hWndDlg,PC_DLG_PIC,LB_ADDSTRING,0,i);
                    }
                }
            for (i=1;i<NUM_SUBGRIDS-1;i++) {
                SendDlgItemMessage(hWndDlg,PC_DLG_MAZE,LB_ADDSTRING,0,i);
                }
            break;

        case WM_MEASUREITEM:
            miInfo = (MEASUREITEMSTRUCT FAR *) lParam;
            miInfo->CtlType = ODT_LISTBOX;
            miInfo->itemID = 0;     // Not used

            if (wParam == PC_DLG_PIC) {
                miInfo->CtlID = PC_DLG_PIC;
                miInfo->itemWidth = 20;
                miInfo->itemHeight = 45;
                miInfo->itemData = 0;
                }
            else {
                miInfo->CtlID = PC_DLG_MAZE;
                miInfo->itemWidth = 30;
                miInfo->itemHeight = 45;
                miInfo->itemData = 0;
                }
            break;

        case WM_DRAWITEM:

            diInfo = (DRAWITEMSTRUCT FAR *) lParam;
            hDC = diInfo->hDC;
            switch(diInfo->CtlID) {

                case PC_DLG_PIC:
                    if (diInfo->itemAction == ODA_DRAWENTIRE) {
                        hPicDC = CreateCompatibleDC(hDC);
                        fptTrav = &fptPic;
                        bFound = FALSE;
                        while (fptTrav->next != (FullPicType FAR *)NULL) {
                            fptTrav = fptTrav->next;
                            if (fptTrav->iPicNum == (int)diInfo->itemData) {
                                bFound = TRUE;
                                break;
                                }
                            }

                        if (bFound) {
                            iCurPic = (int)diInfo->itemData;
                            SelectObject(hPicDC,fptTrav->P[1].hBitmap);
                            x = diInfo->rcItem.right - diInfo->rcItem.left;
                            y = diInfo->rcItem.bottom - diInfo->rcItem.top;
                            fAspect=((float) fptTrav->P[1].ySize)/
                                    ((float) fptTrav->P[1].xSize);

                            StretchBlt(hDC,diInfo->rcItem.left,diInfo->rcItem.top,
                                       (int) ((x < (y/fAspect))? x : y/fAspect),
                                       (int) ((y < (x*fAspect))? y : x*fAspect),
                                       hPicDC,0,0,
                                       fptTrav->P[1].xSize,
                                       fptTrav->P[1].ySize,
                                       SRCCOPY);
                            }

                        DeleteDC(hPicDC);
                        }

                    break;

                case PC_DLG_MAZE:
                    hPMem = GlobalAlloc(GHND,(3*5*5*4*2 + 5)*sizeof(POINT));
                    p = (LPPOINT) GlobalLock(hPMem);
                    hBMem = GlobalAlloc(GHND,(3*5*5*4*2 + 5)*sizeof(BYTE));
                    bType = (LPBYTE) GlobalLock(hBMem);

                    if (diInfo->itemAction == ODA_DRAWENTIRE) {
                        x = diInfo->rcItem.left;
                        y = diInfo->rcItem.top;
                        k = (int)diInfo->itemData;
                        iNumPts = 0;
                        Step = 9;
                        iCurMaze = (int)diInfo->itemData;

                        for (i = 0;i<5; i++) {
                            for (j= 0; j<5; j++) {
                                b = SubGrids[k].Cell[i][j];
                                if (b&NORTH) {
                                    p[iNumPts].x = i*Step;
                                    p[iNumPts].y = j*Step;
                                    bType[iNumPts++] = PT_MOVETO;
                                    p[iNumPts].x = (i+1)*Step;
                                    p[iNumPts].y = j*Step;
                                    bType[iNumPts++] = PT_LINETO;
                                    }
                                if (b&SOUTH) {
                                    p[iNumPts].x = i*Step;
                                    p[iNumPts].y = (j+1)*Step;
                                    bType[iNumPts++] = PT_MOVETO;
                                    p[iNumPts].x = (i+1)*Step;
                                    p[iNumPts].y = (j+1)*Step;
                                    bType[iNumPts++] = PT_LINETO;
                                    }
                                if (b&WEST) {
                                    p[iNumPts].x = i*Step;
                                    p[iNumPts].y = j*Step;
                                    bType[iNumPts++] = PT_MOVETO;
                                    p[iNumPts].x = i*Step;
                                    p[iNumPts].y = (j+1)*Step;
                                    bType[iNumPts++] = PT_LINETO;
                                    }
                                if (b&EAST) {
                                    p[iNumPts].x = (i+1)*Step;
                                    p[iNumPts].y = j*Step;
                                    bType[iNumPts++] = PT_MOVETO;
                                    p[iNumPts].x = (i+1)*Step;
                                    p[iNumPts].y = (j+1)*Step;
                                    bType[iNumPts++] = PT_LINETO;
                                    }
                                }
                            }
                        PolyDraw95(hDC,p,bType,iNumPts);
                        }

                    GlobalUnlock(hBMem);
                    GlobalUnlock(hPMem);
                    GlobalFree(hBMem);
                    GlobalFree(hPMem);
                    break;

                default:
                    break;
                }
//BUGBUG -- shouldn't it be hWindow,hDC???
            ReleaseDC((HWND) diInfo->hDC,hDC);
            break;

        case WM_CLOSE:
            for(i=0;i<NUM_PICS;i++) {
                if (ptSelf.iPicNum != i) {
                    DelPic(i);
                    }
                }
            PostMessage(hWndDlg, WM_COMMAND, IDCANCEL, 0L);
            break;

        case WM_COMMAND:
            switch(wParam) {
                case IDOK:
                    ptSelf.iGridNum = iCurMaze;
                    ptSelf.iPicNum = iCurPic;
                    EndDialog(hWndDlg, TRUE);
                    break;
                case IDCANCEL:
                    EndDialog(hWndDlg, FALSE);
                    break;
                }
            break;

        default:
            return FALSE;
        }

    return(TRUE);
}

/*=====================================================================
Function:   cwCenter()

Inputs:     Handle to window to center, top of window

Outputs:    none

Abstract:
    Does a MoveWindow to center the window in question.
======================================================================*/

void cwCenter(
    HWND hWnd,
    int top
    )
{
    POINT    pt;
    RECT     swp;
    RECT     rParent;
    int      iwidth;
    int      iheight;

    GetWindowRect(hWnd, &swp);
    GetClientRect(hWndMain, &rParent);

    iwidth = swp.right - swp.left;
    iheight = swp.bottom - swp.top;

    pt.x = (rParent.right - rParent.left) / 2;
    pt.y = (rParent.bottom - rParent.top) / 2;
    ClientToScreen(hWndMain, &pt);

    pt.x -= (iwidth / 2);
    pt.y -= (iheight / 2);

    if (top) {
        pt.y += top;
        }

    MoveWindow(hWnd, pt.x, pt.y, iwidth, iheight, FALSE);
}


MAZEDLG.H

#define DLG_NUMDRONES               0x0067
#define DLG_MOVESPEED               0x0068
#define ID_OK                       0x006B
#define ID_CANCEL                   0x006C
#define DLG_INT_LOUDNESS            0x0082
#define DLG_INT_SAY                 0x0083
#define PC_DLG_PIC                  101
#define PC_DLG_MAZE                 107


MAZEWND.C

/***********************************************************************
File:   MazeWnd.c

Abstract:

    This module contains the windows procedure responsible for drawing the 3-d
    maze.

Contents:

    StretchBitmaps() --Prestretches loaded pictures to bitmap size.
    KillSelf() -- Takes care of hassles associated with dying
    MazeWndProc() -- Main entrypoint for drawing 3-d maze

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

#include "winmaze.h"
#include "mazproto.h"
#include "net.h"
#include <mmsystem.h>

/*=====================================================================
Function:   StretchBitmaps()

Inputs:     none

Outputs:    none

Abstract:
    StretchBitmaps() will take care of stretching the appropriate bitmaps
    to their correct dimensions.
======================================================================*/

void StretchBitmaps(
    void
    )
{
    float xSize,ySize,xOrg,yOrg,x,y,z;
    int iPicNum,iRely,iFacing;
    HDC hDC,hSrcDC,hDestDC,hMemDC;
    FullPicType FAR *fptTrav;
    HBITMAP hBM;
    HCURSOR hCursor;

    //
    // Set the cursor to an hourglass since this takes time...
    //
    hCursor = SetCursor(LoadCursor((HINSTANCE)NULL,IDC_WAIT));

    hDC = GetDC(hWndMaze);
    hSrcDC = CreateCompatibleDC(hDC);
    hDestDC = CreateCompatibleDC(hDC);
    hMemDC = CreateCompatibleDC(hDestDC);

    GetClientRect(hWndMaze,(LPRECT) &rMaze);

    //
    // Set x and y to logical panel origin.
    //
    y = (float) (-PANEL_HEIGHT/2);
    x = (float) (-PANEL_WIDTH/2);

    //
    // Loop through all of the pictures we know about.
    //
    for(iPicNum=0;iPicNum < NUM_PICS;iPicNum++) {

        AddPic(iPicNum);

        //
        // Find the fptPic entry for Picture # iPicNum
        //
        fptTrav = &fptPic;
        while (fptTrav->next != NULL) {
            fptTrav = fptTrav->next;
            if (fptTrav->iPicNum == iPicNum) {
                break;
                }
            }
        //
        // Loop from closest to us to furthest away from us for stretching
        //
        for (iRely = 0;iRely < MAX_DRAW_DIST;iRely++) {
            PreStretch[iPicNum][iRely].next = NULL;
            PreStretch[iPicNum][iRely].iPicNum = iPicNum;

            //
            // We need to stretch once per facing
            //
            for (iFacing=0;iFacing<4;iFacing++) {

#if ( _ALPHA_ == 1 )
                z = (float) (iRely ? iRely*PANEL_WIDTH:PANEL_WIDTH/4);
#else
                z = (float) (iRely-1)*PANEL_WIDTH; //- PANEL_WIDTH/2;
#endif

                xSize = MC_TO_SC(fptTrav->P[iFacing].xSize,z);
                ySize = MC_TO_SC(fptTrav->P[iFacing].ySize,z);
                xOrg = MC_TO_SC(fptTrav->P[iFacing].xOrg,z);
                yOrg = MC_TO_SC(fptTrav->P[iFacing].yOrg,z);

                PreStretch[iPicNum][iRely].P[iFacing].xSize = (int) xSize;
                PreStretch[iPicNum][iRely].P[iFacing].ySize = (int) ySize;
                PreStretch[iPicNum][iRely].P[iFacing].xOrg = (int) xOrg;
                PreStretch[iPicNum][iRely].P[iFacing].yOrg = (int) yOrg;
                PreStretch[iPicNum][iRely].M[iFacing].xSize = (int) xSize;
                PreStretch[iPicNum][iRely].M[iFacing].ySize = (int) ySize;
                PreStretch[iPicNum][iRely].M[iFacing].xOrg = (int) xOrg;
                PreStretch[iPicNum][iRely].M[iFacing].yOrg = (int) yOrg;

                //
                // If we're supposed to prestretch this bitmap, do so
                //
                if (((iPicNum == PIC_DRONE)&&bDronePrestretch)||
                     (bPlayerPrestretch)
                   ) {
                    //
                    // If there were already stretched bitmaps, delete them.
                    //
                    if (PreStretch[iPicNum][iRely].P[iFacing].hBitmap !=
                        (HBITMAP)NULL) {
                        DeleteObject(PreStretch[iPicNum][iRely].P[iFacing].hBitmap);
                        }
                    if (PreStretch[iPicNum][iRely].M[iFacing].hBitmap !=
                        (HBITMAP)NULL) {
                        DeleteObject(PreStretch[iPicNum][iRely].M[iFacing].hBitmap);
                        }

                    //
                    // Stretch the bitmap
                    //
                    hBM = CreateCompatibleBitmap(hDC,(int)xSize,(int)ySize);

                    SelectObject(hSrcDC,fptTrav->P[iFacing].hBitmap);
                    SelectObject(hDestDC,hBM);
                    StretchBlt(hDestDC,0,0,(int)xSize,(int)ySize,
                               hSrcDC,0,0,
                               fptTrav->P[iFacing].xSize,
                               fptTrav->P[iFacing].ySize,
                               SRCCOPY);
                    PreStretch[iPicNum][iRely].P[iFacing].hBitmap = hBM;

                    //
                    // Now stretch the mask
                    //
                    hBM = CreateCompatibleBitmap(hMemDC,(int)xSize,(int)ySize);

                    SelectObject(hSrcDC,fptTrav->M[iFacing].hBitmap);
                    SelectObject(hMemDC,hBM);
                    StretchBlt(hMemDC,0,0,(int)xSize,(int)ySize,
                               hSrcDC,0,0,
                               fptTrav->M[iFacing].xSize,
                               fptTrav->M[iFacing].ySize,
                               SRCCOPY);
                    PreStretch[iPicNum][iRely].M[iFacing].hBitmap = hBM;

                    }
                else {
                    //
                    // If we're not stretching bitmaps, we should delete
                    // any we don't need anymore to free memory.
                    //
                    if (PreStretch[iPicNum][iRely].P[iFacing].hBitmap !=
                        (HBITMAP)NULL) {
                        DeleteObject(PreStretch[iPicNum][iRely].P[iFacing].hBitmap);
                        }
                    if (PreStretch[iPicNum][iRely].M[iFacing].hBitmap !=
                        (HBITMAP)NULL) {
                        DeleteObject(PreStretch[iPicNum][iRely].M[iFacing].hBitmap);
                        }
                    PreStretch[iPicNum][iRely].P[iFacing].hBitmap = (HBITMAP)NULL;
                    PreStretch[iPicNum][iRely].M[iFacing].hBitmap = (HBITMAP)NULL;
                    }
                }
            }
        }

    DeleteDC(hMemDC);
    DeleteDC(hSrcDC);
    DeleteDC(hDestDC);
    ReleaseDC(hWndMaze,hDC);

    //
    // Return the cursor.
    //
    SetCursor(hCursor);
    return;
}

/*=====================================================================
Function:   KillSelf()

Inputs:     none

Outputs:    none

Abstract:
    KillSelf() takes care of playing the teleport sound, teleporting us back to
    the sanctuary, and notifying the network that we've died...
======================================================================*/

void KillSelf(void)
{
    RECT rScratch;

    if (!sndPlaySound("tele.wav",SND_ASYNC)) {
        MessageBeep((UINT) -1);
        MessageBeep((UINT) -1);
        MessageBeep((UINT) -1);
        }
    //
    // these are from initmaze.c
    //

    if (bDemoMode) {
        ptSelf.Pos.ix = RandRange(5*X_CELLS_PER_SUBGRID,8*X_CELLS_PER_SUBGRID-1);
        ptSelf.Pos.iy = RandRange(7*Y_CELLS_PER_SUBGRID,8*Y_CELLS_PER_SUBGRID-1);
        PrintTextLine(GetStringRes(IDS_TELEPORT));
        }
    else {
        ptSelf.Pos.ix = 6*X_CELLS_PER_SUBGRID+1;
        ptSelf.Pos.iy = 7*Y_CELLS_PER_SUBGRID+1;
        }
    ptSelf.Pos.Facing = NORTH;
    ptLastPos = ptSelf.Pos;

    bSelfInSanct=TRUE;
    PrintTextLine(GetStringRes(IDS_SANCTUARY));

    if (!SendNetMessage(0,0,&ptSelf.Pos,NP_MOVETO)) {
        MessageBox((HWND)NULL,GetStringRes(IDS_SNDPCKTFAIL),"MazeWndProc",
               MB_ICONEXCLAMATION|MB_APPLMODAL);
        }
    if (bBitmapDraw) {
        PostMessage(hWndMaze,WM_COMMAND,IDM_REDRAW,(DWORD)NULL);
        }
    else {
        InvalidateRect(hWndMaze,&rMaze,TRUE);
        }

    GetClientRect(hWndTopView,&rScratch);
    InvalidateRect(hWndTopView,&rScratch,TRUE);

    PostMessage(hWndScore,WM_COMMAND,WC_UPDATESCORE,ptSelf.ulID);
    PostMessage(hWndScore,WM_COMMAND,WC_UPDATEDIRECTION,0);
    iTimesKilled++;

    return;
}

/*=====================================================================
Function:   MazeWndProc()

Inputs:     Standard windows procedure inputs

Outputs:    returns success/failure

Abstract:
    MazeWndProc is the process responsible for drawing the maze 3-d view
    perspective window. It has the corresponding controls.
======================================================================*/

LONG FAR PASCAL MazeWndProc(
    HWND hWnd,
    UINT Message,
    WPARAM wParam,
    LPARAM lParam
    )
{
    HDC     hDC,hBMDC;
    int     nRc=0,i,ix,iy;
    BYTE bSquare1,bSquare2,Facing;
    PlayerType FAR *ptTrav;
    BOOL bKilled,bNewSelfInSanct,bHitSomething;
    PositionType ptPos;
    char cBuff[132];
    LPRECT rTemp,rPtr;
    HGLOBAL hMem;

    switch (Message) {

        case WM_CREATE:
            if (uiTimer != (UINT) NULL) {
                KillTimer((HWND)NULL,uiTimer);
                }

            if ((iNumDrones)&&(iDroneSpeed != 0)) {
                if (! (uiTimer = SetTimer((HWND) NULL,0,ONE_SECOND/iDroneSpeed,MoveDrone))) {
                    MessageBox(NULL,GetStringRes(IDS_CRETMRFAIL),
GetStringRes2(IDS_FATALERR), MB_ICONEXCLAMATION|MB_APPLMODAL);
                    PostMessage(hWndMain,WM_CLOSE,0,0);
                    }
                }

            InitDrones();

            if ((bBitmapDraw)&&(((HWND)hMaze3DBM) == ((HWND)NULL))) {
                hDC = GetDC(hWnd);
                GetClientRect(hWnd,&rMaze);
                hMazeDC = CreateCompatibleDC(hDC);
                hMaze3DBM = CreateCompatibleBitmap(hDC,rMaze.right-rMaze.left,
                                                  rMaze.bottom-rMaze.top);
                SelectObject(hMazeDC,hMaze3DBM);
                ReleaseDC(hWnd,hDC);
                }
            else {
                hMazeDC = GetDC(hWnd);
                }

            break;

        case WM_COMMAND:
            switch(wParam) {

                case IDM_DRAWPLAYERS:
                    DrawPlayers(hMazeDC,&ptPlayers,NULL);
                    break;

                case IDM_DRAWDRONES:
                    DrawPlayers(hMazeDC,&ptDrones,(LPRECT) lParam);
//                    if (lParam != (LPARAM) NULL) {
//                        hMem = (HGLOBAL) GlobalHandle(SELECTOROF( (LPRECT) lParam));
//                        GlobalUnlock(hMem);
//                        GlobalFree(hMem);
//                    }
                    break;

                case IDM_REDRAW:
                    hDC = GetDC(hWnd);
                    if (lParam != (LPARAM)NULL) {
                        rTemp = (LPRECT)lParam;
                        }
                    else {
                        hMem = GlobalAlloc(GHND,sizeof(RECT));
                        rTemp = (LPRECT) GlobalLock(hMem);
                        if (rTemp == NULL) {
                            MessageBox((HWND)NULL,GetStringRes(IDS_MALLOCFAIL),"MazeWnd",
                                       MB_ICONEXCLAMATION|MB_APPLMODAL);
                            PostMessage(hWndMain,WM_CLOSE,0,0);
                            }
                        GetClientRect(hWnd,rTemp);
                        }

                    DrawMaze(hMazeDC,rTemp);

                    if (bBitmapDraw) {
                        BitBlt(hDC,rTemp->left, rTemp->top,
                               RECTWIDTH(*rTemp),
                               RECTDEPTH(*rTemp),
                               hMazeDC,rTemp->left,rTemp->top,SRCCOPY);
                        }

                    ReleaseDC(hWnd,hDC);
                    hMem = (HGLOBAL) GlobalHandle(SELECTOROF( rTemp));
                    GlobalUnlock(hMem);
                    GlobalFree(hMem);

                default:
                    break;
                }

            break;

        case WM_KEYDOWN:
            bSquare1 = bMaze[ptSelf.Pos.ix][ptSelf.Pos.iy];

            if ((! GamePaused)&&(GameStarted)) {

                switch(wParam) {
                    //
                    // Space = Fire straight ahead
                    //
                    case VK_SPACE:
                        if (InSanctuary(&ptSelf.Pos)) {
                            //
                            //If we shoot while in the Sanctuary, we get chastized.
                            //
                            sprintf(cBuff,GetStringRes(IDS_NOVIOLENCE));
                            PrintTextLine(cBuff);
                            sndPlaySound("sanctu.wav",SND_SYNC);
                            }
                        else {
                            //
                            // Otherwise, fine, shoot.
                            //
                            hDC=GetDC(hWndMaze);
                            hBMDC = CreateCompatibleDC(hDC);
                            SelectObject(hBMDC,hShotBM[1]);
                            BitBlt(hDC,(rMaze.left+rMaze.right)/2 - 12,
                                    (rMaze.top+rMaze.bottom)/2 - 12,
                                    24,24,
                                    hBMDC,0,0,SRCINVERT);

                            if (!sndPlaySound("laser.wav",SND_ASYNC)) {
                                MessageBeep((UINT)-1);
                                }

                            //
                            // Tell everyone else we're firing */
                            //
                            if (!SendNetMessage(0,0,&ptSelf.Pos,NP_SHOTFIRED)) {
                                MessageBox((HWND)NULL,GetStringRes(IDS_SNDPCKTFAIL),"WndProc",
                                           MB_ICONEXCLAMATION|MB_APPLMODAL);
                                }

                            bHitSomething = FALSE;

                            //
                            // Check to see if we hit any drones
                            //
                            ix = ptSelf.Pos.ix;
                            iy = ptSelf.Pos.iy;
                            Facing = ptSelf.Pos.Facing;

                            bSquare2 = bMaze[ix][iy];

                            //
                            // for each square until the next wall, */
                            //
                            do {
                                ptTrav = &ptDrones;
                                        /* Loop through all drones to see if it's there */
                                while (ptTrav->next != NULL) {
                                    ptTrav = ptTrav->next;

                                    if ((ix == ptTrav->Pos.ix)&&(iy == ptTrav->Pos.iy)) {
                                        if (!sndPlaySound("squish.wav",SND_ASYNC)) {
                                            MessageBeep((UINT)-1);
                                            }

                                        FadePic(ptTrav->iPicNum,
                                                ptTrav->Pos.Facing,
                                                hMazeDC,
                                                &ptTrav->rFrom,
                                                &ptTrav->rDrawn);
                                        ptTrav->Pos.ix = RandRange(5*X_CELLS_PER_SUBGRID,8*X_CELLS_PER_SUBGRID-1);
                                        ptTrav->Pos.iy = RandRange(7*Y_CELLS_PER_SUBGRID,8*Y_CELLS_PER_SUBGRID-1);
                                        i = RandRange(1,4);
                                        ptTrav->Pos.Facing = ((i == 1) ? NORTH :
                                                      ((i == 2) ? WEST :
                                                      ((i == 3) ? SOUTH :
                                                      EAST)));
                                        if (bBitmapDraw) {
                                            hMem = GlobalAlloc(GHND,sizeof(RECT));
                                            rPtr = (LPRECT)GlobalLock(hMem);
                                            *rPtr = ptTrav->rDrawn;
                                            PostMessage(hWndMaze,WM_COMMAND,IDM_REDRAW,(DWORD)rPtr);
                                            }
                                        ptSelf.iScore += iDronesKilled*iDroneSpeed*iDroneSpeed*iNumDrones/20;
                                        SendNetMessage(0,0,NULL,NP_SCORE);
                                        iDronesKilled++;
                                        PostMessage(hWndScore,WM_COMMAND,WC_UPDATESCORE,ptSelf.ulID);
                                        bHitSomething = TRUE;
                                        sprintf(cBuff,GetStringRes(IDS_FMT_YOUZAPPED),ptTrav->cUserName);
                                        PrintTextLine(cBuff);
                                        }
                                    }

                                bSquare1 = bSquare2;
                                bSquare2 = bMaze[ix = ADJ_X(ix,Facing)][iy = ADJ_Y(iy,Facing)];

                                } while (!(bHitSomething||
                                         ((bSquare1 & Facing)||(bSquare2 & BACK_TO_ABS(Facing)))));

                            if (!bHitSomething) {
                                Sleep(100);
                                BitBlt(hDC,(rMaze.left+rMaze.right)/2 - 12,
                                       (rMaze.top+rMaze.bottom)/2 - 12,
                                       24,24,
                                       hBMDC,0,0,SRCINVERT);
                                SelectObject(hBMDC,(HGDIOBJ)NULL);
                                }

                            DeleteDC(hBMDC);
                            ReleaseDC(hWndMaze,hDC);
                            }

                        break;

                    case VK_UP:
                        bSquare2 = bMaze[ADJ_X(ptSelf.Pos.ix,ptSelf.Pos.Facing)]
                                    [ADJ_Y(ptSelf.Pos.iy,ptSelf.Pos.Facing)];
                        if ((bSquare1 & ptSelf.Pos.Facing)||(bSquare2&BACK_TO_ABS(ptSelf.Pos.Facing))) {
                            if (!sndPlaySound("blocks.wav",SND_ASYNC)) {
                                MessageBeep((UINT) -1);
                                }
                            }
                        else {
                            ptLastPos = ptSelf.Pos;
                            ptSelf.Pos.ix = ADJ_X(ptSelf.Pos.ix,ptSelf.Pos.Facing);
                            ptSelf.Pos.iy = ADJ_Y(ptSelf.Pos.iy,ptSelf.Pos.Facing);
                            ptTrav = &ptDrones;
                            bKilled = FALSE;
                            while (ptTrav->next != NULL) {
                                ptTrav = ptTrav->next;
                                ptPos = ptTrav->Pos;
                                if ((!InSanctuary(&ptSelf.Pos))&&
                                    (ptSelf.Pos.ix == ptPos.ix)&&
                                    (ptSelf.Pos.iy == ptPos.iy)
                                   ) {
                                    sprintf(cBuff,GetStringRes(IDS_FMT_SLEW),ptTrav->cUserName);
                                    PrintTextLine(cBuff);
                                    iKilledByDrones++;
                                    ptSelf.iScore -= (iDroneSpeed) ?    36/iDroneSpeed/iDroneSpeed : 72;
                                    KillSelf();
                                    SendNetMessage(0,0,NULL,NP_SCORE);

                                    bKilled = TRUE;
                                    break;
                                    }
                                }

                            if (!bKilled) {
                                if (!SendNetMessage(0,0,&ptSelf.Pos,NP_MOVETO)) {
                                    MessageBox((HWND)NULL,GetStringRes(IDS_SNDPCKTFAIL),"MazeWndProc",
                                               MB_ICONEXCLAMATION|MB_APPLMODAL);
                                    }
                                bNewSelfInSanct = InSanctuary(&ptSelf.Pos);
                                if (bSelfInSanct != bNewSelfInSanct) {
                                    sprintf(cBuff, GetStringRes(IDS_FMT_SANCTUARY),
                                            GetStringRes2(bNewSelfInSanct ? IDS_ENTERED : IDS_LEFT));
                                    PrintTextLine(cBuff);
                                    bSelfInSanct = bNewSelfInSanct;
                                    }

                                PostMessage(hWnd,WM_COMMAND,IDM_REDRAW,(LPARAM)NULL);
                                hDC = GetDC(hWndTopView);
                                DrawTopView(hDC,FALSE);
                                ReleaseDC(hWndTopView,hDC);
                                PostMessage(hWndScore,WM_COMMAND,WC_UPDATEDIRECTION,0);
                                }
                            }
                        break;

                    case VK_LEFT:
                        ptLastPos = ptSelf.Pos;
                        ptSelf.Pos.Facing = LEFT_TO_ABS(ptSelf.Pos.Facing);
                        PostMessage(hWnd,WM_COMMAND,IDM_REDRAW,(LPARAM)NULL);
                        if (!SendNetMessage(0,0,&ptSelf.Pos,NP_MOVETO)) {
                            MessageBox((HWND)NULL,GetStringRes(IDS_SNDPCKTFAIL),"MazeWndProc",
                                        MB_ICONEXCLAMATION|MB_APPLMODAL);
                            }
                        hDC = GetDC(hWndTopView);
                        DrawTopView(hDC,FALSE);
                        ReleaseDC(hWndTopView,hDC);
                        PostMessage(hWndScore,WM_COMMAND,WC_UPDATEDIRECTION,0);
                        break;

                    case VK_RIGHT:
                        ptLastPos = ptSelf.Pos;
                        ptSelf.Pos.Facing = RIGHT_TO_ABS(ptSelf.Pos.Facing);
                        PostMessage(hWnd,WM_COMMAND,IDM_REDRAW,(LPARAM)NULL);
                        if (!SendNetMessage(0,0,&ptSelf.Pos,NP_MOVETO)) {
                            MessageBox((HWND)NULL,GetStringRes(IDS_SNDPCKTFAIL),"MazeWndProc",
                                             MB_ICONEXCLAMATION|MB_APPLMODAL);
                            }
                        hDC = GetDC(hWndTopView);
                        DrawTopView(hDC,FALSE);
                        ReleaseDC(hWndTopView,hDC);
                        PostMessage(hWndScore,WM_COMMAND,WC_UPDATEDIRECTION,0);
                        break;

                    case VK_DOWN:
                        bSquare2 = bMaze[ADJ_X(ptSelf.Pos.ix,BACK_TO_ABS(ptSelf.Pos.Facing))]
                                    [ADJ_Y(ptSelf.Pos.iy,BACK_TO_ABS(ptSelf.Pos.Facing))];
                        if ((bSquare2 & ptSelf.Pos.Facing)||(bSquare1&BACK_TO_ABS(ptSelf.Pos.Facing))) {
                            if (!sndPlaySound("blocks.wav",SND_ASYNC)) {
                                MessageBeep((UINT) -1);
                                }
                            }
                        else {
                            ptLastPos = ptSelf.Pos;

                            ptSelf.Pos.ix = ADJ_X(ptSelf.Pos.ix,BACK_TO_ABS(ptSelf.Pos.Facing));
                            ptSelf.Pos.iy = ADJ_Y(ptSelf.Pos.iy,BACK_TO_ABS(ptSelf.Pos.Facing));
                            ptTrav = &ptDrones;
                            bKilled = FALSE;
                            while (ptTrav->next != NULL) {
                                ptTrav = ptTrav->next;
                                ptPos = ptTrav->Pos;
                                if ((!InSanctuary(&ptSelf.Pos))&&
                                    (ptSelf.Pos.ix == ptPos.ix)&&
                                    (ptSelf.Pos.iy == ptPos.iy)
                                   ) {
                                    sprintf(cBuff,GetStringRes(IDS_FMT_SLEW),ptTrav->cUserName);
                                    PrintTextLine(cBuff);
                                    ptSelf.iScore -= (iDroneSpeed) ? 36/iDroneSpeed/iDroneSpeed : 72;
                                    SendNetMessage(0,0,NULL,NP_SCORE);

                                    iKilledByDrones++;
                                    KillSelf();
                                    bKilled = TRUE;
                                    break;
                                    }
                                }
                            if (!bKilled) {
                                if (!SendNetMessage(0,0,&ptSelf.Pos,NP_MOVETO)) {
                                    MessageBox((HWND)NULL,GetStringRes(IDS_SNDPCKTFAIL),"MazeWndProc",
                                               MB_ICONEXCLAMATION|MB_APPLMODAL);
                                    }
                                bNewSelfInSanct = InSanctuary(&ptSelf.Pos);
                                if (bSelfInSanct != bNewSelfInSanct) {
                                    sprintf(cBuff, GetStringRes(IDS_FMT_SANCTUARY),
                                            GetStringRes2(bNewSelfInSanct ? IDS_ENTERED : IDS_LEFT));
                                    PrintTextLine(cBuff);
                                    bSelfInSanct = bNewSelfInSanct;
                                    }
                                PostMessage(hWnd,WM_COMMAND,IDM_REDRAW,(LPARAM)NULL);
                                hDC = GetDC(hWndTopView);
                                DrawTopView(hDC,FALSE);
                                ReleaseDC(hWndTopView,hDC);
                                PostMessage(hWndScore,WM_COMMAND,WC_UPDATEDIRECTION,0);
                                }
                            }
                        break;

                    //
                    // Whisper something
                    //
                    case 'W':
                    case 'w':
                        iLoudness = iWhisperDist;
                        DialogBox(hInst,"INTONE_DLG", hWnd, IntoneDlg);
                        iWhisperDist = iLoudness;
                        break;
                    //
                    // Shout something
                    //
                    case 'S':
                    case 's':
                        iLoudness = iShoutDist;
                        DialogBox(hInst,"INTONE_DLG", hWnd, IntoneDlg);
                        iShoutDist = iLoudness;
                        break;
                    default:
                        SendMessage(hWnd,WM_KEYDOWN,wParam,lParam);
                        break;
                    }
                }
            break;

        case WM_MOVE:
            break;

        case WM_SIZE:
            GetClientRect(hWnd,&rMaze);
            Calc3DMaze();
            StretchBitmaps();
            if (bBitmapDraw) {
                if (((HWND)hMaze3DBM) != (HWND)NULL) {
                    DeleteObject(hMaze3DBM);
                    }
                hDC = GetDC(hWnd);
                if (hMaze3DBM != NULL) {
                    DeleteObject(hMaze3DBM);
                }
                hMaze3DBM = CreateCompatibleBitmap(hDC,rMaze.right-rMaze.left,
                                                   rMaze.bottom-rMaze.top);
                SelectObject(hMazeDC,hMaze3DBM);
                ReleaseDC(hWnd,hDC);
                }
            break;

        case WM_PAINT:
            hMem = GlobalAlloc(GHND,sizeof(RECT));
            rTemp = (LPRECT) GlobalLock(hMem);
            if (rTemp == NULL) {
                MessageBox((HWND)NULL,GetStringRes(IDS_MALLOCFAIL),"MazeWnd",
                           MB_ICONEXCLAMATION|MB_APPLMODAL);
                PostMessage(hWndMain,WM_CLOSE,0,0);
                }
            GetUpdateRect(hWnd,rTemp,FALSE);
            ValidateRect(hWnd,rTemp);
            PostMessage(hWnd,WM_COMMAND,IDM_REDRAW,(LPARAM)rTemp);
            break;

        case WM_CLOSE:
            KillTimer((HWND)NULL,uiTimer);
#ifdef WIN32
            //
            // Destroy the mailslot.
            //
            if (hMailSlot != (HANDLE)NULL) {
                CloseHandle(hMailSlot);
                }
#endif
            DestroyWindow(hWnd);
            if (((HWND)hMaze3DBM) != (HWND)NULL) {
                DeleteObject(hMaze3DBM);
                DeleteDC(hMazeDC);
                }
            if (hWnd == hWndMain) {
                PostQuitMessage(0);
                }
            break;

        default:
            return DefWindowProc(hWnd, Message, wParam, lParam);
            break;
        }

    return(0);
}


MAZPROTO.H

/*****************************************************************
MODULE: MazProto.h

This module contains function prototypes for maze lords. It should
ONLY be included after winmaze.h.
*****************************************************************/

#ifndef _MAZ_PROTO_H_
#define _MAZ_PROTO_H_

#include "winmaze.h"

#ifdef WIN16
                        /* Maze16.c */
BOOL MaskBlt(HDC hDCDest,int X, int Y, int nWidth, int nHeight,
             HDC hDCSrc, int XSrc, int YSrc, HBITMAP hbmMask, int xMask, int yMask, DWORD Rop3);

#endif

BOOL PolyDraw95(HDC, CONST LPPOINT, CONST LPBYTE, int);

                        /* Bitmap.c */
void FadePic(int iPicNum,BYTE bFacing,HDC hDC,LPRECT lprFrom, LPRECT lprDRect);
FullPicType FAR *NewFullPic(int iPicNum,FullPicType FAR *next);
FullPicType FAR *DeleteFullPic(FullPicType FAR *fptTrav);
void DrawClippedPic(int iPicNum,BYTE bFacingIndex,HDC hDC,LPRECT lprDRect,
                    LPRECT lprClip,LPRECT lprFrom, int iRelx, int iRely);
BOOL AddPic(int iPicNum);
BOOL DelPic(int iPicNum);

                        /* Draw.c */

HoleType FAR *DelHole(HoleType FAR *h);
HoleType FAR *NewHole(int x1,int x2,HoleType FAR *next);
HoleType FAR *DrawRect(HoleType FAR *htTrav,int iRelX,int iRelY, BYTE bRelDir);
void DrawMaze(HDC hDC,RECT FAR *rDraw);
void DrawTopView(HDC hDC,BOOL bRedraw);
void DrawPlayers(HDC hDC,PlayerType FAR *ptTrav2,RECT FAR *rUpd);
PlayerDrawType FAR *NewPlayerDraw(PlayerType FAR *p,LPRECT rDraw, LPRECT rClip,
                              int iRelx,int iRely,PlayerDrawType FAR *next);

                        /* Drones.c */

PlayerType FAR *DeletePlayer(PlayerType FAR *ptTrav);
PlayerType FAR *NewPlayer(unsigned long ulID,DWORD dwPID,LPSTR cUserName,
                      LPSTR cComputerName,int ix,int iy, BYTE Facing,
                      int iScore,int iPicNum,int iGridNum, int iGridxPos,
                      int iGridyPos,
                      PlayerType FAR *next);
int RandRange(int iLow, int iHigh);
void InitDrones(void);
LRESULT CALLBACK  MoveDrone(HWND hWnd, UINT wMsg, UINT idTimer, DWORD dwTime);

                        /* InitMaze.c */

void InitMaze(void);
void Calc3DMaze(void);
void InsertSubGrid(int SubGridNo,int iXPos, int iYPos);

                        /* Maze.c */

BOOL InSanctuary(PositionType FAR *Pos);
LONG FAR PASCAL WndProc(HWND hWnd, UINT Message, UINT wParam, LONG lParam);
int RegisterClasses(void);
void UnRegisterClasses(void);
void OneTimeInit(void);
void EachTimeInit(void);
void OneTimeShutdown(void);
void EachTimeShutdown(void);
void StartDemo(void);
void StopDemo(void);

                        /* MazeDlg.c */

BOOL FAR PASCAL DroneDlg(HWND hWndDlg, UINT Message, UINT wParam, LONG lParam);
BOOL FAR PASCAL AboutDlg(HWND hWndDlg, UINT Message, UINT wParam, LONG lParam);
BOOL FAR PASCAL IntoneDlg(HWND hWndDlg, UINT Message, UINT wParam, LONG lParam);
BOOL FAR PASCAL PlayerDlg(HWND hWndDlg,UINT Message,WPARAM wParam,LPARAM lParam);
void cwCenter(HWND hWnd,int top);

                        /* MazeWnd.c */

void KillSelf(void);
LONG FAR PASCAL MazeWndProc(HWND hWnd, UINT Message, UINT wParam, LONG lParam);
void StretchBitmaps(void);

                        /* Network.c */

unsigned long MemCRC(LPSTR buffer,int bytes);
BOOL SendNetMessage(unsigned long ulSender, unsigned long ulDest, LPVOID lpStruct, DWORD dwPacketType);
void MailWatchThread(HANDLE hMailSlot);
BOOL InitNetwork(void);
void ProcessNewMail(void);

                        /* ReadSGrd.c */
int ReadSubGrids(VOID);

                        /* ScoreWnd.c */

LONG FAR PASCAL ScoreWndProc(HWND hWnd, UINT Message, UINT wParam, LONG lParam);

                        /* TextWnd.c */

LONG FAR PASCAL TextWndProc(HWND hWnd, UINT Message, UINT wParam, LONG lParam);
void PrintTextLine(LPSTR cLine);
LPSTR lstrdup(LPSTR s);
                        /* TopWnd.c */

LONG FAR PASCAL TopViewWndProc(HWND hWnd, UINT Message, UINT wParam, LONG lParam);

#endif


NET.H

/********************************************************************
MODULE: Net.H

This module contains the definitions required for network packets, including
specialized structures, network message numbers, and so on.
********************************************************************/

#ifndef _NET_H_INC_
#define _NET_H_INC_

#define NP_NEWPLAYER        1
#define NP_REQUESTIDENTITY  2
#define NP_MOVETO           3
#define NP_SHOTFIRED        4
#define NP_LEAVINGGAME      5
#define NP_HITCONFIRM       6
#define NP_SCORE            7
#define NP_INTONE           8

#define MAX_PACKET_SIZE                400
#ifndef MAX_COMPUTERNAME_LENGTH
#define MAX_COMPUTERNAME_LENGTH         32
#endif
#define MAX_USERNAME_LENGTH             32

                /* A network packet is all crunched together, leading directly
                   from the header on into data. We make cData the last element
                   of the header, then take a pointer to it, and viola!
                   beginning of message-specific information.
                 */
struct _s_NetPacketType {
  unsigned long ulSender;
  unsigned long ulDest;
  DWORD dwPacketType;
  char cData;           // We'll take a pointer to this for start of info.
  };
typedef struct _s_NetPacketType NetPacketType;

struct _s_PlayerInfoType {
  unsigned long ulID;
  DWORD dwPID;
  char cUserName[MAX_USERNAME_LENGTH];
  char cComputerName[MAX_COMPUTERNAME_LENGTH];
  int ix, iy;
  BYTE bFacing;
  int iScore;
  int iPicNum;
  int iGridNum;
  POINT pGridLoc;
  };

typedef struct _s_PlayerInfoType PlayerInfoType;

#endif


NETWORK.C

/***********************************************************************
File:   Network.c

Abstract:

    This is the network communications module for MazeLords. It contains functions
    to implement mailslots, as well as the various protocols and so-on required
    for communications.

Contents:

    MemCRC() -- Generates a 'unique' LONG for different data structures
    SendNetMessage() -- Transmit a pre-made structure to other players
    MailWatchThread() -- Thread process to watch for new mail
    InitNetwork() -- Create mailslot, Send out ID message to network.
    ProcessNewMail() -- Process newly recieved data packets

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

#include <windows.h>
#include <mmsystem.h>
#include "winmaze.h"
#include "mazproto.h"
#include "net.h"
#include "crctable.h"

/*=====================================================================
Function:   MemCRC()

Inputs:     Buffer & buffer length to CRC

Outputs:    Returns single Long corresponding to CRC for structure

Abstract:
    The CRC should provide a unique ID for each player, provided ANY
    information in their data structures is unique. Each player is
    responsible for generating their ID & transmitting it to the other
    players.
======================================================================*/

unsigned long MemCRC(
    LPSTR buffer,
    int bytes
    )
{
    unsigned long crc = 0xFFFFFFFF;
    LPSTR p;

    for ( p = buffer; bytes > 0; bytes-- ) {
        crc = ( crc >> 8 ) ^ CRCtable[(unsigned char) ((unsigned char) crc ^ *p++ ) ];
        }

    return ( crc ^ 0xFFFFFFFF );
}

/*=====================================================================
Function:   SendNetMessage()

Inputs:     Source, destination, structure to send, packet type

Outputs:    Returns success/failure

Abstract:
    SendNetMessage will send a message to all mazelords mailslots in the
    current domain.  Note that this message is broadcast once. It is up
    to the individual recipients to decide whether it is addressed to them,
    or a general message (sent to -1).
======================================================================*/

BOOL SendNetMessage(
    unsigned long ulSender,
    unsigned long ulDest,
    LPVOID lpStruct,
    DWORD dwPacketType
    )
{
    BOOL bResult=TRUE;
    HANDLE hFile;
    DWORD dwScratch,dwLength;
    char cBuff[MAX_PACKET_SIZE];
    LPSTR lpStr;
    NetPacketType FAR *lpN;
    PlayerInfoType FAR *lppiInfo,FAR *lppiInfo2;
    PositionType FAR *lpptPos,FAR *lpptPos2;
    int FAR *iScore;
    IntoneType FAR *itIntone;

    if (!bNetworked) {
        return(TRUE);
        }

    lpN = (NetPacketType FAR *) cBuff;
    lpN->ulSender = (ulSender)? ulSender : ptSelf.ulID;
    lpN->ulDest = ulDest;
    lpN->dwPacketType = dwPacketType;
    dwLength = sizeof(NetPacketType);

    //
    // determine size, type of packet and fill out info accordingly.
    //
    lpStr = (LPSTR) &lpN->cData;

    switch(dwPacketType) {

        case NP_NEWPLAYER:
            lppiInfo = (PlayerInfoType FAR *) lpStr;
            lppiInfo2 = (PlayerInfoType FAR *) lpStruct;
            *lppiInfo = *lppiInfo2;
            dwLength += sizeof(PlayerInfoType);
            break;

        case NP_REQUESTIDENTITY:
        case NP_LEAVINGGAME:
        case NP_HITCONFIRM:
            break;

        case NP_MOVETO:
        case NP_SHOTFIRED:
            lpptPos = (PositionType FAR *) lpStr;
            lpptPos2 = (PositionType FAR *) lpStruct;
            *lpptPos = *lpptPos2;
            dwLength += sizeof(PositionType);
            break;

        case NP_SCORE:
            iScore = (int FAR *) lpStr;
            *iScore = ptSelf.iScore;
            dwLength += sizeof(int);
            break;

        case NP_INTONE:
            itIntone = (IntoneType FAR *) lpStr;
            *itIntone = *((IntoneType FAR *) lpStruct);
            dwLength += sizeof(IntoneType);
            dwLength -= (131 - itIntone->dwLen);
            break;

        default:
            MessageBox((HWND)NULL,GetStringRes(IDS_BADPCKTXMITFAIL),"SendNetMessage",
                       MB_ICONEXCLAMATION|MB_APPLMODAL);
            break;
        }

#ifdef WIN32
    hFile = CreateFile("\\\\*\\mailslot\\mazemslt",
                     GENERIC_WRITE,
                     FILE_SHARE_READ,
                     NULL,
                     OPEN_EXISTING,
                     FILE_ATTRIBUTE_NORMAL,
                     NULL);

    bResult = (hFile != INVALID_HANDLE_VALUE);

    bResult = bResult &&
              WriteFile(hFile,cBuff,dwLength+1,&dwScratch,NULL);

    return(bResult && CloseHandle(hFile));
#else // else it's any operating system except WIN32
    return(TRUE);
#endif
}

/*=====================================================================
Function:   MailWatchThread()

Inputs:     Handle to mailslot to watch

Outputs:    none

Abstract:
    This is a thread which is spawned. As soon as it sees a mail message
    is queued up, it sends a message to the main window notifying Maze
    Lords that new mail has been received.  This is done 5 times a second.
======================================================================*/

void MailWatchThread(
    HANDLE hMailSlot
    )
{
    DWORD cbMaxMsg,cbNextMsg,cMsg,dwReadTimeout;
#ifdef WIN32
    while(TRUE) {

        Sleep(200);
        if (GetMailslotInfo(hMailSlot,&cbMaxMsg,&cbNextMsg,&cMsg,&dwReadTimeout)) {
            if (cMsg > 0) {
                PostMessage(hWndMain,WM_COMMAND,IDT_NEWMAIL,0);
                }
            }
        }
#endif

    //
    // We shouldn't ever get here.
    //
    return;
}

/*=====================================================================
Function:   InitNetwork()

Inputs:     none

Outputs:    returns success/failure

Abstract:
    InitNetwork takes care of creating a mailslot for this user, and
    sending out a packet saying that we're joining the game.
    FALSE indicates that there was a fatal error starting the network.
======================================================================*/

BOOL InitNetwork(
    void
    )
{
    DWORD dwScratch;
    BOOL bRet;
    PlayerInfoType ptPlayScr;

    bRet = TRUE;

    //
    // initialize player list
    //
    ptPlayers.next = (PlayerType FAR *)NULL;

    if (!bNetworked) {
        hMailSlot = NULL;
        return(TRUE);
        }

#ifdef WIN32
    hMailSlot = CreateMailslot("\\\\.\\mailslot\\mazemslt",0,0,NULL);
    if (hMailSlot == INVALID_HANDLE_VALUE) {
        MessageBox(NULL,GetStringRes(IDS_CREMSLOTFAIL),
GetStringRes2(IDS_NETINIT), MB_ICONEXCLAMATION|MB_APPLMODAL);
        bRet = FALSE;
        }

    if (! CreateThread((LPSECURITY_ATTRIBUTES) NULL,
                       0,
                       (LPTHREAD_START_ROUTINE)MailWatchThread,
                       (LPVOID)hMailSlot,
                       0,&dwScratch)) {
        MessageBox(NULL,GetStringRes(IDS_CREMWATCHTHRFAIL),
GetStringRes2(IDS_NETINIT), MB_ICONEXCLAMATION|MB_APPLMODAL);
        bRet = FALSE;
        }
#endif
    ptPlayScr.ulID = ptSelf.ulID;
    ptPlayScr.dwPID = ptSelf.dwPID;
    lstrcpy(ptPlayScr.cUserName,ptSelf.cUserName);
    lstrcpy(ptPlayScr.cComputerName,ptSelf.cComputerName);
    ptPlayScr.ix = ptSelf.Pos.ix;
    ptPlayScr.iy = ptSelf.Pos.iy;
    ptPlayScr.bFacing = ptSelf.Pos.Facing;
    ptPlayScr.iScore = ptSelf.iScore;
    ptPlayScr.iPicNum = ptSelf.iPicNum;
    ptPlayScr.iGridNum = ptSelf.iGridNum;
    ptPlayScr.pGridLoc = ptSelf.pGridLoc;

    return(bRet&&SendNetMessage(0,0,(LPVOID) &ptPlayScr,NP_NEWPLAYER));
}

/*=====================================================================
Function:   ProcessNewMail()

Inputs:     none

Outputs:    none

Abstract:
    ProcessNewMail is responsible for updating character positions, asking
    for more information, etc.
======================================================================*/

void ProcessNewMail(
    void
    )
{
    DWORD cbMaxMsg,cbNextMsg,cMsg,dwReadTimeout;
    NetPacketType FAR *NPkt;
    PlayerInfoType FAR *piPlayerPacket,piScratch;
    PlayerType FAR *ptTrav;
    PositionType FAR *ptPosPacket;
    BOOL bFound,bDrawMessageSent;
    int ix,iy;
    BYTE bSquare1, bSquare2,Facing;
    char cBuff[257],cNetMessBuff[400];
    int FAR *iScore;
    IntoneType FAR *itIntone;

#ifdef WIN32
    GetMailslotInfo(hMailSlot,&cbMaxMsg,&cbNextMsg,&cMsg,&dwReadTimeout);
    bDrawMessageSent = FALSE;

    while (cMsg) {
        if (!ReadFile(hMailSlot,cNetMessBuff,cbNextMsg,&cbMaxMsg,(LPOVERLAPPED) NULL)) {
            MessageBox(NULL,GetStringRes(IDS_RDMSLOTFAIL),"ProcessNewMail",
                       MB_ICONEXCLAMATION|MB_APPLMODAL);
            }

        NPkt = (NetPacketType FAR *) cNetMessBuff;

        //
        // Don't process the message if it's one of our own
        //
        if ((NPkt->ulSender != ptSelf.ulID)&&
            ((NPkt->ulDest == ptSelf.ulID)||(NPkt->ulDest == 0))
           ) {

            piPlayerPacket = (PlayerInfoType FAR *) &(NPkt->cData);
            ptPosPacket = (PositionType FAR *) &(NPkt->cData);
            iScore = (int FAR *) &(NPkt->cData);
            itIntone = (IntoneType FAR *) &(NPkt->cData);

            bFound = FALSE;
            ptTrav = &ptPlayers;

            while (ptTrav->next != NULL) {
                if (ptTrav->next->ulID == NPkt->ulSender) {
                    bFound = TRUE;
                    break;
                    }
                ptTrav = ptTrav->next;
                }

            if ((!bFound)&&(NPkt->dwPacketType != NP_NEWPLAYER)) {
                if(!SendNetMessage(0,NPkt->ulSender,NULL,NP_REQUESTIDENTITY)) {
                    MessageBox(NULL,GetStringRes(IDS_SNDPCKTFAIL),"ProcessNewMail",
                               MB_ICONEXCLAMATION|MB_APPLMODAL);
                    }
                }

            switch (NPkt->dwPacketType) {

                case (NP_NEWPLAYER):
                    if (!bFound) {
                        ptTrav->next = NewPlayer(piPlayerPacket->ulID,
                                     piPlayerPacket->dwPID,
                                     piPlayerPacket->cUserName,
                                     piPlayerPacket->cComputerName,
                                     piPlayerPacket->ix,
                                     piPlayerPacket->iy,
                                     piPlayerPacket->bFacing,
                                     piPlayerPacket->iScore,
                                     piPlayerPacket->iPicNum,
                                     piPlayerPacket->iGridNum,
                                     piPlayerPacket->pGridLoc.x,
                                     piPlayerPacket->pGridLoc.y,
                                     ptTrav->next);
                        if (!AddPic(piPlayerPacket->iPicNum)) {
                            ptTrav->next->iPicNum = PIC_DEFAULT;
                            sprintf(cBuff, GetStringRes(IDS_FMT_BADPICNUM),
                                    piPlayerPacket->iPicNum,piPlayerPacket->cComputerName);
                            PrintTextLine(cBuff);
                            AddPic(PIC_DEFAULT);
                            }

                        //
                        // After we add the user to our own database,
                        // let him know we exist.
                        //
                        sprintf(cBuff,GetStringRes(IDS_FMT_JOIN),ptTrav->next->cUserName);
                        PrintTextLine(cBuff);

                        piScratch.ulID = ptSelf.ulID;
                        piScratch.dwPID = ptSelf.dwPID;
                        strcpy(piScratch.cUserName,ptSelf.cUserName);
                        strcpy(piScratch.cComputerName,ptSelf.cComputerName);
                        piScratch.ix = ptSelf.Pos.ix;
                        piScratch.iy = ptSelf.Pos.iy;
                        piScratch.bFacing = ptSelf.Pos.Facing;
                        piScratch.iScore = ptSelf.iScore;
                        piScratch.iPicNum = ptSelf.iPicNum;
                        piScratch.iGridNum = ptSelf.iGridNum;
                        piScratch.pGridLoc = ptSelf.pGridLoc;
                        if (!SendNetMessage(0,NPkt->ulSender,&piScratch,NP_NEWPLAYER)) {
                            MessageBox(NULL,GetStringRes(IDS_SNDPCKTFAIL),"ProcessNewMail",
                                             MB_ICONEXCLAMATION|MB_APPLMODAL);
                            }

                        //
                        // Finally, do a redraw to make sure if he's in
                        // line of sight we see him. */
                        //
                        PostMessage(hWndMaze,WM_COMMAND,IDM_DRAWPLAYERS,0);
                        PostMessage(hWndScore,WM_COMMAND,WC_ADDSCORE,ptTrav->next->ulID);
                        }
                    break;

                case (NP_REQUESTIDENTITY):
                    piScratch.ulID = ptSelf.ulID;
                    piScratch.dwPID = ptSelf.dwPID;
                    strcpy(piScratch.cUserName,ptSelf.cUserName);
                    strcpy(piScratch.cComputerName,ptSelf.cComputerName);
                    piScratch.ix = ptSelf.Pos.ix;
                    piScratch.iy = ptSelf.Pos.iy;
                    piScratch.bFacing = ptSelf.Pos.Facing;
                    piScratch.iScore = ptSelf.iScore;
                    piScratch.iPicNum = ptSelf.iPicNum;
                    piScratch.iGridNum = ptSelf.iGridNum;
                    piScratch.pGridLoc = ptSelf.pGridLoc;
                    if (!SendNetMessage(0,NPkt->ulSender,&piScratch,NP_NEWPLAYER)) {
                        MessageBox(NULL,GetStringRes(IDS_SNDPCKTFAIL),"ProcessNewMail",
                                             MB_ICONEXCLAMATION|MB_APPLMODAL);
                        }

                    break;

                case (NP_MOVETO):
                    if (bFound) {
                        ptTrav->next->Pos = *ptPosPacket;
                        if (ptTrav->next->Drawn) {
                            if (!bBitmapDraw) {
                                InvalidateRect(hWndMaze,&ptTrav->next->rDrawn,TRUE);
                                }
//
//BUGBUG -- drawing problem for bitmaps
//
                            ptTrav->next->Drawn=FALSE;
                            }
                        else {
                            if (!bDrawMessageSent) {
                                PostMessage(hWndMaze,WM_COMMAND,IDM_DRAWPLAYERS,0);
                                bDrawMessageSent = TRUE;
                                }
                            }
                        PostMessage(hWndScore,WM_COMMAND,WC_UPDATEDIRECTION,ptTrav->next->ulID);
                        }
                    break;

                case (NP_SHOTFIRED):
                    if (bFound) {

                        PlaySound("othrlasr.wav",NULL,SND_FILENAME|SND_ASYNC);

                        //
                        // if the shot was fired in the same 'x' or 'y'
                        // plane, we may have been hit!
                        //
                        if ((ptPosPacket->ix == ptSelf.Pos.ix)||
                            (ptPosPacket->iy == ptSelf.Pos.iy)
                           ) {

                            ix = ptPosPacket->ix;
                            iy = ptPosPacket->iy;
                            Facing = ptPosPacket->Facing;

                            bSquare2 = bMaze[ix][iy];

                            do {
                                //
                                // if we're hit, handle it!
                                //
                                if ((!InSanctuary(&ptSelf.Pos))&&(ptSelf.Pos.ix == ix)&&(ptSelf.Pos.iy == iy)) {
                                    if (!SendNetMessage(0,NPkt->ulSender,NULL,NP_HITCONFIRM)) {
                                        MessageBox(NULL,GetStringRes(IDS_SNDPCKTFAIL),"MazeWndProc",
                                             MB_ICONEXCLAMATION|MB_APPLMODAL);
                                        }
                                    sprintf(cBuff,GetStringRes(IDS_FMT_ZAPPEDYOU),ptTrav->next->cUserName);
                                    PrintTextLine(cBuff);
                                    KillSelf();
                                    break;
                                    }
                                //
                                // otherwise, keep checking.
                                //
                                bSquare1 = bSquare2;
                                bSquare2 = bMaze[ix = ADJ_X(ix,Facing)][iy = ADJ_Y(iy,Facing)];
                                } while (!((bSquare1 & Facing)||(bSquare2 & BACK_TO_ABS(Facing))));
                            }
                        }

                    break;

                case (NP_LEAVINGGAME):
//
// ***BUGBUG*** Might cause problems if character leaving is on screen
//
                    if (bFound) {
                        sprintf(cBuff,GetStringRes(IDS_FMT_LEFT),ptTrav->next->cUserName);
                        PrintTextLine(cBuff);
                        PostMessage(hWndScore,WM_COMMAND,WC_DELETESCORE,ptTrav->next->ulID);
                        ptTrav->next = DeletePlayer(ptTrav->next);
                        PostMessage(hWndMaze,WM_COMMAND,IDM_DRAWPLAYERS,0);
                        }
                    break;

                case (NP_HITCONFIRM):
                    PlaySound("squish.wav",NULL,SND_FILENAME|SND_ASYNC);
                    if (bFound) {
                        FadePic(ptTrav->next->iPicNum,
                                ptTrav->next->Pos.Facing,
                                hMazeDC,
                                &ptTrav->next->rFrom,
                                &ptTrav->next->rDrawn);
                        sprintf(cBuff,GetStringRes(IDS_FMT_HIT),ptTrav->next->cUserName);
                        PrintTextLine(cBuff);
                        }
                    ptSelf.iScore += 50;
                    iPlayersKilled++;

                    SendNetMessage(0,0,NULL,NP_SCORE);
                    PostMessage(hWndScore,WM_COMMAND,WC_UPDATESCORE,ptSelf.ulID);
                    break;

                case (NP_SCORE):
                    if (bFound) {
                        ptTrav->next->iScore = *iScore;
                        PostMessage(hWndScore,WM_COMMAND,WC_UPDATESCORE,ptTrav->next->ulID);
                        }
                    break;

                case NP_INTONE:
                    if (bFound) {
                        ix = ptSelf.Pos.ix - itIntone->Pos.ix;
                        iy = ptSelf.Pos.iy - itIntone->Pos.iy;
                        if ((ix*ix + iy*iy) <= (itIntone->iLoudness * itIntone->iLoudness)) {
                            sprintf(cBuff,"[%s] - \"%s\"",ptTrav->next->cUserName,
                                    itIntone->cBuff);
                            PrintTextLine(cBuff);
                            }
                        }
                    break;

                default:
                    MessageBox(NULL,GetStringRes(IDS_BADMSG),"ProcessNewMail",
                                MB_ICONEXCLAMATION|MB_APPLMODAL);
                    break;
                }
            }

        GetMailslotInfo(hMailSlot,&cbMaxMsg,&cbNextMsg,&cMsg,&dwReadTimeout);
        }

#endif // end if WIN32. This whole function is not present for WIN16.

    return;

}


BITMAP.C

/***********************************************************************
File:   BitMap.c
Date:   5-23-92

Abstract:

    This file contains functions pertaining to drawing a bitmap on the screen.

Contents:

    FadePic() -- Slowly fade a picture on the screen to the background
    DrawClippedPic() -- Draw a picture, clipped to a specified rectangle
    NewFullPic() -- Create a record of type FullPicType
    DeleteFullPic() -- Delete a record of type FullPicType
    AddPic() -- Load a new picture bitmap set
    DelPic() -- Remove a picture bitmap set from memory.

Revision History:

************************************************************************/
#include "winmaze.h"
#include "mazproto.h"

#define MASKROP(fore,back) (DWORD)(((back << 8)&0xFF000000) | fore)
#define MASKROP2(black,white) (DWORD)(((black << 8)&0xFF000000) | white) & 0xFFFF0000

//
// PicSpec goes:
//      Base BM filename, iPicNum for the picture, offsets for each facing:
//              {width, depth, left offset, top offset}
//
PicSpecType PicSpec[] = {
   {"FISH", PIC_FISH,
        {
            {35,41,30,42},
            {62,35,15,49},
            {21,31,36,51},
            {62,31,21,53}
        }},
   {"ROBO", PIC_ROBOT,
        {
            {40,107,19,43},
            {50,107,13,43},
            {43,106,23,44},
            {48,107,30,43}
        }},
   {"SMIL", PIC_SMILEY,
        {
            {62,62,17,40},
            {68,62,17,40},
            {62,62,17,40},
            {70,62,9,40}
        }},
   {"REAP", PIC_REAPER,
        {
            {90,114,5,26},
            {85,114,8,26},
            {90,114,5,26},
            {85,114,8,26}
        }}
   };

int iNumPicSpecs = sizeof(PicSpec) / sizeof(PicSpecType);

/*=====================================================================
Function: FadePic()

Inputs: Picture #, facing, and clipping rectangle to fade.

Outputs:none

Abstract:
    Fade will fade out a given bitmap by XORing its bitmap with a 50% grey tone.
======================================================================*/

void FadePic(
    int iPicNum,
    BYTE bFacing,
    HDC hDC,
    LPRECT lprFrom,
    LPRECT lprDRect
    )
{
    FullPicType FAR *fptTrav;
    BOOL bFound;
    HDC hFadeDC,hMaskDC,hbmFadeDC;
    HBITMAP hbmFade;
    int DestWidth,DestHeight,SrcWidth,SrcHeight;
    int iRelDirIndex,i,j;

    fptTrav = &fptPic;
    bFound = FALSE;

    iRelDirIndex=2;
    while(bFacing != ptSelf.Pos.Facing) {
        iRelDirIndex = (iRelDirIndex+1)%4;
        bFacing = LEFT_TO_ABS(bFacing);
        }

    while(fptTrav->next != NULL) {
        fptTrav = fptTrav->next;
        if (fptTrav->iPicNum == iPicNum) {
            bFound = TRUE;
            break;
            }
        }

    if (bFound) {
        hMaskDC = CreateCompatibleDC(hDC);
        hFadeDC = CreateCompatibleDC(hDC);
        hbmFadeDC = CreateCompatibleDC(hFadeDC);
        SrcWidth = lprFrom->right-lprFrom->left+1;
        SrcHeight = lprFrom->bottom-lprFrom->top+1;
        DestWidth = lprDRect->right-lprDRect->left+1;
        DestHeight = lprDRect->bottom-lprDRect->top+1;
        hbmFade = CreateCompatibleBitmap(hFadeDC,DestWidth,DestHeight);
        SelectObject(hbmFadeDC,hbmFade);
        SelectObject(hFadeDC,hFadeBM[0]);
        SelectObject(hMaskDC,fptTrav->M[iRelDirIndex].hBitmap);

        //
        // Copy the Fade pattern into the Fade Bitmap
        //
        for(i=0;i<DestWidth;i+=PIC_X) {
            for(j=0;j<DestHeight;j+=PIC_Y) {
                BitBlt(hbmFadeDC,i,j,
                       ((i+PIC_X)<=DestWidth) ? PIC_X : DestWidth,
                       ((j+PIC_Y)<=DestHeight) ? PIC_Y : DestHeight,
                       hFadeDC,0,0,
                       SRCCOPY);
                }
            }

        //
        // StretchBlt the mask onto the fade
        //
        StretchBlt(hbmFadeDC,0,0,DestWidth,DestHeight,
                   hMaskDC,0,0,SrcWidth,SrcHeight,
                   NOTSRCERASE);

        //
        // BitBlt the result onto the screen.
        //
        BitBlt(hDC,lprDRect->left,lprDRect->top,DestWidth,DestHeight,
               hbmFadeDC,0,0,
               SRCPAINT);

        DeleteDC(hFadeDC);
        DeleteDC(hMaskDC);
        DeleteDC(hbmFadeDC);
        DeleteObject(hbmFade);
        DeleteObject(hFadeBM[0]);
        hFadeBM[0] = LoadBitmap(hInst,(LPCTSTR)((0)?"FADE2":"FADE1"));

        }

    return;

}

/*=====================================================================
Function: DrawClippedPic()

Inputs: Picture #, Relative facing, clip/dest rectangles,

Outputs:none

Abstract:
    DrawClippedPic will draw the portions of picture # iPicNum visible through
    the clipping window.  Player bitmaps smaller than a full panel in size
    Will need to be scaled.
======================================================================*/

void DrawClippedPic(
    int iPicNum,
    BYTE bFacingIndex,
    HDC hDC,
    LPRECT lprDRect,
    LPRECT lprClip,
    LPRECT lprFrom,
    int iRelx,
    int iRely
    )

{
    RECT rSrc;
    FullPicType FAR *fptTrav;
    BOOL bFound;
    HDC hPicDC,hMaskDC;
    int DestWidth,DestHeight;
    POINT pBlt[3];
    //
    // the floats are offset ratio's, ie percentage of the total
    // width/depth to add to each side of the source rect.
    //
    float sWidth,sDepth,dWidth,dDepth;
    float fRelScaling;

    if (lprClip->right < lprClip->left+2) {
        return;
        }

    fptTrav = &fptPic;
    bFound = FALSE;

    //
    // Check to see if the Picture we're supposed to draw has been loaded.
    //
    AddPic(iPicNum);

    while(fptTrav->next != NULL) {
        fptTrav = fptTrav->next;
        if (fptTrav->iPicNum == iPicNum) {
            bFound = TRUE;
            break;
            }
        }

    if (bFound) {

        //
        // bFound will now indicates whether the picture has been pre-stretched
        //

        bFound = (PreStretch[iPicNum][iRely].P[bFacingIndex].hBitmap != (HANDLE) NULL);
        bFound = bFound && (PreStretch[iPicNum][iRely].M[bFacingIndex].hBitmap != (HANDLE) NULL);

        //
        // We'll need the source dimensions whether it's prestretched or not.
        //
        if (bFound) {
            sWidth = (float) PreStretch[iPicNum][iRely].P[bFacingIndex].xSize;
            sDepth = (float) PreStretch[iPicNum][iRely].P[bFacingIndex].ySize;
            }
        else {
            sWidth = (float) fptTrav->P[bFacingIndex].xSize;
            sDepth = (float) fptTrav->P[bFacingIndex].ySize;
            }

        //
        // Assume that we want the ENTIRE source bitmap.
        //
        rSrc.left = 0;
        rSrc.top = 0;
        rSrc.right = (INT) sWidth-1;
        rSrc.bottom = (INT) sDepth-1;

        //
        // fRelScaling*an offset gives # of pixels of REAL offset.
        //
        fRelScaling = ((float) (lprDRect->bottom - lprDRect->top))/(float)PIC_Y;

        if (bFound) {
            lprDRect->left += PreStretch[iPicNum][iRely].P[bFacingIndex].xOrg;
            lprDRect->top += PreStretch[iPicNum][iRely].P[bFacingIndex].yOrg;
            lprDRect->right = lprDRect->left + (int) sWidth;
            lprDRect->bottom = lprDRect->top + (int) sDepth;
            }
        else {
            lprDRect->left += (int) (fRelScaling*fptTrav->P[bFacingIndex].xOrg);
            lprDRect->top += (int) (fRelScaling*fptTrav->P[bFacingIndex].yOrg);
            lprDRect->right = lprDRect->left + (int) (fRelScaling*sWidth);
            lprDRect->bottom = lprDRect->top + (int) (fRelScaling*sDepth);
            }

        dWidth = (float) lprDRect->right - lprDRect->left;
        dDepth = (float) lprDRect->bottom - lprDRect->top;

        //
        // Rather than doing a clipped bitblt, we just modify the source
        // rectangle to be the parts which are visible.
        //
        if (lprDRect->left < lprClip->left) {
            rSrc.left += (LONG) (sWidth*((float) (lprClip->left - lprDRect->left))/dWidth);
            lprDRect->left = lprClip->left;
            }
        if (lprDRect->top < lprClip->top) {
            rSrc.top += (LONG) (sDepth*((float) lprClip->top - lprDRect->top)/dDepth);
            lprDRect->top = lprClip->top;
            }
        if (lprDRect->right > lprClip->right) {
            rSrc.right += (LONG) (sWidth*((float) lprClip->right - lprDRect->right)/dWidth);
            lprDRect->right = lprClip->right;
            }
        if (lprDRect->bottom > lprClip->bottom) {
            rSrc.bottom += (LONG) (sDepth*((float) lprClip->bottom - lprDRect->bottom)/dDepth);
            lprDRect->bottom = lprClip->bottom;
            }
        //
        // Set the Dest variables to the TRUE physical dest width/depth
        //
        DestWidth = lprDRect->right - lprDRect->left;
        DestHeight = lprDRect->bottom - lprDRect->top+1;

        if ((DestWidth < 1)||(DestHeight < 1)) {
            return;
            }

        hPicDC = CreateCompatibleDC(hDC);
        hMaskDC = CreateCompatibleDC(hPicDC);

        if (bFound) {
            //
            // If the bitmaps are pre-stretched...
            //

            SelectObject(hPicDC,PreStretch[iPicNum][iRely].P[bFacingIndex].hBitmap);
            SelectObject(hMaskDC,PreStretch[iPicNum][iRely].M[bFacingIndex].hBitmap);

            BitBlt(hDC,lprDRect->left,lprDRect->top,DestWidth,DestHeight,
                   hMaskDC,rSrc.left,rSrc.top,
                   SRCAND);
            BitBlt(hDC,lprDRect->left,lprDRect->top,DestWidth,DestHeight,
                   hPicDC,rSrc.left,rSrc.top,
                   SRCPAINT);
            }
        else {
            //
            // If the bitmaps are NOT pre-stretched.
            //
            SelectObject(hPicDC,fptTrav->P[bFacingIndex].hBitmap);
            SelectObject(hMaskDC,fptTrav->M[bFacingIndex].hBitmap);
            //
            // stretch the mask onto the screen
            //
            pBlt[0].x = lprDRect->left;
            pBlt[0].y = lprDRect->top;
            pBlt[1].x = lprDRect->right;
            pBlt[1].y = lprDRect->top;
            pBlt[2].x = lprDRect->left;
            pBlt[2].y = lprDRect->bottom;

            StretchBlt(hDC,lprDRect->left,lprDRect->top,DestWidth,DestHeight,
                       hMaskDC,rSrc.left,rSrc.top,rSrc.right-rSrc.left+1,rSrc.bottom-rSrc.top+1,
                       SRCAND);
            //
            // then the picture as well
            //
            StretchBlt(hDC,lprDRect->left,lprDRect->top,DestWidth,DestHeight,
                       hPicDC,rSrc.left,rSrc.top,rSrc.right-rSrc.left+1,rSrc.bottom-rSrc.top+1,
                       SRCPAINT);
            }

        DeleteDC(hPicDC);
        DeleteDC(hMaskDC);
        }
    else {
        MessageBox(NULL,"Unknown Picture Draw Requested","DrawClippedPic",
                   MB_ICONEXCLAMATION|MB_APPLMODAL);
        }

    *lprFrom = rSrc;

    return;
}

/*=====================================================================
Function: NewFullPic()

Inputs: Picture # to load, pointer to next

Outputs:Returns a pointer to an initialized FullPicType data structure

Abstract:
======================================================================*/

FullPicType FAR *NewFullPic(
    int iPicNum,
    FullPicType FAR *next
    )
{
    FullPicType FAR *fptTemp;
    int i;
    HANDLE hMem;

    hMem = GlobalAlloc(GHND,sizeof(FullPicType));
    fptTemp = (FullPicType FAR *) GlobalLock(hMem);
    if (fptTemp == NULL) {
        MessageBox((HANDLE)NULL,"Out of memory: unable to create NewFullPic.","NewFullPic",
               MB_ICONEXCLAMATION|MB_APPLMODAL);
        }
    else {
        for(i=0;i<4;i++) {
            fptTemp->P[i].hBitmap = (HANDLE) NULL;
            fptTemp->P[i].xSize = PicSpec[iPicNum].PicPos[i].xSize;
            fptTemp->P[i].ySize = PicSpec[iPicNum].PicPos[i].ySize;
            fptTemp->P[i].xOrg = PicSpec[iPicNum].PicPos[i].xOrg;
            fptTemp->P[i].yOrg = PicSpec[iPicNum].PicPos[i].yOrg;
            fptTemp->M[i].hBitmap = (HANDLE) NULL;
            fptTemp->M[i].xSize = PicSpec[iPicNum].PicPos[i].xSize;
            fptTemp->M[i].ySize = PicSpec[iPicNum].PicPos[i].ySize;
            fptTemp->M[i].xOrg = PicSpec[iPicNum].PicPos[i].xOrg;
            fptTemp->M[i].yOrg = PicSpec[iPicNum].PicPos[i].yOrg;
            }
        fptTemp->iPicNum = iPicNum;
        fptTemp->next = next;
        }

    return(fptTemp);

}

/*=====================================================================
Function: DeleteFullPic()

Inputs: pointer to FullPicType to free

Outputs:Returns pointer to ->next element of deleted pic.

Abstract:
======================================================================*/

FullPicType FAR *DeleteFullPic(
    FullPicType FAR *fptTrav
    )
{
    int i;
    FullPicType FAR *fptTemp;
    HANDLE hMem;

    if (fptTrav == NULL) {
        fptTemp = fptTrav;
        }
    else {
        fptTemp = fptTrav->next;

        for (i=0;i<4;i++) {
            if (fptTrav->P[i].hBitmap != (HANDLE) NULL) {
                DeleteObject(fptTrav->P[i].hBitmap);
                }
            if (fptTrav->M[i].hBitmap != (HANDLE) NULL) {
                DeleteObject(fptTrav->M[i].hBitmap);
                }
            }

        hMem = (HGLOBAL) GlobalHandle(SELECTOROF(fptTrav));
        GlobalUnlock(hMem);
        GlobalFree(hMem);
        }

    return(fptTemp);
}

/*=====================================================================
Function: AddPic()

Inputs: Picture # to load

Outputs:Returns success/failure.

Abstract:
    AddPic will take care of loading in a given picture's bitmap, if it isn't
    already in the fptPic list. If the specified type of picture can't be found,
    FALSE is returned.  Note that when we load a picture, we also make sure
    that it is restricted to the confines of its mask by turning everything
    outside the mask BLACK. This is necessary for drawing as well.
======================================================================*/

BOOL AddPic(
    int iPicNum
    )
{
    char cDirs[4] = {'F','L','B','R'};
    FullPicType FAR *fptTrav;
    BOOL bFound;
    int i,j;
    char cBitmapName[32];
    HDC hDC,hPicDC,hMaskDC;

    fptTrav = &fptPic;
    bFound = FALSE;

    //
    // Look to see if the picture's already been loaded into memory. If
    // it has, we won't have to load it.
    //
    while (fptTrav->next != NULL) {
        fptTrav = fptTrav->next;

        if (fptTrav->iPicNum == iPicNum) {
            bFound = TRUE;
            break;
            }
        }

    //
    // If we didn't find the bitmap, we really DO need to load it.
    //
    if (!bFound) {

        //
        // We can only load the requested picture if it's in our list of
        // known pics. Check to make sure it is.
        //
        for(i=0;i<iNumPicSpecs;i++) {
            if (PicSpec[i].iPicNum == iPicNum) {
                bFound = TRUE;
                break;
                }
            }

        if (bFound) {
            hDC = GetDC(hWndMaze);
            hPicDC = CreateCompatibleDC(hDC);
            hMaskDC = CreateCompatibleDC(hPicDC);
            ReleaseDC(hWndMaze,hDC);

            fptTrav->next = NewFullPic(iPicNum,fptTrav->next);
            fptTrav = fptTrav->next;
            strcpy(cBitmapName,PicSpec[i].cBase);
            cBitmapName[5]='\0';

            //
            // Load in the bitmaps
            //
            for(j=0;j<4;j++) {
                cBitmapName[4]=cDirs[j];
                if (fptTrav->P[j].hBitmap != NULL) {
                    DeleteObject(fptTrav->P[j].hBitmap);
                }
                fptTrav->P[j].hBitmap = LoadBitmap(hInst,(LPCTSTR)cBitmapName);
                if (fptTrav->P[j].hBitmap == (HANDLE) NULL) {
                    MessageBox((HANDLE) NULL,"Unable to load bitmap","AddPic",
                               MB_ICONEXCLAMATION|MB_APPLMODAL);
                    bFound = FALSE;
                    }
                //
                // Copy stats for the Bitmap's size/origin into the struct
                //
                fptTrav->P[j].xSize = PicSpec[i].PicPos[j].xSize;
                fptTrav->P[j].ySize = PicSpec[i].PicPos[j].ySize;
                fptTrav->P[j].xOrg = PicSpec[i].PicPos[j].xOrg;
                fptTrav->P[j].yOrg = PicSpec[i].PicPos[j].yOrg;
                }

            cBitmapName[5]='M';
            cBitmapName[6]='\0';

            //
            // Load in the masks for the bitmaps
            //
            for(j=0;j<4;j++) {
                cBitmapName[4]=cDirs[j];
                if (fptTrav->M[j].hBitmap != NULL) {
                    DeleteObject(fptTrav->M[j].hBitmap);
                }
                fptTrav->M[j].hBitmap = LoadBitmap(hInst,(LPCTSTR)cBitmapName);
                if (fptTrav->M[j].hBitmap == (HANDLE) NULL) {
                    MessageBox((HANDLE) NULL,"Unable to load bitmap mask","AddPic",
                                MB_ICONEXCLAMATION|MB_APPLMODAL);
                    bFound = FALSE;
                    }
                //
                // Copy stats for the Mask's size/origin into the struct
                //
                fptTrav->M[j].xSize = PicSpec[i].PicPos[j].xSize;
                fptTrav->M[j].ySize = PicSpec[i].PicPos[j].ySize;
                fptTrav->M[j].xOrg = PicSpec[i].PicPos[j].xOrg;
                fptTrav->M[j].yOrg = PicSpec[i].PicPos[j].yOrg;
                }
/***********BUGBUG
            //
            // Invert the background of the bitmaps from white to black
            //
            for(j=0;j<4;j++) {
                SelectObject(hPicDC,fptTrav->P[j].hBitmap);
                SelectObject(hMaskDC,fptTrav->M[j].hBitmap);
                BitBlt(hPicDC,0,0,
                                 PicSpec[iPicNum].PicPos[i].xSize,
                                 PicSpec[iPicNum].PicPos[i].ySize,
                       hMaskDC,0,0,SRCINVERT);
                }
*************/

            DeleteDC(hPicDC);
            DeleteDC(hMaskDC);
            }
        }

    return(bFound);
}

/*=====================================================================
Function: DelPic()

Inputs: Picture # to Delete from pic list

Outputs:Returns success/failure.

Abstract:
    DelPic will remove a picture, assuming that it is not being used by anyone
    in ptDrones and ptPlayers, and it isn't of type PIC_DEFAULT.
======================================================================*/

BOOL DelPic(
    int iPicNum
    )
{
    FullPicType FAR *fptTrav;
    BOOL bFound,bStillUsed;
    PlayerType FAR *ptTrav;

    if (iPicNum == PIC_DEFAULT) {
        return(TRUE);
        }

    fptTrav = &fptPic;
    bFound = FALSE;

    while (fptTrav->next != NULL) {
        if (fptTrav->next->iPicNum == iPicNum) {
            bFound = TRUE;
            bStillUsed = FALSE;

            ptTrav = &ptPlayers;
            while (ptTrav->next != NULL) {
            ptTrav = ptTrav->next;
            if (ptTrav->iPicNum == iPicNum) {
                bStillUsed = TRUE;
                break;
                }
            }

            ptTrav = &ptDrones;
            while (ptTrav->next != NULL) {
            ptTrav = ptTrav->next;
            if (ptTrav->iPicNum == iPicNum) {
                bStillUsed = TRUE;
                break;
                }
            }

            if (!bStillUsed) {
                fptTrav->next = DeleteFullPic(fptTrav->next);
                }

            break;
            }

        fptTrav = fptTrav->next;
        }

    return(bFound);
}


READSGRD.C

/***********************************************************************
File:   ReadSGrd.c

Abstract:

    This module contains the entrypoint for ReadSubGrids(). This is a file
    to read in the text versions of the various subgrids which users can
    choose from. The subgrids are read into SubGrids[], and translated into
    their various cells with flags of NORTH SOUTH EAST WEST saying what
    walls are present for that cell.

    COMMENTS:   This file needs more error checking put into it. In particular,
                an error routine with popups for missing or invalid files.

Contents:

    ReadSubGrids() -- Reads subgrids in from the resources.

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

#include "winmaze.h"
#include "mazproto.h"

/*=====================================================================
Function:   ReadSubGrids()

Inputs:     none

Outputs:    returns success/failure

Abstract:
    This will read in subgrids from the linked resources, convert them
    from text to bytes with walls, filling the subgrid structre.
======================================================================*/

int ReadSubGrids(
    VOID
    )
{
    char GridName[]="SUBGRID00";
    HRSRC hGrid;
    LPSTR lpBuff;
    HGLOBAL hMem;

    int i,j,k,m;

    //
    // Initialize all cells of each subgrid to 0.
    //
    for (k=0;k<NUM_SUBGRIDS;k++) {
        for (i=0;i<X_CELLS_PER_SUBGRID;i++) {
            for (j=0;j<Y_CELLS_PER_SUBGRID;j++) {
                SubGrids[k].Cell[j][i] = 0;
                }
            }
        }

    //
    // Fill Subgrids with info from SubGrixx.TXT files
    //
    for(i=0;i<NUM_SUBGRIDS;i++) {       // Loop through all subgrids
        GridName[7]=(char) ('0'+(i/10));
        GridName[8]=(char) ('0'+(i%10));
        hGrid = FindResource(hInst,(LPCTSTR)GridName,(LPCTSTR)RT_RCDATA);
        if (hGrid == (HRSRC)NULL) {
            MessageBox((HWND)NULL,GetStringRes(IDS_LDSGRIDRESFAIL),"ReadSubGrids",
                       MB_ICONEXCLAMATION|MB_APPLMODAL);
            }

        hMem = (HGLOBAL) LoadResource(hInst,hGrid);
        lpBuff = LockResource(hMem);
        if (lpBuff == (LPSTR)NULL) {
            MessageBox((HWND)NULL,GetStringRes(IDS_LDSGRIDRESFAIL),"ReadSubGrids",
                         MB_ICONEXCLAMATION|MB_APPLMODAL);
            }

        j=0;

    //
    // For each file, loop through all lines. The top line
    // is ONLY the North wall for the first row.
    // The sixth row in the file will be the fifth row of the subgrid.
    //
    for(m=0;m<6;m++) {
        //
        // Get the NORTH/SOUTH walls.
        //
        for (k=1;k<2*X_CELLS_PER_SUBGRID;k+=2) {
            if (lpBuff[k] == '_') {
                if (j<Y_CELLS_PER_SUBGRID) {
                    SubGrids[i].Cell[k/2][j] |= NORTH;
                    }
                if (j>0) {
                    SubGrids[i].Cell[k/2][j-1] |= SOUTH;
                    }
                }
            }

        //
        // Get the EAST/WEST walls.
        //
        if (j > 0){
            for (k=0;k<2*X_CELLS_PER_SUBGRID+1;k+=2) {
                if (lpBuff[k] == '|') {
                    if ((k/2) < X_CELLS_PER_SUBGRID){
                        SubGrids[i].Cell[k/2][j-1] |= WEST;
                        }
                    if (((int) (k/2))>0) {
                        SubGrids[i].Cell[(k/2)-1][j-1] |= EAST;
                        }
                    }
                }
            }

        while (*lpBuff != '\0') {
            lpBuff++;
            }
        lpBuff++;

        j++;
        }
    }

    return(1);
}


SCOREWND.C

/***********************************************************************
File:   ScoreWnd.c

Abstract:

    This module contains functions to maintain the Score window.

Contents:

    DelScoreType() -- Delete a record of type ScoreType
    NewScoreType() -- Create & initialize a record of type ScoreType
    DrawScore() -- Draws a score entry with parametric update selection
    ScoreWndProc() -- Main windows entrypoint for window with scores

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

#include "winmaze.h"
#include "mazproto.h"
#include "net.h"
#include <mmsystem.h>
#include <string.h>

TEXTMETRIC tmScoreInfo;
RECT rDirRect[4],rBitmapRect; // These will need ix and iy added to be right.

int iTextYPos;

#define SC_PIC    0x1
#define SC_NAME   0x2
#define SC_SCORE  0x4
#define SC_DIRECT 0x8
#define SC_REDRAW 0x10

/*=====================================================================
Function:   DelScoreType()

Inputs:     Pointer to record of ScoreType to delete

Outputs:    returns ->next field of deleted record

Abstract:
    Returns ->next field to facilitate linked list maintenance
======================================================================*/

ScoreType FAR *DelScoreType(
    ScoreType FAR *stTrav
    )
{
    ScoreType FAR *stHold;
    HGLOBAL hMem;

    if (stTrav == (ScoreType FAR *) NULL) {
        stHold = (ScoreType FAR *) NULL;
        }
    else {
        stHold = stTrav->next;
        hMem = (HGLOBAL) GlobalHandle(SELECTOROF(stTrav));
        GlobalUnlock(hMem);
        GlobalFree(hMem);
        }

    return(stHold);
}

/*=====================================================================
Function:   NewScoreType()

Inputs:     Values for ScoreType member elements

Outputs:    Returns pointer to a newly allocated & Init'd record

Abstract:
    Needs no explanation
======================================================================*/

ScoreType FAR *NewScoreType(
    unsigned long ulID,
    int ix,
    int iy,
    int iScore,
    BYTE bDir,
    ScoreType FAR *next
    )
{
    ScoreType FAR *stTrav;
    HGLOBAL hMem;

    hMem = GlobalAlloc(GHND,sizeof(ScoreType));
    stTrav = (ScoreType FAR *) GlobalLock(hMem);
    if (stTrav == (ScoreType FAR *)NULL) {
        MessageBox((HWND)NULL,GetStringRes(IDS_MALLOCFAIL),"NewScoreType",
               MB_ICONEXCLAMATION|MB_APPLMODAL);
        }
    else {
        stTrav->ulID = ulID;
        stTrav->ix = ix;
        stTrav->iy = iy;
        stTrav->iScore = iScore;
        stTrav->bDir = bDir;
        stTrav->next = next;
        }

    return(stTrav);
}

/*=====================================================================
Function:   DrawScore()

Inputs:     Pointer to score structure for to draw, flags for what to update

Outputs:    none

Abstract:
    DrawScore is the workhorse routine for drawing score entries.
======================================================================*/

void DrawScore(
    ScoreType FAR *stTrav,
    DWORD dwSCFlags
    )
{
    char Buff[132];
    UINT uiAlign;
    int x,y,px,py;
    PlayerType FAR *ptTrav;
    FullPicType FAR *fptTrav;
    HBRUSH hWhiteBrush,hBlackBrush,hOld;
    HPEN hWhitePen, hOldPen;
    HDC hDC,hPicDC;
    RECT rScratch;
    BOOL bFound,bIsSelf,bFound2;
    BYTE bDir,bNewDir,bB;
    float fAspect;

    bFound = FALSE;
    ptTrav = &ptPlayers;

    hWhiteBrush = GetStockObject(WHITE_BRUSH);
    hWhitePen = GetStockObject(WHITE_PEN);

    //
    // Find the player structure associated with the score we're updating
    //
    while (ptTrav->next != NULL) {
        ptTrav = ptTrav->next;
        if (ptTrav->ulID == stTrav->ulID) {
            bFound = TRUE;
            break;
            }
        }

    if (!bFound) {
        bIsSelf = (stTrav->ulID == ptSelf.ulID);
        }
    else {
        bIsSelf = FALSE;
        }

    hDC = GetDC(hWndScore);

    //
    // Play the dog-being-kicked sound if the player is doing bad.
    //
    if (stTrav->ulID == ptSelf.ulID) {
        if (iKilledByDrones&&(!(iKilledByDrones%5))&&(stTrav->iScore > ptSelf.iScore)) {
            sndPlaySound("dog.wav",SND_ASYNC);
            }
        }

    //
    // Draw a profile of the player if appropriate
    //
    if ((dwSCFlags&SC_PIC)&&(dwSCFlags&SC_REDRAW)) {
        rScratch.left = rBitmapRect.left + stTrav->ix;
        rScratch.right = rBitmapRect.right + stTrav->ix;
        rScratch.top = rBitmapRect.top + stTrav->iy;
        rScratch.bottom = rBitmapRect.bottom + stTrav->iy;

        hPicDC = CreateCompatibleDC(hDC);
        fptTrav = &fptPic;
        bFound2 = FALSE;
        while (fptTrav->next != (FullPicType FAR *)NULL) {
            fptTrav = fptTrav->next;
            if (fptTrav->iPicNum == ((bIsSelf) ? ptSelf.iPicNum : ptTrav->iPicNum)) {
                bFound2 = TRUE;
                break;
                }
            }

        if (bFound2) {
            SelectObject(hPicDC,fptTrav->P[1].hBitmap);
            x = rScratch.right - rScratch.left;
            y = rScratch.bottom - rScratch.top;
            fAspect=((float) fptTrav->P[1].ySize)/
                     ((float) fptTrav->P[1].xSize);

            StretchBlt(hDC,rScratch.left,
                       rScratch.top+fptTrav->P[1].yOrg*y/PIC_Y,
                       (int) ((x < (y/fAspect))? x : y/fAspect),
                       (int) ((y < (x*fAspect))? y : (int) x*fAspect),
                       hPicDC,0,0,
                       fptTrav->P[1].xSize,
                       fptTrav->P[1].ySize,
                       SRCCOPY);
            }

        DeleteDC(hPicDC);
        }

    //
    // Print the User & computer name of the player if appropriate
    //
    if ((dwSCFlags&SC_NAME)&&(dwSCFlags&SC_REDRAW)) {
        if (bIsSelf) {
            sprintf(Buff,"%s@%s",ptSelf.cUserName,ptSelf.cComputerName);
            }
        else {
           if (bFound) {
               sprintf(Buff,"%s@%s",ptTrav->cUserName,ptTrav->cComputerName);
               }
           }
        TextOut(hDC,80,iTextYPos+stTrav->iy,Buff,lstrlen(Buff));
        }

    //
    // Print the current score of the player if appropriate
    //
    if ((dwSCFlags&SC_SCORE)&&
        ((dwSCFlags&SC_REDRAW)||
        (bFound&&(ptTrav->iScore != stTrav->iScore)) ||
        (bIsSelf&&(ptSelf.iScore != stTrav->iScore)))
       ) {

        hOldPen = SelectObject(hDC,hWhitePen);
        hOld = SelectObject(hDC,hWhiteBrush);
        sprintf(Buff,"%ld",stTrav->iScore);

        Rectangle(hDC,rScore.right-5-((strlen(Buff)+1)* tmScoreInfo.tmAveCharWidth),
                  iTextYPos+stTrav->iy,rScore.right-5,
                  iTextYPos+stTrav->iy + tmScoreInfo.tmHeight);
        SelectObject(hDC,hOldPen);
        SelectObject(hDC,hOld);

        if (bIsSelf) {
            sprintf(Buff,"%ld",ptSelf.iScore);
            stTrav->iScore = ptSelf.iScore;
            }
        else {
            if (bFound) {
                sprintf(Buff,"%ld",ptTrav->iScore);
                stTrav->iScore = ptTrav->iScore;
                }
            else {
                MessageBox((HWND)NULL,GetStringRes(IDS_MDFYSCO),"DrawScore",
                           MB_ICONEXCLAMATION|MB_APPLMODAL);
                }
            }

        uiAlign = SetTextAlign(hDC,TA_TOP | TA_RIGHT);
        TextOut(hDC,rScore.right-5,iTextYPos+stTrav->iy,Buff,lstrlen(Buff));
        SetTextAlign(hDC,uiAlign);
        }

    //
    // Modify the squares which say which direction to go to find this
    // particular player, if appropriate.
    //
    if (bFound&&(dwSCFlags&SC_DIRECT)) {
        x = ptSelf.Pos.ix;
        y = ptSelf.Pos.iy;

        hBlackBrush = GetStockObject(BLACK_BRUSH);

        px = ptTrav->Pos.ix;
        py = ptTrav->Pos.iy;
        bDir = stTrav->bDir;

        bNewDir = (py <= y) ? NORTH : (BYTE)0;
        bNewDir |= (py >= y) ? SOUTH : (BYTE)0;
        bNewDir |= (px >= x) ? EAST : (BYTE)0;
        bNewDir |= (px <= x) ? WEST : (BYTE)0;

        //
        // Now that we have the ABSOLUTE directions, we need to rotate it for
        // our offset from true north.
        //

        bB = ptSelf.Pos.Facing;

        while (bB /=2 ) {
            if (bNewDir & 1) {
                bNewDir = bNewDir / 2 + 8;
                }
            else {
                bNewDir /= 2;
                }
/****
            if (bNewDir & 16) {
                bNewDir = (bNewDir & 15) + 1;
                }
****/
            }

        //
        // Draw square #0
        //
        if ((dwSCFlags&SC_REDRAW)||
            ((bNewDir&(NORTH|WEST)) != (bDir&(NORTH|WEST)))
           ) {
            if ((bNewDir&NORTH)&&(bNewDir&WEST)) {
                hOld = SelectObject(hDC,hBlackBrush);
                }
            else {
                hOld = SelectObject(hDC,hWhiteBrush);
                }

            Rectangle(hDC,rDirRect[0].left+stTrav->ix,rDirRect[0].top+stTrav->iy,
                      rDirRect[0].right+stTrav->ix,rDirRect[0].bottom+stTrav->iy);
            SelectObject(hDC,hOld);
            }

        //
        // Draw square #1
        //
        if ((dwSCFlags&SC_REDRAW)||
            ((bNewDir&(NORTH|EAST)) != (bDir&(NORTH|EAST)))
           ) {
            if ((bNewDir&NORTH)&&(bNewDir&EAST)) {
                hOld = SelectObject(hDC,hBlackBrush);
                }
            else {
                hOld = SelectObject(hDC,hWhiteBrush);
                }

            Rectangle(hDC,rDirRect[1].left+stTrav->ix,rDirRect[1].top+stTrav->iy,
                      rDirRect[1].right+stTrav->ix,rDirRect[1].bottom+stTrav->iy);
            SelectObject(hDC,hOld);
            }

        //
        // Draw square #2
        //
        if ((dwSCFlags&SC_REDRAW)||
            ((bNewDir&(SOUTH|WEST)) != (bDir&(SOUTH|WEST)))
           ) {
            if ((bNewDir&SOUTH)&&(bNewDir&WEST)) {
                hOld = SelectObject(hDC,hBlackBrush);
                }
            else {
                hOld = SelectObject(hDC,hWhiteBrush);
                }

            Rectangle(hDC,rDirRect[2].left+stTrav->ix,rDirRect[2].top+stTrav->iy,
                      rDirRect[2].right+stTrav->ix,rDirRect[2].bottom+stTrav->iy);
            SelectObject(hDC,hOld);
            }

        //
        // Draw square #1
        //
        if ((dwSCFlags&SC_REDRAW)||
            ((bNewDir&(SOUTH|EAST)) != (bDir&(SOUTH|EAST)))
           ) {
            if ((bNewDir&SOUTH)&&(bNewDir&EAST)) {
                hOld = SelectObject(hDC,hBlackBrush);
                }
            else {
                hOld = SelectObject(hDC,hWhiteBrush);
                }

            Rectangle(hDC,rDirRect[3].left+stTrav->ix,rDirRect[3].top+stTrav->iy,
                      rDirRect[3].right+stTrav->ix,rDirRect[3].bottom+stTrav->iy);
            SelectObject(hDC,hOld);
            }

        stTrav->bDir = bNewDir;

        DeleteObject(hBlackBrush);
        }

    DeleteObject(hWhiteBrush);
    DeleteObject(hWhitePen);
    ReleaseDC(hWndScore,hDC);

    return;
}

/*=====================================================================
Function:   ScoreWndProc()

Inputs:     Standard Windows Proc parms

Outputs:    returns success/failure

Abstract:
    Handles updating/controlling the score child window
======================================================================*/

LONG FAR PASCAL ScoreWndProc(
    HWND hWnd,
    UINT Message,
    WPARAM wParam,
    LPARAM lParam
    )
{
    PAINTSTRUCT ps;
    HDC hDC;
    ScoreType FAR *stTrav,FAR *stTrav2;
    BOOL bFound,bFoundpt,bIsSelf;
    PlayerType FAR *ptTrav;
    RECT rScratch;
    HPEN hWhitePen,hOldPen;

    bIsSelf = (lParam == (LPARAM) ptSelf.ulID);
    bFoundpt = FALSE;
    ptTrav=&ptPlayers;
    while(ptTrav->next != NULL) {
        ptTrav = ptTrav->next;
        if (ptTrav->ulID == (UINT) lParam) {
            bFoundpt = TRUE;
            break;
            }
        }

    switch (Message) {
        case WM_CREATE:
            GetClientRect(hWnd,&rScore);
            hDC = GetDC(hWnd);

            //
            // Kludge: fill out scores with our own entry
            //
            Scores.ulID = ptSelf.ulID;
            Scores.ix = 0;
            Scores.iy = 0;
            Scores.iScore = ptSelf.iScore;
            Scores.bDir = 0xFF;
            Scores.next=NULL;

            if(!GetTextMetrics(hDC,&tmScoreInfo)) {
                MessageBox((HWND)NULL,GetStringRes(IDS_GETTXTMTRCSFAIL),
                           "ScoreWndProc",MB_ICONEXCLAMATION|MB_APPLMODAL);
                }

            rBitmapRect.left = 40;
            rBitmapRect.right = rBitmapRect.left + 19;
            rBitmapRect.top = 5;
            rBitmapRect.bottom = rBitmapRect.top+29;

            //
            // 01
            // 23
            //
            rDirRect[0].left = rDirRect[2].left = 5;
            rDirRect[1].left = rDirRect[3].left = rDirRect[0].right = rDirRect[2].right = 20;
            rDirRect[1].right = rDirRect[3].right = 35;
            rDirRect[0].top     = rDirRect[1].top = 5;
            rDirRect[2].top     = rDirRect[3].top = rDirRect[0].bottom = rDirRect[1].bottom = 20;
            rDirRect[2].bottom = rDirRect[3].bottom = 35;

            iTextYPos = rBitmapRect.top + (rBitmapRect.bottom - tmScoreInfo.tmHeight - rBitmapRect.top)/2;

            ReleaseDC(hWnd,hDC);
            break;

        case WM_COMMAND:
            //
            // Note -- we use lParam because we pass WM_COMMAND msg's ourselves.
            //
            bFound = FALSE;
            stTrav = &Scores;
            while (stTrav->next != NULL) {
                if (stTrav->next->ulID == (unsigned long)lParam) {
                    bFound = TRUE;
                    break;
                    }
                stTrav = stTrav->next;
                }

            switch (wParam) {

                case WC_ADDSCORE:
                    if (!bFound) {
                        if ((lParam != 0)&&bFoundpt) {
                            stTrav->next = NewScoreType(ptTrav->ulID,stTrav->ix,
                                                        stTrav->iy+35,ptTrav->iScore,0,stTrav->next);
                            DrawScore(stTrav->next,SC_PIC|SC_NAME|SC_SCORE|SC_DIRECT|SC_REDRAW);
                            }
                        else {
                            MessageBox((HWND)NULL,GetStringRes(IDS_SCOADD0FAIL),"ScoreDlg",
                                       MB_ICONEXCLAMATION|MB_APPLMODAL);
                            }
                        }
                    break;

                case WC_UPDATESCORE:
                    if (bFound&&bFoundpt) {
                        DrawScore(stTrav->next,SC_SCORE);
                        }
                    else {
                        if (bIsSelf) {
                            DrawScore(&Scores,SC_SCORE);
                            }
                        else {
                            MessageBox((HWND)NULL,GetStringRes(IDS_CHGSCOFAIL),"ScoreDlg",
                                       MB_ICONEXCLAMATION|MB_APPLMODAL);
                            }
                        }
                    break;

                case WC_UPDATEDIRECTION:
                    //
                    // if it's a global update
                    //
                    if (lParam == 0) {
                        stTrav = &Scores;
                        while (stTrav->next != NULL) {
                            stTrav = stTrav->next;
                            DrawScore(stTrav,SC_DIRECT);
                            }
                        }
                    else {
                        if (bFound&&bFoundpt) {
                            //
                            // must be updated in routine
                            //
                            DrawScore(stTrav->next,SC_DIRECT);
                            }
                        else {
                            MessageBox((HWND)NULL,GetStringRes(IDS_CHGDIRFAIL),"ScoreDlg",
                                       MB_ICONEXCLAMATION|MB_APPLMODAL);
                            }
                        }
                    break;

                case WC_DELETESCORE:
                    if (bFound) {
                        stTrav2 = stTrav;
                        stTrav->next = DelScoreType(stTrav->next);
                        while (stTrav2->next != NULL) {
                            DrawScore(stTrav2->next,SC_PIC|SC_NAME|SC_SCORE|SC_DIRECT|SC_REDRAW);
                            stTrav2 = stTrav2->next;
                            }
                        rScratch.top = rDirRect[0].top + stTrav->iy + 35;
                        rScratch.bottom = rDirRect[2].bottom + stTrav->iy + 35;
                        rScratch.left = 5;
                        rScratch.right = rScore.right - 5;
                        hDC = GetDC(hWndScore);
                        hWhitePen = GetStockObject(WHITE_PEN);
                        hOldPen = SelectObject(hDC,hWhitePen);
                        Rectangle(hDC,rScratch.left,rScratch.top,
                                  rScratch.right,rScratch.bottom);
                        SelectObject(hDC,hOldPen);
                        DeleteObject(hWhitePen);
                        ReleaseDC(hWndScore,hDC);
                        }
                    else {
                        MessageBox((HWND)NULL,GetStringRes(IDS_DELSCOFAIL),"ScoreDlg",
                                   MB_ICONEXCLAMATION|MB_APPLMODAL);
                        }
                    break;

                default:
                    MessageBox((HWND)NULL,GetStringRes(IDS_BADMSG),"ScoreDlg",
                               MB_ICONEXCLAMATION|MB_APPLMODAL);
                    break;
                }
            break;

        case WM_KEYDOWN:
            SendMessage(hWndMaze,WM_KEYDOWN,wParam,lParam);
            break;

        case WM_MOVE:
            break;

        case WM_SIZE:
//
// BUGBUG -- eventually we'll want to soft-code the now hard-coded rects.
//
            break;

        case WM_PAINT:
            GetClientRect(hWnd,&rScore);
            hDC = BeginPaint(hWnd, &ps);
//
// OPTIMIZE -- this can be set to loop through only visible portions
//
            stTrav = &Scores;
            SetBkMode(hDC, TRANSPARENT);
            do {
                DrawScore(stTrav,SC_PIC|SC_NAME|SC_SCORE|SC_DIRECT|SC_REDRAW);
                stTrav = stTrav->next;
                } while (stTrav != NULL);
            EndPaint(hWnd, &ps);
            break;

        case WM_CLOSE:
            stTrav = &Scores;
            while (stTrav->next != NULL) {
                stTrav->next = DelScoreType(stTrav->next);
            }
            DestroyWindow(hWnd);
            break;

        default:
            return DefWindowProc(hWnd, Message, wParam, lParam);
        }

    return(0);
}


TEXTWND.C

/***********************************************************************
File:   TextWnd.c

Abstract:
    This module contains functions which deal with the Text window. This
    includes a function to print to said window, and scroll.

Contents:

    RefreshText() -- Redraws text in the bottom of the Text message window
    lstrdup() -- same as strdup() but for LPSTR's
    PrintTextLine() -- Prints a string to the text message window, scrolls
    TextWndProc() -- Windows message processing loop for text message window

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

#include "winmaze.h"
#include "mazproto.h"
#include "net.h"
#include <mmsystem.h>
#include <string.h>

#define MAX_TEXT_LINES 80

LPSTR cText[MAX_TEXT_LINES];
TEXTMETRIC tmTextInfo;
int iMaxNumLines,       // Number of lines that will fit in the TextWnd display.
    iCurLine,           // Line # in the text display we're on.
    iTextLines;         // # of lines of text which are valid in cText.

/*=====================================================================
Function: RefreshText()

Inputs: none

Outputs: none

Abstract:
    RefreshText() is responsible for drawing the bottom n lines of the
    display, however many can fit into it. This should only be called when
    a clipping region is in effect and has already been cleared.
======================================================================*/

void RefreshText(
    )
{
    HDC hDC;
    int i;

    hDC = GetDC(hWndText);
    iCurLine = 0;
    for(i=0;i<iMaxNumLines;i++) {
        if ((iTextLines -(iMaxNumLines-i)) >= 0) {
            TextOut(hDC,10,iCurLine * tmTextInfo.tmHeight + 2,
                    cText[iTextLines-(iMaxNumLines-i)],
                    lstrlen(cText[iTextLines-(iMaxNumLines-i)]));
            iCurLine++;
            }
        }

    ReleaseDC(hWndText,hDC);
}

/*=====================================================================
Function: lstrdup()

Inputs: String to duplicate

Outputs:Returns a copy of the string.

Abstract:
    Same as strdup, but works with LPSTR's
======================================================================*/

LPSTR lstrdup(
    LPSTR s
    )
{
    HGLOBAL hMem;
    LPSTR lpRet;

    hMem = GlobalAlloc(GHND,lstrlen(s)+1);
    lpRet = GlobalLock(hMem);
    lstrcpy(lpRet,s);

    return(lpRet);
}

/*=====================================================================
Function: PrintTextLine()

Inputs: Line of text to print

Outputs: none

Abstract:
    PrintTextLine will print a line of text to the text display window, and
    scroll as necessary.
======================================================================*/

void PrintTextLine(
    LPSTR cLine
    )
{
    HDC hDC;
    int i;
    RECT rScroll;
    HGLOBAL hMem;

    hDC = GetDC(hWndText);

    //
    // If we already have MAX_TEXT_LINES of text remembered,
    // we need to zap one to make room for the new line.
    //
    if (iTextLines == MAX_TEXT_LINES) {
        hMem = (HGLOBAL) GlobalHandle(SELECTOROF(cText[0]));
        GlobalUnlock(hMem);
        GlobalFree(hMem);
        for(i=0;i<MAX_TEXT_LINES-1;i++) {
            cText[i] = cText[i+1];
            }
        iTextLines--;
        }

    //
    // Copy the line to be displayed into our buffer
    //
    cText[iTextLines++] = lstrdup(cLine);

    //
    // Scroll if necessary
    //
    if (iCurLine >= iMaxNumLines) {
        GetClientRect(hWndText,&rScroll);
        rScroll.top += 2;
        ScrollWindow(hWndText,0,-tmTextInfo.tmHeight,&rScroll,&rScroll);
        UpdateWindow(hWndText);
        iCurLine--;
        }

    TextOut(hDC,10,iCurLine * tmTextInfo.tmHeight + 2,
            cText[iTextLines-1],
            lstrlen(cText[iTextLines-1]));

    iCurLine++;

    ReleaseDC(hWndText,hDC);

    return;
}

/*=====================================================================
Function: TextWndProc()

Inputs: Standard windows entrypoint parms

Outputs: success

Abstract:
    This is the main procedure to take care of drawing, resizing etc.
    the text message window.
======================================================================*/

LONG FAR PASCAL TextWndProc(
    HWND hWnd,
    UINT Message,
    WPARAM wParam,
    LPARAM lParam
    )
{
    PAINTSTRUCT ps;
    HDC hDC;
    int i;
    HGLOBAL hMem;

    switch (Message) {

        case WM_CREATE:
            for(i=0;i<MAX_TEXT_LINES;i++) {
                cText[i] = (LPSTR) NULL;
//                hMem = GlobalAlloc(GHND,sizeof(char));
//                cText[i] = GlobalLock(hMem);
//                cText[i][0] = '\0';
                }

            iTextLines = iCurLine = 0;
            hDC = GetDC(hWnd);
            if(!GetTextMetrics(hDC,&tmTextInfo)) {
                MessageBox((HWND)NULL,GetStringRes(IDS_GETTXTMTRCSFAIL),
                           "TextWndProc",MB_ICONEXCLAMATION|MB_APPLMODAL);
                }
            ReleaseDC(hWnd,hDC);
            GetClientRect(hWnd,&rText);
            iMaxNumLines = (rText.bottom - rText.top-5)/tmTextInfo.tmHeight;
            break;

        case WM_KEYDOWN:
            SendMessage(hWndMaze,WM_KEYDOWN,wParam,lParam);
            break;

        case WM_MOVE:
            break;

        case WM_SIZE:
            GetClientRect(hWnd,&rText);
            iMaxNumLines = (rText.bottom - rText.top - 5)/tmTextInfo.tmHeight;
            RefreshText();
            break;

        case WM_PAINT:
            GetClientRect(hWnd,&rText);
            hDC = BeginPaint(hWnd, &ps);
            SetBkMode(hDC, OPAQUE);
            RefreshText();
            EndPaint(hWnd, &ps);
            break;

        case WM_CLOSE:
            for (i=0; i<iTextLines ; i++) {
                if (cText[i] != NULL) {
                    hMem = (HGLOBAL) GlobalHandle(SELECTOROF(cText[i]));
                    GlobalUnlock(hMem);
                    GlobalFree(hMem);
                }
            }
            DestroyWindow(hWnd);
            break;

        default:
            return DefWindowProc(hWnd, Message, wParam, lParam);
        }

    return(0);
}


TOPWND.C

/***********************************************************************
File:   TopWnd.c

Abstract:

    This contains the windows procudure responsible for drawing the
    top-view map.

Contents:

    TopViewWndProc() -- windows message processor for Top view window.

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

#include "winmaze.h"
#include "mazproto.h"
#include "net.h"
#include <mmsystem.h>

/*=====================================================================
Function: TopViewWndProc()

Inputs: Standard windows entrypoint parms

Outputs: returns success

Abstract:
    Takes care of maintaining the top view map.
======================================================================*/

LONG FAR PASCAL TopViewWndProc(
    HWND hWnd,
    UINT Message,
    UINT wParam,
    LONG lParam
    )
{
    PAINTSTRUCT ps;
    HDC hDC;

    switch (Message) {

        case WM_KEYDOWN:
           SendMessage(hWndMaze,WM_KEYDOWN,wParam,lParam);
           break;

        case WM_MOVE:
            break;

        case WM_SIZE:
            break;

        case WM_PAINT:
            hDC = BeginPaint(hWnd, &ps);
            SetBkMode(hDC, TRANSPARENT);
            DrawTopView(hDC,TRUE);
            EndPaint(hWnd, &ps);
            break;

        case WM_CLOSE:
            DestroyWindow(hWnd);
            break;

        default:
            return DefWindowProc(hWnd, Message, wParam, lParam);
        }

    return(0);
}


WINMAZE.H

/*************************************************
MODULE: WinMaze.h

This module contains typedefs, etc for Maze Lords.
Prototypes are contained in MazProto.h.
*************************************************/

#ifndef _WINMAZE_H_INC_
#define _WINMAZE_H_INC_

#ifndef _IN_RC_
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#endif
#include <windows.h>
#include <mmsystem.h>
#include "net.h"
#include "MAZEDLG.h"

#ifdef WIN16

#define Sleep(x)                // Sleep doesn't exist.
#define PT_MOVETO 1
#define PT_LINETO 2
#define GET_WM_COMMAND_ID(wp, lp)                   (wp)
#define GET_WM_COMMAND_HWND(wp, lp)                 (HWND)LOWORD(lp)
#define GET_WM_COMMAND_CMD(wp, lp)                  HIWORD(lp)
typedef long INT;
typedef LPCSTR LPCTSTR;

#else  // Else if it's win32

#define SELECTOROF(a) a
#define GET_WM_COMMAND_ID(wp, lp)               LOWORD(wp)
#define GET_WM_COMMAND_HWND(wp, lp)             (HWND)(lp)
#define GET_WM_COMMAND_CMD(wp, lp)              HIWORD(wp)
#undef INT
#define INT int

#endif

#define PIC_X           100     // Bitmaps are 100 pixels wide.
#define PIC_Y           150     // Bitmaps are 150 pixels tall.

#define PIC_FISH        0
#define PIC_ROBOT       1
#define PIC_SMILEY      2
#define PIC_REAPER      3
#define NUM_PICS        4

#define PIC_DRONE       PIC_ROBOT
#define PIC_DEFAULT     PIC_FISH

#define IDM_FILE                  1000
#define IDM_F_HIGHSCORES               1050
#define IDM_F_NEWGAME                  1100
#define IDM_F_EXIT                     1200
#define IDM_OPTIONS               2000
#define IDM_O_PLAYERSTRETCH            2040
#define IDM_O_DRONESTRETCH             2041
#define IDM_O_DRONES                   2050
#define IDM_O_NET                      2055
#define IDM_O_LOCAL                    2085
#define IDM_O_PAUSE                    2100
#define IDM_O_PLAYERSET                2105
#define IDM_F_STOP                     2110
#define IDM_O_BITMAP                   2115
#define IDM_ABOUT                 3000
#define IDM_DRAWPLAYERS           1001
#define IDM_DRAWDRONES            1002
#define IDT_NEWMAIL               4001
#define IDM_REDRAW                5001

// Resource String Definitions
#define IDS_ERR_REGISTER_CLASS   1
#define IDS_ERR_CREATE_WINDOW    2

#define IDS_MALLOCQUIT         16
#define IDS_MALLOCFAIL         17
#define IDS_RECTALLOCFAIL      18
#define IDS_HSCONOTIMPL        19
#define IDS_SAFEZONE           20
#define IDS_CRETMRFAIL         21
#define IDS_SNDPCKTFAIL        22
#define IDS_RDSGRDTXTFLFAIL    23
#define IDS_GETUSRNMFAIL       24
#define IDS_SNDQUITPCKTFAIL    25
#define IDS_DEMOMODE           26

#define IDS_MDFYSCO            28
#define IDS_GETTXTMTRCSFAIL    29
#define IDS_SCOADD0FAIL        30
#define IDS_CHGSCOFAIL         31
#define IDS_CHGDIRFAIL         32
#define IDS_DELSCOFAIL         33
#define IDS_BADMSG             34
#define IDS_MALLOCFAILABT      35
#define IDS_FMT_DRONE          36

#define IDS_FMT_RUNDOWN        38
#define IDS_BADPCKTXMITFAIL    39
#define IDS_CREMSLOTFAIL       40
#define IDS_CREMWATCHTHRFAIL   41
#define IDS_RDMSLOTFAIL        42
#define IDS_FMT_JOIN           43
#define IDS_FMT_ZAPPEDYOU      44
#define IDS_FMT_LEFT           45
#define IDS_FMT_HIT            46
#define IDS_BADCOMMSG             47
#define IDS_LDSGRIDRESFAIL     48
#define IDS_FMT_BADNUMDRONES   49
#define IDS_ERRINPUT           50
#define IDS_FMT_BADMOVESPERSEC 51

#define IDS_TELEPORT           54
#define IDS_SANCTUARY          55

#define IDS_NOVIOLENCE         58
#define IDS_FMT_YOUZAPPED      59
#define IDS_FMT_SLEW           60
#define IDS_HSCO               61
#define IDS_FATALERR           62
#define IDS_ASKSTOP            63
#define IDS_STOP               64
#define IDS_INITFAIL           65
#define IDS_NETINIT            66
#define IDS_FMT_SANCTUARY      67
#define IDS_ENTERED            68
#define IDS_LEFT               69
#define IDS_FMT_BADPICNUM      70

#define WC_UPDATESCORE           1000
#define WC_UPDATEDIRECTION       1010
#define WC_DELETESCORE           1020
#define WC_ADDSCORE              1030

                    // we can see up to 10 squares ahead or to either side
                    // (not including our current square)
#define MAX_DRAW_DIST 10

#define MAX_DRONES 100
#define MAX_DRONE_SPEED 5

#define ONE_SECOND 1000

#define PANEL_WIDTH 100
#define PANEL_HEIGHT 150

        // Flags for walls in a Cell
#define NORTH ((BYTE)0x01)
#define WEST  ((BYTE)0x02)
#define SOUTH ((BYTE)0x04)
#define EAST  ((BYTE)0x08)

#define AWAY        0
#define TOWARD      2

#define FORWARD     0
#define LEFT        1
#define BACK        2
#define RIGHT       3

        // # of Cells in a subgrid
#define X_CELLS_PER_SUBGRID 5
#define Y_CELLS_PER_SUBGRID 5
        // # of Subgrids in the Grid
#define X_SUBGRIDS_PER_GRID 10
#define Y_SUBGRIDS_PER_GRID 10

#define X_SIZE (X_CELLS_PER_SUBGRID * (X_SUBGRIDS_PER_GRID+2))
#define Y_SIZE (Y_CELLS_PER_SUBGRID * (Y_SUBGRIDS_PER_GRID+2))
        // We have SUBGRI00.TXT through SUBGRI06.TXT for players,
#define NUM_PLAYER_SUBGRIDS 7
        // And SUBGRI07.TXT through SUBGRI13.TXT for the palace
#define NUM_PALACE_SUBGRIDS 7
        // SUBGRI14.TXT is completely closed, for unused grids

#define NUM_SUBGRIDS (NUM_PLAYER_SUBGRIDS + NUM_PALACE_SUBGRIDS + 1)
#define SUBGRID_DEFAULT 1

// A maze is an array of characters, with each character being a combination
// of the NORTH, SOUTH, EAST, and WEST bit flags.  If the flag is set, there
// is a wall there. If not, there isn't. Note that both the flag of the
// current cell and of the adjacent cell in that direction must be checked.

        // Adjacent returns the coordinates of the cell in that direction
        // This applies to both Grid and Maze coordinates.
#define ADJ_X(x,dir) ((((BYTE)(dir)) & EAST) ? ((x)+1) : \
              ((((BYTE)(dir)) & WEST) ? ((x)-1) : (x)))
#define ADJ_Y(y,dir) ((((BYTE)(dir)) & SOUTH) ? ((y)+1) : \
              ((((BYTE)(dir)) & NORTH) ? ((y)-1) : (y)))
        // To calculate SubGrid # from a coordinate
#define X_TO_SUBGRID(x) ((int) (x) / X_CELLS_PER_SUBGRID)
#define Y_TO_SUBGRID(y) ((int) (y) / Y_CELLS_PER_SUBGRID)
        // To get origin of a subgrid in coordinates from subgrid #'s
#define SUBGRID_TO_X(x) ((int) (x) * X_CELLS_PER_SUBGRID)
#define SUBGRID_TO_Y(y) ((int) (y) * Y_CELLS_PER_SUBGRID)

        // Convert relative direction to absolute
#define LEFT_TO_ABS(dir) (((((BYTE)dir)) & EAST) ? NORTH : ((BYTE)(((BYTE) (dir))<<1)))
#define RIGHT_TO_ABS(dir) ((((BYTE)(dir)) & NORTH) ? EAST : ((BYTE)(((BYTE) (dir))>>1)))
#define BACK_TO_ABS(dir)  (LEFT_TO_ABS(LEFT_TO_ABS(dir)))

#define RECTWIDTH(r) ((r).right - (r).left)
#define RECTDEPTH(r) ((r).bottom - (r).top)
#define GREATEROF(a,b) (((a)>(b))?(a):(b))
#define LESSEROF(a,b) (((a)<(b))?(a):(b))

struct S_BitMapPicType {
    HBITMAP hBitmap;
    int xSize,ySize;
    int xOrg,yOrg;
    };
typedef struct S_BitMapPicType BitMapPicType;

struct S_PicType {
    BitMapPicType P[4];
    };
typedef struct S_PicType PicType;

struct S_FullPicType {
    BitMapPicType P[4];     // BitMap
    BitMapPicType M[4];     // Black and white Mask
    int     iPicNum;
    struct S_FullPicType FAR *next;
    };
typedef struct S_FullPicType FullPicType;

struct _SLineType {
   int x[2],y[2];
   };

typedef struct _SLineType LineType;

        // A cell is a single square in a sub-grid
typedef BYTE CellType;

        // SubGridCellsType is the array of characters that make up a sub-grid
struct S_SubGridCellsType {
    CellType Cell[X_CELLS_PER_SUBGRID][Y_CELLS_PER_SUBGRID];
    };
typedef struct S_SubGridCellsType SubGridCellsType;

typedef struct {
  int ix, iy;
  BYTE Facing;
  } PositionType;

struct _s_PlayerType {
  unsigned long ulID;
  DWORD dwPID;
  char cUserName[MAX_USERNAME_LENGTH];
  char cComputerName[MAX_COMPUTERNAME_LENGTH];
  PositionType Pos;
  BOOL Drawn;
  RECT rDrawn;
  RECT rFrom;
  int iScore;
  int iPicNum;
  int iGridNum;
  POINT pGridLoc;
  BYTE dwDirectShown;
  struct _s_PlayerType FAR *next;
  };
typedef struct _s_PlayerType PlayerType;

struct _s_PlayerDrawType {
  PlayerType FAR *p;            // Pointer to the player to draw
  RECT rDraw,                   // Rectangle the FULL bitmap would be drawn in
       rClip;                   // Rectangle available for drawing in.
  int iRelx,iRely;
  struct _s_PlayerDrawType FAR *next;
  };

typedef struct _s_PlayerDrawType PlayerDrawType;

        // SubGridType is a full description of a SubGrid.
struct S_SubGridType {
    unsigned long ulOwnerID;
    POINT Pos;
        // Since there are a limited # of subgrids, we might share them.
    SubGridCellsType *SubGrid;
    };

typedef struct S_SubGridType SubGridType;

struct _SObjectType {
    DWORD Class;
    DWORD ObjectID;
    POINT MazePos;
    BYTE Facing;
    struct _SObjectType * next;
};

typedef struct _SObjectType ObjectType;

struct _SHoleType {
    int x[2];
    struct _SHoleType FAR *next;
    };

typedef struct _SHoleType HoleType;

struct _S_ScoreType {
    unsigned long ulID;
    int ix,iy;          // Upper left corner of score entry
    int iScore;
    BYTE bDir;
    struct _S_ScoreType FAR *next;
    };

typedef struct _S_ScoreType ScoreType;

typedef struct {
    int iLoudness;
    PositionType Pos;
    DWORD dwLen;
    char cBuff[132];
    } IntoneType;

typedef struct {
    int xSize,ySize;
    int xOrg,yOrg;
    } PicPosType;

typedef struct {
   char cBase[5];
   int iPicNum;
   PicPosType PicPos[4];
   } PicSpecType;

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

/**********************Function definitions***************************/

int ReadSubGrids(VOID);
int InitGrid(VOID);

LPTSTR   GetStringRes(int id);
LPTSTR   GetStringRes2(int id);

/**********************GLOBAL VARIABLES ****************************/
                // If we're in the main function, the globals
                // will be declared. Otherwise they'll just
                // be given as externs.
#ifndef IN_MAIN
#define EXT_DECL extern
#else
#define EXT_DECL
#endif

EXT_DECL SubGridType FAR  * FAR *Grid;
EXT_DECL SubGridCellsType FAR *SubGrids;
EXT_DECL FILE  *hFile;
EXT_DECL int xc,yc,xscale,yscale;
EXT_DECL RECT CurView;
EXT_DECL char szString[128];   /* variable to load resource strings         */

EXT_DECL char szAppName[20];   /* class name for the window               */
EXT_DECL HINSTANCE hInst;
EXT_DECL HWND hWndMain,hWndMaze,hWndTopView,hWndText,hWndScore;
EXT_DECL RECT rMain,rMaze,rTopView,rText,rScore;
EXT_DECL BYTE FAR * FAR *bMaze;
EXT_DECL LPPOINT FAR * FAR *pPost;
EXT_DECL int iNumDrones,iDroneSpeed,iDroneMoveAlg;
EXT_DECL UINT uiTimer;
EXT_DECL HBITMAP hFishBitmap[4],hFishMask[4];
EXT_DECL HBITMAP hRoboBitmap[4],hRoboMask[4];
EXT_DECL int ViewEnd;           // This is how many squares ahead we can see
EXT_DECL BOOL GamePaused,GameStarted,bNetworked,bSelfInSanct;

EXT_DECL FullPicType fptPic;
EXT_DECL HBITMAP hbScratch;
EXT_DECL float scale;

EXT_DECL PlayerType ptSelf;
EXT_DECL PlayerType ptPlayers;
EXT_DECL PlayerType ptDrones;
EXT_DECL HANDLE hMailSlot;

EXT_DECL PositionType ptLastPos;

EXT_DECL int iPlayersKilled,iDronesKilled;
EXT_DECL int iTimesKilled,iKilledByDrones;
EXT_DECL ScoreType Scores;
EXT_DECL int iWhisperDist, iShoutDist,iLoudness;

EXT_DECL HBITMAP hShotBM[2],hFadeBM[2];

EXT_DECL HBITMAP hMaze3DBM;
EXT_DECL HDC     hMazeDC;
EXT_DECL BOOL    bBitmapDraw;
EXT_DECL BOOL    bPlayerPrestretch,bDronePrestretch;
EXT_DECL FullPicType FAR * FAR *PreStretch;

EXT_DECL BOOL    bDemoMode;
EXT_DECL BOOL    bDemoEnable;
EXT_DECL INT     UserIntCount;

extern PicSpecType PicSpec[];
extern int iNumPicSpecs;

/*******************END GLOBALS *************************************/

#define VIEWER_DIST              (PANEL_WIDTH)
#define MC_TO_SC(x,z) (scale*(VIEWER_DIST * (x)/((z)+(VIEWER_DIST))))

#endif


README.TXT

CDS

The directory samples\rpc\ns\cds contains the following files that
describe the gateway protocol to the DCE Cell Directory Service (CDS):

FILES
=====

File               Description

README.TXT         This readme file
NSICLT.IDL[ACF]    Client-side interface
NSISVR.IDL[ACF]    Server-side interface
NSICOM.IDL[ACF]    Interface common to client and server
NSIMGM.IDL[ACF]    Interface for management

The OSF DCE environment uses the DCE Cell Directory Service
(CDS) for its RPC name service. However, the OSF has not yet
released a specification of the protocol used to access the
CDS. To allow interoperation between DCE computers and RPC
client applications that call RPC name service API
functions, you must use a gateway to the DCE CDS.

To help you develop such a gateway, Microsoft defines an
interface for a gateway protocol in the files NSICLT.IDL,
NSSVR.IDL, and NSICOM.IDL, and provides the client-side
implementation of this gateway protocol in RPCNS.DLL. One
such implementation of the server side gateway is currently
available in the Digital Equipment Corporation DCE Starter
Kit.

To use the gateway so that your RPC application can
interoperate with DCE RPC applications:

1. Verify that the gateway is started on the DCE host

2. Verify that the client is running the TCP/IP transport

3. (Microsoft Windows NT and Windows 95 only) Set the registry entry
   Software\Microsoft\Rpc\NameService\ServerNetworkAddress to a
   string of the form "<ip address of gateway 1>; <ip address
   of gateway 2>...", where <ip address of gateway n> is the ip
   address of the nth gateway in the list.

4. Set the following registry entries under Software\
   Microsoft\Rpc\NameService to the following values:

   Entry            Value

   Protocol         ncacn_ip_tcp
   NetworkAddress   <ip addr gateway 1>; <ip addr gateway 2>...
   Endpoint         <empty string>

   To change registry entry settings on Microsoft Windows NT or Windows 95,
   use the REGEDIT utility. On MS-DOS and Microsoft Windows,
   edit the text file RPCREG.DAT.


NSICLT.IDL

[
 uuid (d3fbb514-0e3b-11cb-8fad-08002b1d29c3),
 version (1.0),
 pointer_default (unique)
]
interface NsiC
{

import"nsicom.idl";

/*
 * N S I _ B I N D I N G _ L O O K U P _ B E G I N
 *
 * Create an import context for an interface and optionally an object.
 */

void nsi_binding_lookup_begin
    (
    [in]        UNSIGNED32               entry_name_syntax,
    [in]        STRING_T                 entry_name,
    [in]        NSI_INTERFACE_ID_T     * if_spec,
    [in]        NSI_UUID_P_T             obj_uuid,
    [in]        UNSIGNED32               binding_max_count,
    [in]        UNSIGNED32               ignore,
    [out]       NSI_NS_HANDLE_T        * import_context,
    [out]       UNSIGNED16             * status
    );

/*
 * N S I _ B I N D I N G _ L O O K U P _ D O N E
 *
 * Delete an import context.
 */

void nsi_binding_lookup_done
    (
    [in,out] NSI_NS_HANDLE_T *import_context,
    [out]    UNSIGNED16      *status
    );

/*
 * N S I _ B I N D I N G _ L O O K U P _ N E X T
 *
 * Look up an interface and optionally an object from a name-service
 * database and return a binding of a compatible server (if found).
 */

void nsi_binding_lookup_next
    (
    [in]     NSI_NS_HANDLE_T             import_context,
    [in,out] NSI_BINDING_VECTOR_P_T    * binding_vector,
    [out]    UNSIGNED16                * status
    );

/*
 * N S I _ M G M T _ H A N D L E _ E N T R Y _ S E T _ E X P _ A G E
 *
 * Set the maxium age that a cached entry can be returned through a lookup
 * context.
 */

void nsi_mgmt_handle_set_exp_age
    (
    [in]        NSI_NS_HANDLE_T          inq_context,
    [in]        UNSIGNED32               expiration_age,
    [out]       UNSIGNED16             * status
    );

}


NSICOM.ACF

[ implicit_handle (handle_t NsiSvrBinding) ] interface NSiTypes
{
}


NSICOM.IDL

/* --------------------------------------------------------------------

                      Microsoft OS/2 LAN Manager
                 Copyright(c) Microsoft Corp., 1990-1995

                           RPC locator

    This file is just a wrapper for MIDL to get a reference to
    types used in the interface.
-------------------------------------------------------------------- */

[
    uuid (00000000-0000-0000-0000-000000000000),
    pointer_default (unique)
]
    interface NSiTypes

{

/*
 * Misc commonly used standard types.
 */

typedef wchar_t  UNSIGNED_CHAR_T;
typedef unsigned long  UNSIGNED32;
typedef unsigned short UNSIGNED16;
typedef unsigned char  UNSIGNED8;
typedef [string, unique] UNSIGNED_CHAR_T *STRING_T;

typedef [string] UNSIGNED_CHAR_T *NSI_STRING_BINDING_T;
typedef [context_handle] void * NSI_NS_HANDLE_T;

typedef struct _NSI_UUID_T
    {
    UNSIGNED32          time_low;
    UNSIGNED16          time_mid;
    UNSIGNED16          time_hi_and_version;
    UNSIGNED8           clock_seq_hi_and_reserved;
    UNSIGNED8           clock_seq_low;
    UNSIGNED8           node[6];
    } NSI_UUID_T;

typedef [unique] NSI_UUID_T *NSI_UUID_P_T;

typedef struct _NSI_BINDING_T
    {
    NSI_STRING_BINDING_T  string;
    UNSIGNED32            entry_name_syntax;
    STRING_T              entry_name;
    } NSI_BINDING_T;

typedef struct _NSI_BINDING_VECTOR_T
    {
    UNSIGNED32            count;
    [size_is(count)]
    NSI_BINDING_T  binding[*];
    } NSI_BINDING_VECTOR_T;

typedef [unique] NSI_BINDING_VECTOR_T *NSI_BINDING_VECTOR_P_T;

typedef struct _NSI_SERVER_BINDING_VECTOR_T
    {
    UNSIGNED32            count;
    [size_is(count)]
    NSI_STRING_BINDING_T  string[*];
    } NSI_SERVER_BINDING_VECTOR_T;

typedef [unique] NSI_SERVER_BINDING_VECTOR_T *NSI_SERVER_BINDING_VECTOR_P_T;

typedef struct _NSI_UUID_VECTOR_T
    {
    UNSIGNED32 count;
    [size_is(count)]
    NSI_UUID_P_T uuid[*];
    } NSI_UUID_VECTOR_T;

typedef [unique] NSI_UUID_VECTOR_T *NSI_UUID_VECTOR_P_T;

typedef struct _NSI_SYNTAX_ID_T
    {
    NSI_UUID_T          id;
    UNSIGNED32          version;
    } NSI_SYNTAX_ID_T;

typedef [unique] NSI_SYNTAX_ID_T *NSI_IF_ID_P_T;

typedef struct _NSI_INTERFACE_ID_T
    {
    NSI_SYNTAX_ID_T Interface;
    NSI_SYNTAX_ID_T TransferSyntax;
    } NSI_INTERFACE_ID_T;

typedef [unique] NSI_INTERFACE_ID_T *NSI_INTERFACE_ID_P_T;

typedef struct _NSI_IF_VECTOR_T
    {
    UNSIGNED32 count;
    [size_is(count)]
    NSI_IF_ID_P_T if_id[*];
    } NSI_IF_ID_VECTOR_T;

// If you change this table, you must update the status mapping vector
// NsiToRpcStatus in client\util.cxx

enum {
    NSI_S_OK,
    NSI_S_NO_MORE_BINDINGS,
    NSI_S_INTERFACE_NOT_FOUND,
    NSI_S_ENTRY_NOT_FOUND,
    NSI_S_NAME_SERVICE_UNAVAILABLE,
    NSI_S_NO_NS_PRIVILEGE,
    NSI_S_UNSUPPORTED_NAME_SYNTAX,
    NSI_S_NOTHING_TO_UNEXPORT,
    NSI_S_INVALID_NAME_SYNTAX,
    NSI_S_INVALID_NS_HANDLE,
    NSI_S_INVALID_OBJECT,
    NSI_S_NOT_ALL_OBJS_UNEXPORTED,    // Was NSI_S_INVALID_INTERFACE, but this
                                      // never could be returned by CSD.
    NSI_S_INVALID_STRING_BINDING,
    NSI_S_SOME_OTHER_ERROR,

    // New error codes added after 4/92 and v 1.0 of CSD deamon.

    NSI_S_NOTHING_TO_EXPORT,
    NSI_S_UNIMPLEMENTED_API,
    NSI_S_NO_INTERFACES_EXPORTED,
    NSI_S_INCOMPLETE_NAME,
    NSI_S_INVALID_VERS_OPTION,
    NSI_S_NO_MORE_MEMBERS,
    NSI_S_ENTRY_ALREADY_EXISTS,
    NSI_S_OUT_OF_MEMORY,
    NSI_S_GROUP_MEMBER_NOT_FOUND,
    NSI_S_STATUS_MAX,
};

}


NSIMGM.ACF

[ implicit_handle (handle_t NsiClntBinding) ] interface NsiM
{
}


NSIMGM.IDL

[
    uuid (d6d70ef0-0e3b-11cb-acc3-08002b1d29c4),
    version (1.0),
    pointer_default (unique)
]

interface NsiM
{

import"nsicom.idl";

/*
 * N S I _ G R O U P _ D E L E T E
 *
 * Remove a group attribute from a name service entry.
 */

void nsi_group_delete
    (
    [in]        UNSIGNED32                    group_name_syntax,
    [in]        STRING_T                      group_name,
    [out]       UNSIGNED16                  * status
    );

/*
 * N S I _ G R O U P _ M B R _ A D D
 *
 * Add a an entry name to a group, if necessary, create the group.
 */

void nsi_group_mbr_add
    (
    [in]        UNSIGNED32                    group_name_syntax,
    [in]        STRING_T                      group_name,
    [in]        UNSIGNED32                    member_name_syntax,
    [in]        STRING_T                      member_name,
    [out]       UNSIGNED16                  * status
    );

/*
 * N S I _ G R O U P _ M B R _ R E M O V E
 *
 * Delete a an entry name to a group.
 */

void nsi_group_mbr_remove
    (
    [in]        UNSIGNED32                    group_name_syntax,
    [in]        STRING_T                      group_name,
    [in]        UNSIGNED32                    member_name_syntax,
    [in]        STRING_T                      member_name,
    [out]       UNSIGNED16                  * status
    );

/*
 * N S I _ G R O U P _ M B R _ I N Q _ B E G I N
 *
 * Create an inquiry context for an group with a certain name syntax..
 */

void nsi_group_mbr_inq_begin
    (
    [in]        UNSIGNED32                    group_name_syntax,
    [in]        STRING_T                      group_name,
    [in]        UNSIGNED32                    member_name_syntax,
    [out]       NSI_NS_HANDLE_T             * inq_context,
    [out]       UNSIGNED16                  * status
    );

/*
 * N S I _ G R O U P _ M B R _ I N Q _ N E X T
 *
 * Return the next group member in name service group.
 */

void nsi_group_mbr_inq_next
    (
    [in]        NSI_NS_HANDLE_T               inq_context,
    [out]       STRING_T                    * member_name,
    [out]       UNSIGNED16                  * status
    );

/*
 * N S I _ G R O U P _ M B R _ I N Q _ D O N E
 *
 * Delete a inquiry context for a group.
 */

void nsi_group_mbr_inq_done
    (
    [in,out]    NSI_NS_HANDLE_T             * inq_context,
    [out]       UNSIGNED16                  * status
    );

/*
 * N S I _ P R O F I L E _ D E L E T E
 *
 * Remove a profile attribute from a name service entry.
 */

void nsi_profile_delete
    (
    [in]        UNSIGNED32                    profile_name_syntax,
    [in]        STRING_T                      profile_name,
    [out]       UNSIGNED16                  * status
    );

/*
 * N S I _ P R O F I L E _ E L T _ A D D
 *
 * Add a an entry name to a profile, if necessary, create the profile.
 */

void nsi_profile_elt_add
    (
    [in]        UNSIGNED32                    profile_name_syntax,
    [in]        STRING_T                      profile_name,
    [in]        NSI_IF_ID_P_T                 if_id,
    [in]        UNSIGNED32                    member_name_syntax,
    [in]        STRING_T                      member_name,
    [in]        UNSIGNED32                    priority,
    [in]        STRING_T                      annotation,
    [out]       UNSIGNED16                  * status
    );

/*
 * N S I _ P R O F I L E _ E L T _ R E M O V E
 *
 * Delete a an entry name to a profile.
 */

void nsi_profile_elt_remove
    (
    [in]        UNSIGNED32                    profile_name_syntax,
    [in]        STRING_T                      profile_name,
    [in]        NSI_IF_ID_P_T                 if_id,
    [in]        UNSIGNED32                    member_name_syntax,
    [in]        STRING_T                      member_name,
    [out]       UNSIGNED16                  * status
    );

/*
 * N S I _ P R O F I L E _ E L T _ I N Q _ B E G I N
 *
 * Create an inquiry context for an profile with a certain name syntax..
 */

void nsi_profile_elt_inq_begin
    (
    [in]        UNSIGNED32                    profile_name_syntax,
    [in]        STRING_T                      profile_name,
    [in]        UNSIGNED32                    inquiry_type,
    [in]        NSI_IF_ID_P_T                 if_id,
    [in]        UNSIGNED32                    vers_option,
    [in]        UNSIGNED32                    member_name_syntax,
    [in]        STRING_T                      member_name,
    [out]       NSI_NS_HANDLE_T             * inq_context,
    [out]       UNSIGNED16                  * status
    );

/*
 * N S I _ P R O F I L E _ E L T _ I N Q _ N E X T
 *
 * Return the next profile member in name service profile.
 */

void nsi_profile_elt_inq_next
    (
    [in]        NSI_NS_HANDLE_T               inq_context,
    [in,out]    NSI_IF_ID_P_T                 if_id,
    [out]       STRING_T                    * member_name,
    [out]       UNSIGNED32                  * priority,
    [out]       STRING_T                    * annotation,
    [out]       UNSIGNED16                  * status
    );

/*
 * N S I _ P R O F I L E _ E L T _ I N Q _ D O N E
 *
 * Delete a inquiry context for a profile.
 */

void nsi_profile_elt_inq_done
    (
    [in,out]    NSI_NS_HANDLE_T             * inq_context,
    [out]       UNSIGNED16                  * status
    );

/*
 * N S I _ E N T R Y _ O B J E C T _ B E G I N
 *
 * Create an inquiry context for an server entry's object UUIDs.
 */

void nsi_entry_object_inq_begin
    (
    [in]        UNSIGNED32                    entry_name_syntax,
    [in]        STRING_T                      entry_name,
    [out]       NSI_NS_HANDLE_T             * inq_context,
    [out]       UNSIGNED16                  * status
    );

/*
 * N S I _ E N T R Y _ O B J E C T _ I N Q _ N E X T
 *
 * Return the object UUID in a server entry.
 */

void nsi_entry_object_inq_next
    (
    [in]        NSI_NS_HANDLE_T               inq_context,
    [in, out]   NSI_UUID_P_T                  uuid,
    [out]       UNSIGNED16                  * status
    );

/*
 * N S I _ E N T R Y _ O B J E C T _ I N Q _ D O N E
 *
 * Delete a inquiry context for a server entry's objects.
 */

void nsi_entry_object_inq_done
    (
    [in,out]    NSI_NS_HANDLE_T             * inq_context,
    [out]       UNSIGNED16                  * status
    );

/*
 * N S I _ E N T R Y _ E X P A N D _ N A M E
 *
 * Expand a cell relative name into a global one.
 */

void nsi_entry_expand_name
    (
    [in]        UNSIGNED32                    entry_name_syntax,
    [in]        STRING_T                      entry_name,
    [out]       STRING_T                    * expanded_name,
    [out]       UNSIGNED16                  * status
    );

/*
 * N S I _ M G M T _ B I N D I N G _ U N E X P O R T
 *
 * Remove interfaces and/or objects from a given server entry.
 */

void nsi_mgmt_binding_unexport
    (
    [in]        UNSIGNED32                    entry_name_syntax,
    [in]        STRING_T                      entry_name,
    [in]        NSI_IF_ID_P_T                 if_id,
    [in]        UNSIGNED32                    vers_option,
    [in]        NSI_UUID_VECTOR_P_T           object_uuid_vec,
    [out]       UNSIGNED16                  * status
    );

/*
 * N S I _ M G M T _ E N T R Y _ D E L E T E
 *
 * Delete a server/group/profile name service entry.
 */

void nsi_mgmt_entry_delete
    (
    [in]        UNSIGNED32                    entry_name_syntax,
    [in]        STRING_T                      entry_name,
    [out]       UNSIGNED16                  * status
    );

/*
 * N S I _ M G M T _ E N T R Y _ C R E A T E
 *
 * Create a name service entry.
 */

void nsi_mgmt_entry_create
    (
    [in]        UNSIGNED32                    entry_name_syntax,
    [in]        STRING_T                      entry_name,
    [out]       UNSIGNED16                  * status
    );

/*
 * N S I _ M G M T _ E N T R Y _ I N Q _ I F D S
 *
 * Inquiry the interfaces at a given name service entry.
 */

void nsi_mgmt_entry_inq_if_ids
    (
    [in]        UNSIGNED32                    entry_name_syntax,
    [in]        STRING_T                      entry_name,
    [out]       NSI_IF_ID_VECTOR_T         ** if_id_vec,
    [out]       UNSIGNED16                  * status
    );

/*
 * N S I _ M G M T _ I N Q  _ E X P _ A G E
 *
 * Inquiry the global age of entries cached.
 */

void nsi_mgmt_inq_exp_age
    (
    [out]       UNSIGNED32                  * expiration_age,
    [out]       UNSIGNED16                  * status
    );

/*
 * N S I _ M G M T _ I N Q  _ S E T _ A G E
 *
 * Set the global age of entries cached.
 */

void nsi_mgmt_inq_set_age
    (
    [in]        UNSIGNED32                    expiration_age,
    [out]       UNSIGNED16                  * status
    );

}


NSISVR.ACF

[ implicit_handle (handle_t NsiSvrBinding) ] interface NsiS
{
}


NSISVR.IDL

[
 uuid (d6d70ef0-0e3b-11cb-acc3-08002b1d29c3),
 version (1.0),
 pointer_default (unique)
]
interface NsiS
{

import"nsicom.idl";

/*
 * N S I _ B I N D I N G _ E X P O R T
 *
 * Export a name service database entry with multiple binding handles
 * and multiple objects for a server.
 */

void nsi_binding_export
    (
    [in]  UNSIGNED32                        entry_name_syntax,
    [in]  STRING_T                          entry_name,
    [in]  NSI_INTERFACE_ID_T              * interface_object,
    [in]  NSI_SERVER_BINDING_VECTOR_P_T     binding,
    [in]  NSI_UUID_VECTOR_P_T               object_uuid_vec,
    [out] UNSIGNED16                      * status
    );

/*
 * N S I _ B I N D I N G _ U N E X P O R T
 *
 * Removes the binding handles for an interface and/or objects from an
 * entry in the name service database.
 */

void nsi_binding_unexport
    (
    [in]  UNSIGNED32                        entry_name_syntax,
    [in]  STRING_T                          entry_name,
    [in]  NSI_INTERFACE_ID_T              * interface_object,
    [in]  NSI_UUID_VECTOR_P_T               object_uuid_vec,
    [out] UNSIGNED16                      * status
    );
}


NSICLT.ACF

[ implicit_handle (handle_t NsiClntBinding) ] interface NsiC
{
}


README.TXT

NHELLO

FILES
=====

The directory samples\rpc\ns\nhello contains the following files for
building the sample distributed application NHELLO:

File            Description

README.TXT      Readme file for the NHELLO sample
NHELLO.IDL      Interface definition language file
NHELLOC.C       Client main program
NHELLOS.C       Server main program
NHELLOP.C       Remote procedures
NSSERV.C        NT Service program
SERVICE.C       Service Control Manager interface
MAKEFILE        Nmake file to build for Windows NT or Windows 95
MAKEFILE.DOS    Nmake file to build for MS-DOS

The directory samples\rpc\ns\cds contains the files that describe the
gateway protocol to the DCE Cell Directory Service (CDS)

-----------------------------------------------------------------------
BUILDING CLIENT AND SERVER APPLICATIONS FOR MICROSOFT WINDOWS NT OR WINDOWS 95:
-----------------------------------------------------------------------

The following environment variables should be set for you already.

  set CPU=i386
  set INCLUDE=%SDKROOT%\h
  set LIB=%SDKROOT%\lib
  set PATH=%SDKROOT%\system32;%SDKROOT%\bin;

Where %SDKROOT% is the root directory for the 32-bit Windows SDK.

For mips, set CPU=mips
For alpha, set CPU=alpha

Build the sample distributed application:
  nmake cleanall
  nmake

This builds the executable programs nhelloc.exe
(client), nhellos.exe (console server), and nsserv
( NT service ).

-----------------------------------------------------------------------
BUILDING THE CLIENT APPLICATION FOR MS-DOS
-----------------------------------------------------------------------

After installing the Microsoft Visual C/C++ version 1.50 development
environment and the 16-bit RPC SDK on a Windows NT or Windows 95
computer, you can build the sample client application from Windows NT
or Windows 95.

  nmake -f makefile.dos cleanall
  nmake -f makefile.dos

This builds the client application nhelloc.exe.

You may also execute the Microsoft Visual C/C++ compiler under MS-DOS.
This requires a two step build process.

  Step One: Compile the .IDL files under Windows NT or Windows 95
     nmake -a -f makefile.dos nhello.h

  Step Two: Compile the C sources (stub and application) under MS-DOS.
     nmake -f makefile.dos

-----------------------------------------------------------------------
RUNNING THE CLIENT AND SERVER APPLICATIONS
-----------------------------------------------------------------------

-- NHELLOS --
On the server, enter

  net start rpclocator
  set start rpcss
  nhellos

(You can also start the RPC Locator and RPC Service by running
the Services program in Control Panel.)

-- NSSERV --
To install the service, first compile everything, and then:

    nsserv -install

Now all you have to do is start it, either using the
"net start" method, the control panel Services applet, or
the SC.EXE utilitie in the MSTOOLS\BIN directory:

    sc start nshelloservice

Once the service has been started, you can use the client
program to verify that it really is working, using the syntax:

    nhelloc

which should cause to service to stop since it calls Shutdown()

If, after playing with the sample you wish to remove the service,
simply say:

    nsserv -remove

Other: NSSERV can be run as a console app by entering

    nsserv -debug

For more information on SERVICES see the SERVICE sample included
with the Win32 SDK.

-- NHELLOC --
On the client, enter

  net start workstation
  nhelloc

Note:  The client and server applications can run on the same Microsoft
Windows NT computer when you use different screen groups.  Also, only NSSERV
or NHELLOS should be run at one time on the same Windows NT computer.

Several command line switches are available to change settings for this
program. For a listing of the switches available from the client
program, enter:

  nhelloc -?

For a listing of switches available from the server program and the
service program enter

  nhellos -?


NHELLO.IDL

[ uuid (5D6E4DA0-0D21-1068-94B1-00DD010662DA),
  version(1.0),
  pointer_default(unique)
]
interface nhello
{

void HelloProc(handle_t nBinding,
               [in, string] unsigned char * pszString);

void Shutdown(handle_t nBinding);

}


NHELLOC.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                       nhello Example

    FILE:       nhelloc.c

    USAGE:      nhelloc  -s string
                         -n name_service_entry_name
                         -t name_syntax_type

    PURPOSE:    Client side of RPC distributed application

    FUNCTIONS:  main() - binds to server and calls remote procedure

    COMMENTS:

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

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include "nhello.h"    // header file generated by MIDL compiler

#define PURPOSE \
"This Microsoft RPC Version 2.0 sample program demonstrates\n\
the use of the name service. For more information about these\n\
RPC API functions, see the RPC programming guide and reference.\n\n"

void Usage(char * pszProgramName)
{
    fprintf(stderr, "%s", PURPOSE);
    fprintf(stderr, "Usage:  %s\n", pszProgramName);
    fprintf(stderr, " -s string\n");
    fprintf(stderr, " -n name_service_entry_name\n");
    fprintf(stderr, " -t name_syntax_type\n");
    exit(1);
}

void _CRTAPI1 main(int argc, char **argv)
{
    unsigned char * pszString     = "hello, world";
    unsigned char * pszEntryName  = "/.:/nhello_sample";
    unsigned char * pszStrBinding = NULL;
    RPC_NS_HANDLE hnsHello;
    RPC_BINDING_HANDLE hHello;
    unsigned long fNameSyntaxType = RPC_C_NS_SYNTAX_DEFAULT;
    RPC_STATUS status;
    unsigned long ulCode;
    short fSuccess = 0;
    short fContinue = 1;
    short i;

    /* allow the user to override settings with command line switches */
    for (i = 1; i < argc; i++) {
        if ((*argv[i] == '-') || (*argv[i] == '/')) {
            switch (tolower(*(argv[i]+1))) {
            case 'n':
                pszEntryName = argv[++i];
                break;
            case 't':
                fNameSyntaxType = (unsigned int) atoi(argv[++i]);
                break;
            case 's':
                pszString = argv[++i];
                break;
            case 'h':
            case '?':
            default:
                Usage(argv[0]);
            }
        }
        else
            Usage(argv[0]);
    }

    RpcTryExcept {
        status = RpcNsBindingImportBegin(fNameSyntaxType,
                                         pszEntryName,
                                         nhello_v1_0_c_ifspec,
                                         NULL,
                                         &hnsHello);
        printf("RpcNsBindingImportBegin returned 0x%x\n", status);
    }
    RpcExcept(1) {
        ulCode = RpcExceptionCode();
        printf("RPC Runtime raised exception 0x%x\n", ulCode);
        fContinue = 0;
    }
    RpcEndExcept

    if ( status != RPC_S_OK )
        fContinue = 0;

    /* The loop is present because the name service may contain "stale" */
    /* and unusable binding handlers.  This is part of the DCE design.  */
    while( fContinue )
    {
        status = RpcNsBindingImportNext(hnsHello,
                                        &hHello);
        printf("RpcNsBindingImportNext returned 0x%x\n", status);

        if ( (status == RPC_S_NO_MORE_BINDINGS) ||
             (status == RPC_S_NAME_SERVICE_UNAVAILABLE ) )
            break;

        if (status != RPC_S_OK)
            continue;

        RpcBindingToStringBinding( hHello, &pszStrBinding );
        printf("StringBinding: %s\n", pszStrBinding );
        RpcStringFree(&pszStrBinding);

        RpcTryExcept {
            printf("Calling remote procedure HelloProc with string %s\n",
                   pszString);
            HelloProc(hHello, pszString);
            fContinue = 0;
            fSuccess = 1;
        }
        RpcExcept(1) {
            ulCode = RpcExceptionCode();
            printf("RPC Runtime raised exception 0x%x\n", ulCode);
            status = RpcBindingFree(&hHello);
            printf("RpcBindingFree returned 0x%x\n", status);
            fContinue = 1;
        }
        RpcEndExcept

    }

    RpcTryExcept {
        status = RpcNsBindingImportDone(&hnsHello);
        printf("RpcNsBindingImportDone returned 0x%x\n", status);
    }
    RpcExcept(1) {
        ulCode = RpcExceptionCode();
        printf("RPC Runtime raised exception 0x%x\n", ulCode);
        fContinue = 0;
    }
    RpcEndExcept

    if ( fSuccess )
    {
        RpcTryExcept {
            Shutdown(hHello);  // Shutdown is a remote procedure
        }
        RpcExcept(1) {
            ulCode = RpcExceptionCode();
            printf("RPC runtime raised exception 0x%x\n", ulCode);
        }
        RpcEndExcept

        status = RpcBindingFree(&hHello);
        printf("RpcBindingFree returned 0x%x\n", status);
    }

    exit(0);

}  // end main()

/*********************************************************************/
/*                 MIDL allocate and free                            */
/*********************************************************************/

void __RPC_FAR * __RPC_USER midl_user_allocate(size_t len)
{
    return(malloc(len));
}

void __RPC_USER midl_user_free(void __RPC_FAR * ptr)
{
    free(ptr);
}

/* end file nhelloc.c */


NHELLOP.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                       nhello Example

    FILE:       nhellop.c

    PURPOSE:    Remote procedures that are linked with the server
                side of RPC distributed application

    FUNCTIONS:  TimeProc() - obtains the time from the server

    COMMENTS:

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

#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include "nhello.h"    // header file generated by MIDL compiler

void HelloProc(handle_t hHello, unsigned char *pszString)
{
    printf("%s\n", pszString);
    return;
}

void Shutdown(handle_t hHello)
{
    RPC_STATUS status;

    printf("Calling RpcMgmtIsServerListening\n");
    status = RpcMgmtIsServerListening(NULL);
    printf("RpcMgmtIsServerListening returned: 0x%x\n", status);

    if ( status == RPC_S_OK )
    {
        printf("Calling RpcMgmtStopServerListening\n");
        status = RpcMgmtStopServerListening(NULL);
        printf("RpcMgmtStopServerListening returned: 0x%x\n", status);
    }

}

/* end file nhellop.c */


NHELLOS.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                         nhello Example

    FILE:       nhellos.c

    USAGE:      nhellos
                        -m maxcalls
                        -n mincalls
                        -f flag for RpcServerListen wait
                        -a nhello_sample_nsi_entry_name
                        -t name_syntax_type

    PURPOSE:    Server side of RPC distributed application nhello

    FUNCTIONS:  main() - registers server as RPC server

    COMMENTS:

****************************************************************************/
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include "nhello.h"   // header file generated by MIDL compiler

BOOL WINAPI ControlHandler ( DWORD dwCtrlType );

void Usage(char * pszProgramName)
{
    fprintf(stderr, "Usage:  %s\n", pszProgramName);
    fprintf(stderr, " -m maxcalls\n");
    fprintf(stderr, " -n mincalls\n");
    fprintf(stderr, " -f flag for RpcServerListen wait\n");
    fprintf(stderr, " -a nhello_sample_nsi_entry_name\n");
    fprintf(stderr, " -t name_syntax_type\n");
    exit(1);
}

void _CRTAPI1 main(int argc, char * argv[])
{
    RPC_STATUS status;
    RPC_BINDING_VECTOR * pBindingVector = NULL;
    unsigned char * pszEntryName    = "/.:/nhello_sample";
    unsigned char * pszSecurity     = NULL;
    unsigned int    cMinCalls       = 1;
    unsigned int    cMaxCalls       = 20;
    unsigned int    fDontWait       = 0;
    unsigned int    fNameSyntaxType = RPC_C_NS_SYNTAX_DEFAULT;
    unsigned int    fRegistered     = 0;
    unsigned int    fEndpoint       = 0;
    unsigned int    fExported       = 0;
    int i;

    /* allow the user to override settings with command line switches */
    for (i = 1; i < argc; i++) {
        if ((*argv[i] == '-') || (*argv[i] == '/')) {
            switch (tolower(*(argv[i]+1))) {
            case 'm':
                cMaxCalls = (unsigned int) atoi(argv[++i]);
                break;
            case 'n':
                cMinCalls = (unsigned int) atoi(argv[++i]);
                break;
            case 'f':
                fDontWait = (unsigned int) atoi(argv[++i]);
                break;
            case 'a':
                pszEntryName = argv[++i];
                break;
            case 't':
                fNameSyntaxType = (unsigned int) atoi(argv[++i]);
                break;
            case 'h':
            case '?':
            default:
                Usage(argv[0]);
            }
        }
        else
            Usage(argv[0]);
    }

    SetConsoleCtrlHandler( ControlHandler, TRUE );

    printf("CallingRpcServerUseAllProtseqs...\n");

    status = RpcServerUseAllProtseqs(cMaxCalls,     // max concurrent calls
                                     pszSecurity);  // Security descriptor
    printf("RpcServerUseAllProtseqs returned 0x%x\n", status);
    if (status) {
        goto cleanup;
    }

    status = RpcServerRegisterIf(nhello_v1_0_s_ifspec, // interface to register
                                 NULL,   // MgrTypeUuid
                                 NULL);  // MgrEpv; null means use default
    printf("RpcServerRegisterIf returned 0x%x\n", status);
    if (status) {
        goto cleanup;
    }
    else
        fRegistered = 1;

    status = RpcServerInqBindings(&pBindingVector);
    printf("RpcServerInqBindings returned 0x%x\n", status);
    if (status) {
        goto cleanup;
    }

    status = RpcEpRegister(nhello_v1_0_s_ifspec,
                           pBindingVector,
                           NULL,
                           "");
    printf("RpcEpRegister returned 0x%x\n", status);
    if (status) {
        goto cleanup;
    }
    else
        fEndpoint = 1;

    status = RpcNsBindingExport(fNameSyntaxType,  // name syntax type
                                pszEntryName,     // nsi entry name
                                nhello_v1_0_s_ifspec,
                                pBindingVector,   // set in previous call
                                NULL);            // UUID vector
    printf("RpcNsBindingExport returned 0x%x\n", status);
    if (status) {
        goto cleanup;
    }
    else
        fExported = 1;

    printf("Calling RpcServerListen\n");
    status = RpcServerListen(cMinCalls,
                             cMaxCalls,
                             fDontWait);  // wait flag
    printf("RpcServerListen returned: 0x%x\n", status);
    if (status) {
        goto cleanup;
    }

    if (fDontWait) {
        printf("Calling RpcMgmtWaitServerListen\n");
        status = RpcMgmtWaitServerListen();  //  wait operation
        printf("RpcMgmtWaitServerListen returned: 0x%x\n", status);
    }

  cleanup:

    if ( fExported )
    {
        status = RpcNsBindingUnexport(RPC_C_NS_SYNTAX_DEFAULT,  // name syntax type
                                       pszEntryName,            // nsi entry name
                                       nhello_v1_0_s_ifspec,
                                       NULL);                   // UUID vector
        printf( "RpcNsBindingUnexport returned 0x%x\n", status);
    }

    if ( fEndpoint )
    {
        status = RpcEpUnregister(nhello_v1_0_s_ifspec,
                                  pBindingVector,
                                  NULL);
        printf( "RpcEpUnregister returned 0x%x\n", status);
    }

    if ( pBindingVector )
    {
        status = RpcBindingVectorFree(&pBindingVector);
        printf( "RpcBindingVectorFree returned 0x%x\n", status);
    }

    if ( fRegistered )
    {
        status = RpcServerUnregisterIf(nhello_v1_0_s_ifspec, // interface to register
                                       NULL,   // MgrTypeUuid
                                       1);     // wait for outstanding calls
        printf( "RpcServerUnregisterIf returned 0x%x\n", status);
    }

}  // end main()

//
//  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 )
{
    RPC_STATUS status;

    switch( dwCtrlType )
    {
        case CTRL_BREAK_EVENT:  // use Ctrl+C or Ctrl+Break to call shutdown
        case CTRL_C_EVENT:

            printf("Calling RpcMgmtIsServerListening\n");
            status = RpcMgmtIsServerListening(NULL);
            printf("RpcMgmtIsServerListening returned: 0x%x\n", status);

            if ( status == RPC_S_OK )
            {
                printf("Calling RpcMgmtStopServerListening\n");
                status = RpcMgmtStopServerListening(NULL);
                printf("RpcMgmtStopServerListening returned: 0x%x\n", status);
            }
            break;

    }
    return FALSE;
}

/*********************************************************************/
/*                 MIDL allocate and free                            */
/*********************************************************************/

void __RPC_FAR * __RPC_USER midl_user_allocate(size_t len)
{
    return(malloc(len));
}

void __RPC_USER midl_user_free(void __RPC_FAR * ptr)
{
    free(ptr);
}

/* end file nhellos.c */


NSSERV.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                       nhello Example

    FILE:       nsserv.c

    USAGE:      nsserv
                         -m maxcalls
                         -n mincalls
                         -f flag for RpcServerListen wait
                         -a nhello_sample_nsi_entry_name
                         -t name_syntax_type

    PURPOSE:    Server side of RPC distributed application nhello

    FUNCTIONS:  main() - registers server as RPC server

    COMMENTS:

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

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <tchar.h>
#include "nhello.h"   // header file generated by MIDL compiler
#include "service.h"

VOID ServiceStart (DWORD dwArgc, LPTSTR *lpszArgv)
{
    RPC_STATUS status;
    RPC_STATUS temp_status;
    RPC_BINDING_VECTOR * pBindingVector = NULL;
    unsigned char * pszEntryName    = "/.:/nhello_sample";
    unsigned char * pszSecurity     = NULL;
    unsigned int    cMinCalls       = 1;
    unsigned int    cMaxCalls       = 20;
    unsigned int    fDontWait       = 0;
    unsigned int    fNameSyntaxType = RPC_C_NS_SYNTAX_DEFAULT;
    BOOL bRegistered = FALSE;
    BOOL bEndpoint = FALSE;
    BOOL bExported = FALSE;
    BOOL bProto = FALSE;
    DWORD i;

    /* allow the user to override settings with command line switches */
    for (i = 1; i < dwArgc; i++) {
        if ((*(lpszArgv[i]) == '-') || (*(lpszArgv[i]) == '/')) {
            switch (tolower(*((lpszArgv[i])+1))) {
            case 'm':
                cMaxCalls = (unsigned int) atoi(lpszArgv[++i]);
                break;
            case 'n':
                cMinCalls = (unsigned int) atoi(lpszArgv[++i]);
                break;
            case 'f':
                fDontWait = (unsigned int) atoi(lpszArgv[++i]);
                break;
            case 'a':
                pszEntryName = lpszArgv[++i];
                break;
            case 't':
                fNameSyntaxType = (unsigned int) atoi(lpszArgv[++i]);
                break;
            }
        }
    }

    // Prepare to Use All Protseqs
    if (!ReportStatusToSCMgr(
        SERVICE_START_PENDING, // service state
        NO_ERROR,              // exit code
        30000))                // wait hint  - UseAll can take quite a while
        goto cleanup;

    printf("CallingRpcServerUseAllProtseqs...\n");

    status = RpcServerUseAllProtseqs( cMaxCalls, pszSecurity );
    printf("RpcServerUseAllProtseqs returned 0x%x\n", status);
    if (status)
        goto cleanup;

    // Prepare to Register Interface
    if (!ReportStatusToSCMgr(
        SERVICE_START_PENDING, // service state
        NO_ERROR,              // exit code
        30000))                 // wait hint
        goto cleanup;

    status = RpcServerRegisterIf(nhello_v1_0_s_ifspec, // interface to register
                                 NULL,   // MgrTypeUuid
                                 NULL);  // MgrEpv; null means use default
    printf( TEXT("RpcServerRegisterIf returned 0x%x\n"), status);
    if (status)
        goto cleanup;
    else
        bRegistered = TRUE;

    // Prepare to Inquire Bindings
    if (!ReportStatusToSCMgr(
        SERVICE_START_PENDING, // service state
        NO_ERROR,              // exit code
        30000))                // wait hint  - Inq can take quite a while
        goto cleanup;

    status = RpcServerInqBindings(&pBindingVector);
    printf( TEXT("RpcServerInqBindings returned 0x%x\n"), status);
    if (status)
        goto cleanup;

    // Prepare to Register Endpoint
    if (!ReportStatusToSCMgr(
        SERVICE_START_PENDING, // service state
        NO_ERROR,              // exit code
        45000))                // wait hint  - Inq can take quite a while
        goto cleanup;

    status = RpcEpRegister(nhello_v1_0_s_ifspec,
                           pBindingVector,
                           NULL,
                           NULL);
    printf( TEXT("RpcEpRegister returned 0x%x\n"), status);
    if (status)
        goto cleanup;
    else
        bEndpoint = TRUE;

    // Prepare to Export to NameService
    if (!ReportStatusToSCMgr(
        SERVICE_START_PENDING, // service state
        NO_ERROR,              // exit code
        45000))                // wait hint
        goto cleanup;

    status = RpcNsBindingExport(fNameSyntaxType,  // name syntax type
                                pszEntryName,     // nsi entry name
                                nhello_v1_0_s_ifspec,
                                pBindingVector,   // set in previous call
                                NULL);            // UUID vector
    printf( TEXT("RpcNsBindingExport returned 0x%x\n"), status);
    if (status)
        goto cleanup;
    else
        bExported = TRUE;

    // Prepare to start listening.  At this point the service is initialized
    if (!ReportStatusToSCMgr(
        SERVICE_RUNNING, // service state
        NO_ERROR,        // exit code
        0))              // wait hint
        goto cleanup;

    printf( TEXT("Calling RpcServerListen\n"));
    status = RpcServerListen(cMinCalls,
                             cMaxCalls,
                             fDontWait );  // wait flag
    printf( TEXT("RpcServerListen returned: 0x%x\n"), status);
    if (status) {
        goto cleanup;
    }

    if (fDontWait) {
        printf("Calling RpcMgmtWaitServerListen\n");
        status = RpcMgmtWaitServerListen();  //  wait operation
        printf("RpcMgmtWaitServerListen returned: 0x%x\n", status);
    }

  cleanup:

    if ( bExported )
    {
        ReportStatusToSCMgr(
            SERVICE_STOP_PENDING,  // service state
            NO_ERROR,              // exit code
            3000);                 // wait hint

        temp_status = RpcNsBindingUnexport(RPC_C_NS_SYNTAX_DEFAULT,  // name syntax type
                                           pszEntryName,     // nsi entry name
                                           nhello_v1_0_s_ifspec,
                                           NULL);            // UUID vector
        printf( TEXT("RpcNsBindingUnexport returned 0x%x\n"), temp_status);
    }

    if ( bEndpoint )
    {
        ReportStatusToSCMgr(
            SERVICE_STOP_PENDING,  // service state
            NO_ERROR,              // exit code
            3000);                 // wait hint

        temp_status = RpcEpUnregister(nhello_v1_0_s_ifspec,
                                      pBindingVector,
                                      NULL);
        printf( TEXT("RpcEpUnregister returned 0x%x\n"), temp_status);
    }

    if ( pBindingVector )
    {
        ReportStatusToSCMgr(
            SERVICE_STOP_PENDING,  // service state
            NO_ERROR,              // exit code
            3000);                 // wait hint

        temp_status = RpcBindingVectorFree(&pBindingVector);
        printf( TEXT("RpcBindingVectorFree returned 0x%x\n"), temp_status);
    }

    if ( bRegistered )
    {
        ReportStatusToSCMgr(
            SERVICE_STOP_PENDING,  // service state
            NO_ERROR,              // exit code
            3000);                 // wait hint

        temp_status = RpcServerUnregisterIf(nhello_v1_0_s_ifspec, // interface to register
                                     NULL,   // MgrTypeUuid
                                     1);     // wait for outstanding calls
        printf( TEXT("RpcServerUnregisterIf returned 0x%x\n"), temp_status);
    }

    ReportStatusToSCMgr(
        SERVICE_STOP_PENDING,  // service state
        NO_ERROR,              // exit code
        3000);                 // wait hint

}

void ServiceStop( )
{
    RPC_STATUS status;

    ReportStatusToSCMgr(
        SERVICE_STOP_PENDING,  // service state
        NO_ERROR,              // exit code
        3000);                 // wait hint

    status = RpcMgmtIsServerListening( NULL );
    printf( TEXT("RpcMgmtIsServerListening returned 0x%x\n"), status);

    if ( status == RPC_S_OK )
        RpcMgmtStopServerListening( NULL );

}

/*********************************************************************/
/*                 MIDL allocate and free                            */
/*********************************************************************/

void __RPC_FAR * __RPC_USER midl_user_allocate(size_t len)
{
    return(malloc(len));
}

void __RPC_USER midl_user_free(void __RPC_FAR * ptr)
{
    free(ptr);
}

/* end file nsserv.c */


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 (C) 1993- 1996, 1995  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 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.
        //
        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 (C) 1993- 1996, 1995  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            "nsserv"
// internal name of the service
#define SZSERVICENAME        "NSHelloService"
// displayed name of the service
#define SZSERVICEDISPLAYNAME "NS Hello Service"
// list of service dependencies - "dep1\0dep2\0\0"
#define SZDEPENDENCIES       "RPCSS\0RPCLOCATOR\0\0"
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//// 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


README.TXT

The RPC\PICKLE directory has 2 subdirectories with pickling samples:

    PICKLP  -  Procedure pickling sample,
    PICKLT  -  Type pickling sample.

Each sample demonstrates a fixed buffer style pickling and an incremental
style pickling depending on the switches being used.

The implicit primitive handle is used as the pickling handle.

With default switches, both samples encode data to a file.
To decode data from the file, use -d switch.


README.TXT

PICKLEP

The PICKLEP program demonstrates data procedure serialization. The program
demonstrates a fixed buffer style pickling and an incremental style pickling
depending on the switches being used.

The implicit_handle primitive is used as the pickling handle.

With default switches, the program encodes data to a file. To decode data
from the file, use -d switch.

FILES
=====

The directory samples\rpc\pickle\picklp contains the following files for
building the distributed application PICKLP:

File          Description

README.TXT    Readme file for the PICKLE sample
PICKLP.IDL    Interface definition language file
PICKLP.ACF    Attribute configuration file
PICKLPC.C     Client main program
MAKEFILE      Nmake file to build for Windows NT or Windows 95
MAKEFILE.DOS  Nmake file to build for MS-DOS

-----------------------------------------------------------------------
BUILDING THE (CLIENT) FOR MICROSOFT WINDOWS NT OR WINDOWS 95
-----------------------------------------------------------------------

The following environment variables should already be set for you:

  set CPU=i386
  set INCLUDE=%SDKROOT%\h
  set LIB=%SDKROOT%\lib
  set PATH=%SDKROOT%\system32;%SDKROOT%\bin

where %SDKROOT% is the root directory for the 32-bit Windows SDK.

For mips,  set CPU=mips
For alpha, set CPU=alpha

Build the sample distributed application:

  nmake cleanall
  nmake

This builds the executable programs picklpc.exe.
There is no server side in this sample as the notion of server does not
apply to pickling.

------------------------------------------
BUILDING THE CLIENT APPLICATION FOR MS-DOS
------------------------------------------

After installing the Microsoft Visual C/C++ version 1.50 development
environment and the 16-bit RPC SDK on a Windows NT or Windows 95
computer, you can build the sample client application from Windows NT
or Windows 95:

  nmake -f makefile.dos cleanall
  nmake -f makefile.dos

This builds the client application callc.exe.

You may also execute the Microsoft Visual C/C++ compiler under MS-DOS.
This requires a two-step build process.

  Step One: Compile the .IDL files under Windows NT or Windows 95:

     nmake -a -f makefile.dos picklp.h

  Step Two: Compile the C sources (stub and application) under MS-DOS:

     nmake -f makefile.dos

-----------------------
RUNNING THE APPLICATION
-----------------------

The default setting for this application is to encode data to
pickle.dat file. Enter:

  picklpc

To decode data that has been encoded, enter:

  picklpc -d

Several other command line switches are available to change settings for
this program. For a listing of the switches available from the client
program, enter:

  picklpc -?


PICKLP.ACF

[ implicit_handle(handle_t ImplicitPicHandle )
]
interface proc_pickle
{

/* Both encode and decode will be used with this set of arguments. */

[encode, decode] ProcPickle();

}


PICKLP.IDL

[ uuid (B75AF600-9CF4-11CD-A076-08002B2BD711),
  version(1.0),
  pointer_default(unique)
]
interface proc_pickle
{

const short ARR_SIZE =  4;
const short BUFSIZE = 1024;

typedef struct _OBJECT1 {
    long    al[ARR_SIZE];
    short   s;
    } OBJECT1;

typedef struct _OBJECT2 {
    short                   sSize;
    [size_is(sSize)] short  as[];
    } OBJECT2;

void
ProcPickle(
    [in,out, string] char * pTextId,
    [in,out]  OBJECT1 *     pObject1,
    [in,out]  OBJECT2 *     pObject2
    );

}


PICKLPC.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                      picklp Example

    FILE:       picklpc.c

    USAGE:      picklpc  -f filename  (file to write to/read from)
                         -d           (serialization direction: decode)
                         -e           (serialization direction: encode)
                         -i           (incremental serialization on)

    PURPOSE:    The only side of RPC application

    FUNCTIONS:  main() - serializes data to or from a file

    COMMENTS:   Data procedure serialization is demonstrated here

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

#include <stdlib.h>
#include <stdio.h>
#include "picklp.h"    // header file generated by MIDL compiler

#define PURPOSE \
"This Microsoft RPC Version 2.0 sample program demonstrates\n\
the use of the [encode,decode] attributes. For more information\n\
about the attributes and RPC API functions, see the RPC programming\n\
guide and reference.\n\n"

/*  Used for incremental style only. */

void __RPC_USER PicAlloc( void * pState, char ** ppBuf, unsigned int * pCount);
void __RPC_USER PicWrite( void * pState, char *  pBuf,  unsigned int Count);
void __RPC_USER PicRead ( void * pState, char ** pBuf,  unsigned int * pCount);

typedef struct PickleControlBlock
{
    unsigned char *         pMemBuffer;
    unsigned char *         pBufferStart;
    unsigned long           LastSize;
} PickleControlBlock;

static PickleControlBlock                 UserState;
static PickleControlBlock * pUserState = &UserState;

void Usage(char * pszProgramName)
{
    fprintf(stderr, "%s", PURPOSE);
    fprintf(stderr, "Usage:  %s\n", pszProgramName);
    fprintf(stderr, " -d\n");
    fprintf(stderr, " -e\n");
    fprintf(stderr, " -ffilename\n");
    fprintf(stderr, " -i \n");
    exit(1);
}

void DumpData(
    char * pszComment,
    char * pszText,
    OBJECT1 * pObject1,
    OBJECT2 * pObject2 )
{
    int i;

    printf( "\n%s\n", pszComment );
    printf( "\tString\n\t\t%s\n", pszText );

    printf( "\tObject1");
    for (i=0; i < ARR_SIZE; i++)
        {
        if ( (i % 5) == 0 )
            printf( "\n\t\t");
        printf( "%08xl  ", pObject1->al[i] );
        }
    printf( "\n\t\t%x\n", pObject1->s );

    printf( "\tObject2");
    printf( "\n\t\t%x", pObject2->sSize );
    for (i=0; i < ARR_SIZE; i++)
        {
        if ( (i % 10) == 0 )
            printf( "\n\t\t");
        printf( "%04x  ", pObject2->as[i] );
        }
    printf( "\n" );
}

void WriteDataToFile(
    char *          pszFileName,
    char *          pbBuffer,
    unsigned long   ulSizeToWrite )
{
    FILE *      pFile;
    size_t      Count;

    if ( pszFileName ) {

        pFile = fopen( pszFileName, "w+b" );
        if ( pFile == NULL ) {
            printf("Cannot open the file for writing\n");
            exit(1);
            }

        Count = sizeof(long);
        if ( fwrite( &ulSizeToWrite, sizeof(byte), Count, pFile) != Count ) {
            printf("Cannot write1 to the file\n");
            exit(1);
            }

        Count = (size_t) ulSizeToWrite;
        if ( fwrite( pbBuffer, sizeof(byte), Count, pFile) != Count ) {
            printf("Cannot write2 to the file\n");
            exit(1);
            }

        if ( fclose( pFile ) != 0) {
            printf("Failed to close the file\n");
            exit(1);
            }
        }
}

void ReadDataFromFile(
    char *          pszFileName,
    char *          pbBuffer,
    unsigned long   ulBufferSize )
{
    FILE *          pFile;
    size_t          Count;
    unsigned long   ulWrittenSize;

    if ( pszFileName ) {

        pFile = fopen( pszFileName, "r+b" );
        if ( pFile == NULL ) {
            printf("Cannot open the file for reading\n");
            exit(1);
            }

        Count = sizeof(long);
        if ( fread( &ulWrittenSize, sizeof(byte), Count, pFile) != Count ) {
            printf("Cannot read1 from the file\n");
            exit(1);
            }

        Count = (size_t)ulWrittenSize;
        if ( fread( pbBuffer, sizeof(byte), Count, pFile) != Count ) {
            printf("Cannot read2 from the file\n");
            exit(1);
            }

        if ( fclose( pFile ) != 0) {
            printf("Failed to close the file\n");
            exit(1);
            }
        }
}

void _CRTAPI1 main(int argc, char **argv)
{
    RPC_STATUS status;

    unsigned char * pbPicklingBuffer = NULL;

    char * pszStyle      = NULL;
    char * pszFileName   = "pickle.dat";
    int i;
    int fEncode = 1;
    int fFixedStyle = 1;

    /* allow the user to override settings with command line switches */
    for (i = 1; i < argc; i++) {
        if ((*argv[i] == '-') || (*argv[i] == '/')) {
            switch (tolower(*(argv[i]+1))) {
            case 'd':
                fEncode = 0;
                break;
            case 'e':
                fEncode = 1;
                break;
            case 'i':
                fFixedStyle = 0;
                break;
            case 'f':
                pszFileName = argv[i] + 2;
                break;
            case 'h':
            case '?':
            default:
                Usage(argv[0]);
            }
        }
        else
            Usage(argv[0]);
    }

    /* Fixed buffer style: the buffer should be big enough.  */
    /* Please note that the buffer has to be aligned at 8.   */

    pbPicklingBuffer = (unsigned char *)
            midl_user_allocate( BUFSIZE * sizeof(unsigned char));

    if ( pbPicklingBuffer == NULL ) {
        printf("Cannot allocate the pickling buffer\n");
        exit(1);
        }
    else
        memset( pbPicklingBuffer, 0xdd, BUFSIZE );

    /*
        Set the pickling handle that will be used for data serialization.
        The global ImplicitPicHandle is used, but it has to be set up.
    */

    if ( fEncode ) {

        unsigned char * pszNameId;
        OBJECT1         Object1;
        OBJECT2   *     pObject2;
        unsigned long   ulEncodedSize = 0;

        printf("\nEncoding run: use -d for decoding\n\n");

        if ( fFixedStyle ) {

            printf("Creating a fixed buffer encoding handle\n");
            status = MesEncodeFixedBufferHandleCreate( pbPicklingBuffer,
                                                       BUFSIZE,
                                                       & ulEncodedSize,
                                                       & ImplicitPicHandle );
            printf("MesEncodeFixedBufferHandleCreate returned 0x%x\n", status);
            if (status) {
                exit(status);
            }
        }
        else {

            pUserState->LastSize = 0;
            pUserState->pMemBuffer = (char *)pbPicklingBuffer;
            pUserState->pBufferStart = (char *)pbPicklingBuffer;

            printf("Creating an incremental encoding handle\n");
            status = MesEncodeIncrementalHandleCreate( pUserState,
                                                       PicAlloc,
                                                       PicWrite,
                                                       & ImplicitPicHandle );
            printf("MesEncodeIncrementalHandleCreate returned 0x%x\n", status);
            if (status) {
                exit(status);
            }
        }

        /* Creating objects to manipulate */

        pszNameId = "Procedure pickling sample";

        for (i = 0; i < ARR_SIZE; i++)
            Object1.al[i] = 0x37370000 + i;
        Object1.s = 0x4646;

        pObject2 = midl_user_allocate( sizeof(OBJECT2) + ARR_SIZE*sizeof(short) );
        if (pObject2 == NULL ) {
            printf("Out of memory for Object2\n");
            exit(1);
        }

        pObject2->sSize = ARR_SIZE;
        for (i = 0; i < ARR_SIZE; i++)
            pObject2->as[i] = 0x7700 + i;

        DumpData( "Data to be encoded", pszNameId, &Object1, pObject2 );

        printf("\nEncoding all the arguments to the buffer\n\n");
        ProcPickle( pszNameId, & Object1, pObject2 );

        printf("Writing the data to the file: %s\n", pszFileName);
        WriteDataToFile( pszFileName,
                         pbPicklingBuffer,
                         fFixedStyle  ? ulEncodedSize
                                      : pUserState->LastSize);

        midl_user_free( pObject2 );
    }
    else {
        char            acNameBuffer[50];
        OBJECT1         Object1;
        OBJECT2   *     pObject2;

        printf("\nDecoding run: use -e for encoding\n\n");

        printf("Reading the data from the file: %s\n\n", pszFileName );
        ReadDataFromFile( pszFileName,
                          pbPicklingBuffer,
                          BUFSIZE );

        if ( fFixedStyle ) {

            printf("Creating a decoding handle\n");
            status = MesDecodeBufferHandleCreate( pbPicklingBuffer,
                                                  BUFSIZE,
                                                  & ImplicitPicHandle );
            printf("MesDecodeFixedBufferHandleCreate returned 0x%x\n", status);
            if (status) {
                exit(status);
            }
        }
        else {

            pUserState->LastSize = 0;
            pUserState->pMemBuffer = (char *)pbPicklingBuffer;
            pUserState->pBufferStart = (char *)pbPicklingBuffer;

            printf("Creating an incremental decoding handle\n");
            status = MesDecodeIncrementalHandleCreate( pUserState,
                                                       PicRead,
                                                       & ImplicitPicHandle );
            printf("MesDecodeIncrementalHandleCreate returned 0x%x\n", status);
            if (status) {
                exit(status);
            }
        }

        /* Creating objects to manipulate */

        pObject2 = midl_user_allocate( sizeof(OBJECT2) + ARR_SIZE*sizeof(short));
        if (pObject2 == NULL ) {
            printf("Out of memory for Object2\n");
            exit(1);
        }

        printf("\nDecoding all the arguments from the buffer\n");
        ProcPickle( acNameBuffer, & Object1, pObject2 );

        DumpData( "Decoded data", acNameBuffer, &Object1, pObject2 );

        midl_user_free( pObject2 );
    }

    printf("\nData serialization done.\n");

    midl_user_free( pbPicklingBuffer );

    printf("\nFreeing the serialization handle.\n");
    status = MesHandleFree( ImplicitPicHandle );
    printf("MesHandleFree returned 0x%x\n", status);

    exit(0);

}  // end main()

/*********************************************************************/
/*                 MIDL allocate and free                            */
/*********************************************************************/

/* A pickling buffer has to be aligned at 8.
   malloc() doesn't guarantee that, so some gimmick has to be done.
*/

#define ALIGN_TO8( p)   (char *)((unsigned long)((char *)p + 7) & ~7)

void __RPC_FAR * __RPC_USER  MIDL_user_allocate(size_t s)
{
    unsigned char * pcAllocated;
    unsigned char * pcUserPtr;

    pcAllocated = (unsigned char *) malloc( s + 15 );
    pcUserPtr =  ALIGN_TO8( pcAllocated );
    if ( pcUserPtr == pcAllocated )
        pcUserPtr = pcAllocated + 8;

    *(pcUserPtr - 1) = pcUserPtr - pcAllocated;

    return( pcUserPtr );
}

void __RPC_USER  MIDL_user_free(void __RPC_FAR *f)
{
    unsigned char * pcAllocated;
    unsigned char * pcUserPtr;

    pcUserPtr = (unsigned char *) f;
    pcAllocated = pcUserPtr - *(pcUserPtr - 1);

    free( pcAllocated );
}

/*********************************************************************/
/*                 Incremental style helper routines                 */
/*********************************************************************/

void  __RPC_USER
PicAlloc(
    void *          pState,
    char **         ppBuf,
    unsigned int  * pCount )
{
    /* This routines "allocates" the next part from the preallocated
       buffer. It could call midl_user_allocate, too.
    */

    PickleControlBlock * pPic = (PickleControlBlock *)pState;

    if ( (pPic->pMemBuffer - pPic->pBufferStart) + *pCount <= BUFSIZE )
        *ppBuf = (char *)pPic->pMemBuffer;
    else {
        printf(" Buffer too small\n");
        exit(1);
    }
}

void __RPC_USER
PicWrite(
    void *          pState,
    char *          pBuf,
    unsigned int    Count )
{
    /* This routine just marks how much of the preallocated buffer is used.
    */

    PickleControlBlock * pPic = (PickleControlBlock *)pState;

    if ( pPic->pMemBuffer != pBuf ) {
        printf(" Buffer poiner corrupted\n");
        exit(1);
    }
    pPic->pMemBuffer += Count;
    pPic->LastSize += Count;
}

void __RPC_USER
PicRead(
    void *          pState,
    char **         ppBuf,
    unsigned int  * pCount )
{
    /* This routine returns another portion of the preread buffer. */

    PickleControlBlock * pPic = (PickleControlBlock *)pState;

    *ppBuf = (char *)pPic->pMemBuffer;
    pPic->pMemBuffer += *pCount;
}

/* end picklpc.c */


README.TXT

PICKLT

The PICKLT program demonstrates data type serialization. The program
demonstrates a fixed buffer style pickling and an incremental style pickling
depending on the switches being used.

The implicit_handle primitive is used as the pickling handle.

With default switches, the program encodes data to a file. To decode data
from the file, use -d switch.

FILES
=====

The directory samples\rpc\pickle\picklt contains the following files for
building the distributed application PICKLT:

File          Description

README.TXT    Readme file for the PICKLT sample
PICKLT.IDL    Interface definition language file
PICKLT.ACF    Attribute configuration file
PICKLTC.C     Client application for type pickling
MAKEFILE      Nmake file to build for Windows NT or Windows 95
MAKEFILE.DOS  Nmake file to build for MS-DOS

------------------------------------------------------------
BUILDING THE (CLIENT) FOR MICROSOFT WINDOWS NT OR WINDOWS 95
------------------------------------------------------------

The following environment variables should already be set for you:

  set CPU=i386
  set INCLUDE=%SDKROOT%\h
  set LIB=%SDKROOT%\lib
  set PATH=%SDKROOT%\system32;%SDKROOT%\bin

where %SDKROOT% is the root directory for the 32-bit Windows SDK.

For mips,  set CPU=mips
For alpha, set CPU=alpha

Build the sample distributed application:

  nmake cleanall
  nmake

This builds the executable programs pickltc.exe.
There is no server side in this sample as the notion of server does not
apply to pickling.

------------------------------------------
BUILDING THE CLIENT APPLICATION FOR MS-DOS
------------------------------------------

After installing the Microsoft Visual C/C++ version 1.50 development
environment and the 16-bit RPC SDK on a Windows NT or Windows 95
computer, you can build the sample client application from Windows NT
or Windows 95:

  nmake -f makefile.dos cleanall
  nmake -f makefile.dos

This builds the client application callc.exe.

You may also execute the Microsoft Visual C/C++ compiler under MS-DOS.
This requires a two-step build process.

  Step One: Compile the .IDL files under Windows NT or Windows 95:

     nmake -a -f makefile.dos picklt.h

  Step Two: Compile the C sources (stub and application) under MS-DOS:

     nmake -f makefile.dos

-----------------------
RUNNING THE APPLICATION
-----------------------

The default setting for this application is to encode data to
pickle.dat file. Enter:

  pickltc

To decode data that has been encoded, enter:

  pickltc -d

Several other command line switches are available to change settings for
this program. For a listing of the switches available from the client
program, enter:

  pickltc -?


PICKLT.ACF

[ implicit_handle(handle_t ImplicitPicHandle )
]
interface type_pickle
{

/* Both encode and decode will be used with OBJECT2 objects.
   We don't need to generate pickling routines for OBJECT1 type.
   If OBJECT1 object needed to be serialized separately,
   it would be easier to put encode,decode attributes on the interface.
*/

typedef [encode, decode] OBJECT2;

}


PICKLT.IDL

[ uuid (B75AF600-9CF4-11CD-A076-08002B2BD711),
  version(1.0),
  pointer_default(unique)
]
interface type_pickle
{

const short ARR_SIZE =  4;
const short BUFSIZE = 1024;

typedef struct _OBJECT1 {
    long            al[ARR_SIZE];
    unsigned short  s;
    } OBJECT1;

/* This is the top level type to be serialized. See the ACF file */

typedef struct _OBJECT2 {
    unsigned short      sData;
    OBJECT1 *           pObject1;
    } OBJECT2;

}


PICKLTC.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                      picklt Example

    FILE:       pickltc.c

    USAGE:      pickltc  -f filename  (file to write to/read from)
                         -d           (serialization direction: decode)
                         -e           (serialization direction: encode)
                         -i           (incremental serialization on)

    PURPOSE:    The only side of RPC application

    FUNCTIONS:  main() - serializes data to or from a file

    COMMENTS:   Data type serialization is demonstrated here

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

#include <stdlib.h>
#include <stdio.h>
#include "picklt.h"    // header file generated by MIDL compiler

#define PURPOSE \
"This Microsoft RPC Version 2.0 sample program demonstrates\n\
the use of the [encode,decode] attributes. For more information\n\
about the attributes and RPC API functions, see the RPC programming\n\
guide and reference.\n\n"

/*  ------------------------------------------------------------------------ */
/*  Used for incremental style only. */

void __RPC_USER PicAlloc( void * pState, char ** ppBuf, unsigned int * pCount);
void __RPC_USER PicWrite( void * pState, char *  pBuf,  unsigned int Count);
void __RPC_USER PicRead ( void * pState, char ** pBuf,  unsigned int * pCount);

typedef struct PickleControlBlock
{
    unsigned char *         pMemBuffer;
    unsigned char *         pBufferStart;
    unsigned long           LastSize;
} PickleControlBlock;

static PickleControlBlock                 UserState;
static PickleControlBlock * pUserState = &UserState;
/*  ------------------------------------------------------------------------ */

void Usage(char * pszProgramName)
{
    fprintf(stderr, "%s", PURPOSE);
    fprintf(stderr, "Usage:  %s\n", pszProgramName);
    fprintf(stderr, " -d\n");
    fprintf(stderr, " -e\n");
    fprintf(stderr, " -ffilename\n");
    fprintf(stderr, " -i \n");
    exit(1);
}

void DumpData(
    char * pszComment,
    OBJECT2 * pObject2 )
{
    int i;

    printf( "\n%s\n", pszComment );

    printf( "\tObject2");
    printf( "\n\t\t%x", pObject2->sData );
    printf( "\n\t\t%p", pObject2->pObject1 );

    if ( pObject2->pObject1 )
        {
        OBJECT1 * pObject1 = pObject2->pObject1;

        printf( "\n\tObject1");
        for (i=0; i < ARR_SIZE; i++)
            {
            if ( (i % 5) == 0 )
                printf( "\n\t\t");
            printf( "%08xl  ", pObject1->al[i] );
            }
        printf( "\n\t\t%x\n", pObject1->s );
        }

    printf( "\n" );
}

void WriteDataToFile(
    char *          pszFileName,
    char *          pbBuffer,
    unsigned long   ulSizeToWrite )
{
    FILE *      pFile;
    size_t      Count;

    if ( pszFileName ) {

        pFile = fopen( pszFileName, "w+b" );
        if ( pFile == NULL ) {
            printf("Cannot open the file for writing\n");
            exit(1);
            }

        Count = sizeof(long);
        if ( fwrite( &ulSizeToWrite, sizeof(byte), Count, pFile) != Count ) {
            printf("Cannot write1 to the file\n");
            exit(1);
            }

        Count = (size_t) ulSizeToWrite;
        if ( fwrite( pbBuffer, sizeof(byte), Count, pFile) != Count ) {
            printf("Cannot write2 to the file\n");
            exit(1);
            }

        if ( fclose( pFile ) != 0) {
            printf("Failed to close the file\n");
            exit(1);
            }
        }
}

void ReadDataFromFile(
    char *          pszFileName,
    char *          pbBuffer,
    unsigned long   ulBufferSize )
{
    FILE *          pFile;
    size_t          Count;
    unsigned long   ulWrittenSize;

    if ( pszFileName ) {

        pFile = fopen( pszFileName, "r+b" );
        if ( pFile == NULL ) {
            printf("Cannot open the file for reading\n");
            exit(1);
            }

        Count = sizeof(long);
        if ( fread( &ulWrittenSize, sizeof(byte), Count, pFile) != Count ) {
            printf("Cannot read1 from the file\n");
            exit(1);
            }

        Count = (size_t)ulWrittenSize;
        if ( fread( pbBuffer, sizeof(byte), Count, pFile) != Count ) {
            printf("Cannot read2 from the file\n");
            exit(1);
            }

        if ( fclose( pFile ) != 0) {
            printf("Failed to close the file\n");
            exit(1);
            }
        }
}

void _CRTAPI1 main(int argc, char **argv)
{
    RPC_STATUS status;

    unsigned char * pbPicklingBuffer = NULL;

    char * pszStyle      = NULL;
    char * pszFileName   = "pickle.dat";
    int i;
    int fEncode = 1;
    int fFixedStyle = 1;

    /* allow the user to override settings with command line switches */
    for (i = 1; i < argc; i++) {
        if ((*argv[i] == '-') || (*argv[i] == '/')) {
            switch (tolower(*(argv[i]+1))) {
            case 'd':
                fEncode = 0;
                break;
            case 'e':
                fEncode = 1;
                break;
            case 'i':
                fFixedStyle = 0;
                break;
            case 'f':
                pszFileName = argv[i] + 2;
                break;
            case 'h':
            case '?':
            default:
                Usage(argv[0]);
            }
        }
        else
            Usage(argv[0]);
    }

    /* Fixed buffer style: the buffer should be big enough.
       Please note that a pickling buffer has to be aligned at 8.

       Also note, the buffer doesn't have to be allocated with
       the standard midl_user_allocate - this may be any allocator
       that alignes at 8.
       In general, midl_user_allocate is not required to return
       a buffer aligned at 8.
    */

    pbPicklingBuffer = (unsigned char *)
            midl_user_allocate( BUFSIZE * sizeof(unsigned char));

    if ( pbPicklingBuffer == NULL ) {
        printf("Cannot allocate the pickling buffer\n");
        exit(1);
        }
    else
        memset( pbPicklingBuffer, 0xdd, BUFSIZE );

    /*
        Set the pickling handle that will be used for data serialization.
        The global ImplicitPicHandle is used, but it has to be set up.
    */

    if ( fEncode ) {

        unsigned char * pszNameId;
        OBJECT1         Object1;
        OBJECT2         Object2a,  *pObject2b;

        unsigned long   ulEncodedSize = 0; /* this is a cumulative size */

        printf("\nEncoding run: use -d for decoding\n\n");

        if ( fFixedStyle ) {

            printf("Creating a fixed buffer encoding handle\n");
            status = MesEncodeFixedBufferHandleCreate( pbPicklingBuffer,
                                                       BUFSIZE,
                                                       & ulEncodedSize,
                                                       & ImplicitPicHandle );
            printf("MesEncodeFixedBufferHandleCreate returned 0x%x\n", status);
            if (status) {
                exit(status);
            }
        }
        else {
            /* Incremental style */

            pUserState->LastSize = 0;
            pUserState->pMemBuffer = (char *)pbPicklingBuffer;
            pUserState->pBufferStart = (char *)pbPicklingBuffer;

            printf("Creating an incremental encoding handle\n");
            status = MesEncodeIncrementalHandleCreate( pUserState,
                                                       PicAlloc,
                                                       PicWrite,
                                                       & ImplicitPicHandle );
            printf("MesEncodeIncrementalHandleCreate returned 0x%x\n", status);
            if (status) {
                exit(status);
            }
        }

        /* Creating objects to manipulate */

        pszNameId = "Pickling sample";

        for (i = 0; i < ARR_SIZE; i++)
            Object1.al[i] = 0x73730000 + i;
        Object1.s = 0x6464;

        Object2a.sData = 0xa9a9;
        Object2a.pObject1 = NULL;

        pObject2b = midl_user_allocate( sizeof(OBJECT2) );
        if (pObject2b == NULL ) {
            printf("Out of memory for Object2b\n");
            exit(1);
        }
        pObject2b->sData = 0xb8b8;
        pObject2b->pObject1 = & Object1;

        DumpData( "Data to be encoded", &Object2a );
        printf("\nEncoding Object2a data to the buffer\n");
        OBJECT2_Encode( &Object2a );

        /* The second object is encoded to the same buffer,
           the size used below is cumulative.
           Note that using procedure pickling instead of type pickling,
           both objects could be encoded with one call.
        */

        DumpData( "Data to be encoded", pObject2b );
        printf("\nEncoding Object2b data to the buffer\n\n");
        OBJECT2_Encode( pObject2b );

        printf("Writing the data to the file: %s\n", pszFileName);
        WriteDataToFile( pszFileName,
                         pbPicklingBuffer,
                         fFixedStyle  ? ulEncodedSize
                                      : pUserState->LastSize);

        midl_user_free( pObject2b );
    }
    else {
        OBJECT2      Object2c, Object2d;

        printf("\nDecoding run: use -e for encoding\n\n");

        printf("Reading the data from the file: %s\n\n", pszFileName );
        ReadDataFromFile( pszFileName,
                          pbPicklingBuffer,
                          BUFSIZE );

        if ( fFixedStyle ) {

            printf("Creating a decoding handle\n");
            status = MesDecodeBufferHandleCreate( pbPicklingBuffer,
                                                  BUFSIZE,
                                                  & ImplicitPicHandle );
            printf("MesDecodeFixedBufferHandleCreate returned 0x%x\n", status);
            if (status) {
                exit(status);
            }
        }
        else {

            pUserState->LastSize = 0;
            pUserState->pMemBuffer = (char *)pbPicklingBuffer;
            pUserState->pBufferStart = (char *)pbPicklingBuffer;

            printf("Creating an incremental decoding handle\n");
            status = MesDecodeIncrementalHandleCreate( pUserState,
                                                       PicRead,
                                                       & ImplicitPicHandle );
            printf("MesDecodeIncrementalHandleCreate returned 0x%x\n", status);
            if (status) {
                exit(status);
            }
        }

        /* Creating objects to manipulate */

        Object2c.pObject1 = NULL;
        Object2d.pObject1 = NULL;

        printf("\nDecoding Object2c data from the buffer\n");
        OBJECT2_Decode( & Object2c );
        DumpData( "Decoded data",  &Object2c );

        printf("\nDecoding Object2d data from the buffer\n");
        OBJECT2_Decode( & Object2d );
        DumpData( "Decoded data",  &Object2d );

        /* Need to free Object1 objects as they may have been allocated
           when unmarshalling data.
        */

        if ( Object2c.pObject1 )
            midl_user_free( Object2c.pObject1 );
        if ( Object2d.pObject1 )
            midl_user_free( Object2d.pObject1 );
    }

    printf("\nData serialization done.\n");

    midl_user_free( pbPicklingBuffer );

    printf("\nFreeing the serialization handle.\n");
    status = MesHandleFree( ImplicitPicHandle );
    printf("MesHandleFree returned 0x%x\n", status);

    exit(0);

}  // end main()

/*********************************************************************/
/*                 MIDL allocate and free                            */
/*********************************************************************/

/* A pickling buffer has to be aligned at 8.
   malloc() doesn't guarantee that, so some gimmick has to be done.
*/

#define ALIGN_TO8( p)   (char *)((unsigned long)((char *)p + 7) & ~7)

void __RPC_FAR * __RPC_USER  MIDL_user_allocate(size_t s)
{
    unsigned char * pcAllocated;
    unsigned char * pcUserPtr;

    pcAllocated = (unsigned char *) malloc( s + 15 );
    pcUserPtr =  ALIGN_TO8( pcAllocated );
    if ( pcUserPtr == pcAllocated )
        pcUserPtr = pcAllocated + 8;

    *(pcUserPtr - 1) = pcUserPtr - pcAllocated;

    return( pcUserPtr );
}

void __RPC_USER  MIDL_user_free(void __RPC_FAR *f)
{
    unsigned char * pcAllocated;
    unsigned char * pcUserPtr;

    pcUserPtr = (unsigned char *) f;
    pcAllocated = pcUserPtr - *(pcUserPtr - 1);

    free( pcAllocated );
}

/*********************************************************************/
/*                 Incremental style helper routines                 */
/*********************************************************************/

void  __RPC_USER
PicAlloc(
    void *          pState,
    char **         ppBuf,
    unsigned int  * pCount )
{
    /* This routines "allocates" the next part from the preallocated
       buffer. It could call midl_user_allocate, too.
    */

    PickleControlBlock * pPic = (PickleControlBlock *)pState;

    if ( (pPic->pMemBuffer - pPic->pBufferStart) + *pCount <= BUFSIZE )
        *ppBuf = (char *)pPic->pMemBuffer;
    else {
        printf(" Buffer too small\n");
        exit(1);
    }
}

void __RPC_USER
PicWrite(
    void *          pState,
    char *          pBuf,
    unsigned int    Count )
{
    /* This routine just marks how much of the preallocated buffer is used.
    */

    PickleControlBlock * pPic = (PickleControlBlock *)pState;

    if ( pPic->pMemBuffer != pBuf ) {
        printf(" Buffer poiner corrupted\n");
        exit(1);
    }
    pPic->pMemBuffer += Count;
    pPic->LastSize += Count;
}

void __RPC_USER
PicRead(
    void *          pState,
    char **         ppBuf,
    unsigned int  * pCount )
{
    /* This routine returns another portion of the preread buffer. */

    PickleControlBlock * pPic = (PickleControlBlock *)pState;

    *ppBuf = (char *)pPic->pMemBuffer;
    pPic->pMemBuffer += *pCount;
}

/* end pickltc.c */


README.TXT

PIPES

The PIPE sample demonstrates how to write a program that uses pipes for
transferring data between the client and the server. The client sends a
file to the server using pipes. The server then "encodes" the data by
adding 1 to every element in the file, and then sends the file back using
pipes. The pipe used in this sample program is a in/out pipe.

This program also allows the user to compile either for UNICIODE or ANSI.
See section below for guidelines on how to compile for UNIOCDE

FILES
=====

The directory SAMPLES\RPC\PIPES contains the following files for
building the sample distributed application PIPES:

File          Description
-------------------------

README.TXT      Readme file for the pipe sample
PIPE.IDL        Interface definition language file
PIPE.ACF        Attribute configuration file
PIPEC.C         Client main program
PIPES.C         Server main program
COMMON.H        Common header file for all the files
PIPEPROC.C      Remote procedures
MAKEFILE        nmake file to build 32-bit client and server app.
                for ANSI characters.
MAKEFILE.UNI    nmake file to build 32-bit client and server app.
                for UNICODE characters.

NMAKE builds the executable programs CLIENT.EXE (client) and
SERVER.EXE (server).

Note: The client and server applications can run on the same
Microsoft Windows NT computer when you use different screen groups.

COMPILING FOR UNICODE:
======================

type nmake /f makefile.uni at the command line. This will cause
the compiler to use the file MAKEFILE.UNI instead of the MAKEFILE.

The reason behind the use of TEXT, TCHAR, _TUCHAR, _tprintf, _tcscpy, _tcscmp,
and _tfopen is that the program can easily be compiled to support UNICODE
(Wide characters) as well as supporting one byte ANSI characters.

    TEXT:   This macro will put an L in front of the string if we are
                compiling for UNICODE, and do nothing if compiling for ANSI
    TCHAR:   expands to either char or wchar_t during compilation
    _TUCHAR:   expands to either unsigned char or wchar_t during compilation
    _tprintf:   expands to either printf or to wsprintfduring compilation
    _tcscpy:   expands to either strcpy or to wcscpy during compilation
    _tcscmp:   expands to either strcmp or to wcscmp during compilation
    _tfopen:   expands to either fopen or to _wfopen during compilation


PIPE.ACF

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/*                  Copyright Microsoft Corp. 1992-1996                 */
/*                      Remote Machine pipe sample                      */
/*                                                                      */
/*  FILE    :   pipe.acf                                                */
/*                                                                      */
/*  PURPOSE :   This file tells the midl compiler that we want to use   */
/*              the implicit binding method for the remote procedure    */
/*              calls                                                   */
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
[implicit_handle (handle_t global_pipe_sample_handle)]
interface pipe_sample
{
}


PIPE.IDL

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*\
|                    Copyright Microsoft Corp. 1992-1996                 |
|                        Remote Machine pipe sample                      |
|                                                                        |
|  FILE      :   pipe.idl                                                |
|                                                                        |
|  PURPOSE   :   This file defines the interface for the pipe example    |
|                                                                        |
\*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
[
uuid(60ceb4a0-c2f1-11cf-9556-00aa00374e69),     // UUID
pointer_default(unique),                        // Pointer default
version(1.0)                                    // Interface version
]
interface pipe_sample
{
// Declare the pipe data type. This should also work for UNICODE.
#ifdef _UNICODE
    typedef pipe wchar_t CHAR_PIPE_TYPE;
#else
    typedef pipe unsigned char CHAR_PIPE_TYPE;
#endif

    /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
    /* ScrambleFile - This procedure sends a file from the client to    */
    /*              the server, the server then "encodes" it , and the  */
    /*              file is sent back to the client                     */
    /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
    void ScrambleFile(
        [in] unsigned long Action,              // Encode / Decode
        [in, out] CHAR_PIPE_TYPE *pipe_data);   // The file content

    /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
    /* ShutDown- This procedure sends a message to the server that it  */
    /*          can stop listen for remote procedure calls              */
    /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
    void ShutDown(void);            // Procedure to shut down the server
}


PIPEC.C

/*************************************************************************
                    Copyright Microsoft Corp. 1992-1996
                        Remote Machine pipe sample

    FILE        :   pipec.c

    USAGE       :   client  <local file> <new file> [options]
                        -s Scramble file
                        -u Unscramble the file
                        -n network_address
                        -p protocol_sequence
                        -e endpoint
                        -o options

    PURPOSE     :   Client side of the RPC distributed application pipes.

    COMMENTS    :   This program shows the client side an application that
                    transfers a file from the client to the server, using
                    pipes. When the file is received at the server it
                    "encodes" the data by incrementing all the characters,
                    and sends the "encoded" file back to the client.

                    Since this program uses the implicit binding method,
                    some of the binding handling must be done at the client
                    side
*************************************************************************/
#include "pipe.h"                   // Generated by the MIDL compiler
#include "common.h"                 // Common definitions

#define CLIENT_BUFFER_SIZE  500     // Can send or recieve 500 elements

// Client side global variables
pipe_state  in_out_pipe_state;      // Pipe state on the client side
_TUCHAR     pcClientBuffer[CLIENT_BUFFER_SIZE];  // Buffer used in PipeAlloc

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/*  Procedure   :   void Usage(_TUCHAR *)                               */
/*  Desc        :   This procedure prints out an error message if the   */
/*                  command line arguments are wrong                    */
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void Usage(_TUCHAR * pszProgramName)
{
    _tprintf(TEXT("USAGE : %s <local_file> <new_file> [-option]\n"),
        pszProgramName);
    _tprintf(TEXT("Options : -s\tTo encode the file\n"));
    _tprintf(TEXT("          -u\tTo Decode the file\n"));
    _tprintf(TEXT("          -n\t<Network Address>\n"));
    _tprintf(TEXT("          -p\t<Protocol Sequence>\n"));
    _tprintf(TEXT("          -e\t<Endpoint>\n"));
    _tprintf(TEXT("          -o\t<Options>\n"));
    exit(EXECUTION_FAILED);
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/*  Procedure   :   PipeAlloc                                           */
/*  Desc        :   This procedure allocates a chunck of memory and     */
/*                  returns a pointer to it.                            */
/*                  In this sample the Alloc routine returns a pointer  */
/*                  to the same global allocated memory every time.     */
/*                  This can be done since after each Pull/Push, data   */
/*                  is read/written to file.                            */
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void PipeAlloc(
    char            *pcStateInfo,       // The state structure
    unsigned long   nReqSize,           // Req. size of buffer in bytes
    _TUCHAR         **pcAllocatedBuffer,// Pointer to the allocated buffer
    unsigned long   *pnAllocatedSize)   // Size of allocated buf. in bytes
{
    // Since the memory is already allocated, just return a pointer to it
    if(nReqSize > (CLIENT_BUFFER_SIZE * sizeof(_TUCHAR)))
    {
        *pnAllocatedSize = CLIENT_BUFFER_SIZE * sizeof(_TUCHAR);
    }
    else
    {
        *pnAllocatedSize = nReqSize;
    }
    *pcAllocatedBuffer = pcClientBuffer;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/*  Procedure   :   PipePull                                            */
/*  Desc        :   This procedure is called by the stub when its ready */
/*                  to send a chunck of the pipe to the server.         */
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void PipePull(
    char            *pcStateInfo,   // The state struct on client side
    _TUCHAR         *pcBuffer,      // Pointer to the buffer
    unsigned long   nMaxBufferSize, // Size of buffer in elements
    unsigned long   *pnSizeToSend)  // Number of elements put in buffer
{
    // Declare a state variable
    pipe_state*psState = (pipe_state *) pcStateInfo;

    // Open the local file, if it is not already opened
    if(psState->hOld == NULL)
    {
        if(NULL == (psState->hOld =
            _tfopen(psState->pszOldName, TEXT("rt"))))
        {
            _tprintf(TEXT("Could not open the file %s\n"),
                psState->pszOldName);
            exit(EXECUTION_FAILED);
        }
    }

    // Read max_buffer_size into the buffer
    *pnSizeToSend = fread(
        pcBuffer,
        sizeof(_TUCHAR),
        nMaxBufferSize,
        psState->hOld);

    if(*pnSizeToSend == 0)      // When end of the data is reached
    {                           // cleanup the application
        fclose(psState->hOld);
        psState->hOld = NULL;
    }
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/*  Procedure   :   PipePush                                            */
/*  Desc        :   This procedure is called by the stub when a chunck  */
/*                  of data is received from the server.                */
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void PipePush(
    char            *nStateInfo,        // State structure on client side
    _TUCHAR         *pcBuffer,          // Ptr to the buffer with the data
    unsigned long   nNumberOfElements)  // Number of elements in the buffer
{
    // Declare a state variable
    pipe_state*psState = (pipe_state *) nStateInfo;

    // Open the New file, if it is not already opened
    if(psState->hNew == NULL)
    {
        if(NULL == (psState->hNew =
            _tfopen(psState->pszNewName, TEXT("w"))))
        {
            _tprintf( TEXT("Unable to create the file %s\n"),
                    psState->pszNewName);
            exit(EXECUTION_FAILED);
        }
    }

    // When number of elements received is 0, this is the end of the pipe
    if(nNumberOfElements == 0)
    {
        // Clean up by closing the opened file
        fclose (psState->hNew);
        psState->hNew = NULL;
    }
    else
    {
        // Write the buffer to the file
        fwrite(pcBuffer, sizeof(_TUCHAR), nNumberOfElements, psState->hNew);
    }
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/*  Client side main program                                            */
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int _CRTAPI1 main(int argc, char *argv[])
{
    RPC_STATUS      nStatus;        // Status returned from RPC-calls
    CHAR_PIPE_TYPE  in_out_pipe;    // Pipe structure used for the pipe
    TCHAR
        *pszOldFileName,        // Name of the input file
        *pszNewFileName;        // Name of the output file
    unsigned long
        nAction = SCRAMBLE,     // Scramble / Unscramble file
        nIdx,                   // Counter in loops
        nNumArgs;               // The number of args from the command line

    // These variables are used for the implicit binding
    _TUCHAR*pszUuid= NULL;
    _TUCHAR*pszProtocolSequence= PROTOCOL_SEQUENCE;
    _TUCHAR*pszNetworkAddress= NULL;
    _TUCHAR*pszEndpoint= END_POINT;
    _TUCHAR*pszOptions= NULL;
    _TUCHAR*pszStringBinding= NULL;

    // Get a common handle on the command line arguments for both
    // UNICODE and ASCII
#ifdef _UNICODE
    LPWSTR*szArglist= CommandLineToArgvW(GetCommandLine(), &nNumArgs);
    if (NULL == szArglist)
    {
        _tprintf(TEXT("SERVER.C : CommandLineToArgW failed"));
        exit(EXECUTION_FAILED);
    }
#else
    char **szArglist= argv;
    nNumArgs = argc;
#endif

    // Check to see that the call was correct
    if (nNumArgs < 3)
    {
        Usage(szArglist[0]);
    }

    // Allow the user to override settings with commandline switches
    for (nIdx = 3; nIdx < nNumArgs; nIdx++)
    {
        if ((_tcscmp(szArglist[nIdx], TEXT("-n")) == 0) ||
            (_tcscmp(szArglist[nIdx], TEXT("-N")) == 0))
        {
            pszNetworkAddress = szArglist[++nIdx];
        }
        else if((_tcscmp(szArglist[nIdx], TEXT("-p")) == 0) ||
            (_tcscmp(szArglist[nIdx], TEXT("-P")) == 0))
        {
            pszProtocolSequence = szArglist[++nIdx];
        }
        else if((_tcscmp(szArglist[nIdx], TEXT("-e")) == 0) ||
                (_tcscmp(szArglist[nIdx], TEXT("-e")) == 0))
        {
            pszEndpoint = szArglist[++nIdx];
        }
        else if((_tcscmp(szArglist[nIdx], TEXT("-o")) == 0) ||
               (_tcscmp(szArglist[nIdx], TEXT("-O")) == 0))
        {
            pszOptions = szArglist[++nIdx];
        }
        else if((_tcscmp(szArglist[nIdx], TEXT("-s")) == 0) ||
                (_tcscmp(szArglist[nIdx], TEXT("-S")) == 0))
        {
            nAction = SCRAMBLE;
        }
        else if((_tcscmp(szArglist[nIdx], TEXT("-u")) == 0) ||
                (_tcscmp(szArglist[nIdx], TEXT("-U")) == 0))
        {
            nAction = UNSCRAMBLE;
        }
        else
        {
            Usage(szArglist[0]);
        }
    }

    // Allocate space for the filenames
    if(NULL == (pszOldFileName= (TCHAR *) midl_user_allocate(
        sizeof(TCHAR) * (_tcsclen(szArglist[1]) + 1))))
    {
        _tprintf(TEXT("Memory allocation error\n"));
        exit(EXECUTION_FAILED);
    }
    if(NULL == (pszNewFileName= (TCHAR *) midl_user_allocate(
        sizeof(TCHAR) * (_tcsclen(szArglist[2]) + 1))))
    {
        _tprintf(TEXT("Memory allocation error\n"));
        exit(EXECUTION_FAILED);
    }

    // Initialize the state variables for the in pipe
    _tcscpy(pszOldFileName, szArglist[1]);
    _tcscpy(pszNewFileName, szArglist[2]);

    // Since we are doing implicit binding, we need to initialize the
    // global varaible global_pipe_sample_handle from the client.
    // Use a function to concatenate the elements of the string
    // binding into the proper sequence
    nStatus = RpcStringBindingCompose(
        pszUuid,
        pszProtocolSequence,
        pszNetworkAddress,
        pszEndpoint,
        pszOptions,
        &pszStringBinding);
    EXIT_IF_FAIL(nStatus, "RpcStringBindingCompose");

    // Set the binding handle that will be used to bind to the server
    nStatus = RpcBindingFromStringBinding(
        pszStringBinding,
        &global_pipe_sample_handle);        // The global handle
    EXIT_IF_FAIL(nStatus, "RpcBindingFromStringBinding");

    /* Initialize pipestructure */
    in_out_pipe_state.hOld = NULL;
    in_out_pipe_state.hNew = NULL;
    in_out_pipe_state.pszOldName = pszOldFileName;
    in_out_pipe_state.pszNewName = pszNewFileName;

    in_out_pipe.state= (char *) &in_out_pipe_state;
    in_out_pipe.alloc= PipeAlloc;
    in_out_pipe.pull= PipePull;
    in_out_pipe.push= PipePush;

    RpcTryExcept
    {
        // Call the remote procedure to send the file to remote machine
        _tprintf(TEXT("Calling the remote procedure ScrambleFile()\n"));
        ScrambleFile(nAction, &in_out_pipe);
        _tprintf(TEXT("Call to ScrambleFile() returned\n"));
    }
    RpcExcept(DO_EXCEPTION)
    {
        _tprintf(TEXT("Run-time exception %08X = %d in %s\n"),
                RpcExceptionCode(), RpcExceptionCode(), TEXT(__FILE__));
        exit(EXECUTION_FAILED);
    }
    RpcEndExcept

    // Deallocate the memory used for the names in the state variable
    midl_user_free(pszOldFileName);
    midl_user_free(pszNewFileName);

    // Deallocate the memory used for the ARGLIST if using UNICODE
#ifdef _UNICODE
    if (NULL != szArglist)
        free(szArglist);
#endif

    // Shut down the server
    ShutDown();

    return (EXECUTION_OK);
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/* Procedure    :   midl_user_allocate() and midl_user_free()           */
/* Desc.        :   These procedure are declared in the header file     */
/*                  generated by the midl compiler. These procedures    */
/*                  should be used for all memory allocation and        */
/*                  deallocation.                                       */
/*                  These procedures are also called by the stub code to*/
/*                  allocate and free memory.                           */
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void __RPC_FAR * __RPC_API midl_user_allocate(size_t nLen)
{
    return (malloc(nLen));
}

void __RPC_API midl_user_free(void __RPC_FAR * lpvPointer)
{
    if(NULL == lpvPointer)
    {
        free (lpvPointer);
    }
}


PIPEPROC.C

/*************************************************************************
                    Copyright Microsoft Corp. 1992-1996
                        Remote Machine pipe sample

  FILE      :   pipeproc.c

  PURPOSE   :Remote procedures used in the RPC distributed application
                pipe sample

  COMMENTS  :   This file is linked together with the server side of the
                application.

*************************************************************************/
#include "pipe.h"       // Generated by the MIDL compiler
#include "common.h"     // Common definitions is locataed in  this file

#define MAX_BUFFER_SIZE 1000       // Buffer used on the server side

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/* Procedure    :   void ScrambleFile(unsigned long, CHAR_PIPE_TYPE *)  */
/* Desc.        :   This procedure receives a file from the client, and */
/*                  then "encodes" the file by adding 1 to every        */
/*                  character, and then sends the file back to the      */
/*                  client. The file is sent back and forth using pipes */
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void ScrambleFile(unsigned long nAction, CHAR_PIPE_TYPE *in_out_pipe)
{
    FILE    *fpTemp;                    // Pointer to the temporary file
    _TUCHAR pcBuffer[MAX_BUFFER_SIZE];  // Buffer to store the elements in

    unsigned long
        nActualTransferCount,   // Actual number of elements received
        nIdx,                   // Counter in loop
        nElementsToSend;        // Number of elements to send to the client
    BOOL bDone = FALSE;         // Inidicates when the pipe is done

    // Open up a file to temporary store the pulled data in
    fpTemp = _tfopen(TEXT("tempfile.oak"), TEXT("w+"));

    _tprintf(TEXT("Retrieving data from the client...\n"));
    // First process the entire pull. Input pipes must be transmitted first
    while(FALSE == bDone)
    {
        in_out_pipe->pull(      // Grab a chunck of data from the client
            in_out_pipe->state, // Pointer to the state
            pcBuffer,           // Buffer to put data in
            MAX_BUFFER_SIZE,    // Max number of elements to receive
            &nActualTransferCount); // Actual number of elements received

        // A data transfer count of 0 means the end of the pipe
        if(nActualTransferCount == 0)
        {
            bDone = TRUE;
        }
        else        // Save the data to an intermediate file
        {
            fwrite(pcBuffer, sizeof(_TUCHAR), nActualTransferCount, fpTemp);
        }
    }

    // Reset the flag
    bDone = FALSE;

    // Reset the filehandle to point at start of file
    fseek(fpTemp, 0, SEEK_SET);

    _tprintf(TEXT("Sending the data back to the client...\n"));
    // Now we can send the data back
    while(FALSE == bDone)
    {
        // Read a number of bytes from the file, into the buffer
        nElementsToSend = fread(pcBuffer,
                                 sizeof(_TUCHAR),
                                 MAX_BUFFER_SIZE,
                                 fpTemp);

        // If no more elements to send, we are done. Must send 0 to the
        // client to notify that the pipe is empty
        if(nElementsToSend == 0)
        {
            bDone = TRUE;
        }

        // According to the action, scramble or descramble the data
        for (nIdx = 0; nIdx < nElementsToSend; nIdx++)
        {
            if(nAction == SCRAMBLE)
            {
                pcBuffer[nIdx]++;
            }
            else
            {
                pcBuffer[nIdx]--;
            }
        }

        // Send the buffer to the client
        in_out_pipe->push(      // Send a chunck of data to the client
            in_out_pipe->state, // Pointer to the state
            pcBuffer,             // Date to send is in this buffer
            nElementsToSend);  // The number of elements to send
    }// End while (!done)

    // Close the file
    fclose(fpTemp);
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/* Procedure    :   void ShutDown(void);                                */
/* Desc.        :   This procedure send a message to the server that it */
/*                  canstop listening for remote procedure calls       */
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void ShutDown(void)
{
    RPC_STATUS nStatus;

    // Tell the server to stop listening for remote procedure calls
    _tprintf(TEXT("Shutting down the server\n"));
    nStatus = RpcMgmtStopServerListening(NULL);
    EXIT_IF_FAIL(nStatus, "RpcMgmtStopServerListening");
}


PIPES.C

/*************************************************************************
                    Copyright Microsoft Corp. 1992-1996
                        Remote Machine pipe sample

  FILE      :   pipes.c

  USAGE     :   server  -p protocol_sequence
                        -e endpoint

  PURPOSE   :   Server side of the RPC distributed application pipe.

  COMMENTS  :   This application uses the implicit binding method.

*************************************************************************/
#include "pipe.h"       // Generated by the MIDL compiler
#include "common.h"     // Common definitions is locataed in  this file

// Local Procedures
void CleanUpServer();   // Unregisters the interface

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/*  Procedure   :   void Usage(_TUCHAR *)                               */
/*  Desc        :   This procedure prints out an error message if the   */
/*                  command line arguments are wrong                    */
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void Usage(_TUCHAR * pszProgramName)
{
    _tprintf(TEXT("USAGE : %s [-option]\n"), pszProgramName);
    _tprintf(TEXT("Options : -p Protocol Sequence\n"));
    _tprintf(TEXT("          -e Endpoint\n"));
    exit(EXECUTION_FAILED);
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/* The server main program                                              */
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int _CRTAPI1 main(int argc, char *argv[])
{
    RPC_STATUS      nStatus;        // Error status returned
    int             nNumArgs;       // Number of command line arguments
    int             nIdx;           // Counter in loops

    // Variabels used for selecting the protocol and the endpoint
    _TUCHAR *pszProtocolSequence    = PROTOCOL_SEQUENCE;
    _TUCHAR *pszEndpoint            = END_POINT;
    _TUCHAR *pszSecurity            = NULL;

// Get a common handle on the command line arguments for both UNICODE
// and ASCII
#ifdef _UNICODE
    LPWSTR*szArglist = CommandLineToArgvW(GetCommandLine(), &nNumArgs);
    if (NULL == szArglist)
    {
        _tprintf(TEXT("SERVER.C : CommandLineToArgW failed"));
        exit(EXECUTION_FAILED);
    }
#else
    char **szArglist= argv;
    nNumArgs = argc;
#endif

    /* Allow the user to override settings with commandline switches    */
    for (nIdx = 1; nIdx < nNumArgs; nIdx++)
    {
        if((_tcscmp(szArglist[nIdx], TEXT("-p")) == 0) ||
           (_tcscmp(szArglist[nIdx], TEXT("-P")) == 0))
        {
            pszProtocolSequence = szArglist[++nIdx];
        }
        else if((_tcscmp(szArglist[nIdx], TEXT("-e")) == 0) ||
                (_tcscmp(szArglist[nIdx], TEXT("-e")) == 0))
        {
            pszEndpoint = szArglist[++nIdx];
        }
        else
        {
            Usage(szArglist[0]);
        }
    }

    /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
    /* Register the interface with the RPC run-time library             */
    /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
    _tprintf(TEXT("Registering the interface\n"));
    nStatus = RpcServerRegisterIf(
        pipe_sample_v1_0_s_ifspec,  // Interface specification
        NULL,                       // UUID to associate with MgrEnv arg.
        NULL);                      // Managers entry point vector. (None)
    EXIT_IF_FAIL(nStatus, "RpcServerRegisterIf");

    /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
    /* Select Protocal sequence : This sample uses namedpipes as the    */
    /* default protocol. The RpcServerUseProtseqEp function tells the   */
    /* RPC run-time library to use the specified protocol sequence      */
    /* combined with the specified endpoint for receiving remote        */
    /* procedure calls                                                  */
    /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
    _tprintf(TEXT("Selecting the protocol sequence to use.\t\"%s\"\n"),
            pszProtocolSequence);
    nStatus = RpcServerUseProtseqEp(
        pszProtocolSequence,            // String with the protocol in
        RPC_C_PROTSEQ_MAX_REQS_DEFAULT, // Max number of calls
        pszEndpoint,                    // Endpoint addres information
        pszSecurity);                   // Security
    EXIT_IF_FAIL(nStatus, "RpcServerUseProtseqsEp");

    /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
    /* Now start listening for remote procedure calls from the client   */
    /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
    RpcTryExcept
    {
        _tprintf(TEXT("Listening for remote calls...\n"));
        nStatus = RpcServerListen(
            1,                              // The minimum number of calls
            RPC_C_LISTEN_MAX_CALLS_DEFAULT, // The maximum number of calls
            FALSE);                         // Continue until stopped
        EXIT_IF_FAIL(nStatus, "RpcServerListen");
    }
    RpcExcept(DO_EXCEPTION)
    {
        // Print out the exception code
        _tprintf(TEXT("Run-time exception %u in %s at line %d\n"),
                RpcExceptionCode(), TEXT(__FILE__), __LINE__);
        exit(EXECUTION_FAILED);
    }
    RpcEndExcept

    // If no exceptions occured, clean up the server and exit
    CleanUpServer();

    // Deallocate the memory used for the ARGLIST if using UNICODE
#ifdef _UNICODE
    if (NULL != szArglist)
        free(szArglist);
#endif

    return (EXECUTION_OK);
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/* Procedure    : void CleanUpServer(RPC_BINDING_VECTOR);               */
/* Desc.        : This procedure will unregister the interface          */
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void CleanUpServer(void)
{
    RPC_STATUS nStatus;          // Error status returned from RPC calls

    /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
    /* Unregister the interface from the RPC run-time library           */
    /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
    _tprintf(TEXT("Unregistering the Interface"));
    nStatus = RpcServerUnregisterIf(
        NULL, NULL, // Prevents server from receiving any new remote calls
        FALSE);    // Wait until all the active calls are complete
    EXIT_IF_FAIL(nStatus, "RpcServerUnRegisterIf");
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/* Procedure    :   midl_user_allocate() and midl_user_free()           */
/* Desc.        :   These procedure are declared in the header file     */
/*                  generated by the midl compiler. These procedures    */
/*                  should be used for all memory allocation and        */
/*                  deallocation.                                       */
/*                  These procedures are also called by the stub code to*/
/*                  allocate and free memory.                           */
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void __RPC_FAR * __RPC_API midl_user_allocate(size_t len)
{
    return (malloc(len));
}

void __RPC_API midl_user_free(void __RPC_FAR * ptr)
{
    if(NULL != ptr)
        free (ptr);
}


COMMON.H

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/*                  Copyright Microsoft Corp. 1992-1996                 */
/*                      Remote Machine pipe sample                      */
/*                                                                      */
/*  FILE    :   common.h                                                */
/*                                                                      */
/*  PURPOSE :   Definitions used in the program                         */
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
#ifndef __COMMON_PIPE_SAMPLE__      // If this file has be included before
#define __COMMON_PIPE_SAMPLE__      // Don't include it again

#include <shellapi.h>               // CommandLineToArgvw is defined here
#include <tchar.h>                  // Included to support UNICODE/ANSI
#include <stdio.h>
#include <stdlib.h>

// Common type definition and defines used in the program
// Return values from the program to the OS
#define EXECUTION_FAILED    -1
#define EXECUTION_OK        0

// Exception handler macro
#define DO_EXCEPTION        1       // Execute the exception block

#define SCRAMBLE    1               // Action to take - Encode the file
#define UNSCRAMBLE  2               // Action to take - Decode the file

#define PROTOCOL_SEQUENCE   TEXT("ncacn_np")
#define END_POINT           TEXT("\\pipe\\pipe_sample")

// This struct is used by the client to keep track of the state
typedef struct{
    FILE        *hOld;          // Handle to the old file
    FILE        *hNew;          // Handle to the new file
    _TUCHAR     *pszOldName;       // Name of the old file
    _TUCHAR     *pszNewName;       // Name of the new file
} pipe_state;

// Macro for printing out error message and exit the program if an
// error occured
#define EXIT_IF_FAIL(x, string){   \
    ((x) != RPC_S_OK)?_tprintf(TEXT("%s returned with error: %d\n"), \
    TEXT(string), (x)),exit(x):0;}

#endif  //  __COMMON_PIPE_SAMPLE__


README.TXT

RPCSS

The RPCSS program demonstrates the rpcss memory management model.

FILES
=====

The directory samples\rpc\rpcssm contains the following files for building
the sample distributed application RPCSS:

File          Description

README.TXT    Readme file for the rpcssm sample
rpcssm.IDL    Interface definition language file
rpcssm.ACF    Attribute configuration file
rpcssmC.C     Client main program
rpcssmS.C     Server main program
rpcssmP.C     Remote procedures
MAKEFILE      Nmake file to build for Windows NT or Windows 95

-------------------------------------------
BUILDING CLIENT AND SERVER APPLICATIONS FOR
MICROSOFT WINDOWS NT OR WINDOWS 95
-------------------------------------------

The following environment variables should already be set for you:

  set CPU=i386
  set INCLUDE=%SDKROOT%\h
  set LIB=%SDKROOT%\lib
  set PATH=%SDKROOT%\system32;%SDKROOT%\bin

where %SDKROOT% is the root directory for the 32-bit Windows SDK.

For mips,  set CPU=mips
For alpha, set CPU=alpha

Build the sample distributed application:

  nmake cleanall
  nmake

This builds the executable programs rpcssmc.exe (client) and
rpcssms.exe (server).

------------------------------------------
BUILDING THE CLIENT APPLICATION FOR MS-DOS
------------------------------------------

This sample does not currently support a DOS build.

------------------------------------------
RUNNING THE CLIENT AND SERVER APPLICATIONS
------------------------------------------

On the server, enter:

  rpcssms

On the client, enter:

  net start workstation
  rpcssmc

Note:  The client and server applications can run on the same Microsoft
Windows NT computer when you use different screen groups.

Several command-line switches are available to change settings for this
program. For a listing of the switches available from the client program,
enter:

  rpcssmc -?

For a listing of switches available from the server program, enter:

  rpcssms -?

---------------------
RUNNING ON WINDOWS 95
---------------------

If you wish to run the server on Windows 95, you will need to use
the LRPC protocol.

To start the server, enter:

 rpcssms -p ncalrpc

To start the client (from the same machine), enter:

 rpcssmc -p ncalrpc


RPCSSM.ACF

[ implicit_handle(handle_t hStarter),
  enable_allocate
]
interface rpcssm
{
}


RPCSSM.IDL

[ uuid (A4F1DB00-CA47-1067-B31F-00DD010662DA),
  version(1.0),
  pointer_default(unique)
]
interface rpcssm
{

/* =======================================================================
                       data types
======================================================================= */

const short LIST_SIZE = 4;

typedef struct _lbox {
    long               data;
    struct _lbox *     next;
} LBOX, * PBOX;

/* =======================================================================
                      remote procedures
======================================================================= */

void
InOutList( [in,out] PBOX  *  ppBox );

void Shutdown(void);

}


RPCSSMC.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                      rpcssm Example

    FILE:       rpcssmc.c

    USAGE:      rpcssmc  -n network_address
                         -p protocol_sequence
                         -e endpoint
                         -o options

    PURPOSE:    Client side of RPC distributed application

    FUNCTIONS:  main() - binds to server and calls remote procedure

    COMMENTS:   This distributed application uses the rpcssm package.

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

#include <stdlib.h>
#include <stdio.h>
#include "rpcssm.h"    // header file generated by MIDL compiler

#define PURPOSE \
"This Microsoft RPC Version 2.0 sample program demonstrates\n\
the use of the [context_handle] attribute. For more information\n\
about attributes and RPC API functions, see the RPC programming\n\
guide and reference.\n\n"

void AllocateSmList( PBOX * ppBox, short sListSize );
void AllocateList( PBOX * ppBox, short sListSize );
void InitList( PBOX  pBox, long lStartValue );
void FreeList( PBOX  pBox );
void PrintList( PBOX pBox );

void Usage(char * pszProgramName)
{
    fprintf(stderr, "%s", PURPOSE);
    fprintf(stderr, "Usage:  %s\n", pszProgramName);
    fprintf(stderr, " -p protocol_sequence\n");
    fprintf(stderr, " -n network_address\n");
    fprintf(stderr, " -e endpoint\n");
    fprintf(stderr, " -o options\n");
    exit(1);
}

void _CRTAPI1 main(int argc, char **argv)
{
    RPC_STATUS status;
    PBOX       pBox = NULL;

    unsigned char * pszUuid             = NULL;
    unsigned char * pszProtocolSequence = "ncacn_np";
    unsigned char * pszNetworkAddress   = NULL;
    unsigned char * pszEndpoint         = "\\pipe\\rpcssm";
    unsigned char * pszOptions          = NULL;
    unsigned char * pszStringBinding    = NULL;
    int i;

    /* allow the user to override settings with command line switches */
    for (i = 1; i < argc; i++) {
        if ((*argv[i] == '-') || (*argv[i] == '/')) {
            switch (tolower(*(argv[i]+1))) {
            case 'p':  // protocol sequence
                pszProtocolSequence = argv[++i];
                break;
            case 'n':  // network address
                pszNetworkAddress = argv[++i];
                break;
            case 'e':
                pszEndpoint = argv[++i];
                break;
            case 'o':
                pszOptions = argv[++i];
                break;
            case 'h':
            case '?':
            default:
                Usage(argv[0]);
            }
        }
        else
            Usage(argv[0]);
    }

    RpcTryExcept {

        /* Use a convenience function to concatenate the elements of  */
        /* the string binding into the proper sequence.               */
        status = RpcStringBindingCompose(pszUuid,
                                         pszProtocolSequence,
                                         pszNetworkAddress,
                                         pszEndpoint,
                                         pszOptions,
                                         &pszStringBinding);
        printf("RpcStringBindingCompose returned 0x%x\n", status);
        printf("pszStringBinding = %s\n", pszStringBinding);
        if (status) {
            exit(status);
        }

        /* Set the binding handle that will be used to bind to the server. */
        status = RpcBindingFromStringBinding(pszStringBinding,
                                             &hStarter);
        printf("RpcBindingFromStringBinding returned 0x%x\n", status);
        if (status) {
            exit(status);
        }

        /* Now a different case: a client stub is in an enabled environment */

        status = RpcSmEnableAllocate();
        printf("RpcSmEnableAllocate returned 0x%x\n", status);
        if (status) {
            exit(status);
        }

        pBox = NULL;
        AllocateSmList( &pBox, 4 );
        InitList( pBox, 0xabcd0000 );
        PrintList( pBox );

        printf("Calling the remote procedure InOutList\n");
        InOutList( &pBox );
        PrintList( pBox );

        /* No need to free the memory allocated with RpcSmAllocate,
           RpcSmDisableAllocate would free it.
        */

        status = RpcSmDisableAllocate();
        printf("RpcSmDisableAllocate returned 0x%x\n", status);
        if (status) {
            exit(status);
        }

        printf(" All nodes from this case freed by RpcSmDisableAllocate().\n" );
        printf(" Client with memory environment done.\n\n" );

        Shutdown();

        /*  Free the string binding */
        status = RpcStringFree(&pszStringBinding);
        printf("RpcStringFree returned 0x%x\n", status);
        if (status) {
            exit(status);
        }

        status = RpcBindingFree(&hStarter);
        printf("RpcBindingFree returned 0x%x\n", status);
        if (status) {
            exit(status);
        }
    }
    RpcExcept(1) {

        #if defined(WIN32)
            printf( "Unexpected exception: %ld\n", RpcExceptionCode() );
        #else
            printf( "Unexpected exception: %d\n", RpcExceptionCode() );
        #endif
        printf("Did you set a net address (like -n server_name etc.)?\n");
        exit(1);
    }
    RpcEndExcept;

    exit(0);

}  // end main()

//=====================================================================
//        List allocation/deallocation routines
//=====================================================================

void
AllocateSmList( PBOX * ppBox, short sListSize)
{
    PBOX    pBox, head;
    int     i = 0;

    //.. Allocate a list of boxes, if needed  (when *ppBox==NULL).

    if ( *ppBox == NULL ) {

        RPC_STATUS status;

        head = 0;
        for (i = 0; i < sListSize; i++)
        {
            pBox = (PBOX) RpcSmAllocate( sizeof(LBOX), &status);
            if ( status != RPC_S_OK )
            {
                printf("AllocateList FAILED: not enough memory\n");
                break;
            }
            pBox->next = head;
            head = pBox;
        }
        *ppBox = head;
    }
    printf("%d nodes allocated.\n", i);
}

//=====================================================================
//        Initialization and pprint routines
//=====================================================================

void  InitList( PBOX  pBox, long lStartValue )
{
    int i = 0;

    while( pBox ) {
        pBox->data = lStartValue + ++i;
        pBox = pBox->next;
    }
    printf("%d nodes inited.\n", i);
}

// --------------------------------------------------------------------

void
PrintList( PBOX  pBox )
{
    int i = 0;

    while( pBox ) {

        if ( (i % 4) != 0 )
            printf("  data[%d]= %lx", i, pBox->data);
        else
            printf( "\n  data[%d]= %lx", i, pBox->data);
        pBox = pBox->next;
        i++;
    }
    printf("\n" );
}

/* end rpcssmc.c */


RPCSSMP.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                        rpcssm Example

    FILE:       rpcssmp.c

    PURPOSE:    Remote procedures used in server application rpcssms

    FUNCTIONS:  InOutList() - Adds new nodes to the list.

    COMMENTS:   This distributed application uses the rpcssm package.

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

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "rpcssm.h"    // header file generated by MIDL compiler

void AllocateSmList( PBOX * ppBox, short sListSize );
void InitList( PBOX  pBox, long lStartValue );
void PrintList( PBOX pBox );

void * pvPersistent    = NULL;
void * pvNonPersistent = NULL;

void
InOutList( PBOX * ppBox )
{
    /* As this routine executes in the enabled memory environment,
       a convienient way to allocate a non-persistent memory
       is to use RpcSmAllocate or RpcSsAllocate.
    */

    if ( ppBox) {

        PBOX p = *ppBox;
        while ( p->next )
            p = p->next;

        AllocateSmList( & p->next, 2 );
    }
    else {
        AllocateSmList( ppBox, 5 );
    }

    InitList( *ppBox, 0x98760000 );
    PrintList( *ppBox );

    /* To allocate a memory to be available after the call, means other
       than RpcS*Allocate with default allocators should be used.
    */

    if ( pvPersistent == NULL )
        {
        pvPersistent = malloc(100); /* or midl_user_allocate(100) */
        }

    pvNonPersistent = RpcSsAllocate( 100 );
}

/* The Shutdown procedure tells the server to stop listening */
/* for client requests.                                      */
void Shutdown(void)
{
    RPC_STATUS status;

    /* Freeing the persistent memory */

    if ( pvPersistent )
        free( pvPersistent );

    printf("Calling RpcMgmtStopServerListening\n");
    status = RpcMgmtStopServerListening(NULL);
    printf("RpcMgmtStopServerListening returned: 0x%x\n", status);
    if (status) {
        exit(status);
    }

    printf("Calling RpcServerUnregisterIf\n");
    status = RpcServerUnregisterIf(NULL, NULL, FALSE);
    printf("RpcServerUnregisterIf returned 0x%x\n", status);
    if (status) {
        exit(status);
    }
}

//=====================================================================
//        List allocation/deallocation routines
//=====================================================================

void
AllocateSmList( PBOX * ppBox, short sListSize)
{
    PBOX    pBox, head;
    int     i = 0;

    //.. Allocate a list of boxes, if needed (when *ppBox==NULL).

    if ( *ppBox == NULL ) {

        RPC_STATUS status;

        head = 0;
        for (i = 0; i < sListSize; i++)
        {
            pBox = (PBOX) RpcSmAllocate( sizeof(LBOX), &status);
            if ( status != RPC_S_OK )
            {
                printf("AllocateList FAILED: not enough memory\n");
                break;
            }
            pBox->next = head;
            head = pBox;
        }
        *ppBox = head;
    }
    printf("%d nodes allocated.\n", i);
}

//=====================================================================
//        Initialization and pprint routines
//=====================================================================

void  InitList( PBOX  pBox, long lStartValue )
{
    int i = 0;

    while( pBox ) {
        pBox->data = lStartValue + ++i;
        pBox = pBox->next;
    }
    printf("%d nodes inited.\n", i);
}

// --------------------------------------------------------------------

void
PrintList( PBOX  pBox )
{
    int i = 0;

    while( pBox ) {

        if ( (i % 4) != 0 )
            printf("  data[%d]= %lx", i, pBox->data);
        else
            printf("\n  data[%d]= %lx", i, pBox->data);
        pBox = pBox->next;
        i++;
    }
    printf("\n" );
}

/* end file rpcssmp.c */


RPCSSMS.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                        rpcssm Example

    FILE:       rpcssms.c

    USAGE:      rpcssms  -p protocol_sequence
                         -e endpoint
                         -m max calls
                         -n min calls
                         -f flag for RpcServerListen

    PURPOSE:    Server side of RPC distributed application rpcssm

    FUNCTIONS:  main() - registers server as RPC server

    COMMENTS:   This distributed application uses a context handle.

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

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include "rpcssm.h"    // header file generated by MIDL compiler

#define PURPOSE \
"This Microsoft RPC Version 2.0 sample program demonstrates\n\
the [context_handle] attribute. For more information\n\
about this attribute and the RPC API functions, see the\n\
RPC programming guide and reference.\n\n"

void Usage(char * pszProgramName)
{
    fprintf(stderr, "%s", PURPOSE);
    fprintf(stderr, "Usage:  %s\n", pszProgramName);
    fprintf(stderr, " -p protocol_sequence\n");
    fprintf(stderr, " -e endpoint\n");
    fprintf(stderr, " -m maxcalls\n");
    fprintf(stderr, " -n mincalls\n");
    fprintf(stderr, " -f flag_wait_op\n");
    exit(1);
}

void _CRTAPI1 main(int argc, char * argv[])
{
    RPC_STATUS status;
    unsigned char * pszProtocolSequence = "ncacn_np";
    unsigned char * pszSecurity         = NULL;
    unsigned char * pszEndpoint         = "\\pipe\\rpcssm";
    unsigned int    cMinCalls           = 1;
    unsigned int    cMaxCalls           = 20;
    unsigned int    fDontWait           = TRUE;
    int i;

    /* allow the user to override settings with command line switches */
    for (i = 1; i < argc; i++) {
        if ((*argv[i] == '-') || (*argv[i] == '/')) {
            switch (tolower(*(argv[i]+1))) {
            case 'p':  // protocol sequence
                pszProtocolSequence = argv[++i];
                break;
            case 'e':
                pszEndpoint = argv[++i];
                break;
            case 'm':
                cMaxCalls = (unsigned int) atoi(argv[++i]);
                break;
            case 'n':
                cMinCalls = (unsigned int) atoi(argv[++i]);
                break;
            case 'f':
                fDontWait = (unsigned int) atoi(argv[++i]);
                break;
            case 'h':
            case '?':
            default:
                Usage(argv[0]);
            }
        }
        else
            Usage(argv[0]);
    }

    status = RpcServerUseProtseqEp(pszProtocolSequence,
                                   cMaxCalls,
                                   pszEndpoint,
                                   pszSecurity);  // Security descriptor
    printf("RpcServerUseProtseqEp returned 0x%x\n", status);
    if (status) {
         exit(status);
    }

    status = RpcServerRegisterIf(rpcssm_ServerIfHandle, // interface to register
                                 NULL,   // MgrTypeUuid
                                 NULL);  // MgrEpv; null means use default
    printf("RpcServerRegisterIf returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    printf("Calling RpcServerListen\n");
    status = RpcServerListen(cMinCalls,
                             cMaxCalls,
                             fDontWait);
    printf("RpcServerListen returned: 0x%x\n", status);
    if (status) {
        exit(status);
    }

    if (fDontWait) {
        printf("Calling RpcMgmtWaitServerListen\n");
        status = RpcMgmtWaitServerListen();  //  wait operation
        printf("RpcMgmtWaitServerListen returned: 0x%x\n", status);
        if (status) {
            exit(status);
        }
    }

} // end main()

/*********************************************************************/
/*                 MIDL allocate and free                            */
/*********************************************************************/

void __RPC_FAR * __RPC_USER midl_user_allocate(size_t len)
{
    return(malloc(len));
}

void __RPC_USER midl_user_free(void __RPC_FAR * ptr)
{
    free(ptr);
}

/* end file rpcssms.c */


README.TXT

RPCSVC

The RPCSVC program demonstrates how to implement an NT system service that
uses RPC.

SUMMARY
=======

This particular RPC service is designed to show the performance effects of
various parameters to RPC APIs and interface design.

The service can be started and stopped from either the Services control
panel, the net start/stop command line, or by using the Service Controller
utility (see below).

The service also provides command-line parameters which install, remove, or
run (debug) the service as a console application.

This sample (and service.c) is based on the win32\service sample.

FILES
=====

The directory samples\rpc\handles\rpcsvc contains the following files to
build the distributed application RPCSVC:

File          Description

README.TXT    Readme file for the RPCSVC sample
RPCSVC.IDL    Interface definition language file
RPCSVC.ACF    Attribute configuration file
CLIENT.C      Client main program
SERVER.C      Server main program
SERVICE.C     Service APIs
SERVICE.H
MAKEFILE      Nmake file to build for Windows NT or Windows 95

TO USE:
-------

To install the service, first compile everything, and then type:

  rpcsvc -install

To start the service, use the control panel or type:

  net start simplerpcservice

Once the service has been started, you can use the CLIENT program to verify
that it really is working, using the syntax:

  svcclnt [-i iterations] [-t protseq] [-n servername] [-s security]

By default, the service listens to ncalrpc, ncacn_np, ncacn_ip_tcp and
ncadg_ip_udp.  If no parameters are passed to the client, it uses ncalrpc
with default LPC security.

If, after playing with the sample you wish to remove the service, simply
run:

  rpcsvc -remove

You can run the service from the command line with:

  rpcsvc -debug

MORE INFORMATION:
=================

See the sample in samples\win32\service for more information on NT system
service and how service.c and service.h work.

server.c implements a bunch of APIs:

    ServiceStart() is called when the server is starting. It uses server
    transports, registers its endpoints and interfaces and starts listening.

    ServiceStop() is called when the service should stop. It just calls
    RpcMgmtStopServerListening().

    Manager APIs (implement the remote side of the operations defined in the
    rpcsvc.idl file.

Client.c binds to the server (as specified on the command line) and then
times a bunch of different RPC calls to the server.


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 1995 - 1998 Microsoft Corporation.  All Rights Reserved.
//
//  MODULE:   client.c
//
//  PURPOSE:  This program is a command line oriented
//            demonstration of the Simple RPC service sample.
//
//  FUNCTIONS:
//    main(int argc, char **argv);
//    StartTime()
//    EndTime();
//    DoTimings();
//
//  COMMENTS:
//
//  AUTHOR:
//      Mario Goertzel - RPC Development
//
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <rpc.h>
#include "rpcsvc.h"

void Usage(void)
{
    printf("Usage:\n"
        "\t-n <server addr>  - Defaults to local machine\n"
        "\t-t <protseq>      - Defaults to ncalrpc (fast, local only)\n"
        "\t-i <iterations>   - Defaults to 100\n"
        "\t-s <security lvl> - Default none, range none (1) to privacy (6)\n"
        );
    return;
}

//
//  FUNCTIONS: StartTime()
//             EndTime()
//
//  USAGE:
//      StartTime();
//        // Do some work.
//      mseconds = EndTime();
//
//  RETURN VALUE:
//      Milliseconds between StartTime() and EndTime() calls.

LARGE_INTEGER Time;

void StartTime(void)
{
    QueryPerformanceCounter(&Time);
}

ULONG EndTime()
{
    LARGE_INTEGER liDiff;
    LARGE_INTEGER liFreq;

    QueryPerformanceCounter(&liDiff);

    liDiff.QuadPart -= Time.QuadPart;
    liDiff.QuadPart *= 1000; // Adjust to milliseconds, shouldn't overflow...

    (void)QueryPerformanceFrequency(&liFreq);

    return ((ULONG)(liDiff.QuadPart / liFreq.QuadPart));
}

//
//  FUNCTION: DoTimings
//
//  PURPOSE: Calls and times various RPC calls.
//           (Avoid cluttering up main())
//
//  PARAMETERS:
//    Binding     - Binding to the server.
//    iIterations - Number of times to make each call.
//
//  RETURN VALUE:
//    n/a
//
//
void DoTimings(RPC_BINDING_HANDLE Binding,
               UINT iIterations)
{
    ULONG mseconds;
    UINT i;
    RPC_STATUS status;
    byte bBuffer[4096];
    ULONG lBufferLength;
    ULONG lBufferSize;

    // Time Pings() (void calls)

    StartTime();
    for(i = iIterations; i; i--)
        {
        status = Ping(Binding);
        if (status != RPC_S_OK)
            goto Cleanup;
        }
    mseconds = EndTime();

    printf("%4d calls in %8d milliseconds - void calls.\n", iIterations, mseconds);

    // Time [in] buffer's
    //

    lBufferLength = BUFFER_SIZE;
    lBufferSize   = BUFFER_SIZE;
    StartTime();
    for(i = iIterations; i; i--)
        {
        status = BufferIn1(Binding, bBuffer, lBufferLength, lBufferSize);
        if (status != RPC_S_OK)
            {
            goto Cleanup;
            }
        }
    mseconds = EndTime();

    printf("%4d calls in %8d milliseconds - 100 byte buffer in (1).\n", iIterations, mseconds);

    lBufferLength = BUFFER_SIZE;

    StartTime();
    for(i = iIterations; i; i--)
        {
        status = BufferIn3(Binding, bBuffer, lBufferLength);
        if (status != RPC_S_OK)
            {
            goto Cleanup;
            }
        }
    mseconds = EndTime();

    printf("%4d calls in %8d milliseconds - 100 byte buffer in (2).\n", iIterations, mseconds);

    lBufferLength = BUFFER_SIZE;

    StartTime();
    for(i = iIterations; i; i--)
        {
        status = BufferIn3(Binding, bBuffer, lBufferLength);
        if (status != RPC_S_OK)
            {
            goto Cleanup;
            }
        }
    mseconds = EndTime();

    printf("%4d calls in %8d milliseconds - 100 byte buffer in (3).\n", iIterations, mseconds);

    // Time [out] buffer's

    lBufferLength = BUFFER_SIZE;

    StartTime();
    for(i = iIterations; i; i--)
        {
        status = BufferOut1(Binding, bBuffer, &lBufferLength);
        if (status != RPC_S_OK)
            {
            goto Cleanup;
            }
        }
    mseconds = EndTime();

    printf("%4d calls in %8d milliseconds - 100 byte buffer out (1).\n", iIterations, mseconds);

    lBufferLength = BUFFER_SIZE;
    lBufferSize   = BUFFER_SIZE;

    StartTime();
    for(i = iIterations; i; i--)
        {
        status = BufferOut2(Binding, bBuffer, lBufferSize, &lBufferLength);
        if (status != RPC_S_OK)
            {
            goto Cleanup;
            }
        }
    mseconds = EndTime();

    printf("%4d calls in %8d milliseconds - 100 byte buffer out (2).\n", iIterations, mseconds);

    StartTime();
    for(i = iIterations; i; i--)
        {
        BUFFER Buffer;
        Buffer.BufferLength = 0;
        Buffer.Buffer = 0;

        status = BufferOut3(Binding, &Buffer);
        if (status != RPC_S_OK)
            {
            goto Cleanup;
            }
        MIDL_user_free(Buffer.Buffer);
        }
    mseconds = EndTime();

    printf("%4d calls in %8d milliseconds - 100 byte buffer out (3).\n", iIterations, mseconds);

    lBufferLength = BUFFER_SIZE;

    StartTime();
    for(i = iIterations; i; i--)
        {
        status = BufferOut4(Binding, bBuffer, &lBufferLength);
        if (status != RPC_S_OK)
            {
            goto Cleanup;
            }
        }
    mseconds = EndTime();

    printf("%4d calls in %8d milliseconds - 100 byte buffer out (4).\n", iIterations, mseconds);

    // Time arrays of structures

    {
    struct BAD1 abad1[50];
    struct BAD2 abad2[50];
    struct GOOD agood[50];

    for (i = 0; i < 50; i++)
        {
        abad2[i].e = (BAD_ENUM)i % 4 + 1;
        agood[i].e = (GOOD_ENUM)i % 4 + 5;
        }

    StartTime();
    for(i = iIterations; i; i--)
        {
        status = StructsIn1(Binding, &abad1[0]);
        if (status != RPC_S_OK)
            {
            goto Cleanup;
            }
        }
    mseconds = EndTime();

    printf("%4d calls in %8d milliseconds - 2 mod 4 aligned structs.\n", iIterations, mseconds);

    StartTime();
    for(i = iIterations; i; i--)
        {
        status = StructsIn2(Binding, &abad2[0]);
        if (status != RPC_S_OK)
            {
            goto Cleanup;
            }
        }
    mseconds = EndTime();

    printf("%4d calls in %8d milliseconds - structs with an enum.\n", iIterations, mseconds);

    StartTime();
    for(i = iIterations; i; i--)
        {
        status = StructsIn3(Binding, &agood[0]);
        if (status != RPC_S_OK)
            {
            goto Cleanup;
            }
        }
    mseconds = EndTime();

    printf("%4d calls in %8d milliseconds - structs with v1_enum.\n", iIterations, mseconds);
    }

    // Linked lists

    {
    LIST list;
    PLIST plist = &list;
    for (i = 0; i < LIST_SIZE - 1; i++)
        {
        plist->pNext = MIDL_user_allocate(sizeof(LIST));
        plist->data = i;
        if (plist->pNext == 0)
            {
            status = RPC_S_OUT_OF_MEMORY;
            goto Cleanup;
            }
        plist = plist->pNext;
        }
    plist->data = i;
    plist->pNext = 0;

    StartTime();
    for(i = iIterations; i; i--)
        {
        status = ListIn(Binding, &list);
        if (status != RPC_S_OK)
            {
            goto Cleanup;
            }
        }
    mseconds = EndTime();

    printf("%4d calls in %8d milliseconds - [in] linked list.\n", iIterations, mseconds);

    StartTime();
    for(i = iIterations; i; i--)
        {
        status = ListOut1(Binding, &list);
        if (status != RPC_S_OK)
            {
            goto Cleanup;
            }
        // Freeing the list here would cause all the elements
        // to be allocated again on the next call.
        }
    mseconds = EndTime();

    printf("%4d calls in %8d milliseconds - [out] linked list (1).\n", iIterations, mseconds);

    StartTime();
    for(i = iIterations; i; i--)
        {
        status = ListOut2(Binding, &list);
        if (status != RPC_S_OK)
            {
            goto Cleanup;
            }
        // Freeing the list here would cause all the elements
        // to be allocated again on the next call.
        }
    mseconds = EndTime();

    printf("%4d calls in %8d milliseconds - [out] linked list (2).\n", iIterations, mseconds);

    // Free allocated elements of the list.
    plist = list.pNext;
    while(plist)
        {
        PLIST tmp = plist;
        plist = plist->pNext;
        MIDL_user_free(tmp);
        }
    }

    // Unions

    {
    BAD_UNION badunionArray[UNION_ARRAY_LEN];
    GOOD_UNION goodunion;
    ARM_ONE armone;
    ULONG ulArray[UNION_ARRAY_LEN];

    goodunion.Tag = 1;
    goodunion.u.pOne = &armone;
    armone.DataLength = UNION_ARRAY_LEN;
    armone.Data = ulArray;

    for(i = 0; i < UNION_ARRAY_LEN; i++)
        {
        ulArray[i] = i;
        badunionArray[i].Tag = 1;
        badunionArray[i].u.ulData = i;
        }

    StartTime();
    for(i = iIterations; i; i--)
        {
        status = UnionCall1(Binding, UNION_ARRAY_LEN, badunionArray);
        if (status != RPC_S_OK)
            {
            goto Cleanup;
            }
        }
    mseconds = EndTime();

    printf("%4d calls in %8d milliseconds - [in] array of unions.\n", iIterations, mseconds);

    StartTime();
    for(i = iIterations; i; i--)
        {
        status = UnionCall2(Binding, &goodunion);
        if (status != RPC_S_OK)
            {
            goto Cleanup;
            }
        }
    mseconds = EndTime();

    printf("%4d calls in %8d milliseconds - [in] union of arrays.\n", iIterations, mseconds);

    }

    // Time pings() (null calls) which impersonate the client.

    StartTime();
    for(i = iIterations; i; i--)
        {

        status = CheckSecurity(Binding);

        if (status != RPC_S_OK)
            {
            if (status == RPC_S_ACCESS_DENIED)
                {
                printf("Access denied, try -s 2 or higher.\n");
                return;
                }
            goto Cleanup;
            }

        }
    mseconds = EndTime();

    printf("%4d calls in %8d milliseconds - void call w/ impersonation\n", iIterations, mseconds);

Cleanup:

    if (status != RPC_S_OK)
        {
        printf("Call failed - %d\n", status);
        }

    return;
}

//
//  FUNCTION: main
//
//  PURPOSE: Parses arguments and binds to the server.
//
//  PARAMETERS:
//    argc - number of command line arguments
//    argv - array of command line arguments
//
//  RETURN VALUE:
//    Program exit code.
//
//
int main(int argc, char *argv[])
{
    char *serverAddress = NULL;
    char *protocol = "ncalrpc";
    UINT iIterations = 100;
    unsigned char *stringBinding;
    RPC_BINDING_HANDLE Binding;
    RPC_STATUS status;
    ULONG SecurityLevel = RPC_C_AUTHN_LEVEL_NONE;

    argc--;
    argv++;
    while(argc)
        {
        if (   argv[0][0] != '-'
            && argv[0][0] != '/')
            {
            Usage();
            return(1);
            }

        switch(argv[0][1])
            {
            case 'n':
                if (argc < 2)
                    {
                    Usage();
                    return(1);
                    }
                serverAddress = argv[1];
                argc--;
                argv++;
                break;
            case 't':
                if (argc < 2)
                    {
                    Usage();
                    return(1);
                    }
                protocol = argv[1];
                argc--;
                argv++;
                break;
            case 'i':
                if (argc < 2)
                    {
                    Usage();
                    return(1);
                    }
                iIterations = atoi(argv[1]);
                argc--;
                argv++;
                break;
            case 's':
                if (argc < 2)
                    {
                    Usage();
                    return(1);
                    }
                SecurityLevel = atoi(argv[1]);
                if (SecurityLevel > RPC_C_AUTHN_LEVEL_PKT_PRIVACY)
                    {
                    Usage();
                    return(1);
                    }
                argc--;
                argv++;
                break;
            default:
                Usage();
                return(1);
                break;
            }

        argc--;
        argv++;
        }

    status = RpcStringBindingCompose(0,
                                     protocol,
                                     serverAddress,
                                     0,
                                     0,
                                     &stringBinding);
    if (status != RPC_S_OK)
        {
        printf("RpcStringBindingCompose failed - %d\n", status);
        return(1);
        }

    status = RpcBindingFromStringBinding(stringBinding, &Binding);

    if (status != RPC_S_OK)
        {
        printf("RpcBindingFromStringBinding failed - %d\n", status);
        return(1);
        }

    status =
    RpcBindingSetAuthInfo(Binding,
                          0,
                          SecurityLevel,
                          RPC_C_AUTHN_WINNT,
                          0,
                          0
                         );

    if (status != RPC_S_OK)
        {
        printf("RpcBindingSetAuthInfo failed - %d\n", status);
        return(1);
        }

    status = Ping(Binding);

    if (status != RPC_S_OK)
        {
        printf("Ping failed - %d\n", status);
        }

    printf("Connected.\n");

    //
    // Call and time various RPC calls.
    //

    DoTimings(Binding, iIterations);

    // Cleanup

    status = RpcBindingFree(&Binding);

    // ASSERT(status == RPC_S_OK):

    status = RpcStringFree(&stringBinding);

    // ASSERT(status == RPC_S_OK);

    return(0);
}

void * __RPC_USER MIDL_user_allocate(size_t size)
{
    return(HeapAlloc(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, size));
}

void __RPC_USER MIDL_user_free( void *pointer)
{
    HeapFree(GetProcessHeap(), 0, pointer);
}


RPCSVC.ACF

// 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 (C) 1995  Microsoft Corporation.  All Rights Reserved.
//
//  MODULE:   rpcsvc.acf
//
//  PURPOSE:  .ACF file for rpcsvc .idl.  This controls the
//            aspects of the interface which do not affect
//            the wire. (error handling, memory allocation, etc)
//
//

#ifdef SERVER

//
// On the server side of your application using the fast RpcSmAllocate
// is recommended in server's which do many allocations.
// Although it may be used on the client, this requires more work
// and has little performance impact.
//
#define ENABLE_ALLOC ,enable_allocate
#else
#define ENABLE_ALLOC /* client */
#endif

interface RpcServiceSample
{

    // The [comm_status] and [fault_status] attributes cause the stubs
    // to return any errors in an error_status_t parameter or return
    // value.  Otherwise, any communication errors will get raised as
    // an exception.  This has no performance affect, but it keeps the
    // client code cleaner.

    [comm_status, fault_status] Ping();

    [comm_status, fault_status] BufferIn1();
    [comm_status, fault_status] BufferIn2();
    [comm_status, fault_status] BufferIn3();

    [comm_status, fault_status] BufferOut1();
    [comm_status, fault_status] BufferOut2();
    [comm_status, fault_status] BufferOut3();
    [comm_status, fault_status] BufferOut4();

    [comm_status, fault_status] StructsIn1();
    [comm_status, fault_status] StructsIn2();
    [comm_status, fault_status] StructsIn3();

    [comm_status, fault_status] ListIn();
    [comm_status, fault_status] ListOut1();
    [comm_status, fault_status ENABLE_ALLOC] ListOut2();

    [comm_status, fault_status] UnionCall1();
    [comm_status, fault_status] UnionCall2();

    [comm_status, fault_status] CheckSecurity();
}


RPCSVC.IDL

// 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.
//
//  MODULE:   rpcsvc.idl
//
//  PURPOSE:  Simple RPC service .idl file.  This defines the wire
//            "contract" between a client and server using this
//            interface.  All data types and functions (methods,
//            operations) that go over the wire must be defined here.
//
//
[
uuid(15cd3850-28ca-11ce-a4e8-00aa006116cb),
version(1.0)
]
interface RpcServiceSample
{

    error_status_t
Ping(
    [in] handle_t Binding
    );

    error_status_t
CheckSecurity(
    [in] handle_t Binding
    );

// Sending a buffer (writing) to the server.

//
// BufferIn1 and BufferIn2 both use length_is() and size_is().
// This slows performance, because a larger buffer must be
// allocated on the sever and the data copied into this larger
// buffer.
//
// BufferIn2 has the most problems because it forces a 16K
// allocation which may be much larger then needed and requires
    // and extra data copy.  It also limits clients to writing 16K
    // at a time.
//
// The BufferIn3() function saves the allocation and copy
    // and gives the client complete control of the size.
    //
// Notes: Avoid length_is() on [in] parameters.  Usually
    //        size_is() is all that is really needed.

    const unsigned long BUFFER_SIZE = 100;

error_status_t
BufferIn1(
         [in] handle_t Binding,
         [length_is(BufferLength), size_is(BufferSize),
      in ] byte Buffer[],
         [in] unsigned long BufferLength,
             [in] unsigned long BufferSize
         );

    error_status_t
BufferIn2(
         [in] handle_t Binding,
         [length_is(BufferLength), in ] byte Buffer[16*1024],
             [in] unsigned long BufferLength
             );

    error_status_t
    BufferIn3(
         [in] handle_t Binding,
         [size_is(BufferLength), in ] byte Buffer[],
         [in] unsigned long BufferLength
             );

    // Getting a buffer (reading) from the server.
    //
    // In BufferOut1 the size of the output is limited to 16K,
    // which will force extra round trips when more then 16K
    // is being returned. This requires the client to supply a
    // 16K buffer and the stub to allocate 16K on the server side.
    //
    // In BufferOut2 the client decides how big the buffer
    // should be and only as much as the client wants is
    // allocated in the server.  Similar to BufferOut4
    // which is better.
    //
    // In BufferOut3 the server allocates a buffer exactly
    // as large as it wants.  This results in two allocations,
    // one in the server manager and one in the client.  This
    // is best if the clients have no idea how much data the
    // server will give them.
    //
    // In BufferOut4 the client decides how big the buffer
    // should be.  The server can shorten (this should
    // be uncommon) the buffer if needed.
    //
    // Notes: Consider how the client and server interact
    //   in your application when choosing a "read" style
    //   interface.

    error_status_t
    BufferOut1(
              [in] handle_t Binding,
              [length_is(*pBufferLength)] byte Buffer[16*1024],
              [out] unsigned long *pBufferLength
              );

    error_status_t
    BufferOut2(
              [in] handle_t Binding,
              [size_is(BufferSize), length_is(*pBufferLength),
               out] byte Buffer[],
              [in] unsigned long BufferSize,
              [out] unsigned long *pBufferLength
              );

    typedef struct {
        unsigned long BufferLength;
        [unique, size_is(BufferLength)] byte *Buffer;
        } BUFFER;

    error_status_t
    BufferOut3(
              [in] handle_t Binding,
              [out] BUFFER *pBuffer
              );

    error_status_t
    BufferOut4(
              [in] handle_t Binding,
              [out, size_is(*pBufferLength)] byte Buffer[],
              [in, out] unsigned long *pBufferLength
              );

    // Structures and Enums.
    //
    // Use -Zp8 (or higher). (Default on Win32 platforms)
    //
    // Make sure the structure ends on a 0 mod 4 address.
    //
    // Use [v1_enum] on all enumerated types.
    //
    // Following these rules will result in structures (and
    // arrays of structures) which can be memcpy()'ed to and from
    // the wire.  Otherwise the structures will be copied
    // member-by-member. (ouch!)
    //

    typedef enum {
        A = 1,
        B,
        C,
        D
        } BAD_ENUM;

    typedef [v1_enum] enum {
        E = 5,
        F,
        G,
        H
        } GOOD_ENUM;

    struct BAD1 {
        long l;
        short s;  // Ends on 6 % 4 = 2 byte address.
        };

    struct BAD2 {
        BAD_ENUM e;  // 16bits on the wire, 32bits in memory!
        long l;      // How big is it?
        };

    struct GOOD {
        GOOD_ENUM e;  // v1_enum 32bits on wire and in memory.
        long l;
        long l2;      // Ends on 12 % 4 = 0 byte address.
        };

    error_status_t
    StructsIn1(
              [in] handle_t Binding,
              [in] struct BAD1 array[50]
              );

    error_status_t
    StructsIn2(
              [in] handle_t Binding,
              [in] struct BAD2 array[50]
              );

    error_status_t
    StructsIn3(
              [in] handle_t Binding,
              [in] struct GOOD array[50]
              );
    // Linked list examples
    //
    // Follow the rules for structs when defining your linked
    // list nodes.
    //
    // ListIn and ListOut1 are the basic linked list functions,
    // ListOut1 is more expensive because each node must be copied
    // into nodes on the client.  (In ListIn the list pointers
    // are fixed up in one big buffer.)
    //
    // ListOut2 is identical to ListOut1 except that it turns
    // on the RPC allocator in the server.
    //
    // Linked lists are not very efficient for RPC because the
    // often require many memory allocations.  When possible,
    // replace linked lists with variably sized arrays of structures.
    //

    const unsigned long LIST_SIZE = 50;

    typedef struct LIST {
        [unique] struct LIST *pNext;
        unsigned long data;
        } LIST;

    typedef [unique] LIST *PLIST;

    error_status_t
    ListIn(
          [in] handle_t Binding,
          [in] PLIST pList
          );

    error_status_t
    ListOut1(
            [in] handle_t Binding,
            [out] LIST *pListHead
            );

    // [enable_allocate] in .acf.
    error_status_t
    ListOut2(
            [in] handle_t Binding,
            [out] LIST *pListHead
            );

    // Unions
    //
    // Avoid using very many unions. For example, don't pass an array in
    // which each element contains a union with several arms.  Instead,
    // define several different structures and define a union with several
    // arms each being an array of one of the different types of structures.
    //
    // Do NOT define a [default] arm for your unions.  This way a future
    // version of the union can include new arms and still interoperate
    // with older versions of the interface.
    //
    // Avoid passing unions by value.
    //

    const unsigned long UNION_ARRAY_LEN = 50;

    typedef struct BAD_UNION {
        unsigned long Tag;
        [switch_is(Tag)] union
            {
            [case(1)]
                unsigned long ulData;
            [case(2)]
                unsigned hyper uhData;
            [default]
                // The default arm here prevents the addition of
                // another case in future versions.
                ;
            } u;
        } BAD_UNION;

    typedef struct ONE {
        unsigned long DataLength;
        [size_is(DataLength)] unsigned long *Data;
        } ARM_ONE;

    typedef struct TWO {
        unsigned long DataLength;
        [size_is(DataLength)] unsigned hyper *Data;
        } ARM_TWO;

    typedef struct {
        unsigned long Tag;
        [switch_is(Tag)] union
            {
            [case (1)]
                [unique] ARM_ONE *pOne;
            [case (2)]
                [unique] ARM_TWO *pTwo;

            // May add [case(3)] in a future version.
            //
            // When calling a down level server with Tag == 3 the
            // error RPC_S_INVALID_TAG will get returned.  Values
            // 1 and 2 will continue to work.
            //
            // If there was a [default] arm this wouldn't work.

            } u;
        } GOOD_UNION;

        error_status_t
        UnionCall1(
                  [in] handle_t Binding,
                  [in] unsigned long Length,
                  [in, size_is(Length)] BAD_UNION ArrayOfUnions[]
                  );

        error_status_t
        UnionCall2(
                  [in] handle_t Binding,
                  [in] GOOD_UNION *pUnionContainingArrays
                  );

}


SERVER.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.
//
//  MODULE:   server.c
//
//  PURPOSE:  Implements the body of the RPC service sample.
//
//  FUNCTIONS:
//            Called by service.c:
//            ServiceStart(DWORD dwArgc, LPTSTR *lpszArgv);
//            ServiceStop( );
//
//            Called by RPC:
//            error_status_t Ping(handle_t)
//
//  COMMENTS: The ServerStart and ServerStop functions implemented here are
//            prototyped in service.h.  The other functions are RPC manager
//            functions prototypes in rpcsvc.h.
//
//
//  AUTHOR: Craig Link - Microsoft Developer Support
//          Mario Goertzel - RPC Development
//

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <process.h>
#include <tchar.h>
#include <rpc.h>
#include "service.h"
#include "rpcsvc.h"

//
// RPC configuration.
//

// This service listens to all the protseqs listed in this array.
// This should be read from the service's configuration in the
// registery.

TCHAR *ProtocolArray[] = { TEXT("ncalrpc"),
                           TEXT("ncacn_ip_tcp"),
                           TEXT("ncacn_np"),
                           TEXT("ncadg_ip_udp")
                         };

// Used in RpcServerUseProtseq, for some protseqs
// this is used as a hint for buffer size.
ULONG ProtocolBuffer = 3;

// Use in RpcServerListen().  More threads will increase performance,
// but use more memory.
ULONG MinimumThreads = 3;

//
//  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:
//    Starts the service listening for RPC requests.
//
VOID ServiceStart (DWORD dwArgc, LPTSTR *lpszArgv)
{
    UINT i;
    RPC_BINDING_VECTOR *pbindingVector = 0;
    RPC_STATUS status;
    BOOL fListening = FALSE;

    ///////////////////////////////////////////////////
    //
    // Service initialization
    //

    //
    // Use protocol sequences (protseqs) specified in ProtocolArray.
    //

    for(i = 0; i < sizeof(ProtocolArray)/sizeof(TCHAR *); i++)
        {

        // Report the status to the service control manager.
        if (!ReportStatusToSCMgr(
            SERVICE_START_PENDING, // service state
            NO_ERROR,              // exit code
            3000))                 // wait hint
            return;

        status = RpcServerUseProtseq(ProtocolArray[i],
                                     ProtocolBuffer,
                                     0);

        if (status == RPC_S_OK)
            {
            fListening = TRUE;
            }
        }

    if (!fListening)
        {
        // Unable to listen to any protocol!
        //
        AddToMessageLog(TEXT("RpcServerUseProtseq() failed\n"));
        return;
        }

    // Report the status to the service control manager.
    //
    if (!ReportStatusToSCMgr(
        SERVICE_START_PENDING, // service state
        NO_ERROR,              // exit code
        3000))                 // wait hint
        return;

    // Register the services interface(s).
    //

    status = RpcServerRegisterIf(RpcServiceSample_v1_0_s_ifspec,   // from rpcsvc.h
                                 0,
                                 0);

    if (status != RPC_S_OK)
        return;

    // Report the status to the service control manager.
    //
    if (!ReportStatusToSCMgr(
        SERVICE_START_PENDING, // service state
        NO_ERROR,              // exit code
        3000))                 // wait hint
        return;

    // Register interface(s) and binding(s) (endpoints) with
    // the endpoint mapper.
    //

    status = RpcServerInqBindings(&pbindingVector);

    if (status != RPC_S_OK)
        {
        return;
        }

    status = RpcEpRegister(RpcServiceSample_v1_0_s_ifspec,   // from rpcsvc.h
                           pbindingVector,
                           0,
                           0);

    if (status != RPC_S_OK)
        {
        return;
        }

    // Report the status to the service control manager.
    //
    if (!ReportStatusToSCMgr(
        SERVICE_START_PENDING, // service state
        NO_ERROR,              // exit code
        3000))                 // wait hint
        return;

    // Enable NT LM Security Support Provider (NtLmSsp service)
    //
    status = RpcServerRegisterAuthInfo(0,
                                       RPC_C_AUTHN_WINNT,
                                       0,
                                       0
                                       );
    if (status != RPC_S_OK)
        {
        return;
        }

    // Report the status to the service control manager.
    //
    if (!ReportStatusToSCMgr(
        SERVICE_START_PENDING, // service state
        NO_ERROR,              // exit code
        3000))                 // wait hint
        return;

    // Start accepting client calls.
    //
    status = RpcServerListen(MinimumThreads,
                             RPC_C_LISTEN_MAX_CALLS_DEFAULT,  // rpcdce.h
                             TRUE);                           // don't block.

    if (status != RPC_S_OK)
        {
        return;
        }

    // Report the status to the service control manager.
    //
    if (!ReportStatusToSCMgr(
        SERVICE_RUNNING,       // service state
        NO_ERROR,              // exit code
        0))                    // wait hint
        return;

    //
    // End of initialization
    //
    ////////////////////////////////////////////////////////////

    ////////////////////////////////////////////////////////////
    //
    // Cleanup
    //

    // RpcMgmtWaitServerListen() will block until the server has
    // stopped listening.  If this service had something better to
    // do with this thread, it would delay this call until
    // ServiceStop() had been called. (Set an event in ServiceStop()).
    //
    status = RpcMgmtWaitServerListen();

    // ASSERT(status == RPC_S_OK)

    // Remove entries from the endpoint mapper database.
    //
    RpcEpUnregister(RpcServiceSample_v1_0_s_ifspec,   // from rpcsvc.h
                    pbindingVector,
                    0);

    // Delete the binding vector
    //
    RpcBindingVectorFree(&pbindingVector);

    //
    ////////////////////////////////////////////////////////////
    return;
}

//
//  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()
{
    // Stop's the server, wakes the main thread.

    RpcMgmtStopServerListening(0);
}

//
//  FUNCTION: Ping
//
//  PURPOSE: Implements the Ping() operation.
//
//  PARAMETERS:
//    none
//
//  RETURN VALUE:
//    none
//
//  COMMENTS:
//    Ping() operation defined in rpcsvc.idl.
//    Used by clients to test the connection.
//

error_status_t
Ping(
    handle_t h
    )
{
    return(0);
}

//
//  FUNCTIONS: BufferIn1, BufferIn2, BufferIn3
//
//  PURPOSE: Implements three different methods
//           for sending data to the server.
//
//  PARAMETERS:
//    see rpcsvc.idl
//
//  RETURN VALUE:
//    error_status_t - 0;
//

error_status_t
BufferIn1(
         handle_t h,
         byte Buffer[],
         unsigned long Length,
         unsigned long Size
         )
{
    return(0);
}

error_status_t
BufferIn2(
         handle_t h,
         byte Buffer[],
         unsigned long Length
         )
{
    return(0);
}

error_status_t
BufferIn3(
         handle_t h,
         byte Buffer[],
         unsigned long Length
         )
{
    return(0);
}

//
//  FUNCTIONS: BufferOut1, BufferOut2, BufferOut3, BufferOut4
//
//  PURPOSE: Implements four different methods
//           for reading data from the server.
//
//  PARAMETERS:
//    see rpcsvc.idl
//
//  RETURN VALUE:
//    error_status_t - 0;
//

error_status_t
BufferOut1(
         handle_t h,
         byte Buffer[],
         unsigned long *pLength
         )
{
    *pLength = BUFFER_SIZE;
    return(0);
}

error_status_t
BufferOut2(
          handle_t h,
          byte Buffer[],
          unsigned long Size,
          unsigned long *pLength
          )
{
    *pLength = BUFFER_SIZE;
    return(0);
}

error_status_t
BufferOut3(
          handle_t h,
          BUFFER *pBuffer
          )
{
    pBuffer->BufferLength = BUFFER_SIZE;
    pBuffer->Buffer = MIDL_user_allocate(BUFFER_SIZE);

    if (pBuffer->Buffer == 0)
        {
        return(RPC_S_OUT_OF_MEMORY);
        }
    return(0);
}

error_status_t
BufferOut4(
          handle_t h,
          byte Buffer[],
          unsigned long *pLength
          )
{
    *pLength = BUFFER_SIZE;
    return(0);
}

//
//  FUNCTIONS: StructsIn1, StructsIn2, StructsIn3
//
//  PURPOSE: Implements server side of the struct/enum operations.
//
//  PARAMETERS:
//    see rpcsvc.idl
//
//  RETURN VALUE:
//    error_status_t - 0;
//
//
error_status_t
StructsIn1(
          handle_t h,
          struct BAD1 array[50]
          )
{
    return(0);
}

error_status_t
StructsIn2(
          handle_t h,
          struct BAD2 array[50]
          )
{
    return(0);
}

error_status_t
StructsIn3(
          handle_t h,
          struct GOOD array[50]
          )
{
    return(0);
}

//
//  FUNCTIONS: ListIn, ListOut1, ListOut2
//
//  PURPOSE: Implements server side of linked list functions.
//
//
//  PARAMETERS:
//    see rpcsvc.idl
//
//  RETURN VALUE:
//    error_status_t - 0;
//
//  NOTES:
//    Since ListOut2 uses [enable_allocate] it
//    must allocate all memory for parameters
//    with RpcSsAllocate().
//

error_status_t
ListIn(
      handle_t h,
      PLIST pList
      )
{
    return(0);
}

error_status_t
ListOut1(
        handle_t h,
        LIST *pList
        )
{
    int i;
    for(i = 0; i < LIST_SIZE; i++)
        {
        pList->data = i;
        pList->pNext = MIDL_user_allocate(sizeof(LIST));
        if (pList->pNext == 0)
            {
            return(RPC_S_OUT_OF_MEMORY);
            }
        pList = pList->pNext;
        }

    pList->data = i;
    pList->pNext = 0;

    return(0);
}

error_status_t
ListOut2(
        handle_t h,
        LIST *pList
        )
{
    int i;
    for(i = 0; i < LIST_SIZE; i++)
        {
        pList->data = i;
        pList->pNext = RpcSsAllocate(sizeof(LIST));
        // RpcSsAllocate raises an exception when it
        // fails.  Use RpcSmAllocate is this is
        // undesirable.
        pList = pList->pNext;
        }

    pList->data = i;
    pList->pNext = 0;

    return(0);
}

//
//  FUNCTIONS: UnionCall1, UnionCall2
//
//  PURPOSE: Implements server side of the Union functions.
//
//  PARAMETERS:
//    see rpcsvc.idl
//
//  RETURN VALUE:
//    error_status_t - 0;
//
//
error_status_t
UnionCall1(
          handle_t h,
          unsigned long Length,
          BAD_UNION aUnion[]
          )
{
    return(0);
}

error_status_t
UnionCall2(
          handle_t h,
          GOOD_UNION *pUnion
          )
{
    return(0);
}

//
//  FUNCTION: CheckSecurity
//
//  PURPOSE: Demonstrates the RPC security APIs.
//
//  PARAMETERS:
//    h - binding to client which made the call.
//
//  RETURN VALUE:
//    0 - no error
//
error_status_t
CheckSecurity(
             handle_t h
             )
{
    RPC_STATUS status;

    // At this point the thread is running in the server
    // security context.  There is guarantee that the client
    // even used a secure connection.

    status = RpcImpersonateClient(h);

    if (status != RPC_S_OK)
        {
        return(RPC_S_ACCESS_DENIED);
        }

    // This thread is now running in the clients security context.

    //
    // The server should now open a file, mutex, event or its own data
    // structure which has an ACL associated with it to check that the
    // client has the right to access the server's protected data.
    //

    status = RpcRevertToSelf();

    // ASSERT(status == RPC_S_OK);

    // This thread is now running in the server's security context.

    return(0);
}

//
//  FUNCTIONS: MIDL_user_allocate and MIDL_user_free
//
//  PURPOSE: Used by stubs to allocate and free memory
//           in standard RPC calls. Not used when
//           [enable_allocate] is specified in the .acf.
//
//
//  PARAMETERS:
//    See documentations.
//
//  RETURN VALUE:
//    Exceptions on error.  This is not required,
//    you can use -error allocation on the midl.exe
//    command line instead.
//
//
void * __RPC_USER MIDL_user_allocate(size_t size)
{
    return(HeapAlloc(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, size));
}

void __RPC_USER MIDL_user_free( void *pointer)
{
    HeapFree(GetProcessHeap(), 0, pointer);
}


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 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.
        //
        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            "RpcSvc"
// internal name of the service
#define SZSERVICENAME        "SimpleRpcService"
// displayed name of the service
#define SZSERVICEDISPLAYNAME "Simple RPC Service"
// list of service dependencies - "dep1\0dep2\0\0"
#define SZDEPENDENCIES       "RPCSS\0\0"
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//// 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


README.TXT

STROUT

The STROUT sample demonstrates how to allocate memory at a server for
a two-dimensional object (an array of pointers) and pass it back to the
client as an [out]-only parameter. The client then frees the memory.
This technique allows the stub to call the server without knowing in
advance how much data will be returned.
This program also allows the user to compile either for UNICODE or ANSI.
See section below for guidelines on how to compile for UNICODE

FILES
=====

The directory samples\rpc\strout contains the following files for
building the sample distributed application STROUT:

File          Description
-------------------------

README.TXT      Readme file for the strout sample
STROUT.IDL      Interface definition language file
STROUT.ACF      Attribute configuration file
CLIENT.C        Client main program
SERVER.C        Server main program
COMMON.H        Common header file for all the files
REMOTE.C        Remote procedures
MAKEFILE        nmake file to build 32-bit client and server applications
                for ANSI characters.
MAKEFILE.UNI    nmake file to build 32-bit client and server applications
                for UNICODE characters.

NMAKE builds the executable programs CLIENT.EXE (client) and
SERVER.EXE (server).

Note: The client and server applications can run on the same
Microsoft Windows NT computer when you use different screen groups.

COMPILING FOR UNICODE:
======================

type nmake /f makefile.uni at the command line. This will cause
the compiler to use the file MAKEFILE.UNI instead of the MAKEFILE.

The reason behind the use of TEXT, TCHAR, _TUCHAR, _tprintf, _tcscpy,
_tcscmp, and _tcslen is that these macros expand to either
one byte character ANSI functions or to UNICODE (Wide characters) functions
when they are compiled
    TEXT    :   This macro will put an L in front of the string if we are
                compiling for UNICODE
    TCHAR   :   Maps to either char or wchar_t
    _TUCHAR :   Maps to either unsigned char or wchar_t
    _tprintf:   Maps to either printf or wsprintf
    _tcslen :   Maps to either strlen or wcslen
    _tcscpy :   Maps to either strcpy or wcscpy
    _tcscmp :   Maps to either strcmp or wcscmp


COMMON.H

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/*                  Copyright Microsoft Corp. 1992-1996                 */
/*                      Remote Machine strout sample                    */
/*                                                                      */
/*  FILE    :   common.h                                                */
/*                                                                      */
/*  PURPOSE :   Definitions used in the program                         */
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
#ifndef __COMMON_STROUT__   // If this file has be included before
#define __COMMON_STROUT__   // Don't include it again

#include <windows.h>        // The CommandLineToArgvW is defined here
#include <stdio.h>
#include <stdlib.h>
#include <tchar.h>          // Included to support UNICODE/ANSI

// Return values from the program to the OS
#define EXECUTION_FAILED    -1
#define EXECUTION_OK        0

// Exception handler macro
#define DO_EXCEPTION        1       // Execute the exception block

// Macro for printing out error message and exit the program if an
// error occured
#define EXIT_IF_FAIL(x, string){\
    ((x) != RPC_S_OK)?_tprintf(TEXT("%s returned with error: %d\n"), \
    TEXT(string), (x)),exit(x):0;}

// Common type definition and defines used in the program
#define PROTOCOL_SEQUENCE   TEXT("ncacn_np")
#define END_POINT           TEXT("\\pipe\\strout_sample")
#defineIGNORE_CHAR         TEXT('=')
#define NULL_CHAR           TEXT('\0')

#endif  // ifndef __COMMON_STROUT__


CLIENT.C

/*************************************************************************
                    Copyright Microsoft Corp. 1992-1996
                        Remote Machine strout sample

  FILE      :   client.c

  USAGE     :   client  -n network_address
                        -p protocol_sequence
                        -e endpoint
                        -o options

  PURPOSE   :   Client side of the RPC distributed application strout.

  COMMENTS  :   This program shows how to call remote procedures that
                allocate memory for a two dimensional array (an array of
                character pointers). It calls the server that allocates
                memory to store all the environment strings in, and copies
                the environment variables to this memory. The client then
                displays all the data on the client machine.

                Since this program uses the implicit binding method, some
                of the binding handling must be done at the client side
*************************************************************************/

#include "strout.h"     /* Generated by the midl compiler               */
#include "common.h"     /* Definitions that are common to all files    */

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/*  Procedure   :   void Usage(_TUCHAR *)                               */
/*  Desc        :   This procedure prints out an error message if the   */
/*                  command line arguments are wrong                    */
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void Usage(_TUCHAR * pszProgramName)
{
    _tprintf(TEXT("USAGE : %s [-option]\n"), pszProgramName);
    _tprintf(TEXT("Options : -n Network Address\n"));
    _tprintf(TEXT("          -p Protocol Sequence\n"));
    _tprintf(TEXT("          -e Endpoint\n"));
    _tprintf(TEXT("          -o Options\n"));
    exit(EXECUTION_FAILED);
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/* The client main program                                              */
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int main(int argc, char *argv[])
{
    RPC_STATUS nStatus;     /* Return value from the RPC calls          */
    unsigned int
        nIdx,               /* Counter in loops                         */
        nNumArgs;           /* Number of commandline arguments          */
    unsigned long
        nNumLines = 0;      /* Number of lines in the environment block */
    str*pEnvBlock;         /* Pointer to an array of string pointers   */

    // These variables are used for the implicit binding
    _TUCHAR*pszUuid            = NULL;
    _TUCHAR*pszProtocolSequence= PROTOCOL_SEQUENCE;
    _TUCHAR*pszNetworkAddress  = NULL;
    _TUCHAR*pszEndpoint        = END_POINT;
    _TUCHAR*pszOptions         = NULL;
    _TUCHAR*pszStringBinding   = NULL;

    /* Get a common handle on the command line arguments for both       */
    /* UNICODE and ASCII                                                */
#ifdef _UNICODE
    LPWSTR*szArglist = CommandLineToArgvW(GetCommandLine(), &nNumArgs);
    if (NULL == szArglist)
    {
        _tprintf(TEXT("SERVER.C : CommandLineToArgW failed"));
        exit(EXECUTION_FAILED);
    }
#else
    char **szArglist= argv;
    nNumArgs = argc;
#endif

    /* Allow the user to override settings with commandline switches    */
    for (nIdx = 1; nIdx < nNumArgs; nIdx++)
    {
        if ((_tcscmp(szArglist[nIdx], TEXT("-n")) == 0) ||
            (_tcscmp(szArglist[nIdx], TEXT("-N")) == 0))
        {
            pszNetworkAddress = szArglist[++nIdx];
        }
        else if((_tcscmp(szArglist[nIdx], TEXT("-p")) == 0) ||
            (_tcscmp(szArglist[nIdx], TEXT("-P")) == 0))
        {
            pszProtocolSequence = szArglist[++nIdx];
        }
        else if((_tcscmp(szArglist[nIdx], TEXT("-e")) == 0) ||
                (_tcscmp(szArglist[nIdx], TEXT("-e")) == 0))
        {
            pszEndpoint = szArglist[++nIdx];
        }
        else if((_tcscmp(szArglist[nIdx], TEXT("-o")) == 0) ||
                (_tcscmp(szArglist[nIdx], TEXT("-O")) == 0))
        {
            pszOptions = szArglist[++nIdx];
        }
        else
        {
            Usage(szArglist[0]);
        }
    }

    /* Since we are using implicit binding, we need to do some binding  */
    /* from the client side as well.                                    */
    /* Use a function to concatenate the elements of the string         */
    /* binding into the proper sequence                                 */
    nStatus = RpcStringBindingCompose(
        pszUuid,
        pszProtocolSequence,
        pszNetworkAddress,
        pszEndpoint,
        pszOptions,
        &pszStringBinding);
    EXIT_IF_FAIL(nStatus, "RpcStringBindingCompose");

    /* Set the binding handle that will be used to bind to the server   */
    nStatus = RpcBindingFromStringBinding(
        pszStringBinding,
        &global_strout_sample_handle);      /* The global handle used   */
    EXIT_IF_FAIL(nStatus, "RpcBindingFromStringBinding");

    /* Initialize the pointer to NULL */
    pEnvBlock = NULL;

    RpcTryExcept                /* Catch any exception that occurs      */
    {
        /* Call the remote procedure */
        _tprintf(TEXT("Calling the remote procedure \'GetRemoteEnv\' \n"));
        GetRemoteEnv(&nNumLines, &pEnvBlock);
    }
    RpcExcept(DO_EXCEPTION)
    {
        _tprintf(TEXT("Run-time exception %08X = %d in %s\n"),
            RpcExceptionCode(), RpcExceptionCode(), TEXT(__FILE__));
        exit(EXECUTION_FAILED);
    }
    RpcEndExcept

    /* Print out the result */
    _tprintf(TEXT("There are %d environment variables\n"), nNumLines);
    _tprintf(TEXT("They are:\n"));
    for (nIdx = 0; nIdx < nNumLines; nIdx++)
    {
        _tprintf(TEXT("\t%s\n"), pEnvBlock[nIdx]);
    }

    // Deallocate all the memory used for the EnvBlock
    for (nIdx = 0; nIdx < nNumLines; nIdx++)
    {
        midl_user_free(pEnvBlock[nIdx]);
    }
    midl_user_free(pEnvBlock);

    // Deallocate the memory used for the ARGLIST if using UNICODE
#ifdef _UNICODE
    if (NULL != szArglist)
        free(szArglist);
#endif

    /* When the call is done, shut down the server application */
    ShutDown();

    return (EXECUTION_OK);
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/* Procedure    :   midl_user_allocate() and midl_user_free()           */
/* Desc.        :   These procedure are declared in the header file     */
/*                  generated by the midl compiler. These procedures    */
/*                  should be used for all memory allocation and        */
/*                  deallocation.                                       */
/*                  These procedures are also called by the stub code to*/
/*                  allocate and free memory.                           */
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void __RPC_FAR * __RPC_API midl_user_allocate(size_t nLen)
{
    return (malloc(nLen));
}

void __RPC_API midl_user_free(void __RPC_FAR * lpvPointer)
{
    if (lpvPointer != NULL)
        free (lpvPointer);
}


REMOTE.C

/*************************************************************************
                    Copyright Microsoft Corp. 1992-1996
                        Remote Machine strout sample

  FILE      :   remote.c

  PURPOSE   :   The remote procedures that will be called from the client.

  COMMENTS  :   These procedures will be linked into the server side of
                the application.

*************************************************************************/
#include "strout.h"                 //Generated by the MIDL compiler
#include "common.h"                 //Common definitions for all files

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/*  Procedure   :   void GetRemoteEnv(unsigned long, str **)            */
/*  Desc.       :   This procedure get the environment variables from   */
/*                  the server and reuturns a pointer to an array of    */
/*                  pointer to the environment strings                  */
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void GetRemoteEnv(unsigned long *nNumLines, str **psEnvironmentBlock)
{
    int nIdx = 0;       // Loop counter
    TCHAR
        *pcTemp,        // Temporary pointer to the environment block
        *pcEnv;         // Pointer to the environment block

    // Get pointer to environment block
    pcEnv = (TCHAR *) GetEnvironmentStrings();

    // First count how many lines, must know how much memory to allocate
    *nNumLines  = 0;            // Initialize number of lines to 0
    pcTemp = pcEnv;             // Set tempptr equal to envptr
    while (*pcTemp != NULL_CHAR)
    {
        // Don't count the lines that starts with IGNORE_CHAR
        if(*pcTemp != IGNORE_CHAR)
        {
            (*nNumLines)++;     // Increase the number of lines
        }

        // Increment the string pointer. Each line ends in \0, and
        // \0\0 means end of block
        pcTemp += (_tcslen(pcTemp) + 1);
    }

    // Allocate the memory needed for the line pointer
    if(NULL == (*psEnvironmentBlock = (str *)
        midl_user_allocate((*nNumLines) * sizeof(str))))
    {
        _tprintf(TEXT("REMOTE.C : Memory allocation error\n"));
        return;
    }

    // Iterate through all the environment strings, allocate memory,
    // and copy them
    while (*pcEnv != NULL_CHAR)
    {
        // Don't count the lines that starts with IGNORE_CHAR
        if(*pcEnv != IGNORE_CHAR)
        {
            // Allocate the space needed for the strings
            (*psEnvironmentBlock)[nIdx] = (str) midl_user_allocate(
                sizeof(TCHAR) * (_tcslen(pcEnv) + 1));

            // Copy the environment string to the allocated memory
            _tcscpy((*psEnvironmentBlock)[nIdx++], pcEnv);
        }

        // Increment the string pointer. Each line ends in \0, and
        // \0\0 means end of block
        pcEnv += (_tcslen(pcEnv) + 1);
    }
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/* Procedure    :   void ShutDown(void);                                */
/* Desc.        :   This procedure send a message to the server that it */
/*                  canstop listening for remote procedure calls       */
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void ShutDown(void)
{
    RPC_STATUS nStatus;

    // Tell the server to stop listening for remote procedure calls
    _tprintf(TEXT("Shutting down the server\n"));
    nStatus = RpcMgmtStopServerListening(NULL);
    EXIT_IF_FAIL(nStatus, "RpcMgmtStopServerListening");
}


SERVER.C

/*************************************************************************
                    Copyright Microsoft Corp. 1992-1996
                        Remote Machine strout sample

  FILE      :   server.c

  USAGE     :   server  -p protocol_sequence
                        -e endpoint

  PURPOSE   :   Server side of the RPC distributed application strout.

  COMMENTS  :   This application uses the implicit binding method.

*************************************************************************/
#include "strout.h"                 /* Generated by the midl compiler   */
#include "common.h"                 /* Common definitions in this file  */

/* Local Procedures */
void CleanUpServer(void);

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/*  Procedure   :   void Usage(_TUCHAR *)                               */
/*  Desc        :   This procedure prints out an error message if the   */
/*                  command line arguments are wrong                    */
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void Usage(_TUCHAR * pszProgramName)
{
    _tprintf(TEXT("USAGE : %s [-option]\n"), pszProgramName);
    _tprintf(TEXT("Options : -p Protocol Sequence\n"));
    _tprintf(TEXT("          -e Endpoint\n"));
    exit(EXECUTION_OK);
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/* The server main program                                              */
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int main(int argc, char *argv[])
{
    int             nNumArgs,   /* The number of command line arguments */
                    nIdx;       /* Counter in loops                     */
    RPC_STATUS      nStatus;    /* Error status returned                */

    // Variabels used for selecting the protocol and the endpoint
    _TUCHAR *pszProtocolSequence    = PROTOCOL_SEQUENCE;
    _TUCHAR *pszEndpoint            = END_POINT;
    _TUCHAR *pszSecurity            = NULL;

/* Get a common handle on the command line arguments for both UNICODE   */
/* and ASCII                                                            */
#ifdef _UNICODE
    LPWSTR*pArglist = CommandLineToArgvW(GetCommandLine(), &nNumArgs);
    if (NULL == pArglist)
    {
        _tprintf(TEXT("SERVER.C : CommandLineToArgW failed"));
        exit(EXECUTION_FAILED);
    }
#else
    char **pArglist = argv;
    nNumArgs = argc;
#endif

    /* Allow the user to override settings with commandline switches    */
    for (nIdx = 1; nIdx < nNumArgs; nIdx++)
    {
        if((_tcscmp(pArglist[nIdx], TEXT("-p")) == 0) ||
           (_tcscmp(pArglist[nIdx], TEXT("-P")) == 0))
        {
            pszProtocolSequence = pArglist[++nIdx];
        }
        else if((_tcscmp(pArglist[nIdx], TEXT("-e")) == 0) ||
                (_tcscmp(pArglist[nIdx], TEXT("-e")) == 0))
        {
            pszEndpoint = pArglist[++nIdx];
        }
        else
        {
            Usage(pArglist[0]);
        }
    }

    /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
    /* Register the interface with the RPC run-time library             */
    /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
    _tprintf(TEXT("Registering the interface\n"));
    nStatus = RpcServerRegisterIf(
        strout_sample_v1_0_s_ifspec,    // Interface specification
        NULL,                           // UUID to ass. with MgrEnv arg.
        NULL);                          // Managers entry point vector.
    EXIT_IF_FAIL(nStatus, "RpcServerRegisterIf");

    /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
    /* Select Protocal sequence : This sample uses namedpipes as the    */
    /* default protocol. The RpcServerUseProtseqEp function tells the   */
    /* RPC run-time library to use the specified protocol sequence      */
    /* combined with the specified endpoint for receiving remote        */
    /* procedure calls                                                  */
    /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
    _tprintf(TEXT("Selecting the protocol sequence: \"%s\"\n"),
        pszProtocolSequence);
    nStatus = RpcServerUseProtseqEp(
        pszProtocolSequence,            // String with the protocol in
        RPC_C_PROTSEQ_MAX_REQS_DEFAULT, // Max number of calls
        pszEndpoint,                    // Endpoint addres information
        pszSecurity);                   // Security
    EXIT_IF_FAIL(nStatus, "RpcServerUseProtseqsEp");

    /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
    /* Now start listening for remote procedure calls from the client   */
    /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
    RpcTryExcept
    {
        _tprintf(TEXT("Listening for remote calls...\n"));
        nStatus = RpcServerListen(
            1,                              // The minimum number of calls
            RPC_C_LISTEN_MAX_CALLS_DEFAULT, // The maximum number of calls
            FALSE);                         // Cont. until expl. stopped
        EXIT_IF_FAIL(nStatus, "RpcServerListen");
    }
    RpcExcept(DO_EXCEPTION)
    {
        // Print out the exception code
        _tprintf(TEXT("Run-time exception %u in %s at line %d\n"),
                RpcExceptionCode(), TEXT(__FILE__), __LINE__);
        exit(EXECUTION_FAILED);
    }
    RpcEndExcept

    // If no exceptions occured, clean up the server and exit
    CleanUpServer();

    // Deallocate the memory used for the ARGLIST if using UNICODE
#ifdef _UNICODE
    if (NULL != pArglist)
        free(pArglist);
#endif

    return (EXECUTION_OK);
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/* Procedure    : void CleanUpServer(RPC_BINDING_VECTOR);               */
/* Desc.        : This procedure will unregister the interface          */
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void CleanUpServer(void)
{
    RPC_STATUS nStatus;          // Error status returned from RPC calls

    /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
    /* Unregister the interface from the RPC run-time library           */
    /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
    _tprintf(TEXT("Unregistering the Interface"));
    nStatus = RpcServerUnregisterIf(
        NULL, NULL, // Prevents server from receiving new remote calls
        FALSE);     // Wait until all the active calls are complete
    EXIT_IF_FAIL(nStatus, "RpcServerUnRegisterIf");
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/* Procedure    :   midl_user_allocate() and midl_user_free()           */
/* Desc.        :   These procedure are declared in the header file     */
/*                  generated by the midl compiler. These procedures    */
/*                  should be used for all memory allocation and        */
/*                  deallocation.                                       */
/*                  These procedures are also called by the stub code   */
/*                  to allocate and free memory.                        */
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void __RPC_FAR * __RPC_API midl_user_allocate(size_t nLen)
{
    return (malloc(nLen));
}

void __RPC_API midl_user_free(void __RPC_FAR * lpvPointer)
{
    if(NULL != lpvPointer)
        free (lpvPointer);
}


STROUT.ACF

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/*                  Copyright Microsoft Corp. 1992 - 1996               */
/*                      Remote Machine strout sample                    */
/*                                                                      */
/*  FILE    :   strout.acf                                              */
/*                                                                      */
/*  PURPOSE :   This file tells the midl compiler that we want to use   */
/*              the implicit binding method for the remote procedure    */
/*              calls                                                   */
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
[implicit_handle (handle_t global_strout_sample_handle)]
interface strout_sample
{
}


STROUT.IDL

/*************************************************************************
            Copyright Microsoft Corp. 1992 - 1996
                Remote Machine strout sample

    FILE    :   strout.idl

    PURPOSE :   This file defines the interface for the strout example

*************************************************************************/
[
uuid(cab31280-b996-11cf-9548-00aa00374e69), // UUID
pointer_default(unique),                    // Pointer default
version(1.0)                                // Program version
]
interface strout_sample
{

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/* Define a type for the second dimension variables that should be      */
/* returned from the server. This should also work for UNICODE          */
/* characters                                                           */
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
#if defined _UNICODE
    typedef [string, unique] wchar_t * str; // String of wide chars
#else
    typedef [string, unique] char * str;    // String of ASCII chars
#endif

    /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
    /* Declaration of the remote procedure.                             */
    /* This procedure will get the environment strings from the server  */
    /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
    void GetRemoteEnv(
        // Number of lines in the envirionment block
        [in, out] unsigned long   * Lines,
        // Pointer to an array of pointers to the lines in the env. block.
        // MUST have comma before *Lines in size_of  to set the size of
        // the right dimension, which is the size of the array of pointers
        [out, size_is(,*Lines)] str **EnvBlock);

    // Procedure to shut down the server
    void ShutDown(void);
}


README.TXT

WHELLO

The WHELLO program is a client/server implementation of "Hello, World". The
client and server applications can run on the same Windwos NT workstation.

FILES
=====

The directory samples\rpc\whello contains the following files for building
the sample distributed application WHELLO:

File          Description

README.TXT    Readme file for the WHELLO sample
WHELLO.IDL    Interface definition language file
WHELLO.ACF    Attribute configuration file
WHELLOC.C     Client main program
WHELLO.RC     Client resource file
WHELLO.DLG    Client dialog box definitions
WHELLO.DEF    Client module definition file
WHELLOS.C     Server main program
WHELLOP.C     Remote procedures
MAKEFILE      Nmake file for Windows NT or Windows 95
MAKEFILE.WIN  Nmake file for Win 3.x

-------------------------------------------
BUILDING CLIENT AND SERVER APPLICATIONS FOR
MICROSOFT WINDOWS NT OR WINDOWS 95:
-------------------------------------------

The following environment variables should be set for you already.

  set CPU=i386
  set INCLUDE=%SDKROOT%\h
  set LIB=%SDKROOT%\lib
  set PATH=%SDKROOT%\system32;%SDKROOT%\bin;

Where %SDKROOT% is the root directory for the 32-bit Windows SDK.

For mips, set CPU=mips
For alpha, set CPU=alpha

Build the sample server application:

  nmake cleanall
  nmake

These commands build the executable program whellos.exe
and whelloc.exe for Microsoft Windows NT or Windows 95.

--------------------------------------------
BUILDING THE CLIENT APPLICATION FOR WIN 3.X
--------------------------------------------

After installing the Microsoft Visual C/C++ version 1.50 development
environment and the 16-bit RPC SDK on a Windows NT or Windows 95
computer, you can build the sample client application from Windows NT
or Windows 95.

  nmake -f makefile.win cleanall
  nmake -f makefile.win

This builds the client application whelloc.exe.

You may also execute the Microsoft Visual C/C++ compiler under MS-DOS.
This requires a two step build process.

  Step One: Compile the .IDL files under Windows NT or Windows 95
     nmake -a -f makefile.win whello.h

  Step Two: Compile the C sources (stub and application) under MS-DOS.
     nmake -f makefile.win

------------------------------------------
RUNNING THE CLIENT AND SERVER APPLICATIONS
------------------------------------------

On the server, enter

  whellos

On the client, enter

  net start workstation
  whelloc

Note:  The client and server applications can run on
the same Microsoft Windows NT or Windows 95 computer when you use
different screen groups.  If you run the client on the
Microsoft MS-DOS and Windows computer, choose the Run
command from the File menu in the Microsoft Windows 3.x
Program Manager and enter whelloc.exe.

Several command line switches are available to change
settings for the server application. For a listing of
available switches, enter

  whellos -?

---------------------
RUNNING ON WINDOWS 95
---------------------

If you wish to run the server on Windows 95, you will need to use
the LRPC protocol.

To start the server, enter

 whellos -p ncalrpc

To start the client (from the same machine), enter

 whelloc

Change the protocol to "ncalrpc" by selecting "Protocol sequence name"
from the "Remote Call" menu and entering

 ncalrpc


WHELLO.ACF

[implicit_handle(handle_t hWHello)]
interface whello
{

}


WHELLO.IDL

[ uuid(2F5F6520-CA46-1067-B319-00DD010662DA),
  version(1.0),
  pointer_default(unique)
]
interface whello
{

void HelloProc([in, string] unsigned char * pszString);

void Shutdown(void);

}


WHELLOC.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                        whello Example

    FILE:       whelloc.c

    PURPOSE:    RPC sample Windows client
                Based on Win 3.x SDK Generic template for Windows applications

    FUNCTIONS:  WinMain()         - call initialization function,
                                    process message loop
                InitApplication() - initializ window data and register window
                InitInstance()    - save instance handle and create main window
                MainWndProc()     - process messages

                About()    - process messages for "About" dialog box
                Server()   - process messages for "Server" dialog box
                Endpoint() - process messages for "Endpoint" dialog box
                Send()     - process messages for "Send" dialog box;
                             sends on OK

                midl_user_allocate() - memory allocation function needed by RPC
                midl_user_free()     - memory free function needed by RPC

    COMMENTS:   Windows version of the "Hello, world" example.

                Windows can have several copies of your application
                running at the same time.  The variable hInst keeps
                track of which instance the application is so that
                processing will be to the correct window.

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

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <windowsx.h>       // select between win16 or win32
#include <string.h>
#include "whello.h"         // header file generated by MIDL compiler
#include "whelloc.h"        // client-specific header file

/* global data */

unsigned char   pszProtocolSequence[MAXPROTSEQ];
unsigned char   pszNetworkAddress[UNCLEN+1];
unsigned char   pszEndpoint[PATHLEN+1];
unsigned char * pszUuid            = NULL;
unsigned char * pszOptions         = NULL;
unsigned char * pszStringBinding   = NULL;
unsigned char   pszString[MSGLEN];

int fBound = FALSE;     // flag indicates whether client bound to server

HANDLE hInst;           // current instance
HCURSOR hHourGlass;     // during calls to RPC API functions

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

    FUNCTION: WinMain(HINSTANCE, HINSTANCE, LPSTR, 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,     // current instance
                   HINSTANCE hPrevInstance, // previous instance
                   LPSTR     lpCmdLine,     // command line
                   int       nCmdShow)      // show-window type (open/icon)
{
    MSG msg;

    UNREFERENCED_PARAMETER(lpCmdLine);

    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
                      (HWND)NULL,  // 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
    }

    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(HANDLE hInstance)    // current instance
{
    WNDCLASS  wc;

    /* Fill in window class structure with parameters that */
    /* describe the main window.                           */
    wc.style = 0;
    wc.lpfnWndProc = (WNDPROC)MainWndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(hInstance, "HelloIcon");
    wc.hCursor = LoadCursor(0, IDC_ARROW);
    wc.hbrBackground = GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName =  "GenericMenu";
    wc.lpszClassName = "GenericWClass";

    /* 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(HANDLE hInstance,    // Current instance identifier.
                  int    nCmdShow)     // Param for first ShowWindow() call.
{
    HWND hWnd;    // Main window handle.

    /* Save the instance handle in static variable, which will be used in  */
    /* many subsequence calls from this application to Windows.            */
    hInst = hInstance;
    hHourGlass = LoadCursor(0, IDC_WAIT);

    /* Create a main window for this application instance.  */
    hWnd = CreateWindow("GenericWClass",           // See RegisterClass() call.
                        "RPC Sample Application",  // Text for window title bar.
                        WS_OVERLAPPEDWINDOW,       // Window style.
                        CW_USEDEFAULT,    // Default horizontal position.
                        CW_USEDEFAULT,    // Default vertical position.
                        CW_USEDEFAULT,    // Default width.
                        CW_USEDEFAULT,    // Default height.
                        (HWND) NULL,      // Overlapped windows have no parent.
                        (HMENU) NULL,     // Use the window class menu.
                        hInstance,        // This instance owns this window.
                        (LPVOID) NULL     // Pointer not needed.
                        );

    /* If window could not be created, return "failure" */
    if (!hWnd)
        return(FALSE);

    /* Initialize RPC binding data */
    strcpy(pszProtocolSequence, DEFAULT_PROT_SEQ);
    strcpy(pszEndpoint, DEFAULT_ENDPOINT);
    pszNetworkAddress[0] = '\0';
    strcpy(pszString, DEFAULT_MESSAGE);

    /* Make the window visible; update its client area; and return "success" */
    ShowWindow(hWnd, nCmdShow);  // Show the window
    UpdateWindow(hWnd);          // Send WM_PAINT message

    return(TRUE);                // Return the value from PostQuitMessage
}

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

    FUNCTION: MainWndProc(HWND, UINT, WPARAM, LPARAM)

    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 generic.rc file and turn control
              over to the About() function.  When it returns, free the
              intance address.

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

long APIENTRY MainWndProc(HWND   hWnd,       // window handle
                          UINT   message,    // type of message
                          WPARAM wParam,     // additional information
                          LPARAM lParam)     // additional information
{
    DLGPROC lpProc;     // pointer to the dialog box function

    switch (message) {

    case WM_CREATE:

#ifdef WIN16
        RpcWinSetYieldInfo (hWnd, FALSE, 0, 0L); // To make TCP/IP happy
#else
        PostMessage(hWnd, WM_COMMAND, IDM_BIND, 0L);    // bind to server
#endif
        break;

    case WM_COMMAND:    // message: command from application menu
        switch (wParam) {

        case IDM_BIND:
            if (Bind(hWnd) != RPC_S_OK)
                PostMessage(hWnd, WM_DESTROY, 0, 0L);
            break;

        case IDM_ABOUT:
            lpProc = MakeProcInstance(About, hInst);
            DialogBox(hInst,        // current instance
                      "AboutBox",   // resource to use
                      hWnd,         // parent handle
                      lpProc);      // About() instance address
            FreeProcInstance(lpProc);
            break;

        case IDM_PROTSEQ:
            lpProc = MakeProcInstance(Protseq, hInst);
            DialogBox(hInst,        // current instance
                      "ProtseqBox", // resource to use
                      hWnd,         // parent handle
                      lpProc);      // Server instance address
            FreeProcInstance(lpProc);
            break;

        case IDM_SERVER:
            lpProc = MakeProcInstance(Server, hInst);
            DialogBox(hInst,        // current instance
                      "ServerBox",  // resource to use
                      hWnd,         // parent handle
                      lpProc);      // Server instance address
            FreeProcInstance(lpProc);
            break;

        case IDM_ENDPOINT:
            lpProc = MakeProcInstance(Endpoint, hInst);
            DialogBox(hInst,        // current instance
                      "EndpointBox",// resource to use
                      hWnd,         // parent handle
                      lpProc);      // Server instance address
            FreeProcInstance(lpProc);
            break;

        case IDM_SEND:
            lpProc = MakeProcInstance(Send, hInst);
            DialogBox(hInst,        // current instance
                      "SendBox",    // resource to use
                      hWnd,         // parent handle
                      lpProc);      // Server instance address
            FreeProcInstance(lpProc);
            break;

        case IDM_EXIT:
            DestroyWindow(hWnd);
            if (fBound == TRUE) {
                RpcTryExcept {
                    Shutdown();     // shut down the server
                }
                RpcExcept(1) {
                    MessageBox(hWnd,
                               EXCEPT_MSG,
                               "Remote Procedure Call",
                               MB_ICONINFORMATION);
                }
                RpcEndExcept
            }
            break;

        default:        // Let Windows process it
            return(DefWindowProc(hWnd, message, wParam, lParam));

        }
        break;

    case WM_DESTROY:    // message: window being destroyed
        PostQuitMessage(0);
        break;

    default:            // Passes it on if unprocessed
        return(DefWindowProc(hWnd, message, wParam, lParam));

    }

    return(0);
}

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

    FUNCTION: Protseq(HWND, unsigned, WORD, LONG)

    PURPOSE:  Processes messages for "Protseq" 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 Protseq(HWND hDlg,       // window handle of the dialog box
                     UINT message,     // type of message
                     UINT wParam,      // message-specific information
                     LONG lParam)
{

    UNREFERENCED_PARAMETER(lParam);

    switch (message) {

    case WM_INITDIALOG:    // message: initialize dialog box
        SetDlgItemText((HANDLE)hDlg, IDD_PROTSEQNAME, pszProtocolSequence);
        return(TRUE);

    case WM_COMMAND:       // message: received a command
        switch(wParam) {

        case IDCANCEL:     // System menu close command?
            EndDialog(hDlg, FALSE);
            return(TRUE);

        case IDOK:         // "OK" box selected?
            GetDlgItemText(hDlg, IDD_PROTSEQNAME, pszProtocolSequence, MAXPROTSEQ);

            if (Bind(hDlg) != RPC_S_OK) {
                EndDialog(hDlg, FALSE);
                return(FALSE);
            }

            EndDialog(hDlg, TRUE);
            return(TRUE);

        }

    }

    return(FALSE);  // Didn't process a message
}

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

    FUNCTION: Server(HWND, unsigned, WORD, LONG)

    PURPOSE:  Processes messages for "Server" 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 Server(HWND hDlg,        // window handle of the dialog box
                     UINT message,     // type of message
                     UINT wParam,      // message-specific information
                     LONG lParam)
{
    HCURSOR hOld;

    UNREFERENCED_PARAMETER(lParam);

    switch (message) {

    case WM_INITDIALOG:    // message: initialize dialog box
        SetDlgItemText((HANDLE)hDlg, IDD_SERVERNAME, pszNetworkAddress);
        return(TRUE);

    case WM_COMMAND:       // message: received a command
        switch(wParam) {

        case IDCANCEL:     // System menu close command?
            EndDialog(hDlg, FALSE);
            return(TRUE);

        case IDOK:         // "OK" box selected?
            GetDlgItemText(hDlg, IDD_SERVERNAME, pszNetworkAddress, UNCLEN);

            hOld = SetCursor(hHourGlass);
            if (Bind(hDlg) != RPC_S_OK) {
                EndDialog(hDlg, FALSE);
                return(FALSE);
            }

            SetCursor(hOld);
            EndDialog(hDlg, TRUE);
            return(TRUE);

        }

    }

    return(FALSE);  // Didn't process a message
}

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

    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(HWND hDlg,         // window handle of the dialog box
                    UINT message,      // type of message
                    UINT wParam,       // message-specific information
                    LONG lParam)
{
    UNREFERENCED_PARAMETER(lParam);

    switch (message) {

    case WM_INITDIALOG:    // message: initialize dialog box
        return(TRUE);

    case WM_COMMAND:       // message: received a command
        if (wParam == IDOK || wParam == IDCANCEL) {
           EndDialog(hDlg, TRUE);
           return(TRUE);
        }
        break;

    }

    return(FALSE);  // Didn't process a message
}

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

    FUNCTION: Endpoint(HWND, unsigned, WORD, LONG)

    PURPOSE:  Processes messages for "Endpoint" 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 Endpoint(HWND hDlg,        // window handle of the dialog box
                       UINT message,     // type of message
                       UINT wParam,      // message-specific information
                       LONG lParam)
{
    HCURSOR hOld;

    UNREFERENCED_PARAMETER(lParam);

    switch (message) {

    case WM_INITDIALOG:    // message: initialize dialog box
        SetDlgItemText(hDlg, IDD_ENDPOINTNAME, pszEndpoint);
        return(TRUE);

    case WM_COMMAND:       // message: received a command
        switch(wParam) {

        case IDCANCEL:
            EndDialog(hDlg, FALSE);
            return(TRUE);

        case IDOK:
            GetDlgItemText(hDlg, IDD_ENDPOINTNAME, pszEndpoint, PATHLEN);

            hOld = SetCursor(hHourGlass);
            if (Bind(hDlg) != RPC_S_OK) {
                EndDialog(hDlg, FALSE);
                return(FALSE);
            }

            SetCursor(hOld);
            EndDialog(hDlg, TRUE);
            return(TRUE);

        }

    }

    return(FALSE);  // Didn't process a message
}

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

    FUNCTION: Send(HWND, unsigned, WORD, LONG)

    PURPOSE:  Processes messages for "Send" 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 Send(HWND hDlg,        // window handle of the dialog box
                   UINT message,     // type of message
                   UINT wParam,      // message-specific information
                   LONG lParam)
{
    UNREFERENCED_PARAMETER(lParam);

    switch (message) {

    case WM_INITDIALOG:    // message: initialize dialog box
        SetDlgItemText(hDlg, IDD_MESSAGE, pszString);
        return(TRUE);

    case WM_COMMAND:       // message: received a command
        switch(wParam) {

        case IDCANCEL:
            EndDialog(hDlg, FALSE);
            return(TRUE);

        case IDOK:
            GetDlgItemText(hDlg, IDD_MESSAGE, pszString, MSGLEN);

            RpcTryExcept {
                HelloProc(pszString);  // make call with user message
            }
            RpcExcept(1) {
                char pszFail[MSGLEN];

                sprintf(pszFail, "%s (0x%x)\n", EXCEPT_MSG, RpcExceptionCode());
                MessageBox(hDlg,
                           pszFail,
                           "Remote Procedure Call",
                           MB_ICONINFORMATION);
            }
            RpcEndExcept

            EndDialog(hDlg, TRUE);
            return(TRUE);

        }

    }

    return(FALSE);  // Didn't process a message
}

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

    FUNCTION: midl_user_allocate(size_t)

    PURPOSE:  Allocate memory as needed by the RPC runtime library

    COMMENTS: The stubs or runtime libraries may need to allocate memory.
              By convention, they call a user-specified function named
              midl_user_allocate.  In this application, no memory
              management is needed, so a dummy function is provided.

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

void __RPC_FAR * __RPC_API midl_user_allocate(size_t len)
{
    UNREFERENCED_PARAMETER(len);
    return(NULL);  // no memory management required
}

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

    FUNCTION: midl_user_free(void *)

    PURPOSE:  Free memory as needed by the RPC runtime library

    COMMENTS: The stubs or runtime libraries may need to free memory.
              By convention, they call a user-specified function named
              midl_user_free.  In this application, no memory allocation
              is needed so a dummy function is provided.

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

void __RPC_API midl_user_free(void __RPC_FAR * ptr)
{
    UNREFERENCED_PARAMETER(ptr);
    return;    // no memory management required
}

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

    FUNCTION: Bind(HWND)

    PURPOSE:  Make RPC API calls to bind to the server application

    COMMENTS: The binding calls are made from InitInstance() and
              whenever the user changes the server name or endpoint.
              If the bind operation is successful, the global flag
              fBound is set to TRUE.

              The global flag fBound is used to determine whether to
              call the RPC API function RpcBindingFree.

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

RPC_STATUS Bind(HWND hWnd)
{
    RPC_STATUS status;
    char pszFail[MSGLEN];

    if (fBound == TRUE) {  // unbind only if bound
        status = RpcStringFree(&pszStringBinding);
        if (status) {
            MessageBox(hWnd, "RpcStringFree failed", "RPC Error", MB_ICONSTOP);
            return(status);
        }

        status = RpcBindingFree(&hWHello);
        if (status) {
            MessageBox(hWnd, "RpcBindingFree failed", "RPC Error", MB_ICONSTOP);
            return(status);
        }

        fBound = FALSE;  // unbind successful; reset flag
    }

    status = RpcStringBindingCompose(pszUuid,
                                     pszProtocolSequence,
                                     pszNetworkAddress,
                                     pszEndpoint,
                                     pszOptions,
                                     &pszStringBinding);
    if (status) {
        sprintf(pszFail, "RpcStringBindingCompose failed: (0x%x)\nNetwork Address = %s\n",
                status, pszNetworkAddress);
        MessageBox(hWnd,
                   pszFail,
                   "RPC Runtime Error",
                   MB_ICONEXCLAMATION);
        return(status);
    }

    status = RpcBindingFromStringBinding(pszStringBinding,
                                         &hWHello);
    if (status) {
        sprintf(pszFail, "RpcBindingFromStringBinding failed: (0x%x)\nString = %s\n",
                status, pszStringBinding);
        MessageBox(hWnd,
                   pszFail,
                   "RPC Runtime Error",
                   MB_ICONEXCLAMATION);
        return(status);
    }

    fBound = TRUE;  // bind successful; reset flag

    return(status);
}

/**** end whelloc.c ****/


WHELLOC.H

#ifdef WIN16
#define APIENTRY         FAR PASCAL
#define UNREFERENCED_PARAMETER
#endif

#define IDM_ABOUT         100
#define IDM_PROTSEQ       200
#define IDM_SERVER        300
#define IDM_EXIT          400
#define IDM_ENDPOINT      500
#define IDM_SEND          600
#define IDM_BIND          700

#define IDD_PROTSEQNAME   201
#define IDD_SERVERNAME    301
#define IDD_ENDPOINTNAME  401
#define IDD_MESSAGE       501

#define CNLEN              25    /* computer name length */
#define UNCLEN        CNLEN+2    /* \\computername       */
#define PATHLEN           260    /* Path                 */
#define MSGLEN            300    /* arbitrary large number for message size */
#define MAXPROTSEQ         20    /* protocol sequence "ncacn_np" */

#define DEFAULT_ENDPOINT    "\\pipe\\whello"
#define DEFAULT_PROT_SEQ    "ncacn_np"
#define DEFAULT_MESSAGE     "hello, world"

#define EXCEPT_MSG "The remote procedure call failed. \
Please make sure the server application is running and \
that the server name and endpoint name are correct."

int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int);
BOOL InitApplication(HANDLE);
BOOL InitInstance(HANDLE, int);

LONG APIENTRY MainWndProc(HWND, UINT, UINT, LONG);
BOOL APIENTRY About(HWND, UINT, UINT, LONG);
BOOL APIENTRY Protseq(HWND, UINT, UINT, LONG);
BOOL APIENTRY Server(HWND, UINT, UINT, LONG);
BOOL APIENTRY Endpoint(HWND, UINT, UINT, LONG);
BOOL APIENTRY Send(HWND, UINT, UINT, LONG);
RPC_STATUS Bind(HWND);


WHELLOP.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                         whello Example

    FILE:       whellop.c

    PURPOSE:    Remote procedures that are linked with the server
                side of RPC distributed application

    FUNCTIONS:  HelloProc() - prints "hello, world" or other string
                sent by client to server

    COMMENTS:   Windows version of the "Hello, world" example.

                Windows can have several copies of your application
                running at the same time.  The variable hInst keeps
                track of which instance the application is so that
                processing will be to the correct window.

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

#include <stdlib.h>
#include <stdio.h>
#include "whello.h"    // header file generated by MIDL compiler

void HelloProc(unsigned char * pszString)
{
    printf("%s\n", pszString);
}

void Shutdown(void)
{
    RPC_STATUS status;

    printf("Calling RpcMgmtStopServerListening\n");
    status = RpcMgmtStopServerListening(NULL);
    printf("RpcMgmtStopServerListening returned: 0x%x\n", status);
    if (status) {
        exit(status);
    }

    printf("Calling RpcServerUnregisterIf\n");
    status = RpcServerUnregisterIf(NULL, NULL, FALSE);
    printf("RpcServerUnregisterIf returned 0x%x\n", status);
    if (status) {
        exit(status);
    }
}

/* end of file whellop.c */


WHELLOS.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                          whello Example

     FILE:      whellos.c

     USAGE:     whellos  -p protocol_sequence
                         -e endpoint
                         -m max calls
                         -n min calls
                         -f flag for RpcServerListen

     PURPOSE:    Server side of RPC distributed application whello

     FUNCTIONS:  main() - registers server as RPC server

     COMMENTS:   Windows version of the "Hello, World" example.

                 Windows can have several copies of your application
                 running at the same time.  The variable hInst keeps
                 track of which instance the application is so that
                 processing will be to the correct window.

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

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include "whello.h"    // header file generated by MIDL compiler

#define PURPOSE \
"This Microsoft RPC Version 2.0 sample program demonstrates\n\
the use of the [string] attribute. For more information\n\
about the attributes and the RPC API functions, see the\n\
RPC programming guide and reference.\n\n"

void Usage(char * pszProgramName)
{
    fprintf(stderr, "%s", PURPOSE);
    fprintf(stderr, "Usage:  %s\n", pszProgramName);
    fprintf(stderr, " -p protocol_sequence\n");
    fprintf(stderr, " -e endpoint\n");
    fprintf(stderr, " -m maxcalls\n");
    fprintf(stderr, " -n mincalls\n");
    fprintf(stderr, " -f flag_wait_op\n");
    exit(1);
}

void _CRTAPI1 main(int argc, char * argv[])
{
    RPC_STATUS status;
    unsigned char * pszProtocolSequence = "ncacn_np";
    unsigned char * pszSecurity         = NULL;
    unsigned char * pszEndpoint         = "\\pipe\\whello";
    unsigned int    cMinCalls           = 1;
    unsigned int    cMaxCalls           = 20;
    unsigned int    fDontWait           = FALSE;
    int i;

    /* allow the user to override settings with command line switches */
    for (i = 1; i < argc; i++) {
        if ((*argv[i] == '-') || (*argv[i] == '/')) {
            switch (tolower(*(argv[i]+1))) {
            case 'p':  // protocol sequence
                pszProtocolSequence = argv[++i];
                break;
            case 'e':
                pszEndpoint = argv[++i];
                break;
            case 'm':
                cMaxCalls = (unsigned int) atoi(argv[++i]);
                break;
            case 'n':
                cMinCalls = (unsigned int) atoi(argv[++i]);
                break;
            case 'f':
                fDontWait = (unsigned int) atoi(argv[++i]);
                break;
            case 'h':
            case '?':
            default:
                Usage(argv[0]);
            }
        }
        else
            Usage(argv[0]);
    }

    status = RpcServerUseProtseqEp(pszProtocolSequence,
                                   cMaxCalls,
                                   pszEndpoint,
                                   pszSecurity);  // Security descriptor
    printf("RpcServerUseProtseqEp returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    status = RpcServerRegisterIf(whello_ServerIfHandle, // interface to register
                                 NULL,   // MgrTypeUuid
                                 NULL);  // MgrEpv; null means use default
    printf("RpcServerRegisterIf returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    printf("Calling RpcServerListen\n");
    status = RpcServerListen(cMinCalls,
                             cMaxCalls,
                             fDontWait);
    printf("RpcServerListen returned: 0x%x\n", status);
    if (status) {
        exit(status);
    }

    if (fDontWait) {
        printf("Calling RpcMgmtWaitServerListen\n");
        status = RpcMgmtWaitServerListen();  //  wait operation
        printf("RpcMgmtWaitServerListen returned: 0x%x\n", status);
        if (status) {
            exit(status);
        }
    }

}  // end main()

/*********************************************************************/
/*                 MIDL allocate and free                            */
/*********************************************************************/

void __RPC_FAR * __RPC_API midl_user_allocate(size_t len)
{
    return(malloc(len));
}

void __RPC_API midl_user_free(void __RPC_FAR * ptr)
{
    free(ptr);
}

/* end whellos.c */


README.TXT

WINTYP

The WINTYP sample demonstrates how to write a program that uses RPC calls to
transfer windows datatypes between the client and the server.
The client reads in a bitmap and when the user selects to modify the image,
the bitmap is sent to the server, where the server is doing the modification.
The bitmap is then sent back to the client, where the new bitmap is displayed.

There are two choices for manipulating the images, either to flip it, or to
find the edges in it. The find edge procedure will only work when the
screen resolution is set to 256 colors. The reason for this is that the
algorithm used in the remote procedures only works with 8 bits per pixel.

This program also allows the user to compile either for UNICODE or ANSI.
See section below for guidelines on how to compile for UNICODE

FILES
=====

The directory SAMPLES\RPC\WINTYP contains the following files for
building the sample distributed application WINTYP:

File          Description
-------------------------

README.TXT      Readme file for the pipe sample
WINTYP.IDL      Interface definition language file
WINTYP.ACF      Attribute configuration file
CLIENT.C        Client main program
CLIENT.H        Header file specific for the client side
CLIENT.RC       Resource file for the client application
CLIRES.H        Resource header file
SERVER.C        Server main program
COMMON.H        Common header file for all the files
REMOTE.C        Remote procedures
MAKEFILE        nmake file to build 32-bit client and server app.
                for ANSI characters.
MAKEFILE.UNI    nmake file to build 32-bit client and server app.
                for UNICODE characters.

NMAKE builds the executable programs CLIENT.EXE (client) and
SERVER.EXE (server).

Note: The client and server applications can run on the same
Microsoft Windows NT computer when you use different screen groups.

COMPILING FOR UNICODE:
======================

type nmake /f makefile.uni at the command line. This will cause
the compiler to use the file MAKEFILE.UNI instead of the MAKEFILE.

The reason behind the use of TEXT, TCHAR, _TUCHAR, _tprintf, _tcscpy,
_tcscmp, and _stprintf is that these macros expand to either
regular one byte ANSI functions or to UNICODE (Wide characters) functions
when they are compiled
    TEXT    :   This macro will put an L in front of the string if we are
                compiling for UNICODE
    TCHAR   :   Maps to either char or wchar_t
    _TUCHAR :   Maps to either unsigned char or wchar_t
    _tprintf:   Maps to either printf or wsprintf
    _stprintf:  Maps to either sprintf or swprintf
    _tcscpy :   Maps to either strcpy or wcscpy
    _tcscmp :   Maps to either strcmp or wcscmp


CLIENT.H

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//                  Copyright Microsoft Corp. 1992-1996
//                      Remote Machine WinTyp sample
//
// FILE     :   client.h
//
// PURPOSE  :   Definitions and function prototypes used on client side
//
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

//------------------------------------------------------------------------
//      Global variables used on the client side
//------------------------------------------------------------------------
HWND        g_hWnd;                 // Handle to the window
HINSTANCE   g_hInstance;            // Handle to the instance
LPTSTR      g_szClassName =         // The class name on client side
        TEXT("ClientClassName");
LPTSTR      g_szWindowCaption =     // The caption shown in the window
        TEXT("WinTyp RPC sample program -- Client side");
BITMAP      g_Bitmap;               // A bitmap structure
HBITMAP     g_hBitmap;              // Handle to the bitmap stucture
HPALETTE    g_hPalette;             // A handle to the palette used
BOOL        BITMAP_READ = FALSE;    // Indicates if bitmap is read
BOOL        EDGE_FOUND = FALSE;     // Indicates if edges is found
BOOL        g_bBound = FALSE;       // Indicates is binding has been done
TCHAR       g_szFileName[100];      // Name of file with bitmap
UINT        g_BitsPerPixel;         // Number of bits per pixel

//------------------------------------------------------------------------
//      Function prototyping
//------------------------------------------------------------------------
int WINAPI          WinMain (HINSTANCE, HINSTANCE, LPSTR, int);
LRESULT CALLBACK    ClientProc(HWND, UINT, WPARAM, LPARAM);
BOOL                Init(void);
BOOL CALLBACK       AboutDlgProc(HWND, UINT, WPARAM, LPARAM);
BOOL CALLBACK       BindingDlgProc(HWND, UINT, WPARAM, LPARAM);
BOOL                GetBitmapFileName(void);
void                RedrawClientArea(HDC);
BOOL                ReadBitmapFromFile(void);
void                CleanUp();
HPALETTE            LoadPaletteFromFile(LPTSTR lpszFileName);
RPC_STATUS          Bind(HWND hWnd);

//------------------------------------------------------------------------
//      Variables used for finding the binding with the server
//      Since the variables uses definitions from the COMMON.H file
//      remember to include "common.h" before this file
//------------------------------------------------------------------------
RPC_STATUS      status;
_TUCHAR         *pszUuid                    = NULL;
_TUCHAR         pszProtocolSequence[100];
_TUCHAR         pszNetworkAddress[100];
_TUCHAR         pszEndpoint[100];
_TUCHAR         *pszOptions                 = NULL;
_TUCHAR         *pszStringBinding           = NULL;


CLIRES.H

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//                  Copyright Microsoft Corp. 1992-1996
//                      Remote Machine WinTyp sample
//
// FILE     :   clires.h
//
// PURPOSE  :   Definition used by the resources on the client side.
//
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
#define IDI_CLIENT_ICON                 101

#define IDR_CLIENT_MENU                 102

#define IDR_ACCELERATOR1                103

#define IDD_ABOUT_DIALOG                104
#define IDD_BINDING_DIALOG              105

// Defintions used for the menus
#define IDM_MENU_FILE_OPEN              40001
#define IDM_MENU_FILE_CLOSE             40002
#define IDM_MENU_FILE_EXIT              40004
#define IDM_MENU_TOOLS_EDGE             40005
#define IDM_MENU_TOOLS_FLIP             40006
#define IDM_MENU_FILE_BINDING           40007
#define IDM_MENU_ABOUT                  65535

// Names defined for the controls in the dialog boxes
#define IDC_PROTSEQ_NAME                 110
#define IDC_ENDPOINT_NAME                111
#define IDC_SERVER_NAME                  112


COMMON.H

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/*                  Copyright Microsoft Corp. 1992-1996                 */
/*                      Remote Machine pipe sample                      */
/*                                                                      */
/* FILE     :   common.h                                                */
/*                                                                      */
/* PURPOSE  :   Definitions used in the program                         */
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
#ifndef __COMMON_WINTYP_SAMPLE__    // If this file already included -
#define __COMMON_WINTYP_SAMPLE__    // don't include it again

#include <stdio.h>
#include <stdlib.h>

#include <tchar.h>                  // Included to support UNICODE/ANSI

// Common type definition and defines used in the program
#define FINDEDGE    1       // Action to take - Find edges in image
#define FLIPIMAGE   2       // Action to take - Flip the imagedata

#define PROTOCOL_SEQUENCE   TEXT("ncacn_np")
#define END_POINT           TEXT("\\pipe\\wintyp_sample")

// Return values from the program to the OS
#define EXECUTION_FAILED    -1
#define EXECUTION_OK        0

// Exception handler macro
#define DO_EXCEPTION        1       // Execute the exception block

/* Macro for printing out error message and exit the program if an      */
/* error occured                                                        */
#define EXIT_IF_FAIL(x, string){\
    ((x) != RPC_S_OK)?_tprintf(TEXT("%s returned with error: %d\n"), \
    TEXT(string), (x)),exit(x):0;}

// Macro for printing messages to the screen
#define PRINT(X, Y) MessageBox(g_hWnd, (X), (Y), MB_OK )

#endif// __COMMON_WINTYP_SAMPLE__


CLIENT.C

/*************************************************************************
                        Copyright Microsoft Corp. 1992-1996
                            Remote Machine WinType sample

    FILE        :   CLIENT.C

    PURPOSE     :   Client side of the RPC distributed application wintyp

    COMMENTS    :   This program shows the client side an application that
                    transfers a Windows datatype (HBITMAP) from the client
                    to the server, using Remote Procedure Calls. When the
                    Bitmap is received on the server end, it modifies the
                    bitmap, and sends it back.

                    Since this program uses the implicit binding method,
                    some of the binding handling must be done at the
                    client side.

*************************************************************************/
#include <windows.h>            // Required for all Windows programs
#include <commdlg.h>            // Support for the common dialogboxes

#include "common.h"             // Common headerfile for client and server
#include "client.h"             // Header file for the client
#include "clires.h"             // Definitions for the resource file
#include "wintyp.h"             // Generated by the MIDL compiler

TCHAR   szBuffer[200];          // Buffer used for printing messages

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//  Procedure   :   WinMain()
//  Desc.       :   Windows main procedure. Calls up the initialization
//                  of the program , and starts up the message pump
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
int WINAPI WinMain  (HINSTANCE  hInstance,
                     HINSTANCE  hPrevInstance,
                     LPSTR      lpszCmdLine,
                     int        nCmdShow)
{
    MSG         uMsg;           // The message struct
    HACCEL      hAccels;        // Handle to the accelerators

    // Set the global instance
    g_hInstance = hInstance;

    // Perform registration of the class and create the window
    if(!Init())
    {
        return FALSE;
    }

    // Display the window
    ShowWindow(g_hWnd, nCmdShow);

    // Load the accelerators
    hAccels = LoadAccelerators(hInstance,
        MAKEINTRESOURCE(IDR_ACCELERATOR1));

    // Add a message pump to take care of messages
    while (GetMessage(&uMsg, NULL, 0, 0))    // NULL only for WM_QUIT msg.
    {
        if(!TranslateAccelerator(g_hWnd, hAccels, &uMsg))
        {
            TranslateMessage(&uMsg);
            DispatchMessage(&uMsg);
        }
    }

    return uMsg.wParam;
}

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Function :   Init()
// Purpose  :   This function is called once from the start of the program
//              It will register the window class, create the window, and
//              perform the binding to the server.
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
BOOL Init()
{
    WNDCLASS    sWC;                        // The window class

    // Fill in the fields of the window class struct
    sWC.style = 0;                          // Style info
    sWC.lpfnWndProc  = (WNDPROC) ClientProc;// Proc. to handle messsages
    sWC.cbClsExtra   = 0;                   // Extra bytes for class
    sWC.cbWndExtra   = 0;                   // Extra bytes for window
    sWC.hInstance    = g_hInstance;         // Handle to current instance
    sWC.hIcon        = LoadIcon(g_hInstance,// Handle to icon for class
        MAKEINTRESOURCE(IDI_CLIENT_ICON));
    sWC.hCursor      = LoadCursor(NULL, IDC_ARROW); // Handle to cursor
    sWC.hbrBackground= GetStockObject(GRAY_BRUSH);  // Brush for backgr.
    sWC.lpszMenuName =
        MAKEINTRESOURCE(IDR_CLIENT_MENU);           // Name of menu to use
    sWC.lpszClassName= g_szClassName;               // Class name

    // Register the window class
    if(!RegisterClass(&sWC))
    {
        return FALSE;
    }

    // Create the window
    g_hWnd = CreateWindowEx(
        0,                      // Style
        g_szClassName,          // The class name
        g_szWindowCaption,      // Caption showed at top of window
        WS_OVERLAPPEDWINDOW,    // The window style
        CW_USEDEFAULT,          // x-coordinate
        CW_USEDEFAULT,          // y-coordinate
        CW_USEDEFAULT,          // Width of window
        CW_USEDEFAULT,          // Height of window
        NULL,                   // Parent window
        NULL,                   // Menu
        g_hInstance,            // Handle to current instance
        NULL);                  // lpvParam

    // Return false if creation failed
    if(g_hWnd == NULL)
    {
        return FALSE;
    }

    // Initialize the binding address
    pszNetworkAddress[0] = 0;
    _tcscpy(pszEndpoint, END_POINT);
    _tcscpy(pszProtocolSequence, PROTOCOL_SEQUENCE);

    // Create the binding
    if(Bind(g_hWnd) != RPC_S_OK)
    {
        return FALSE;
    }

    return TRUE;                // Initialization OK
}

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Procedure    :   ClientWindowProc()
// Desc.        :   This procedure takes care of all the message handling
//                  from the client main program
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
LRESULT CALLBACK    ClientProc(HWND   hWnd,
                               UINT    uMsg,
                               WPARAM  wParam,
                               LPARAM  lParam)
{
    PAINTSTRUCT sPS;                        // The paintstructure
    HDC         hDC = GetDC(g_hWnd);        // Handle to the screen
    HMENU       hMenu = GetMenu(g_hWnd);    // Handle to the menu
    RECT        sRect;

    switch(uMsg)
    {
        case WM_COMMAND :
    switch(LOWORD(wParam))
            {
                case    IDM_MENU_FILE_OPEN  :       // File open was chosen
                    // Get the filename to read
                    if(GetBitmapFileName())
                    {
                        // Read the data from the file. This call will fill
                        // out the global variables g_hBitmap and g_Bitmap
                        if(!ReadBitmapFromFile())
                        {
                            break;
                        }

                        // Resize the client area to fit the image
                        GetWindowRect(g_hWnd, &sRect);
                        MoveWindow(g_hWnd,          // Handle to the window
                            sRect.left,             // New X position
                            sRect.top,              // New Y position
                            g_Bitmap.bmWidth +      // New width
                            GetSystemMetrics(SM_CXSIZEFRAME) * 2,
                            g_Bitmap.bmHeight +     // The new height
                            GetSystemMetrics(SM_CYSIZEFRAME) * 2 +
                            GetSystemMetrics(SM_CYMENUSIZE) +
                            GetSystemMetrics(SM_CYCAPTION),
                            TRUE);                  // Make a repaint

                        // Force an update of the screen
                        GetClientRect(g_hWnd, &sRect);
                        InvalidateRect(g_hWnd, &sRect, TRUE);
                        UpdateWindow(g_hWnd);

                        // Enable the rest of the menu options
                        EnableMenuItem(hMenu, IDM_MENU_FILE_CLOSE,
                            MF_ENABLED);
                        if(GetDeviceCaps(hDC, BITSPIXEL) == 8)
                        {
                            EnableMenuItem(hMenu, IDM_MENU_TOOLS_EDGE,
                                MF_ENABLED);
                        }
                        EnableMenuItem(hMenu, IDM_MENU_TOOLS_FLIP,
                            MF_ENABLED);
                    }   // if(GetBitmapFileName())
                    break;

                case    IDM_MENU_FILE_CLOSE :   // File close was chosen
                    // If a bitmap has been read in, redraw the client
                    // area to the background color.
                    if(BITMAP_READ)
                    {
                        // Reset the BITMAP_READ indicator
                        BITMAP_READ = FALSE;

                        // Rewrite the screen
                        RedrawClientArea(hDC);

                        // Disable the menu options
                        EnableMenuItem(hMenu, IDM_MENU_FILE_CLOSE,
                            MF_GRAYED);
                        EnableMenuItem(hMenu, IDM_MENU_TOOLS_EDGE,
                            MF_GRAYED);
                        EnableMenuItem(hMenu, IDM_MENU_TOOLS_FLIP,
                            MF_GRAYED);

                        // Delete the bitmap object
                        DeleteObject(g_hBitmap);
                    }
                    break;

                case    IDM_MENU_FILE_EXIT  :   // Exit program was chosen
                    PostMessage(g_hWnd, WM_CLOSE, 0, 0);
                    break;

                case    IDM_MENU_TOOLS_EDGE :   // Want to find edges
                    if (!EDGE_FOUND)
                    {
                        ChangeBitmapRemote(FINDEDGE, &g_hBitmap);
                        RedrawClientArea(hDC);
                        EDGE_FOUND = TRUE;
                    }
                    break;

                case    IDM_MENU_TOOLS_FLIP :   // Want to flip the bitmap
                    ChangeBitmapRemote(FLIPIMAGE, &g_hBitmap);
                    RedrawClientArea(hDC);
                    break;

                case    IDM_MENU_FILE_BINDING:      // Binding dialog box
                    DialogBox(g_hInstance,
                        MAKEINTRESOURCE(IDD_BINDING_DIALOG),
                        hWnd,
                        (DLGPROC) BindingDlgProc);
                    break;

                case    IDM_MENU_ABOUT      :       // About dialog box
                    DialogBox(g_hInstance,
                        MAKEINTRESOURCE(IDD_ABOUT_DIALOG),
                        hWnd,
                        (DLGPROC) AboutDlgProc);
                    break;
            }
            break;

        case WM_PAINT :
            BeginPaint(g_hWnd, &sPS);    // Gets a device context
            RedrawClientArea(sPS.hdc);   // Redraws the screen
            EndPaint(g_hWnd, &sPS);      // Releases a device context
            break;

        case WM_CLOSE   :               // User requests windows closure
            ShutDown();                 // Shut down the server
            CleanUp();                  // Deallocate some memory
            DestroyWindow(hWnd);        // Generates the WM_DESTROY message
            break;

        case WM_DESTROY :
            PostQuitMessage(0);         // Puts a WM_QUIT in the queue
            break;                      // => GetMessage returns NULL

        default :
            ReleaseDC(g_hWnd, hDC);
            return DefWindowProc(hWnd, uMsg, wParam, lParam);
    }

    ReleaseDC(g_hWnd, hDC);
    return (0L);
}

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Procedure    :   void GetBitMapFileName(void)
// Desc.        :   This procedure calls the common dialog box
//                  GetFileNameOpen to get the filename of a bitmap file
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
BOOL GetBitmapFileName(void)
{
OPENFILENAME    sOfn;       // The OPENFILENAME struct

// Variable used to filter out only the files needed
LPCTSTR szFilter[] = {TEXT("Windows Bitmaps (*.bmp)"), TEXT("*.bmp"),
                         TEXT("")};

// Fill in the OPENFILENAME Structure
sOfn.lStructSize         = sizeof(OPENFILENAME);
sOfn.hwndOwner           = g_hWnd;
sOfn.hInstance           = g_hInstance;
sOfn.lpstrFilter         = *szFilter;
sOfn.lpstrCustomFilter   = NULL;
sOfn.nMaxCustFilter      = 0;
sOfn.nFilterIndex        = 0;
sOfn.lpstrFile           = g_szFileName;
sOfn.nMaxFile            = _MAX_PATH;
sOfn.lpstrFileTitle      = NULL;
sOfn.nMaxFileTitle       = _MAX_FNAME + _MAX_EXT;
sOfn.lpstrInitialDir     = NULL;
sOfn.lpstrTitle          = NULL;
sOfn.Flags               = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
sOfn.nFileOffset         = 0;
sOfn.nFileExtension      = 0;
sOfn.lpstrDefExt         = TEXT("bmp");
sOfn.lCustData           = 0L;
sOfn.lpfnHook            = NULL;
sOfn.lpTemplateName      = NULL;

    // This function call will display a Open File dialog box, and read
    // the name into the the global varaible g_szFileName. It will return
    // TRUE if a filename was chosen, and OK button clicked, else it will
    // return FALSE
    return GetOpenFileName(&sOfn);
}

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Procedure    :   ReadBitmapFromFile()
// Desc.        :   This procedure reads a bitmap from a file and sets the
//                  global variables g_Bitmap and g_hBitmap.
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
BOOL ReadBitmapFromFile(void)
{
    UINT    uLoadFlags;
    HDC     hDC = GetDC(g_hWnd);

    // Read in the palette from the file. Must do this first, because this
    // procedure checks for valid fileformats
    g_hPalette = LoadPaletteFromFile(g_szFileName);

    // If a file has already been read, get rid of that bitmap
    if (BITMAP_READ)
    {
        DeleteObject(g_hBitmap);
        BITMAP_READ = FALSE;
    }

    // Load image as a DIB image if its a 256 color image and the screen
    // resolution is in 256 colors
    if(g_BitsPerPixel == 8 && GetDeviceCaps(hDC, BITSPIXEL) == 8)
    {
        uLoadFlags = LR_LOADFROMFILE | LR_CREATEDIBSECTION;
    }
    else
    {
        uLoadFlags = LR_LOADFROMFILE;
    }

    // Load the bitmap image in from the global filename found with the
    // GetFileNameOpen common dialogbox
    g_hBitmap = (HBITMAP) LoadImage(
        NULL,                   // Want to load bitmap from file
        g_szFileName,           // Filename where bitmap is stored
        IMAGE_BITMAP,           // Type of image to be loaded
        0, 0,                   // The desired width and height
        uLoadFlags);

    // If the reading failed
    if (g_hBitmap == 0)
    {
        return FALSE;
    }

    // Obtain the infornation about the bitmap, from the handle returned
    // by the LoadImage function. Must delete this object before quitting
    GetObject(g_hBitmap, sizeof(BITMAP),(LPVOID) &g_Bitmap);

    BITMAP_READ = TRUE;         // Let PAINT know what to repaint
    EDGE_FOUND = FALSE;         // Not found the edges in this image yet

    return TRUE;
}

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Procedure    :   BOOL CALLBACK AboutDlgProc()
// Desc.        :   This procedure is called when the user chooses the
//                  About menu. It displays a Dialogbox defined in the
//                  client.rc file and returns when the user clicks on the
//                  OK button
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
BOOL CALLBACK AboutDlgProc(HWND hDlg,
                           UINT uMsg,
                           WPARAM wParam,
                           LPARAM lParam)
{
    switch(uMsg)
    {
        case WM_INITDIALOG :            // Return TRUE, windows sets the
            return TRUE;                // focus on the OK button

        case WM_COMMAND :
            switch(LOWORD(wParam))
            {
                case IDOK:              // If the user clicks the OK button
                    EndDialog(hDlg, 0); // Take the dialogbox away
                    return TRUE;
            }
        default:
            return FALSE;           // Windows should handle the message
  }
}

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Procedure    :   BOOL CALLBACK BindingDlgProc()
// Desc.        :   This procedure is called when the user chooses Server
//                  from the File menu. It displays a dialogbox defined in
//                  the client.rc file. Here the user can enter a specific
//                  binding to a remote server by specifying the network -
//                  protocol and the endpoint to use, and the name of the
//                  machine that runs the server program.
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
BOOL CALLBACK BindingDlgProc(HWND hDlg,
                             UINT uMsg,
                             WPARAM wParam,
                             LPARAM lParam)
{
    switch (uMsg)
    {
        case WM_INITDIALOG:
            // Fill in the edit boxes with the current values
            SetDlgItemText((HANDLE)hDlg, IDC_PROTSEQ_NAME,
                pszProtocolSequence);
            SetDlgItemText((HANDLE)hDlg, IDC_ENDPOINT_NAME, pszEndpoint);
            SetDlgItemText((HANDLE)hDlg, IDC_SERVER_NAME,
                pszNetworkAddress);
            return(TRUE);

        case WM_COMMAND:
            switch(LOWORD(wParam))
            {
                case IDCANCEL:     // Cancel button clicked
                    EndDialog(hDlg, FALSE);
                    return(TRUE);

                case IDOK:         // OK button clicked
                    GetDlgItemText(hDlg, IDC_PROTSEQ_NAME,
                        pszProtocolSequence, 30);
                    GetDlgItemText(hDlg, IDC_SERVER_NAME,
                        pszNetworkAddress, 30);
                    GetDlgItemText(hDlg, IDC_ENDPOINT_NAME,
                        pszEndpoint, 30);

                    // If we get a binding, all is OK
                    if (Bind(hDlg) == RPC_S_OK)
                    {
                        EndDialog(hDlg, FALSE);
                        return(TRUE);
                    }
                    // If we couldn't bind, just keep asking for new input
            }   // switch((LOWORD(WPARAM))
    }   // switch(uMsg)

    return(FALSE);  // Didn't process a message
}

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Procedure    :   RedrawClientArea(HDC)
// Desc.        :   This procedure redraw the client area by either
//                  displaying the bitmap, or simply drawing a gray
//                  background.
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
void RedrawClientArea(HDC hDC)                // Handle to the screen
{
    HDC     hDCMem;         // A memory device context
    RECT    sRect;          // Rectangle containing the entire client area

    // Do a check to see if the bitmap has been read in. If it hasn't, we
    // leave the screen blank
    if(BITMAP_READ)
    {
        if(g_BitsPerPixel == 8 && g_hPalette != 0)
        {
            // Select and realize the palette to use
            SelectPalette(hDC, g_hPalette, TRUE);
            RealizePalette(hDC);
        }

        hDCMem = CreateCompatibleDC(hDC);
        SelectObject (hDCMem, g_hBitmap);
        SetMapMode (hDCMem, GetMapMode(hDC));

        // Draw the bitmap using the BitBlt function
        if(BitBlt(
            hDC,                // The destination device context
            0, 0,               // Coordinates of the destination rectangle
            g_Bitmap.bmWidth,   // Width of the dest. and source rectangle
            g_Bitmap.bmHeight,// Height of the dest. and source rectangle
            hDCMem,
            0, 0,
            SRCCOPY))
        {
            DeleteDC(hDCMem);
        }
    }
    else        // We want to wipe the screen with the background color
    {
        GetClientRect(g_hWnd,  &sRect);
        FillRect(hDC, &sRect, GetStockObject(GRAY_BRUSH));
    }
    return;
}

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Funciton     :   LoadPaletteFromFile
// Desc.        :   This procedure loads a palette froma dib-file. It
//                  checks to see that this is a 256-color image, and
//                  then reads in the palette, and returns a handle
//                  to it
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
HPALETTE LoadPaletteFromFile(LPTSTR lpszFileName)
{
    HANDLE              hFile;          // Bitmap filename
    HPALETTE            hPalette;       // Handle to the palette
    HANDLE              hMem,           // A handle to a memory location
                        hCore;          // Handle to memory
    DWORD               dwBitmapType,   // The type of the bitmap
                        dwSize;         // A size of memory to allocate
    int                 nIdx,           // Index in loops
                        nPalEntries;    // # of entries in the palette
    LPSTR               lpCore;         // Pointer to memory bytes
    LPLOGPALETTE        lpMem;          // A mem pointer to the LOGPALETTE
    BITMAPFILEHEADER    sBFH;           // This is always filled in
    BITMAPINFOHEADER    sBIH;           // Used if this is a Windows bitmap
    BITMAPCOREHEADER    sBCH;           // Used if this is a OS/2 bitmap
    DWORD               dwDummy;

    // Initialize the palette handle to NULL
    hPalette = NULL;

    // Open the file where the bitmap is stored
    hFile = CreateFile(
        lpszFileName,
        GENERIC_READ,
        FILE_SHARE_READ,
        NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        0);
    if(hFile != 0)
    {
        // Read the BITMAPFILEHEADER from the file
        if (ReadFile(hFile, &sBFH,sizeof(BITMAPFILEHEADER),
            &dwDummy, NULL))
        {
            // Any valid bitmap file should have filetype = 'BM'
            if(sBFH.bfType == (UINT)0x4D42)
            {
                // Determine type of bitmap.
                // The next WORD (4 bytes) in the file (offset 14) will be
                // either 40 - indicating that this is a Windows Bitmap,
                // and therefore will be using a BITMAPINFOHEADER, or it
                // will be 14 - indicating that this is a OS/2 1.x bitmap,
                // and therefore will be using a BITMAPCOREHEADER. 14 and
                // 40 are the respective sizes of the HEADERS
                ReadFile(hFile, &dwBitmapType, sizeof(DWORD),
                    &dwDummy, NULL);
                SetFilePointer(hFile,sizeof(BITMAPFILEHEADER),
                    NULL, FILE_BEGIN);

                // Load up INFO header.  If bitmap is a CORE
                // header, then we need to convert to INFO header.
                if(dwBitmapType == sizeof(BITMAPCOREHEADER))
                {
                    // Read in the COREHEADER
                    ReadFile(hFile, &sBCH,sizeof(BITMAPCOREHEADER),
                        &dwDummy, NULL);

                    // Fill out the missing parts of the INFOHEADER
                    sBIH.biSize          = sizeof(BITMAPINFOHEADER);
                    sBIH.biWidth         = sBCH.bcWidth;
                    sBIH.biHeight        = sBCH.bcHeight;
                    sBIH.biPlanes        = sBCH.bcPlanes;
                    sBIH.biBitCount      = sBCH.bcBitCount;
                    sBIH.biCompression   = BI_RGB;
                    sBIH.biSizeImage     = 0;
                    sBIH.biXPelsPerMeter = 0;
                    sBIH.biYPelsPerMeter = 0;
                    sBIH.biClrUsed       = 0;
                    sBIH.biClrImportant  = 0;
                }
                else
                {
                    // Read in the INFOHEADER
                    ReadFile(hFile, &sBIH,sizeof(BITMAPINFOHEADER),
                        &dwDummy, NULL);
                }

                //-------------------------
                // Palette creation
                //-------------------------

                // Figure out the number of entries in the palette
                // i.e. if the number of Bits per pixel (biBitCount) is 8,
                // the number of palette entries will be 1 << 8 = 256
                nPalEntries = ((UINT)1 << sBIH.biBitCount);
                g_BitsPerPixel = sBIH.biBitCount;

                // Determine the size of the memory to allocate
                // (The size of all mem needed for the palette)
                dwSize   = sizeof(LOGPALETTE) +
                    (nPalEntries * sizeof(PALETTEENTRY));

                // Aloocate space for the palette. this just returns a
                // handle to the memory. We must use the function
                // GlobalLock to get a pointer to the memory
                if(hMem = GlobalAlloc(GHND,dwSize))
                {
                    // Extract a pointer to the memory just allocated, and
                    // cast it as a pointer to a LOGPALETTE struct
                    if(lpMem = (LPLOGPALETTE)GlobalLock(hMem))
                    {
                        // Set LogPalette structure.
                        lpMem->palVersion    = 0x0300;
                        lpMem->palNumEntries = nPalEntries;

                        // Read in ColorTable. COREHEADER files uses the
                        // RGB triplet to store the values of the palette
                        if(dwBitmapType == sizeof(BITMAPCOREHEADER))
                        {
                            // Calculate the size of color map
                            dwSize = (nPalEntries * sizeof(RGBTRIPLE));

                            // Allocate memory to store this in
                            if(hCore = GlobalAlloc(GHND,dwSize))
                            {
                                // Get a pointer to the memory,and LOCK it
                                if(lpCore = GlobalLock(hCore))
                                {
                                    // Read the palette data into memory
                                    ReadFile(hFile, lpCore, dwSize,
                                        &dwDummy, NULL);

                                    // Put the palette data into the
                                    // palette struct
                                    for(nIdx=0; nIdx < nPalEntries; nIdx++)
                                    {
                                        lpMem->palPalEntry[nIdx].peBlue  =
                                            (((RGBTRIPLE FAR * )lpCore) + nIdx)->rgbtBlue;
                                        lpMem->palPalEntry[nIdx].peGreen =
                                            (((RGBTRIPLE FAR * )lpCore)+nIdx)->rgbtGreen;
                                        lpMem->palPalEntry[nIdx].peRed   =
                                            (((RGBTRIPLE FAR * )lpCore)+nIdx)->rgbtRed;
                                        lpMem->palPalEntry[nIdx].peFlags = 0;
                                    }

                                    // Unlock the memory used
                                    GlobalUnlock(hCore);
                                }

                                // Free up the memory used
                                GlobalFree(hCore);
                            }
                        }
                        else
                        {
                            // The INFOHEADER version of a bitmap file uses
                            // RGB Quads to store the color map
                            dwSize = (nPalEntries * sizeof(RGBQUAD));

                            // Allocate memory to store this in
                            if(hCore = GlobalAlloc(GHND,dwSize))
                            {
                                // Get a pointer to the memory,and LOCK it
                                if(lpCore = GlobalLock(hCore))
                                {
                                    // Read the palette data into memory
                                    ReadFile(hFile, lpCore,dwSize,
                                        &dwDummy, NULL);

                                    // Put the palette data into the
                                    // palette struct
                                    for(nIdx=0; nIdx < nPalEntries; nIdx++)
                                    {
                                        lpMem->palPalEntry[nIdx].peBlue  =
                                            (((LPRGBQUAD)lpCore)+nIdx)->rgbBlue;
                                        lpMem->palPalEntry[nIdx].peGreen =
                                            (((LPRGBQUAD)lpCore)+nIdx)->rgbGreen;
                                        lpMem->palPalEntry[nIdx].peRed   =
                                            (((LPRGBQUAD)lpCore)+nIdx)->rgbRed;
                                        lpMem->palPalEntry[nIdx].peFlags = 0;
                                    }

                                    // UNLOCK the data just used
                                    GlobalUnlock(hCore);

}

                                // Free up the memory used to store the
                                // palette data
                                GlobalFree(hCore);
                            }
                        }

                        // Create a handle to the palette
                        hPalette = CreatePalette(lpMem);

                        // Unlock the memory used for storing the data to
                        // the LOGPALETTE
                        GlobalUnlock(hMem);
                    }

                    // Free up the memory used by the LOGPALETTE structure
                    GlobalFree(hMem);
                }
            }
        }
        else// This is NOT a bitmap file
        {
            PRINT(TEXT("This is not a BITMAP file"),
                TEXT("Invalid Fileformat"));
            return FALSE;
        }

        // Close the file we have been reading from
        CloseHandle(hFile);
    }

    // Return the handle to the palette to the calling structure
    return(hPalette);
}

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Procedure    :   RPC_STATUS Bind(HWND)
// Desc.        :   This procedure will make RPC API calls to bind to the
//                  server application.
//                  The binding calls are made from Init() and whenever
//                  the user changes the server name or endpoint. If the
//                  bind operation is successful, the global flag fBound
//                  is set to TRUE. The global flag fBound is used to
//                  determine whether to call the RPC API function
//                  RpcBindingFree.
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
RPC_STATUS Bind(HWND hWnd)
{
    RPC_STATUS nStatus;

    if (g_bBound == TRUE)     // unbind only if bound
    {
        nStatus = RpcStringFree(&pszStringBinding);
        if (nStatus)
        {
            _stprintf(szBuffer,TEXT("RpcStringFree failed 0x%x"), nStatus);
            MessageBox(hWnd,
                       szBuffer,
                       TEXT("RPC Sample Application"),
                       MB_ICONSTOP);
            return(nStatus);
        }

        nStatus = RpcBindingFree(&global_wintyp_sample_handle);
        if (nStatus)
        {
            _stprintf(szBuffer,TEXT("RpcBindingFree failed 0x%x"), nStatus);
            MessageBox(hWnd,
                       szBuffer,
                       TEXT("RPC Sample Application"),
                       MB_ICONSTOP);
            return(nStatus);
        }

        g_bBound = FALSE;  // unbind successful; reset flag
    }

    nStatus = RpcStringBindingCompose(pszUuid,
                                     pszProtocolSequence,
                                     pszNetworkAddress,
                                     pszEndpoint,
                                     pszOptions,
                                     &pszStringBinding);
    if (nStatus)
    {
        _stprintf(szBuffer,
            TEXT("RpcStringBindingCompose returned : (0x%x)\nNetwork Address = %s\n"),
            nStatus, pszNetworkAddress);
        MessageBox(hWnd, szBuffer, TEXT("RPC Sample Application"),
            MB_ICONINFORMATION);
        return(nStatus);
    }

    nStatus = RpcBindingFromStringBinding(pszStringBinding,
                            &global_wintyp_sample_handle);
    if (nStatus)
    {
        _stprintf(szBuffer,
            TEXT("RpcBindingFromStringBinding returned: (0x%x)\nString = %s\n"),
             nStatus, pszStringBinding);
        MessageBox(hWnd, szBuffer, TEXT("RPC Sample Application"),
            MB_ICONINFORMATION);
        return(nStatus);
    }

    g_bBound = TRUE;  // bind successful; reset flag

    return(nStatus);
}

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Procedure    :   CleanUp()
// Desc.        :   This procedure deletes the object, and the memory used
//                  in the program.
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
void CleanUp(void)
{
    // Destroy the object used for the bitmap
    DeleteObject(g_hBitmap);

    // Delete the space taken up by the binding
    if (g_bBound)
    {
        status = RpcStringFree(&pszStringBinding);
        if (status)
        {
            _stprintf(szBuffer,TEXT("RpcStringFree failed 0x%x"), status);
            MessageBox(g_hWnd,
                       szBuffer,
                       TEXT("RPC Sample Application"),
                       MB_ICONSTOP);
            exit(EXECUTION_FAILED);
        }

        status = RpcBindingFree(&global_wintyp_sample_handle);
        if (status)
        {
            _stprintf(szBuffer,TEXT("RpcBindingFree failed 0x%x"), status);
            MessageBox(g_hWnd,
                       szBuffer,
                       TEXT("RPC Sample Application"),
                       MB_ICONSTOP);
            exit(EXECUTION_FAILED);
        }
    }
}

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Procedure    :   midl_user_allocate() and midl_user_free()
// Desc.        :   These procedure are declared in the header file
//                  generated by the midl compiler. These procedures
//                  should be used for all memory allocation and
//                  deallocation.
//                  These procedures are also called by the stub code to
//                  allocate and free memory.
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
void __RPC_FAR * __RPC_API midl_user_allocate(size_t nLen)
{
    return (malloc(nLen));
}

void __RPC_API midl_user_free(void __RPC_FAR * lpvPointer)
{
    if(NULL != lpvPointer)
        free (lpvPointer);
}


REMOTE.C

/*************************************************************************
                Copyright Microsoft Corp. 1992-1996
                    Remote Machine WinType sample

    FILE        :   REMOTE.C

    PURPOSE     :   The actual functions on the server side of the RPC
                    distributed application wintyp that are called from
                    the client side.

    COMMENTS    :   This sample is made to demonstrate the passing of a
                    Window Type from the client to the server. The
                    colordepth in the edge detection funciton is limited
                    to 256 color because of the simpler algorithm

*************************************************************************/
#include "wintyp.h"         // Generated by the MIDL compiler
#include "common.h"         // Common definitions for all files
#include <windows.h>        // All windows programs must include this file

#define THRESHOLD   75      // Threshold value in findedges function

// Local procedures used only within this file
BYTE **Get2DPointer(BYTE *, LONG, LONG);
void FindEdges(BYTE **, BYTE **, LONG, int, int);
void FlipImage(BYTE **, BYTE **, LONG, int, int);

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Function     :   BOOL ChangeBitmapRemote ()
// Desc.        :   This function is called from the client side. First it
//                  allocates space for the data in the image passed from
//                  the client. Then it will analyze the function call,
//                  and perform a findedge, or flipimage operation on the
//                  bitmap passed from the client.
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
BOOL ChangeBitmapRemote(unsigned short nAction, HBITMAP *hBM)
{
    BITMAP sBM;              // A structure to load the bitmap data into
    BOOL   bRetValue;        // The value to return from this function
    BYTE   *pbOldBitmapBits, // Buffer to hold the old values of the bitmap
           *pbNewBitmapBits, // Buffer to hold the new values of the bitmap
           **pbOldImage,     // 2D-pointer to the old image
           **pbNewImage;     // 2D-pointer to the new image
    long   nImageSize;       // The size of the image in bytes

    _tprintf(TEXT("In the remote procedure \'ChangeBitmapRemote\'\n"));
    // Obtain information about the BITMAP from the handle to the bitmap
    GetObject(*hBM, sizeof(BITMAP), (LPVOID) &sBM);

    // Calculate the total number of bytes the image occupies
    nImageSize = sBM.bmHeight * sBM.bmWidthBytes;

    // Allocate memory for the buffers to store the image data in
    if (NULL == (pbOldBitmapBits = (BYTE *) malloc (nImageSize)))
    {
        _tprintf(TEXT("Memory allocation error"));
        return FALSE;
    }

    if (NULL == (pbNewBitmapBits = (BYTE *) malloc (nImageSize)))
    {
        _tprintf(TEXT("Memory allocation error"));
        return FALSE;
    }

    // Get the data from the image, and fill into the old image buffer
    GetBitmapBits(*hBM, nImageSize, (LPVOID) pbOldBitmapBits);

    // Create 2D-array pointers to the image data for easier acces
    if(NULL == (pbOldImage = Get2DPointer(pbOldBitmapBits,
        sBM.bmHeight, sBM.bmWidthBytes)))
    {
        return FALSE;
    }
    if(NULL == (pbNewImage = Get2DPointer(pbNewBitmapBits,
        sBM.bmHeight, sBM.bmWidthBytes)))
    {
        return FALSE;
    }

    switch(nAction)
    {
        case FINDEDGE :
            FindEdges(pbOldImage,
                    pbNewImage,
                    nImageSize,
                    sBM.bmWidth,
                    sBM.bmHeight);

            // Copy the new image data into the bitmap to return to client
            SetBitmapBits(*hBM, nImageSize, (const void *) pbNewBitmapBits);
            bRetValue = TRUE;
            break;

        case FLIPIMAGE:
            FlipImage(pbOldImage,
                    pbNewImage,
                    nImageSize,
                    sBM.bmWidthBytes,
                    sBM.bmHeight);

            // Copy the new image data into the bitmap to return to client
            SetBitmapBits(*hBM, nImageSize,
                (const void *) pbNewBitmapBits);
            bRetValue = TRUE;
            break;

        default :
            bRetValue = FALSE;
    }

    // Deallocate the memory used for the imagebuffers and the 2D-pointers
    free(pbOldBitmapBits);
    free(pbNewBitmapBits);
    free(pbOldImage);
    free(pbNewImage);

    _tprintf(TEXT("Leaving the remote procedure \'ChangeBitmapRemote\'\n"));

    return bRetValue;
}

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Function     :   void FindEdges ()
// Desc.        :   This function finds the edges in a given image by
//                  using the Sobel edge operators. The image will be
//                  changed to the edges corresponding to the boundaries
//                  in it
// Comments     :   The algorithm used works best on grayscale images, but
//                  it should produce acceptable results on a 256-color
//                  image as well
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
void FindEdges(BYTE **pbOldImage,       // 2D-pointer to existing image
               BYTE **pbNewImage,       // 2D-pointer to the new image
               LONG nImageSize,         // The size of the image in bytes
               int nWidth,              // The width of the image
               int nHeight)             // The Height of the image
{
    int
        nIdx1, nIdx2,   // Counters in loops
        nGx, nGy,       // Values found by applying the masks Gx and Gy
        nMax = 0;       // Max value for the edge

    // Go through every pixel in the image, and find the edges
    for (nIdx1 = 1; nIdx1 < (nHeight - 1); nIdx1++)
    {
        for (nIdx2 = 1; nIdx2 < (nWidth - 1); nIdx2++)
        {
            // Find Gx
            nGx =(( (int)pbOldImage[nIdx1+1][nIdx2-1] +
                  2*(int)pbOldImage[nIdx1+1][nIdx2]   +
                    (int)pbOldImage[nIdx1+1][nIdx2+1]  )
                    -
                 (  (int)pbOldImage[nIdx1-1][nIdx2-1] +
                  2*(int)pbOldImage[nIdx1-1][nIdx2]   +
                    (int)pbOldImage[nIdx1-1][nIdx2+1]  ))  / 8;

            // Find Gy
            nGy =(( (int)pbOldImage[nIdx1-1][nIdx2+1] +
                  2*(int)pbOldImage[nIdx1][nIdx2+1]   +
                    (int)pbOldImage[nIdx1+1][nIdx2+1]  )
                    -
                 (  (int)pbOldImage[nIdx1-1][nIdx2-1] +
                  2*(int)pbOldImage[nIdx1][nIdx2-1]   +
                    (int)pbOldImage[nIdx1+1][nIdx2-1]  ))  / 8;

            // Enter value into the new image
            pbNewImage[nIdx1][nIdx2] = (BYTE)((abs(nGx) + abs(nGy))/2);
        }
    }

// Find the max value
    for (nIdx1 = 1; nIdx1 < (nHeight-1); nIdx1++)
    {
        for (nIdx2 = 0; nIdx2 < nWidth; nIdx2++)
        {
            if (pbNewImage[nIdx1][nIdx2] > nMax)
            {
                nMax = pbNewImage[nIdx1][nIdx2];
            }
        }
    }

    // Normalize every point in the image
    for (nIdx1 = 1; nIdx1 < (nHeight-1); nIdx1++)
    {
        for (nIdx2 = 0; nIdx2 < nWidth; nIdx2++)
        {
            pbNewImage[nIdx1][nIdx2]=(pbNewImage[nIdx1][nIdx2] * 255)/nMax;
        }
    }

    // Now set every pixel value that is less than a threshold to 0, and
    // the rest to 255
    for (nIdx1 = 1; nIdx1 < (nHeight-1); nIdx1++)
    {
        for (nIdx2 = 0; nIdx2 < nWidth; nIdx2++)
        {
            if(pbNewImage[nIdx1][nIdx2] > THRESHOLD)
            {
                pbNewImage[nIdx1][nIdx2] = 255;
            }
            else
            {
                pbNewImage[nIdx1][nIdx2] = 0;
            }
        }
    }

}

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Function     :   void FlipImage ()
// Desc.        :   This function will flip the image upside down
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
void FlipImage(BYTE **pbOldImage,   // 2D-pointer to existing image
               BYTE **pbNewImage,   // 2D-pointer to the new image
               LONG nImageSize,     // The size of the image in bytes
               int nWidth,          // The width of the image
               int nHeight)         // The Height of the image
{
    int     nIdx1, nIdx2;                   // Counters in loops

    // Copy the old image, line by line, into the new image
    for (nIdx1 = 0; nIdx1 < nHeight; nIdx1++)
    {
        for (nIdx2 = 0; nIdx2 < nWidth; nIdx2++)
        {
            pbNewImage[nHeight - nIdx1 - 1][nIdx2] =
                pbOldImage[nIdx1][nIdx2];
        }
    }

}

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Function :   Get2DPointer(BITMAP *)
// Purpose  :   This procedure will get a 2D-pointer to the data in the
//              bitmap, which is much easier to work with than a single
//              pointer.
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
BYTE **Get2DPointer(BYTE *pbBuffer, LONG nHeight, LONG nWidthBytes)
{
    BYTE **pbRetValue;        // The return value
    int nIdx;

    if (NULL == (pbRetValue = (BYTE **) malloc (nHeight * sizeof(BYTE *))))
    {
        _tprintf(TEXT("Memory allocation error"));
        return NULL;
    }

    for (nIdx = 0; nIdx < nHeight; nIdx++)
        pbRetValue[nIdx] = pbBuffer + (nIdx * nWidthBytes);

    return pbRetValue;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/* Procedure    :   void ShutDown(void)                                 */
/* Desc.        :   This procedure send a message to the server that it */
/*                  can stop listening for remote procedure calls       */
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void ShutDown(void)
{
    RPC_STATUS nStatus;

    /* Tell the server to stop listening for remote procedure calls */
    _tprintf(TEXT("Shutting down the server\n"));
    nStatus = RpcMgmtStopServerListening(NULL);
    EXIT_IF_FAIL(nStatus, "RpcMgmtStopServerListening");
}


SERVER.C

/*************************************************************************
                    Copyright Microsoft Corp. 1992-1996
                        Remote Machine pipe sample

  FILE      :   server.c

  USAGE     :   server  -p protocol_sequence
                        -e endpoint

  PURPOSE   :   This file contains the functions needed to set up the
                server side to receive remote procedure calls

  COMMENTS  :   This application uses the implicit binding method.

*************************************************************************/
#include "common.h"     // Common definitions is locataed in  this file
#include "wintyp.h"     // Generated by the MIDL compiler

// Local Procedures
void CleanUpServer();   // Unregisters the interface

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/*  Procedure   :   void Usage(_TUCHAR *)                               */
/*  Desc        :   This procedure prints out an error message if the   */
/*                  command line arguments are wrong                    */
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void Usage(_TUCHAR * pszProgramName)
{
    _tprintf(TEXT("USAGE : %s [-option]\n"), pszProgramName);
    _tprintf(TEXT("Options : -p Protocol Sequence\n"));
    _tprintf(TEXT("          -e Endpoint\n"));
    exit(EXECUTION_FAILED);
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/* The server main program                                              */
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int main (int argc, char *argv[])
{
    RPC_STATUS      nStatus;        // Error status returned
    int             nNumArgs;       // Number of command line arguments
    int             nIdx;           // Counter in loops

    // Variabels used for selecting the protocol and the endpoint
    _TUCHAR *pszProtocolSequence    = PROTOCOL_SEQUENCE;
    _TUCHAR *pszEndpoint            = END_POINT;
    _TUCHAR *pszSecurity            = NULL;

    // Get a common handle on the command line arguments for both UNICODE
    // and ASCII
#ifdef _UNICODE
    LPWSTR*szArglist = CommandLineToArgvW(GetCommandLine(), &nNumArgs);
    if (NULL == szArglist)
    {
        _tprintf(TEXT("SERVER.C : CommandLineToArgW failed"));
        exit(EXECUTION_FAILED);
    }
#else
    char **szArglist = argv;
    nNumArgs = argc;
#endif

    // Allow the user to override settings with commandline switches
    for (nIdx = 1; nIdx < nNumArgs; nIdx++)
    {
        if((_tcscmp(szArglist[nIdx], TEXT("-p")) == 0) ||
           (_tcscmp(szArglist[nIdx], TEXT("-P")) == 0))
            pszProtocolSequence = szArglist[++nIdx];
        else if((_tcscmp(szArglist[nIdx], TEXT("-e")) == 0) ||
                (_tcscmp(szArglist[nIdx], TEXT("-e")) == 0))
            pszEndpoint = szArglist[++nIdx];
        else
            Usage(szArglist[0]);
    }

    /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
    /* Register the interface with the RPC run-time library             */
    /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
    _tprintf(TEXT("Registering the interface\n"));
    nStatus = RpcServerRegisterIf(
        wintyp_sample_v1_0_s_ifspec,// Interface specification
        NULL,                       // UUID to associate with MgrEnv arg.
        NULL);                      // Managers entry point vector. (None)
    EXIT_IF_FAIL(nStatus, "RpcServerRegisterIf");

    /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
    /* Select Protocal sequence : This sample uses namedpipes as the    */
    /* default protocol. The RpcServerUseProtseqEp function tells the   */
    /* RPC run-time library to use the specified protocol sequence      */
    /* combined with the specified endpoint for receiving remote        */
    /* procedure.                                                       */
    /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
    _tprintf(TEXT("Selecting the protocol sequence to use. \"%s\"\n"),
        pszProtocolSequence);
    nStatus = RpcServerUseProtseqEp(
        pszProtocolSequence,            // String with the protocol in
        RPC_C_PROTSEQ_MAX_REQS_DEFAULT, // Max number of calls
        pszEndpoint,                    // Endpoint addres information
        pszSecurity);                   // Security
    EXIT_IF_FAIL(nStatus, "RpcServerUseProtseqsEp");

    /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
    /* Now start listening for remote procedure calls from the client   */
    /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
    RpcTryExcept
    {
        _tprintf(TEXT("Listening for remote calls...\n"));
        nStatus = RpcServerListen(
            1,                              // The minimum number of calls
            RPC_C_LISTEN_MAX_CALLS_DEFAULT, // The maximum number of calls
            FALSE);                         // Cont. until stopped
        EXIT_IF_FAIL(nStatus, "RpcServerListen");
    }
    RpcExcept(DO_EXCEPTION)
    {
        // Print out the exception code
        _tprintf(TEXT("Run-time exception %u in %s at line %d\n"),
            RpcExceptionCode(), TEXT(__FILE__), __LINE__);
    }
    RpcEndExcept

    // If no exceptions occured, clean up the server and exit
    CleanUpServer();

    // Deallocate the memory used for the ARGLIST if using UNICODE
#ifdef _UNICODE
    if (NULL != szArglist)
        free(szArglist);
#endif

    return (EXECUTION_OK);
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/* Procedure: void CleanUpServer(RPC_BINDING_VECTOR);               */
/* Desc.: This procedure will unregister the interface          */
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void CleanUpServer(void)
{

    RPC_STATUS nStatus;        // Error status from RPC-runtime calls

    /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
    /* Unregister the interface from the RPC run-time library           */
    /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
    _tprintf(TEXT("Unregistering the Interface"));
    nStatus = RpcServerUnregisterIf(
        NULL, NULL,     // Prevents server from receiving new remote calls
        FALSE);         // Wait until all the active calls are complete
    EXIT_IF_FAIL(nStatus, "RpcServerUnRegisterIf");
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/* Procedure    :   midl_user_allocate() and midl_user_free()           */
/* Desc.        :   These procedure are declared in the header file     */
/*                  generated by the midl compiler. These procedures    */
/*                  should be used for all memory allocation and        */
/*                  deallocation.                                       */
/*                  These procedures are also called by the stub code to*/
/*                  allocate and free memory.                           */
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void __RPC_FAR * __RPC_API midl_user_allocate(size_t nLen)
{
    return (malloc(nLen));
}

void __RPC_API midl_user_free(void __RPC_FAR * lpvPointer)
{
    if(lpvPointer != NULL)
        free (lpvPointer);
}


WINTYP.ACF

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/*                  Copyright Microsoft Corp. 1992-1996                 */
/*                      Remote Machine pipe sample                      */
/*                                                                      */
/* FILE     :   pipe.acf                                                */
/*                                                                      */
/* PURPOSE  :   This file tells the midl compiler that we want to use   */
/*              the implicit binding method for the remote procedure    */
/*              calls                                                   */
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
[implicit_handle (handle_t global_wintyp_sample_handle)]
interface wintyp_sample
{
}


WINTYP.IDL

/*************************************************************************
                Copyright Microsoft Corp. 1992-1996
                    Remote Machine strout sample

    FILE    :   wintyp.idl

    PURPOSE :   This file defines the interface for the strout example

*************************************************************************/
[
uuid(da2a84e0-cabd-11cf-a8a4-00a0c9038450),     // UUID
pointer_default(unique),                        // Pointer default
version(1.0)                                    // Program version
]
interface wintyp_sample
{
    import "wtypes.idl";    // Need this to support the window dataypes

    /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
    /* Declaration of the remote procedure.                             */
    /* This procedure will send the bitmap to the server, the server    */
    /* will then either flip the image, or find the edges in it, before */
    /* sending it back to the client.                                   */
    /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
    BOOL ChangeBitmapRemote(
        [in] unsigned short action,     // The action to take on the image
        [in, out] HBITMAP   *bitmap);   // The bitmap to send over the line

    void ShutDown(void);            // Procedure to shut down the server
}


README.TXT

YIELD

The YIELD program demonstrates use of the RpcWinSetYieldInfo function to
prevent a distributed application from blocking during lengthy remote
procedure calls.

FILES
=====

The directory samples\rpc\yield contains the following files for building
the sample distributed application YIELD:

File          Description

README.TXT    Readme file for the YIELD sample
YIELD.IDL     Interface definition language file
YIELD.ACF     Attribute configuration file
YIELDC.C      Client main program
YIELDC.RC     Client resource file
YIELDC.DLG    Client dialog box definitions
YIELDC.DEF    Client module definition file
YIELDS.C      Server main program
YIELDP.C      Remote procedures
MAKEFILE      Nmake file for Windows NT or Windows 95
MAKEFILE.win  Nmake file for Win 3.x

-------------------------------------------------------------------
BUILDING SERVER APPLICATION FOR MICROSOFT WINDOWS NT OR WINDOWS 95:
-------------------------------------------------------------------

The following environment variables should be set for you already.

  set CPU=i386
  set INCLUDE=%SDKROOT%\h
  set LIB=%SDKROOT%\lib
  set PATH=%SDKROOT%\system32;%SDKROOT%\bin;

Where %SDKROOT% is the root directory for the 32-bit Windows SDK.

For mips, set CPU=mips
For alpha, set CPU=alpha

Build the sample server application:

  nmake cleanall
  nmake

These commands build the server executable program YIELDS.EXE.

--------------------------------------------
BUILDING THE CLIENT APPLICATION FOR WIN 3.X
--------------------------------------------

After installing the Microsoft Visual C/C++ version 1.50 development
environment and the 16-bit RPC SDK on a Windows NT or Windows 95
computer, you can build the sample client application from Windows NT
or Windows 95.

  nmake -f makefile.win cleanall
  nmake -f makefile.win

This builds the client application yieldc.exe.

You may also execute the Microsoft Visual C/C++ compiler under MS-DOS.
This requires a two step build process.

  Step One: Compile the .IDL files under Windows NT or Windows 95.
     nmake -a -f makefile.win yield.h

  Step Two: Compile the C sources (stub and application) under MS-DOS.
     nmake -f makefile.win

------------------------------------------
RUNNING THE CLIENT AND SERVER APPLICATIONS
------------------------------------------

On the server, enter

  yields

On the client, choose the Run command from the File menu in the
Microsoft Windows 3.x Program Manager and enter YIELDC.EXE.

Several command line switches are available to change settings for
the server application. For a listing of available switches, enter

  yields -?

Bug: If you cancel in the middle of a custom yield, you must wait
for the number of seconds you originally set before making another
remote procedure call.


YIELD.ACF

[implicit_handle(handle_t hYield)]
interface yield
{

}


YIELD.IDL

[ uuid (87A118A0-11DB-1068-94B2-00DD010662DA),
  version (1.0),
  pointer_default(unique)
]
interface yield
{

void YieldProc([in] short cSeconds);

void Shutdown(void);

}


YIELDC.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                       yield Example

    FILE:       yieldp.c

    PURPOSE:    RPC sample Windows client
                Based on Win 3.x SDK Generic template for Windows applications

    FUNCTIONS:  WinMain()         - same as Windows generic example
                InitApplication() - same as Windows generic example
                InitInstance()    - same as Windows generic example
                MainWndProc()     - processes messages

                About()     - processes messages for "About" dialog box
                BindInfo()  - processes messages for "Bind" dialog box
                WaitInfo()  - processes messages for "Wait" dialog box
                YieldInfo() - processes messages for "Yield" dialog box

                Bind()               - calls the RPC API functions
                midl_user_allocate() - memory allocation function needed by RPC
                midl_user_free()     - memory free function needed by RPC

    COMMENTS:   This sample application demonstrates the yield capability
                of the Microsoft RPC for Microsoft Windows 3.x using the
                RpcWinSetYieldInfo API function.

                By yielding, you can prevent your distributed application
                from blocking during lengthy remote procedure calls.

                This sample is based on the Win 3.x generic example. To focus
                attention on the RPC-related aspects of this application,
                many comments from the Windows-only version are removed.

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

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include <windowsx.h>
#include "yield.h"     // header file generated by MIDL compiler
#include "yieldc.h"    // client-specific header file

/* global data */

unsigned char   pszProtocolSequence[MAXPROTSEQ+1];
unsigned char   pszNetworkAddress[NETLEN+1];
unsigned char   pszEndpoint[PATHLEN+1];
unsigned char * pszUuid           = NULL;
unsigned char * pszOptions        = NULL;
unsigned char * pszStringBinding  = NULL;

int fBound = FALSE;    // flag indicates whether client is bound to server
int fCancel = FALSE;   // flag indicates whether the user has chosen CANCEL

unsigned int cWaitSec; // parameter to remote procedure Sleep()

int   fCustomYield;    // TRUE = custom yield, FALSE = standard yield
DWORD dwOtherInfo;     // depends on the value of fCustomYield

HANDLE  hInst;         // current instance
HCURSOR hHourGlass;    // during calls to RPC API functions
HWND    hWndMain;      // main handle

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

    FUNCTION: WinMain(HINSTANCE, HINSTANCE, LPSTR, 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,      // current instance
                   HINSTANCE hPrevInstance,  // previous instance
                   LPSTR     lpCmdLine,      // command line
                   int       nCmdShow)       // show-window type
{
    MSG msg;

    UNREFERENCED_PARAMETER(lpCmdLine);

    if (! hPrevInstance)                   // no other instances of app running
        if (! InitApplication(hInstance))  // initialize shared things
            return(FALSE);                 // exit 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
                      (HWND)NULL,  // handle of window receiving the message
                      0,           // lowest message to examine
                      0))          // highest message to examine
    {
        TranslateMessage(&msg);    // translate virtual key codes
        DispatchMessage(&msg);     // dispatche message to window
    }

    return(msg.wParam);  // return 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(HANDLE hInstance)    // current instance
{
    WNDCLASS wc;

    /* Fill in the window class structure with parameters that   */
    /* describe the main window                                  */
    wc.style         = 0;
    wc.lpfnWndProc   = (WNDPROC) MainWndProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.hInstance     = hInstance;
    wc.hIcon         = LoadIcon(hInstance, "YieldIcon");
    wc.hCursor       = LoadCursor((HANDLE) NULL, IDC_ARROW);
    wc.hbrBackground = GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName  = "GenericMenu";
    wc.lpszClassName = "GenericWClass";

    /* 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 global
               variable and create and display the main program window.

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

BOOL InitInstance(HANDLE  hInstance,    // current instance
                  int     nCmdShow)     // param for first ShowWindow() call
{
    HWND hWnd;

    /* Save the instance handle in global variable, which will be used */
    /* in many subsequence calls from this application to Windows      */
    hInst = hInstance;
    hHourGlass = LoadCursor((HANDLE) NULL, IDC_WAIT);

    /* Create a main window for this application instance */
    hWnd = CreateWindow("GenericWClass",
                        "RPC Sample Application",
                        WS_OVERLAPPEDWINDOW,
                        CW_USEDEFAULT,
                        CW_USEDEFAULT,
                        CW_USEDEFAULT,
                        CW_USEDEFAULT,
                        (HWND) NULL,
                        (HMENU) NULL,
                        hInstance,
                        (LPVOID) NULL
                        );

    /* If the window cannot be created, return "failure" */
    if (!hWnd)
        return(FALSE);

    /* Initialize RPC binding data */
    strcpy(pszProtocolSequence, DEFAULT_PROT_SEQ);
    strcpy(pszEndpoint, DEFAULT_ENDPOINT);
    pszNetworkAddress[0] = '\0';

    /* Bind client to server */
    fBound = FALSE;

    /* Initialize the parameter to the remote procedure Sleep() */
    cWaitSec = DEFAULT_WAIT;

    /* Initialize the parameters to RpcWinSetYieldInfo() */
    fCustomYield = FALSE;         // FALSE = std yield
    dwOtherInfo = (DWORD) NULL;   // NULL = RPC-supplied dialog box

    RpcWinSetYieldInfo(hWnd,                  // handle
                       fCustomYield,          // standard or custom yield?
                       WM_RPC_YIELD_MESSAGE,  // 0 = no message is posted
                       dwOtherInfo);          // depends on fCustomYield value

    /* Make the window visible, update its client area, and return "success" */
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    return(TRUE);
}

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

    FUNCTION: MainWndProc(HWND, UINT, WPARAM, LPARAM)

    PURPOSE:  Processes messages

    MESSAGES: WM_COMMAND  - application menu (About dialog box)
              WM_DESTROY  - destroy window

    COMMENTS: Based on the Windows generic sample.
              Several new menu items are added to demonstrate the
              RpcWinSetYieldInfo function.

              The "Bind" menu is associated with the "GetBindInfo"
              dialog box function.  GetBindInfo allows the user to
              set the parameters for the RpcStringBindingCompose and
              RpcBindingFromStringBinding functions.  A flag, fBound,
              keeps track of whether the client application is bound
              to a remote server.  If this flag indicates that the
              client application is not bound to the server, it calls
              the "Bind" utility function to call the
              RPC API functions that establish the binding.

              The "Yield" menu is associated with the "GetYieldInfo"
              dialog box function. GetYieldInfo allows the user to
              select the yielding model:  standard yield with an
              RPC run-time library-supplied dialog box; standard
              yield with a user-supplied dialog box; or custom yield.
              These methods are described in detail in the documentation
              for the RpcWinSetYieldInfo function.

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

long APIENTRY MainWndProc(HWND hWnd,       // window handle
                          UINT message,    // type of message
                          WPARAM wParam,   // additional information
                          LPARAM lParam    // additional information
                          )
{
    DLGPROC lpProc;    // pointer to the dialog box function

    /* copy the window handle for CustomYield() */
    hWndMain = hWnd;

    switch (message) {

    case WM_CREATE:
        /* Win 3.x is client-only; force user to specify server */
        PostMessage(hWnd, WM_COMMAND, IDM_BIND, 0L);
        break;

    case WM_COMMAND:
        switch (wParam) {

        case IDM_ABOUT:
            lpProc = MakeProcInstance(About, hInst);
            DialogBox(hInst,
                      "AboutBox",
                      hWnd,
                      lpProc);
            FreeProcInstance(lpProc);
            break;

        case IDM_BIND:
            lpProc = MakeProcInstance(GetBindInfo, hInst);
            DialogBox(hInst,
                      "BindBox",
                      hWnd,
                      lpProc);
            FreeProcInstance(lpProc);
            break;

        case IDM_WAIT:
            lpProc = MakeProcInstance(GetWaitInfo, hInst);
            DialogBox(hInst,
                      "WaitBox",
                      hWnd,
                      lpProc);
            FreeProcInstance(lpProc);
            break;

        case IDM_YIELD:
            lpProc = MakeProcInstance(GetYieldInfo, hInst);
            DialogBox(hInst,
                      "YieldBox",
                      hWnd,
                      lpProc);
            FreeProcInstance(lpProc);
            break;

        case IDM_EXIT:
            DestroyWindow(hWnd);
            if (fBound == TRUE) {
                RpcTryExcept {
                    Shutdown();     // shut down the server
                }
                RpcExcept(1) {
                    MessageBox(hWnd,
                               EXCEPT_MSG,
                               "Remote Procedure Call",
                               MB_ICONINFORMATION);
                }
                RpcEndExcept
            }
            break;

        default:
            return(DefWindowProc(hWnd, message, wParam, lParam));

        }
        break;

    case WM_RPC_YIELD_MESSAGE:   // signals beginning or end of yield period
        if (wParam == 0)
            SetWindowText(hWnd, YIELD_END_MSG);
        else if (wParam == 1)
            SetWindowText(hWnd, YIELD_START_MSG);
        break;

    case WM_DESTROY:
        PostQuitMessage(0);
        break;

    default:  // passes it on if unprocessed
        return(DefWindowProc(hWnd, message, wParam, lParam));

    }

    return(0L);
}

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

    FUNCTION: About(HWND, unsigned, WORD, LONG)

    PURPOSE:  "About" dialog box

    COMMENTS:

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

BOOL APIENTRY About(HWND hDlg,
                    UINT message,
                    UINT wParam,
                    LONG lParam)
{
    UNREFERENCED_PARAMETER(lParam);

    switch (message) {

    case WM_INITDIALOG:
        return(TRUE);

    case WM_COMMAND:
        if (wParam == IDOK || wParam == IDCANCEL) {
            EndDialog(hDlg, TRUE);
            return(TRUE);
        }
        break;

    }

    return(FALSE);
}

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

    FUNCTION: GetBindInfo(HWND, unsigned, WORD, LONG)

    PURPOSE:  Collect components of string binding;
              protocol sequence, network address, endpoint

    COMMENTS:

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

BOOL APIENTRY GetBindInfo(HWND hDlg,
                          UINT message,
                          UINT wParam,
                          LONG lParam)
{
    HCURSOR hOld;

    UNREFERENCED_PARAMETER(lParam);

    switch (message) {

    case WM_INITDIALOG:  // fill in dialog's edit boxes
        SetDlgItemText(hDlg, IDD_ENDPOINT, pszEndpoint);
        SetDlgItemText(hDlg, IDD_PROT_SEQ, pszProtocolSequence);
        SetDlgItemText(hDlg, IDD_NET_ADDR, pszNetworkAddress);
        return(TRUE);

    case WM_COMMAND:
        switch(wParam) {

        case IDCANCEL:
            EndDialog(hDlg, FALSE);
            return(TRUE);

        case IDOK:
            GetDlgItemText(hDlg, IDD_PROT_SEQ, pszProtocolSequence, MAXPROTSEQ);
            GetDlgItemText(hDlg, IDD_ENDPOINT, pszEndpoint, PATHLEN);
            GetDlgItemText(hDlg, IDD_NET_ADDR, pszNetworkAddress, NETLEN);

            hOld = SetCursor(hHourGlass);
            if (Bind(hDlg) != RPC_S_OK) {  // bind to server
                EndDialog(hDlg, FALSE);
                return(FALSE);
            }

            SetCursor(hOld);
            EndDialog(hDlg, TRUE);
            return(TRUE);
       }
    }

    return(FALSE);
}

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

    FUNCTION: GetWaitInfo(HWND, unsigned, WORD, LONG)

    PURPOSE:  Prompt user for the parameter to the remote call,
              then make the remote procedure call.

    COMMENTS:

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

BOOL APIENTRY GetWaitInfo(HWND hDlg,     // window handle of the dialog box
                          UINT message,  // type of message
                          UINT wParam,   // message-specific information
                          LONG lParam)
{
    int fError;

    UNREFERENCED_PARAMETER(lParam);

    switch (message) {

    case WM_INITDIALOG:
        SetDlgItemInt(hDlg, IDD_WAITTIME, cWaitSec, FALSE);
        return(TRUE);

    case WM_COMMAND:
        switch(wParam) {

        case IDCANCEL:
            EndDialog(hDlg, FALSE);
            fCancel = TRUE;
            return(TRUE);

        case IDOK:
            cWaitSec = GetDlgItemInt(hDlg, IDD_WAITTIME, &fError, FALSE);
            if (cWaitSec <= 0)            // check for valid entry
                cWaitSec = DEFAULT_WAIT;  // set an appropriate value

            RpcTryExcept {
                YieldProc(cWaitSec);      // make the remote procedure call
            }
            RpcExcept(1) {
                unsigned long ulCode;
                char pszFail[MSGLEN];

                ulCode = RpcExceptionCode();
                if (ulCode != RPC_S_CALL_FAILED) {
                    sprintf(pszFail, "%s (0x%x)\n", EXCEPT_MSG, ulCode);
                    MessageBox(hDlg,
                               pszFail,
                               "Remote Procedure Call",
                               MB_ICONINFORMATION);
                }
            }
            RpcEndExcept

            EndDialog(hDlg, TRUE);
            return(TRUE);
        }
    }

    return(FALSE);
}

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

    FUNCTION: CustomYield(void)

    PURPOSE:  Message handler during custom yield

    MESSAGES: WM_RPC_YIELD_MESSAGE - end of yield message
              WM_COMMAND           - Input received

    COMMENTS: The callback function must retrieve messages from the
              message queue.

              The function must return TRUE when the RPC operation
              has completed. The function must return FALSE when the
              user cancels the RPC operation.

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

BOOL FAR PASCAL __export CustomYield(void)
{
    MSG msg;

    fCancel = FALSE;

    while (TRUE) {  // message processing
        GetMessage(&msg, (HWND)NULL, 0, 0);

        if (msg.message == WM_RPC_YIELD_MESSAGE)
            return(TRUE);  // RPC operation is complete

        if (fCancel == TRUE)
            return(FALSE);

        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return(TRUE);
}

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

    FUNCTION: GetYieldInfo(HWND, unsigned, WORD, LONG)

    PURPOSE:  Check radio buttons to see which yield method
              was selected by the user, then set the parameters
              to RpcWinSetYieldInfo and call RpcWinSetYieldInfo.

    COMMENTS:

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

BOOL APIENTRY GetYieldInfo(HWND hDlg,     // window handle of the dialog box
                           UINT message,  // type of message
                           UINT wParam,   // message-specific information
                           LONG lParam)
{
    int fCheck;

    UNREFERENCED_PARAMETER(lParam);

    switch (message) {

    case WM_INITDIALOG:  // message: initialize dialog box
        if (fCustomYield)
            CheckRadioButton(hDlg, IDD_STD_RPC, IDD_CUSTOM, IDD_CUSTOM);
        else if (dwOtherInfo != (DWORD) NULL)
            CheckRadioButton(hDlg, IDD_STD_RPC, IDD_CUSTOM, IDD_STD_USER);
        else
            CheckRadioButton(hDlg, IDD_STD_RPC, IDD_CUSTOM, IDD_STD_RPC);
        return(TRUE);

    case WM_COMMAND:     // message: received a command
        switch(wParam) {

            case IDCANCEL:   // System menu close command?
                EndDialog(hDlg, FALSE);
                return(TRUE);

            case IDOK:
                /* which radio button is checked: Custom yield? */
                fCheck = (int) SendDlgItemMessage(hDlg,
                                                  IDD_CUSTOM,
                                                  BM_GETCHECK,
                                                  0,
                                                  0);
                if (fCheck == TRUE) {
                    fCustomYield = TRUE;
                    dwOtherInfo = (DWORD) MakeProcInstance(CustomYield, hInst);
                }
                else {
                    fCustomYield = FALSE;

                    /* Standard yield, user-supplied dialog? */
                    fCheck = (int) SendDlgItemMessage(hDlg,
                                                      IDD_STD_USER,
                                                      BM_GETCHECK,
                                                      0,
                                                      0);
                    if (fCheck == TRUE) {
                        HRSRC hrsrc;

                        hrsrc = FindResource(hInst,
                                             "USERYIELDBOX",
                                             RT_DIALOG);
                        dwOtherInfo = LoadResource(hInst, hrsrc);
                    }
                    else {
                        /* Assume standard-yield, rpc-supplied dialog box */
                        dwOtherInfo = (DWORD) NULL;
                    }
                }

                RpcWinSetYieldInfo(hWndMain,
                                   fCustomYield,
                                   WM_RPC_YIELD_MESSAGE,
                                   dwOtherInfo);

                EndDialog(hDlg, TRUE);
                return(TRUE);
        }
    }

    return(FALSE);
}

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

    FUNCTION: midl_user_allocate(size_t)

    PURPOSE:  Allocate memory as needed by the RPC runtime library

    COMMENTS: The stubs or runtime libraries may need to allocate memory.
              By convention, they call a user-specified function named
              midl_user_allocate.  In this application, no memory
              management is needed, so a dummy function is provided.

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

void __RPC_FAR * __RPC_USER midl_user_allocate(size_t len)
{
    UNREFERENCED_PARAMETER(len);
    return(NULL);  // no memory management required
}

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

    FUNCTION: midl_user_free(void *)

    PURPOSE:  Free memory as needed by the RPC runtime library

    COMMENTS: The stubs or runtime libraries may need to free memory.
              By convention, they call a user-specified function named
              midl_user_free.  In this application, no memory allocation
              is needed so a dummy function is provided.

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

void __RPC_USER midl_user_free(void __RPC_FAR * ptr)
{
    UNREFERENCED_PARAMETER(ptr);
    return;    // no memory management required
}

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

    FUNCTION: Bind(HWND)

    PURPOSE:  Make RPC API calls to bind to the server application

    COMMENTS: The binding calls are made from InitInstance() and whenever
              the user changes the protocol sequence, network address, or
              endpoint. If the bind operation is successful, the global
              flag fBound is set to TRUE.

              The global flag fBound is used to determine whether to call
              the RPC API function RpcBindingFree.

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

RPC_STATUS Bind(HWND hWnd)
{
    RPC_STATUS status;
    char pszFail[MSGLEN];

    if (fBound == TRUE) {  // unbind only if bound
        status = RpcStringFree(&pszStringBinding);  // remote calls done; unbind
        if (status) {
            MessageBox(hWnd, "RpcStringFree failed", "RPC Error", MB_ICONSTOP);
            return(status);
        }

        status = RpcBindingFree(&hYield);  // remote calls done; unbind
        if (status) {
            MessageBox(hWnd, "RpcBindingFree failed", "RPC Error", MB_ICONSTOP);
            return(status);
        }

        fBound = FALSE;  // unbind successful; reset flag
    }

    status = RpcStringBindingCompose(pszUuid,
                                     pszProtocolSequence,
                                     pszNetworkAddress,
                                     pszEndpoint,
                                     pszOptions,
                                     &pszStringBinding);
    if (status) {
        sprintf(pszFail, "RpcStringBindingCompose failed: (0x%x)\nNetwork Address = %s\n",
                status, pszNetworkAddress);
        MessageBox(hWnd,
                   pszFail,
                   "RPC Runtime Error",
                   MB_ICONEXCLAMATION);
        return(status);
    }

    status = RpcBindingFromStringBinding(pszStringBinding,
                                         &hYield);
    if (status) {
        sprintf(pszFail, "RpcBindingFromStringBinding failed: (0x%x)\nString = %s\n",
                status, pszStringBinding);
        MessageBox(hWnd,
                   pszFail,
                   "RPC Runtime Error",
                   MB_ICONEXCLAMATION);
        return(status);
    }

    fBound = TRUE;  // bind successful; reset flag

    return(status);
}

/**** end yieldc.c ****/


YIELDC.H

#ifdef WIN16
#define APIENTRY     PASCAL
#define UNREFERENCED_PARAMETER
#endif

#define IDM_ABOUT    100    /* menu */
#define IDM_BIND     200    /* menu */
#define IDM_WAIT     300    /* menu */
#define IDM_YIELD    400    /* menu */
#define IDM_EXIT     500    /* menu */

#define IDD_PROT_SEQ 201    /* control for Protocol Sequence */
#define IDD_NET_ADDR 203    /* control for Network Address   */
#define IDD_ENDPOINT 202    /* control for Endpoint          */
#define IDD_WAITTIME 301    /* control for # seconds to wait */

#define IDD_CUSTOM   403    /* radio button selection */
#define IDD_STD_USER 402    /* radio button selection */
#define IDD_STD_RPC  401    /* radio button selection */

#define NETLEN        32    /* network address length       */
#define PATHLEN      260    /* Path                         */
#define MSGLEN       300    /* error message in display box */
#define MAXPROTSEQ    20    /* protocol sequence "ncacn_np" */

#define DEFAULT_ENDPOINT    "\\pipe\\yield"
#define DEFAULT_PROT_SEQ    "ncacn_np"
#define DEFAULT_WAIT        15         /* # seconds to wait */

#define WM_RPC_YIELD_MESSAGE WM_USER+1

#define YIELD_START_MSG      "Yield period start..."
#define YIELD_END_MSG        "Yield period complete"
#define YIELD_PAINT_MSG      "Paint during yield..."

#define EXCEPT_MSG          "The remote procedure call raised an exception. \
Please make sure the server application is running and \
that the network address and endpoint are correct."

/* Function prototypes; same as Win SDK Generic sample */
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int);
BOOL InitApplication(HANDLE);
BOOL InitInstance(HANDLE, int);
LONG APIENTRY MainWndProc(HWND, UINT, UINT, LONG);

/* Function prototypes specific to this RPC Yield sample */
BOOL APIENTRY About(HWND, UINT, UINT, LONG);        /* dialog function */
BOOL APIENTRY GetBindInfo(HWND, UINT, UINT, LONG);  /* dialog function */
BOOL APIENTRY GetYieldInfo(HWND, UINT, UINT, LONG); /* dialog function */
BOOL APIENTRY GetWaitInfo(HWND, UINT, UINT, LONG);  /* dialog function */
RPC_STATUS Bind(HWND); /* utility that calls RPC API functions */


YIELDP.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                       yield Example

    FILE:       yieldp.c

    PURPOSE:    Remote procedures that are linked with the server
                side of RPC distributed application

    FUNCTIONS:  YieldProc() - sleeps the amount of time specified
                              by client to server

    COMMENTS:

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

#include <stdlib.h>
#include <stdio.h>
#include "yield.h"    // header file generated by MIDL compiler

void YieldProc(short cSeconds)
{
    printf("Calling Sleep for %d seconds...\n", cSeconds);
    Sleep(cSeconds * 1000);
    printf("Awake for next call...\n");
}

void Shutdown(void)
{
    RPC_STATUS status;

    printf("Calling RpcMgmtStopServerListening\n");
    status = RpcMgmtStopServerListening(NULL);
    printf("RpcMgmtStopServerListening returned: 0x%x\n", status);
    if (status) {
        exit(status);
    }

    printf("Calling RpcServerUnregisterIf\n");
    status = RpcServerUnregisterIf(NULL, NULL, FALSE);
    printf("RpcServerUnregisterIf returned 0x%x\n", status);
    if (status) {
        exit(status);
    }
}

/* end file yieldp.c */


YIELDS.C

/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996
                      yield Example

    FILE:   yields.c

    USAGE:  yields  -p protocol_sequence
                    -e endpoint
                    -m max calls
                    -n min calls
                    -f flag for RpcServerListen

    PURPOSE:    Server side of RPC distributed application yield

    FUNCTIONS:  main() - registers server as RPC server

    COMMENTS:   This sample program generates a linked list to
                demonstrate how the list can be transmitted over
                the network more efficiently as a sized array.
                The pointers are rebuilt on the server side.

                The [transmit_as] attribute (used in the typedef of
                DOUBLE_LINK_TYPE in the file yield.IDL) requires the
                four user-supplied functions whose names start with
                the name of the presented type, DOUBLE_LINK_TYPE.

                The [in, out] attributes applied to remote procedure
                parameters require the two user-supplied functions
                midl_user_allocate and midl_user_free.

                The other functions are utilities that are used to
                build or display the data structures.

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

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include "yield.h"    // header file generated by MIDL compiler

#define PURPOSE \
"This Microsoft RPC Version 2.0 sample program demonstrates\n\
the use of the [string] attribute. For more information\n\
about the attributes and the RPC API functions, see the\n\
RPC programming guide and reference.\n\n"

void Usage(char * pszProgramName)
{
    fprintf(stderr, "%s", PURPOSE);
    fprintf(stderr, "Usage:  %s\n", pszProgramName);
    fprintf(stderr, " -p protocol_sequence\n");
    fprintf(stderr, " -e endpoint\n");
    fprintf(stderr, " -m maxcalls\n");
    fprintf(stderr, " -n mincalls\n");
    fprintf(stderr, " -f flag_wait_op\n");
    exit(1);
}

void _CRTAPI1 main(int argc, char * argv[])
{
    RPC_STATUS status;
    unsigned char * pszProtocolSequence = "ncacn_np";
    unsigned char * pszSecurity     = NULL;
    unsigned char * pszEndpoint     = "\\pipe\\yield";
    unsigned int    cMinCalls       = 1;
    unsigned int    cMaxCalls       = 20;
    unsigned int    fDontWait       = FALSE;
    int i;

    // allow the user to override settings with command line switches
    for (i = 1; i < argc; i++) {
        if ((*argv[i] == '-') || (*argv[i] == '/')) {
            switch (tolower(*(argv[i]+1))) {
            case 'p':  // protocol sequence
                pszProtocolSequence = argv[++i];
                break;
            case 'e':
                pszEndpoint = argv[++i];
                break;
            case 'm':
                cMaxCalls = (unsigned int) atoi(argv[++i]);
                break;
            case 'n':
                cMinCalls = (unsigned int) atoi(argv[++i]);
                break;
            case 'f':
                fDontWait = (unsigned int) atoi(argv[++i]);
                break;
            case 'h':
            case '?':
            default:
                Usage(argv[0]);
            }
        }
        else
            Usage(argv[0]);
    }

    status = RpcServerUseProtseqEp(pszProtocolSequence,
                                   cMaxCalls,
                                   pszEndpoint,
                                   pszSecurity);  // Security descriptor
    printf("RpcServerUseProtseqEp returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    status = RpcServerRegisterIf(yield_ServerIfHandle, // interface to register
                                 NULL,   // MgrTypeUuid
                                 NULL);  // MgrEpv; null means use default
    printf("RpcServerRegisterIf returned 0x%x\n", status);
    if (status) {
        exit(status);
    }

    printf("Calling RpcServerListen\n");
    status = RpcServerListen(cMinCalls,
                             cMaxCalls,
                             fDontWait);
    printf("RpcServerListen returned: 0x%x\n", status);
    if (status) {
        exit(status);
    }

    if (fDontWait) {
        printf("Calling RpcMgmtWaitServerListen\n");
        status = RpcMgmtWaitServerListen();  //  wait operation
        printf("RpcMgmtWaitServerListen returned: 0x%x\n", status);
        if (status) {
            exit(status);
        }
    }

} /* end main() */

// ====================================================================
//                MIDL allocate and free
// ====================================================================

void __RPC_FAR * __RPC_USER midl_user_allocate(size_t len)
{
    return(malloc(len));
}

void __RPC_USER midl_user_free(void __RPC_FAR * ptr)
{
    free(ptr);
}

/* end yields.c */


README.TXT

Description:

This sample exercises MprAdmin API's against an NT 5.0 Dial-Up Server/Router
to enumerate info on connected ports and users, to retrieve and clear port
statistics, to reset and disconnect ports, and to retreive and set users'
dialin and callback privileges.

Platform: NT 5.0**
  ** or NT 4.0 + "Routing and Remote Access" add-on, which is available as a
     free download from www.microsoft.com

Requirements:

NT Server running Dial-Up Server/Router, configured with dialin ports.  The
dialin ports can be any valid media (e.g. modem, ISDN, PPTP, etc), as long
as a user can make a RAS connection to the server.

Source code:
admapit.c

Build instructions:
TBD

Usage:

1. Run this console application via the command-line:

ADMAPIT \\SERVERNAME

   where \\SERVERNAME is the computername of a local or remote NT Dial-Up
   Server/Router,   which is currently running and has ports configured
   for remote users to dialin.

2. Make a RAS connection to the server, and ADMAPIT will cycle through and
   enumerate port and user info, dumping statistics, resetting the port,
   manipulating user permissions, and finally disconnecting the RAS
   connection.  View results in the debugger and/or in three log files
   (*.LOG) which are created in the directory where ADMAPIT is run.

Limitations:

This sample is designed to run against an NT Server that is a member of an
NT Server domain.  If run against a standalone server, MprAdminGetPDCServer
will fail due to incorrect parameters, and consequently the API's
MprAdminUserGetInfo and MprAdminUserSetInfo will not get exercised.

List of API's exercised in the sample application:

MprAdminIsServiceRunning

MprAdminServerConnect
MprAdminServerDisconnect

MprAdminPortEnum
MprAdminPortGetInfo
MprAdminPortClearStats
MprAdminPortReset
MprAdminPortDisconnect

MprAdminConnectionEnum
MprAdminConnectionGetInfo
MprAdminConnectionClearStats

MprAdminGetPDCServer
MprAdminUserGetInfo
MprAdminUserSetInfo

MprAdminBufferFree

MprAdminGetErrorString


ADMAPIT.C

/******************************************************************************
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1992 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This sample source code is only intended as a supplement to
*       MprAdmin API usage and is meant to help users with the
*       MprAdmin API calling convention. Use this code at your own risk.
*       Microsoft disclaims any implied warranty. This sample code can
*       be copied and distributed.

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

/********************************************************************
*  admapit.c -- Sample program demonstrating the use of different
*                     MprAdmin API
*
*  Comments:
*
* This program expects \\Servername  as its arguments.
*
* MprAdminGetErrorString API returns error string. However
* this API may return an Error 87 (Invalid Parameter) for the non
* RAS error codes. This API is designed for only RAS related error codes.
* For more Info. check the documentation. I am using this API for all the
* errors but I print the error codes before calling this API
*
*****************************************************************************/
/***************************************************************************
*  Functions:
*      Init2(void);
*      Init1(void);
*      PrintGetStringError(DWORD dwResult);
*      UserPrivilege(WCHAR *DomainName, WCHAR *UserName);
*      PrintPriv(WCHAR *DomainName, WCHAR *UserName);
*      PrintClearStats(WCHAR * RasSrv, WCHAR *wszPortName);
*      WINAPI RasAdminClearDisc(LPVOID param);
*      IsServiceRunning(WCHAR *RasSrv);
*      Debug_Print_Connection0(RAS_CONNECTION_0 *pRasConnection0);
*      Debug_Print_Connection1(RAS_CONNECTION_1 *pRasConnection1);
*      Debug_Print_RAS_PORT0(RAS_PORT_0 *pRasPort0);
*      Debug_Print_RAS_PORT1(RAS_PORT_1 *pRasPort1);
*      File_Print_Connection0(RAS_CONNECTION_0 *pRasConnection0);
*      File_Print_Connection1(RAS_CONNECTION_1 *pRasConnection1);
*      File_Print_RAS_PORT0(RAS_PORT_0 *pRasPort0);
*      File_Print_RAS_PORT1(RAS_PORT_1 *pRasPort1);
*      ProcPortClearStat(HANDLE hPort);
*      ProcConnectionClearStat(HANDLE hConnection);
*      ProcPortReset(HANDLE hPort);
*      ProcPortDisconnect(HANDLE hPort);
*      ProcConnectionGetInfo(HANDLE hConnection);
*      ProcPortGetInfo(HANDLE hPort);
*****************************************************************************/

#define  sleeptime 10000

#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include <stdlib.h>
#include <lm.h>
#include <stdio.h>
#include <time.h>
#include <ctype.h>
#include <conio.h>
#include <mprapi.h>
#include <memory.h>

//Function Definitions
VOID Init2(void);
VOID Init1(void);
VOID PrintGetStringError(DWORD dwResult);
VOID UserPrivilege(WCHAR *DomainName, WCHAR *UserName);
VOID PrintPriv(WCHAR *DomainName, WCHAR *UserName);
VOID PrintClearStats(WCHAR * RasSrv, WCHAR *wszPortName);
DWORD WINAPI RasAdminClearDisc(LPVOID param);
INT IsServiceRunning(WCHAR *RasSrv);
VOID Debug_Print_Connection0(RAS_CONNECTION_0 *pRasConnection0);
VOID Debug_Print_Connection1(RAS_CONNECTION_1 *pRasConnection1);
VOID Debug_Print_RAS_PORT0(RAS_PORT_0 *pRasPort0);
VOID Debug_Print_RAS_PORT1(RAS_PORT_1 *pRasPort1);
VOID File_Print_Connection0(RAS_CONNECTION_0 *pRasConnection0);
VOID File_Print_Connection1(RAS_CONNECTION_1 *pRasConnection1);
VOID File_Print_RAS_PORT0(RAS_PORT_0 *pRasPort0);
VOID File_Print_RAS_PORT1(RAS_PORT_1 *pRasPort1);
VOID ProcPortClearStat(HANDLE hPort);
VOID ProcConnectionClearStat(HANDLE hConnection);
VOID ProcPortReset(HANDLE hPort);
VOID ProcPortDisconnect(HANDLE hPort);
VOID ProcConnectionGetInfo(HANDLE hConnection);
VOID ProcPortGetInfo(HANDLE hPort);

// Global Variables
WCHAR*
StrDupWFromA(
    CHAR* psz );

#define Number_Of_Flags 6

HANDLE LogFile  = NULL;
HANDLE cLogFile = NULL;
HANDLE ErrLogFile = NULL;

CHAR buf[120];
BOOL First_Call = TRUE;
DWORD Status = 0;
DWORD dwResult = 0;
DWORD dwBytesWritten = 0;
CHAR szBuf[120];
BOOL ErrLogInValid = FALSE;
BOOL cErrLogInValid = FALSE;
BOOL LogInValid = TRUE;
WCHAR szUserAccountServer[UNLEN+1];
PRAS_USER_0 ppRasUser0;
RAS_USER_0 RasUser0;
WCHAR lpszServerName[50];
WCHAR RasSrv[64];
BOOL Quit=FALSE;

RAS_SERVER_HANDLE  phRasServer;

// Function Starts
WCHAR*
StrDupWFromA(
    CHAR* psz )

    /* Returns heap block containing a copy of 0-terminated string 'psz' or
    ** NULL on error or if 'psz' is NULL.  The output string is converted to
    ** UNICODE.  It is caller's responsibility to Free the returned string.
    */
{
   WCHAR* pszNew = NULL;

   if (psz)
   {
      DWORD cb;

      cb = MultiByteToWideChar( CP_ACP, 0, psz, -1, NULL, 0 );

      pszNew = malloc( cb * sizeof(TCHAR) );
      if (!pszNew)
      {
         return NULL;
      }

      cb = MultiByteToWideChar( CP_ACP, 0, psz, -1, pszNew, cb );
      if (cb == 0)
      {
         free( pszNew );
         return NULL;
      }
   }
   return pszNew;
}

//
// Format and write debug information to OutputDebugString
//
ULONG
_cdecl
DbgPrint(
    PCH Format,
    ...
    )
{
   CHAR szbuffer[255];
   WCHAR buffer[255];
   WCHAR *temp = NULL;
   LPINT lpResult=0;
   va_list marker;
   va_start (marker,Format);
   if (IsTextUnicode(marker, sizeof(marker),lpResult))
   {
      temp = StrDupWFromA(Format);
      wvsprintfW (buffer,temp, marker);
      OutputDebugString (buffer);
      free(temp);
   }
   else
   {
      wvsprintfA (szbuffer,Format, marker);
      temp=StrDupWFromA(szbuffer);
      OutputDebugString (temp);
      free(temp);
   }
   return TRUE;
}

//
// Init Functions to setup logfiles
//

VOID Init1(void)
{
   //Store port  information in this file
   LogInValid = FALSE;
   LogFile = CreateFile(TEXT("Admin.log"),
                        GENERIC_READ|GENERIC_WRITE,
                        FILE_SHARE_READ,
                        (LPSECURITY_ATTRIBUTES) NULL,
                        CREATE_ALWAYS,
                        0,
                        (HANDLE) NULL);

   if (LogFile== INVALID_HANDLE_VALUE)
   {
      LogInValid = TRUE;
   }

}

VOID Init2(void)
{
   cErrLogInValid = FALSE;
   ErrLogInValid = FALSE;
   //Stores Statistics and port disconnect information in this file
   cLogFile = CreateFile(TEXT("AdminStats.log"),
                         GENERIC_READ|GENERIC_WRITE,
                         FILE_SHARE_READ,
                         (LPSECURITY_ATTRIBUTES) NULL,
                         CREATE_ALWAYS,
                         0,
                         (HANDLE) NULL);
   if (cLogFile== INVALID_HANDLE_VALUE)
   {
      cErrLogInValid = TRUE;
   }

   //Stores user privilege information in this file
   ErrLogFile = CreateFile(TEXT("AdminUser.log"),
                           GENERIC_READ|GENERIC_WRITE,
                           FILE_SHARE_READ,
                           (LPSECURITY_ATTRIBUTES) NULL,
                           CREATE_ALWAYS,
                           0,
                           (HANDLE) NULL);

   if (ErrLogFile== INVALID_HANDLE_VALUE)
   {
      ErrLogInValid = TRUE;
   }

}

//
// Function to print error strings
//
VOID PrintGetStringError(DWORD dwResult)
{
CHAR  szBuf[120];
DWORD dwBytesWritten = 0;

   DbgPrint("ERROR  MprAdminGetErrorString      %d\n",dwResult);
   sprintf(szBuf, "ERROR  MprAdminGetErrorString      %d\n",dwResult);
   WriteFile(LogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
}

//
// Function to see if the service running
//
INT IsServiceRunning(WCHAR *RasSrv)
{
DWORD dwResult;
CHAR  szBuf[120];
DWORD dwBytesWritten = 0;

   //Calling MprAdminIsServiceRunning
   // Parameters:
   // IN LPWSTR   * RasSrv
   dwResult = MprAdminIsServiceRunning(RasSrv);
   if (dwResult)
   {
      DbgPrint("Ras AND Router Serivce running on the Server\n");
      sprintf(szBuf, "Ras AND Router Serivce running on the Server\n");
      WriteFile(LogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
      //Calling MprAdminServerConnect
      // Parameters
      // IN LPWSTR    * RasSrv
      // OUT RAS_SERVER_HANDLE * phRasServer
      dwResult = MprAdminServerConnect(RasSrv, &phRasServer);
      if (dwResult != NO_ERROR)
      {
         DbgPrint("Failed to Connect to Server\n");
         sprintf(szBuf, "Failed to Connect to Server\n");
         WriteFile(LogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
         return 0;
      }
   }
   else
   {
      DbgPrint("Ras AND Router Serivce is NOT running on the Server\n");
      sprintf(szBuf, "Ras AND Router Serivce is NOT running on the Server\n");
      WriteFile(LogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
      return 0;
   }
   return 1;
}

//
// Function to print user privileges to logfile
//
VOID PrintPriv(WCHAR *DomainName, WCHAR *UserName)
{
//DWORD Permission;
DWORD dwBytesWritten = 0;
CHAR  szBuf[120];

     /*********** from mprapi.h ****************************************************/
     //
     // Note: Bit 0 MUST represent NoCallback due to a quirk of the "userparms"
     //       storage method.  When a new LAN Manager user is created, bit 0 of the
     //       userparms field is set to 1 and all other bits are 0.  These bits are
     //       arranged so this "no Dial-In info" state maps to the "default Dial-In
     //       privilege" state.
     //
     // #define RASPRIV_NoCallback        0x01
     // #define RASPRIV_AdminSetCallback  0x02
     // #define RASPRIV_CallerSetCallback 0x04
     // #define RASPRIV_DialinPrivilege   0x08
     //
     // #define RASPRIV_CallbackType (RASPRIV_AdminSetCallback \
     //                             | RASPRIV_CallerSetCallback \
     //                             | RASPRIV_NoCallback)
     /*****************************************************************************/

     sprintf(szBuf,"\nDomainName =  %ws\nUserName = %ws\n",DomainName, UserName);
     WriteFile(ErrLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);

     sprintf(szBuf,"ppRasUser0->bfPrivilege = 0x%x\n",ppRasUser0->bfPrivilege);
     WriteFile(ErrLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);

     //
     // display the privilege flags
     //
     if ((ppRasUser0->bfPrivilege) & (RASPRIV_NoCallback))
     {
        sprintf(szBuf, "\t\tRASPRIV_NoCallback        = 0x%x\n", (ppRasUser0->bfPrivilege & (RASPRIV_NoCallback)));
        WriteFile(ErrLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
     }
     if ((ppRasUser0->bfPrivilege) & (RASPRIV_AdminSetCallback))
     {
        sprintf(szBuf, "\t\tRASPRIV_AdminSetCallback  = 0x%x\n", (ppRasUser0->bfPrivilege & (RASPRIV_AdminSetCallback)));
        WriteFile(ErrLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
     }
     if ((ppRasUser0->bfPrivilege) & (RASPRIV_CallerSetCallback))
     {
        sprintf(szBuf, "\t\tRASPRIV_CallerSetCallback = 0x%x\n", (ppRasUser0->bfPrivilege & (RASPRIV_CallerSetCallback)));
        WriteFile(ErrLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
     }
     if ((ppRasUser0->bfPrivilege) & (RASPRIV_DialinPrivilege))
     {
        sprintf(szBuf, "\t\tRASPRIV_DialinPrivilege   = 0x%x\n", (ppRasUser0->bfPrivilege & (RASPRIV_DialinPrivilege)));
        WriteFile(ErrLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
     }
}

//
// Function to get and change User Dialin Privileges.
// It goes through all the Dialin Privileges for a user.
//
VOID UserPrivilege(WCHAR *DomainName, WCHAR *UserName)
{

DWORD dwResult;
CHAR  szBuf[120];
DWORD dwBytesWritten = 0;
WCHAR Buf[512];
WCHAR *lpszString;
static DWORD LoopPriv = 0;
static BYTE Dialin = 1;
lpszString = &Buf[0];
ppRasUser0 = &RasUser0;

   // API Called:     MprAdminGetPDCServer
   // Parameters:
   //  IN WCHAR * lpwsDomainName
   //  IN WCHAR * lpwsServerName
   //  OUT WCHAR * lpwsPDCServer
   dwResult = MprAdminGetPDCServer(DomainName, NULL, &szUserAccountServer[0]);
   if (dwResult == NO_ERROR)
   {
      // API Called:     MprAdminUserGetInfo
      // Parameters:
      // IN WCHAR * lpwsServerName
      // IN WCHAR * lpwsUserName
      // IN DWORD dwLevel
      // OUT LPBYTE *lpbBuffer
      dwResult = MprAdminUserGetInfo(szUserAccountServer, UserName,(DWORD) 0, (LPBYTE) ppRasUser0);
      if (dwResult != NO_ERROR)
      {
         sprintf(szBuf,"ERROR MprAdminUserGetInfo     %d\n",dwResult);
         WriteFile(ErrLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
         sprintf(szBuf,"Parameters in:\n\tlpszServer=%ws\n\tlpszUser=%ws\n\tdwLevel=0,\n\tlpbBuffer)\n",
                        szUserAccountServer, UserName);
         WriteFile(ErrLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);

         dwResult = MprAdminGetErrorString( dwResult, &lpszString);
         if (dwResult == NO_ERROR)
         {
            sprintf(szBuf, "ERROR  MprAdminUserGetInfo: string from MprAdminGetErrorString:  %ws\n",lpszString);
            WriteFile(ErrLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
         }
         else
            PrintGetStringError(dwResult);
      }
      else //MprAdminUserGetInfo returned success
      {
         PrintPriv(szUserAccountServer, UserName);
         //
         // cycle thru various privilege values
         //
         if (LoopPriv > 3)
         {
            LoopPriv = 0;
            Dialin = 1;
         }
         if (LoopPriv < 4)
         {
            if (LoopPriv == 3) //invalid value - never true
               wcscpy(ppRasUser0->wszPhoneNumber, TEXT("99999999"));

            if (LoopPriv == 0)
            {
               ppRasUser0->bfPrivilege =  Dialin;
               Dialin = Dialin * 2;
               LoopPriv++;
            }
            else
            {
               ppRasUser0->bfPrivilege = (RASPRIV_DialinPrivilege | Dialin  );
               Dialin = Dialin * 2 ;
               LoopPriv++;
            }
         }
         // API Called:     MprAdminUserSetInfo
         // Parameters:
         // IN WCHAR * lpwsServerName
         // IN WCHAR * lpwsUserName
         // IN DWORD dwLevel
         // IN LPBYTE lpbBuffer
         dwResult = MprAdminUserSetInfo(szUserAccountServer, UserName, 0, (LPBYTE)ppRasUser0);
         if (dwResult != NO_ERROR)
         {
            sprintf(szBuf,"ERROR: MprAdminUserSetInfo     %d\n",dwResult);
            WriteFile(ErrLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);

            sprintf(szBuf,"Parameters in:\n\tlpszServer=%ws\n\tlpszUser=%ws\n\tdwLevel=0,\n\tlpbBuffer)\n",
                           szUserAccountServer, UserName);
            WriteFile(ErrLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
            // dump this out to see what privileges were passed in UserSetInfo call
            PrintPriv(szUserAccountServer, UserName);

            dwResult = MprAdminGetErrorString( dwResult, &lpszString);
            if (dwResult == ERROR_SUCCESS)
            {
               sprintf(szBuf, "ERROR  MprAdminUserSetInfo: string from MprAdminGetErrorString:  %ws\n",lpszString);
               WriteFile(ErrLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
            }
            else
               PrintGetStringError(dwResult);
         }
         // API Called:     MprAdminUserSetInfo
         // Parameters:
         // IN WCHAR * lpwsServerName
         // IN WCHAR * lpwsUserName
         // IN DWORD dwLevel
         // IN LPBYTE lpbBuffer
         if (LoopPriv == 0)
         {
            ppRasUser0->bfPrivilege = (ppRasUser0->bfPrivilege | RASPRIV_DialinPrivilege);
            dwResult = MprAdminUserSetInfo(szUserAccountServer, UserName,0,(LPBYTE) ppRasUser0);
            if (dwResult == ERROR_SUCCESS)
            {
               PrintPriv(DomainName,UserName);
            }
            else //MprAdminUserSetInfo failed
            {
               sprintf(szBuf,"ERROR  MprAdminUserSetInfo     %d\n",dwResult);
               WriteFile(ErrLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
               sprintf(szBuf,"Parameters in:\n\tlpszServer=%ws\n\tlpszUser=%ws\n\tdwLevel=0,\n\tlpbBuffer)\n",
                              szUserAccountServer, UserName);
               WriteFile(ErrLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
               // dump this out to see what privileges were passed in UserSetInfo call
               PrintPriv(szUserAccountServer, UserName);

               dwResult = MprAdminGetErrorString(dwResult, &lpszString);
               if (dwResult == NO_ERROR)
               {
                  sprintf(szBuf, "ERROR  MprAdminUserSetInfo: error string from MprAdminGetErrorString:  %ws\n",lpszString);
                  WriteFile(ErrLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
               }
               else
                  PrintGetStringError(dwResult);
            }
         }

         //Free up the buffer from previous MprAdminUserGetInfo
         //dwResult=MprAdminBufferFree(ppRasUser0);

         // API Called:     RasAdmimUserGetInfo
         // Parameters:
         // IN WCHAR * lpwsServerName
         // IN WCHAR * lpwsUserName
         // IN DWORD dwLevel
         // OUT LPBYTE *lpbBuffer
         dwResult = MprAdminUserGetInfo(szUserAccountServer, UserName,0,(LPBYTE) ppRasUser0);
         if (dwResult != NO_ERROR)
         {
            sprintf(szBuf,"ERROR: MprAdminUserGetInfo     %d\n",dwResult);
            WriteFile(ErrLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
            sprintf(szBuf,"Parameters in:\n\tlpszServer=%ws\n\tlpszUser=%ws\n\tdwLevel=0,\n\tlpbBuffer)\n",
                           szUserAccountServer, UserName);
            WriteFile(ErrLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
            dwResult = MprAdminGetErrorString( dwResult, &lpszString);
            if (dwResult == ERROR_SUCCESS)
            {
               sprintf(szBuf, "ERROR  MprAdminUserGetInfo: error string from MprAdminGetErrorString:  %ws\n",lpszString);
               WriteFile(ErrLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
            }
            else
               PrintGetStringError(dwResult);
         }

      } // end of loop for MprAdminUserGetInfo returning success

   }
   else //MprAdminGetPDCServer returned an error
   {
      DbgPrint("ERROR  MprAdminGetPDCServer     %d\n",dwResult);
      sprintf(szBuf,"ERROR  MprAdminGetPDCServer     %d\n",dwResult);
      WriteFile(ErrLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
      sprintf(szBuf,"Parameters in:\n\tlpszDomain=%ws\n\tlpszServer=NULL\n", DomainName);
      WriteFile(ErrLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);

      dwResult = MprAdminGetErrorString(dwResult, &lpszString);
      if (dwResult == NO_ERROR)
      {
         sprintf(szBuf, "ERROR  MprAdminGetPDCServer      %ws\n",lpszString);
         WriteFile(ErrLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
      }
      else
         PrintGetStringError(dwResult);
   }

}  //end UserPrivilege()

//
// dump connection0 struct to debug output
//
VOID Debug_Print_Connection0(RAS_CONNECTION_0 *pRasConnection0)
{
WCHAR *Interface_Type[] =
   {
   TEXT("ROUTER_IF_TYPE_CLIENT"),
   TEXT("ROUTER_IF_TYPE_HOME_ROUTER"),
   TEXT("ROUTER_IF_TYPE_FULL_ROUTER"),
   TEXT("ROUTER_IF_TYPE_DEDICATED"),
   TEXT("ROUTER_IF_TYPE_INTERNAL"),
   TEXT("ROUTER_IF_TYPE_INTERNAL")
   };

   //dumping pRasConnection0
   DbgPrint("pRasConnection0->hConnection  0x%x\n",pRasConnection0->hConnection);
   DbgPrint("pRasConnection0->hInterface   0x%x\n",pRasConnection0->hInterface);
   DbgPrint("pRasConnection0->dwConnectDuration  %d\n",pRasConnection0->dwConnectDuration);
   DbgPrint("pRasConnection0->dwInterfaceType  %ws\n",Interface_Type[pRasConnection0->dwInterfaceType]);
   DbgPrint("pRasConnection0->dwConnectionFlags  %d\n",pRasConnection0->dwConnectionFlags);
   DbgPrint("pRasConnection0->wszInterfaceName  %ws\n",pRasConnection0->wszInterfaceName);
   DbgPrint("pRasConnection0->wszUserName  %ws\n",pRasConnection0->wszUserName);
   DbgPrint("pRasConnection0->wszLogonDomain  %ws\n",pRasConnection0->wszLogonDomain);
   DbgPrint("pRasConnection0->wszRemoteComputer  %ws\n",pRasConnection0->wszRemoteComputer);

}

//
// dump connection1 struct to debug output
//
VOID Debug_Print_Connection1(RAS_CONNECTION_1 *pRasConnection1)
{
    //dumping pRasConnection1
    DbgPrint( "\n\npRasConnection1->hConnection  0x%x\n",pRasConnection1->hConnection);
    DbgPrint( "pRasConnection1->hInterface   0x%x\n",pRasConnection1->hInterface);
    DbgPrint( "pRasConnection1->PppInfo.nbf.dwError %d\n",pRasConnection1->PppInfo.nbf.dwError);
    DbgPrint( "pRasConnection1->PppInfo.nbf.wszWksta %ws\n",pRasConnection1->PppInfo.nbf.wszWksta);
    DbgPrint( "pRasConnection1->PppInfo.ip.dwError %d\n",pRasConnection1->PppInfo.ip.dwError);
    DbgPrint( "pRasConnection1->PppInfo.ip.wszAddress %ws\n",pRasConnection1->PppInfo.ip.wszAddress);
    DbgPrint( "pRasConnection1->PppInfo.ip.wszRemoteAddress %ws\n",pRasConnection1->PppInfo.ip.wszRemoteAddress);
    DbgPrint( "pRasConnection1->PppInfo.ipx.dwError %d\n",pRasConnection1->PppInfo.ipx.dwError);
    DbgPrint( "pRasConnection1->PppInfo.ipx.wszAddress %ws\n",pRasConnection1->PppInfo.ipx.wszAddress);
    DbgPrint( "pRasConnection1->PppInfo.at.dwError  %d\n",pRasConnection1->PppInfo.at.dwError );
    DbgPrint( "pRasConnection1->PppInfo.at.wszAddress %ws\n",pRasConnection1->PppInfo.at.wszAddress);

    //connection stats
    DbgPrint( "pRasConnection1->dwBytesXmited=%d\n", pRasConnection1->dwBytesXmited );
    DbgPrint( "pRasConnection1->dwBytesRcved=%d\n", pRasConnection1->dwBytesRcved );
    DbgPrint( "pRasConnection1->dwFramesXmited=%d\n", pRasConnection1->dwFramesXmited );
    DbgPrint( "pRasConnection1->dwFramesRcved=%d\n", pRasConnection1->dwFramesRcved);
    DbgPrint( "pRasConnection1->dwCrcErr=%d\n", pRasConnection1->dwCrcErr);
    DbgPrint( "pRasConnection1->dwTimeoutErr=%d\n", pRasConnection1->dwTimeoutErr);
    DbgPrint( "pRasConnection1->dwAlignmentErr=%d\n", pRasConnection1->dwAlignmentErr);
    DbgPrint( "pRasConnection1->dwHardwareOverrunErr=%d\n", pRasConnection1->dwHardwareOverrunErr);
    DbgPrint( "pRasConnection1->dwFramingErr=%d\n", pRasConnection1->dwFramingErr);
    DbgPrint( "pRasConnection1->dwBufferOverrunErr=%d\n", pRasConnection1->dwBufferOverrunErr);
}

//
// dump port0 struct to debug output
//
VOID Debug_Print_RAS_PORT0(RAS_PORT_0 *pRasPort0)
{
WCHAR *Line_Condition[] =
   {
   TEXT("RAS_PORT_NON_OPERATIONAL"),
   TEXT("RAS_PORT_DISCONNECTED"),
   TEXT("RAS_PORT_CALLING_BACK"),
   TEXT("RAS_PORT_LISTENING"),
   TEXT("RAS_PORT_AUTHENTICATING"),
   TEXT("RAS_PORT_AUTHENTICATED"),
   TEXT("RAS_PORT_INITIALIZING")
   };

WCHAR *Hdw_Error[] =
   {
   TEXT("RAS_HARDWARE_OPERATIONAL"),
   TEXT("RAS_HARDWARE_FAILURE")
   };

   //dumping pRasPort0
   DbgPrint("pRasPort0->hPort  %d\n",pRasPort0->hPort);
   DbgPrint("pRasPort0->hConnection   0x%x\n",pRasPort0->hConnection);
   DbgPrint("pRasPort0->dwPortCondition  %ws\n",Line_Condition[pRasPort0->dwPortCondition-1]);
   DbgPrint("pRasPort0->dwTotalNumberOfCalls  %d\n",pRasPort0->dwTotalNumberOfCalls);
   DbgPrint("pRasPort0->dwConnectDuration  %d\n",pRasPort0->dwConnectDuration);
   DbgPrint("pRasPort0->wszPortName  %ws\n",pRasPort0->wszPortName);
   DbgPrint("pRasPort0->wszMediaName  %ws\n",pRasPort0->wszMediaName);
   DbgPrint("pRasPort0->wszDeviceName  %ws\n",pRasPort0->wszDeviceName);
   DbgPrint("pRasPort0->wszDeviceType  %ws\n",pRasPort0->wszDeviceType);

}

//
// dump port1 struct to debug output
//
VOID Debug_Print_RAS_PORT1(RAS_PORT_1 *pRasPort1)
{
WCHAR *Line_Condition[] =
   {
   TEXT("RAS_PORT_NON_OPERATIONAL"),
   TEXT("RAS_PORT_DISCONNECTED"),
   TEXT("RAS_PORT_CALLING_BACK"),
   TEXT("RAS_PORT_LISTENING"),
   TEXT("RAS_PORT_AUTHENTICATING"),
   TEXT("RAS_PORT_AUTHENTICATED"),
   TEXT("RAS_PORT_INITIALIZING")
   };

WCHAR *Hdw_Error[] =
   {
   TEXT("RAS_HARDWARE_OPERATIONAL"),
   TEXT("RAS_HARDWARE_FAILURE")
   };

   //dumping pRasPort1
   DbgPrint("pRasPort1->hPort  %d\n",pRasPort1->hPort);
   DbgPrint("pRasPort1->hConnection   0x%x\n",pRasPort1->hConnection);
   DbgPrint("pRasPort1->dwHardwareCondition  %ws\n",Hdw_Error[pRasPort1->dwHardwareCondition]);

   //connection stats
   DbgPrint( "pRasPort1->dwLineSpeed=%d\n", pRasPort1->dwLineSpeed );
   DbgPrint( "pRasPort1->dwBytesXmited=%d\n", pRasPort1->dwBytesXmited );
   DbgPrint( "pRasPort1->dwBytesRcved=%d\n", pRasPort1->dwBytesRcved );
   DbgPrint( "pRasPort1->dwFramesXmited=%d\n", pRasPort1->dwFramesXmited );
   DbgPrint( "pRasPort1->dwFramesRcved=%d\n", pRasPort1->dwFramesRcved);
   DbgPrint( "pRasPort1->dwCrcErr=%d\n", pRasPort1->dwCrcErr);
   DbgPrint( "pRasPort1->dwTimeoutErr=%d\n", pRasPort1->dwTimeoutErr);
   DbgPrint( "pRasPort1->dwAlignmentErr=%d\n", pRasPort1->dwAlignmentErr);
   DbgPrint( "pRasPort1->dwHardwareOverrunErr=%d\n", pRasPort1->dwHardwareOverrunErr);
   DbgPrint( "pRasPort1->dwFramingErr=%d\n", pRasPort1->dwFramingErr);
   DbgPrint( "pRasPort1->dwBufferOverrunErr=%d\n", pRasPort1->dwBufferOverrunErr);
}

//
// dump connection0 struct to logfile
//
VOID File_Print_Connection0(RAS_CONNECTION_0 *pRasConnection0)
{
WCHAR *Interface_Type[] =
   {
   TEXT("ROUTER_IF_TYPE_CLIENT"),
   TEXT("ROUTER_IF_TYPE_HOME_ROUTER"),
   TEXT("ROUTER_IF_TYPE_FULL_ROUTER"),
   TEXT("ROUTER_IF_TYPE_DEDICATED"),
   TEXT("ROUTER_IF_TYPE_INTERNAL"),
   TEXT("ROUTER_IF_TYPE_INTERNAL")
   };

   //dumping pRasConnection0
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf,"dumping pRasConnection0\n\n");
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf,"pRasConnection0->hConnection  0x%x\n",pRasConnection0->hConnection);
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf,"pRasConnection0->hInterface   0x%x\n",pRasConnection0->hInterface);
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf,"pRasConnection0->dwConnectDuration  %d\n",pRasConnection0->dwConnectDuration);
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf,"pRasConnection0->dwInterfaceType  %ws\n",Interface_Type[pRasConnection0->dwInterfaceType]);
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf,"pRasConnection0->dwConnectionFlags  %d\n",pRasConnection0->dwConnectionFlags);
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf,"pRasConnection0->wszInterfaceName  %ws\n",pRasConnection0->wszInterfaceName);
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf,"pRasConnection0->wszUserName  %ws\n",pRasConnection0->wszUserName);
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf,"pRasConnection0->wszLogonDomain  %ws\n",pRasConnection0->wszLogonDomain);
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf,"pRasConnection0->wszRemoteComputer  %ws\n",pRasConnection0->wszRemoteComputer);
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);

}

//
// dump connection1 info to logfile
//
VOID File_Print_Connection1(RAS_CONNECTION_1 *pRasConnection1)
{

   //dumping pRasConnection1
   sprintf(buf,"dumping pRasConnection1\n");
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf, "\n\npRasConnection1->hConnection  0x%x\n",pRasConnection1->hConnection);
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf, "pRasConnection1->hInterface   0x%x\n",pRasConnection1->hInterface);
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf, "pRasConnection1->PppInfo.nbf.dwError %d\n",pRasConnection1->PppInfo.nbf.dwError);
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf, "pRasConnection1->PppInfo.nbf.wszWksta %ws\n",pRasConnection1->PppInfo.nbf.wszWksta);
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf, "pRasConnection1->PppInfo.ip.dwError %d\n",pRasConnection1->PppInfo.ip.dwError);
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf, "pRasConnection1->PppInfo.ip.wszAddress %ws\n",pRasConnection1->PppInfo.ip.wszAddress);
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf, "pRasConnection1->PppInfo.ip.wszRemoteAddress %ws\n",pRasConnection1->PppInfo.ip.wszRemoteAddress);
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);

sprintf(buf, "pRasConnection1->PppInfo.ipx.dwError %d\n",pRasConnection1->PppInfo.ipx.dwError);
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf, "pRasConnection1->PppInfo.ipx.wszAddress %ws\n",pRasConnection1->PppInfo.ipx.wszAddress);
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf, "pRasConnection1->PppInfo.at.dwError  %d\n",pRasConnection1->PppInfo.at.dwError );
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf, "pRasConnection1->PppInfo.at.wszAddress %ws\n",pRasConnection1->PppInfo.at.wszAddress);

   //connection stats
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf, "pRasConnection1->dwBytesXmited=%d\n", pRasConnection1->dwBytesXmited );
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf, "pRasConnection1->dwBytesRcved=%d\n", pRasConnection1->dwBytesRcved );
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf, "pRasConnection1->dwFramesXmited=%d\n", pRasConnection1->dwFramesXmited );
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf, "pRasConnection1->dwFramesRcved=%d\n", pRasConnection1->dwFramesRcved);
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf, "pRasConnection1->dwCrcErr=%d\n", pRasConnection1->dwCrcErr);
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf, "pRasConnection1->dwTimeoutErr=%d\n", pRasConnection1->dwTimeoutErr);
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf, "pRasConnection1->dwAlignmentErr=%d\n", pRasConnection1->dwAlignmentErr);
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf, "pRasConnection1->dwHardwareOverrunErr=%d\n", pRasConnection1->dwHardwareOverrunErr);
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf, "pRasConnection1->dwFramingErr=%d\n", pRasConnection1->dwFramingErr);
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf, "pRasConnection1->dwBufferOverrunErr=%d\n", pRasConnection1->dwBufferOverrunErr);
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
}

//
// dump port0 info to logfile
//
VOID File_Print_RAS_PORT0(RAS_PORT_0 *pRasPort0)
{
WCHAR *Line_Condition[] =
   {
   TEXT("RAS_PORT_NON_OPERATIONAL"),
   TEXT("RAS_PORT_DISCONNECTED"),
   TEXT("RAS_PORT_CALLING_BACK"),
   TEXT("RAS_PORT_LISTENING"),
   TEXT("RAS_PORT_AUTHENTICATING"),
   TEXT("RAS_PORT_AUTHENTICATED"),
   TEXT("RAS_PORT_INITIALIZING")
   };

WCHAR *Hdw_Error[] =
   {
   TEXT("RAS_HARDWARE_OPERATIONAL"),
   TEXT("RAS_HARDWARE_FAILURE")
   };

   //dumping pRasPort0 to a file
   sprintf(buf,"dumping pRasPort0 to file\n");
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf,"pRasPort0->hPort  %d\n",pRasPort0->hPort);
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf,"pRasPort0->hConnection   0x%x\n",pRasPort0->hConnection);
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf,"pRasPort0->dwPortCondition  %ws\n",Line_Condition[pRasPort0->dwPortCondition-1]);
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf,"pRasPort0->dwTotalNumberOfCalls  %d\n",pRasPort0->dwTotalNumberOfCalls);
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf,"pRasPort0->dwConnectDuration  %d\n",pRasPort0->dwConnectDuration);
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf,"pRasPort0->wszPortName  %ws\n",pRasPort0->wszPortName);
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf,"pRasPort0->wszMediaName  %ws\n",pRasPort0->wszMediaName);
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf,"pRasPort0->wszDeviceName  %ws\n",pRasPort0->wszDeviceName);
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf,"pRasPort0->wszDeviceType  %ws\n",pRasPort0->wszDeviceType);
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);

}

//
// dump port1 info to logfile
//
VOID File_Print_RAS_PORT1(RAS_PORT_1 *pRasPort1)
{
WCHAR *Line_Condition[] =
   {
   TEXT("RAS_PORT_NON_OPERATIONAL"),
   TEXT("RAS_PORT_DISCONNECTED"),
   TEXT("RAS_PORT_CALLING_BACK"),
   TEXT("RAS_PORT_LISTENING"),
   TEXT("RAS_PORT_AUTHENTICATING"),
   TEXT("RAS_PORT_AUTHENTICATED"),
   TEXT("RAS_PORT_INITIALIZING")
   };

WCHAR *Hdw_Error[] =
   {
   TEXT("RAS_HARDWARE_OPERATIONAL"),
   TEXT("RAS_HARDWARE_FAILURE")
   };

   //dumping pRasPort1
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf,"\n dumping pRasPort1\n");
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf,"pRasPort1->hPort  %d\n",pRasPort1->hPort);
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf,"pRasPort1->hConnection   0x%x\n",pRasPort1->hConnection);
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf,"pRasPort1->dwHardwareCondition  %ws\n",Hdw_Error[pRasPort1->dwHardwareCondition]);

   //connection stats
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf, "pRasPort1->dwLineSpeed=%d\n", pRasPort1->dwLineSpeed );
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf, "pRasPort1->dwBytesXmited=%d\n", pRasPort1->dwBytesXmited );
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf, "pRasPort1->dwBytesRcved=%d\n", pRasPort1->dwBytesRcved );
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf, "pRasPort1->dwFramesXmited=%d\n", pRasPort1->dwFramesXmited );
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf, "pRasPort1->dwFramesRcved=%d\n", pRasPort1->dwFramesRcved);
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf, "pRasPort1->dwCrcErr=%d\n", pRasPort1->dwCrcErr);
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf, "pRasPort1->dwTimeoutErr=%d\n", pRasPort1->dwTimeoutErr);
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf, "pRasPort1->dwAlignmentErr=%d\n", pRasPort1->dwAlignmentErr);
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf, "pRasPort1->dwHardwareOverrunErr=%d\n", pRasPort1->dwHardwareOverrunErr);
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf, "pRasPort1->dwFramingErr=%d\n", pRasPort1->dwFramingErr);
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf, "pRasPort1->dwBufferOverrunErr=%d\n", pRasPort1->dwBufferOverrunErr);
   WriteFile(cLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);

}

//
// Function to clear port statistics.
//
VOID ProcPortClearStat(HANDLE hPort)
{
DWORD dwBytesWritten = 0;
CHAR szBuf[120];
WCHAR Buf[512];
WCHAR *lpszString;
lpszString = &Buf[0];

   // API Called:     MprAdminPortClearStats
   // Parameters:
   // IN RAS_SERVER_HANDLE phRasServer
   // IN HANDLE  hPort
   Status = MprAdminPortClearStats(phRasServer, hPort);
   if (Status != NO_ERROR)
   {
      sprintf(szBuf, "ERROR MprAdminPortClearStats   %d\n",Status);
      WriteFile(LogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
      dwResult = MprAdminGetErrorString(Status,&lpszString);
      if (dwResult == ERROR_SUCCESS)
      {
         DbgPrint("ERROR  MprAdminPortClearStats      %ws\n",lpszString);
         sprintf(szBuf, "ERROR  MprAdminPortClearStats      %ws\n",lpszString);
         WriteFile(LogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
      }
      else
         PrintGetStringError(dwResult);
   }
   else
   {
      DbgPrint("\n\nMprAdminPortClearStats Called From ProcPortClearStat, Now dumping structs \n");
      sprintf(szBuf, "\n\nMprAdminPortClearStats Called From ProcPortClearStat, Now dumping structs\n");
      WriteFile(cLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
      ProcPortGetInfo(hPort);
   }

}

//
// Function to clear connection statistics.
//
VOID ProcConnectionClearStat(HANDLE hConnection)
{
DWORD dwBytesWritten = 0;
CHAR szBuf[120];
WCHAR Buf[512];
WCHAR *lpszString;
lpszString = &Buf[0];

   // API Called:     MprAdminConnectionClearStats
   // Parameters:
   // IN RAS_SERVER_HANDLE  phRasServer
   // IN HANDLE  hConnection
   Status = MprAdminConnectionClearStats(phRasServer, hConnection);
   if (Status != NO_ERROR)
   {
      sprintf(szBuf, "ERROR MprAdminConnectionClearStats   %d\n",Status);
      WriteFile(LogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
      dwResult = MprAdminGetErrorString(Status, &lpszString);
      if (dwResult == ERROR_SUCCESS)
      {
         DbgPrint("ERROR  MprAdminConnectionClearStats      %ws\n",lpszString);
         sprintf(szBuf, "ERROR  MprAdminConnectionClearStats      %ws\n",lpszString);
         WriteFile(LogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
      }
      else
         PrintGetStringError(dwResult);
   }
   else
   {
      DbgPrint("\n\nMprAdminConnectionClearStats Called From ProcConnectionClearStat, Now dumping structs \n");
      sprintf(szBuf, "\n\nMprAdminConnectionClearStats Called From ProcConnectionClearStat, Now dumping structs\n");
      WriteFile(cLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
      ProcConnectionGetInfo(hConnection);
   }
}

//
// Function to reset port.
//
VOID ProcPortReset(HANDLE hPort)
{
DWORD dwBytesWritten = 0;
CHAR szBuf[120];
WCHAR Buf[512];
WCHAR *lpszString;
lpszString = &Buf[0];

   // API Called:     MprAdminPortReset
   // Parameters:
   // IN RAS_SERVER_HANDLE  phRasServer
   // IN HANDLE  hPort
   Status = MprAdminPortReset(phRasServer, hPort);
   if (Status != NO_ERROR)
   {
      sprintf(szBuf, "ERROR MprAdminPortReset   %d\n",Status);
      WriteFile(LogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
      dwResult = MprAdminGetErrorString(Status, &lpszString);
      if (dwResult == ERROR_SUCCESS)
      {
         DbgPrint("ERROR  MprAdminPortReset      %ws\n",lpszString);
         sprintf(szBuf, "ERROR  MprAdminPortReset      %ws\n",lpszString);
         WriteFile(LogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
      }
      else
         PrintGetStringError(dwResult);
   }
   else
   {
      DbgPrint("\n\nMprAdminPortReset Called From ProcPortReset, Now dumping structs \n");
      sprintf(szBuf, "\n\nMprAdminPortReset Called From ProcPortReset, Now dumping structs\n");
      WriteFile(cLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
      ProcPortGetInfo(hPort);
   }
}

//
// Function to disconnect port.
//
VOID ProcPortDisconnect(HANDLE hPort)
{
DWORD dwBytesWritten = 0;
CHAR szBuf[120];
WCHAR Buf[512];
WCHAR *lpszString;
lpszString = &Buf[0];

   // API Called:     MprAdminPortDisconnect
   // Parameters:
   // IN RAS_SERVER_HANDLE  phRasServer
   // IN HANDLE  hPort
   Status = MprAdminPortDisconnect(phRasServer, hPort);
   if (Status != NO_ERROR)
   {
      sprintf(szBuf, "ERROR MprAdminPortDisconnect   %d\n",Status);
      WriteFile(LogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
      dwResult = MprAdminGetErrorString( Status, &lpszString);
      if (dwResult == ERROR_SUCCESS)
      {
         DbgPrint("ERROR  MprAdminPortDisconnect      %ws\n",lpszString);
         sprintf(szBuf, "ERROR  MprAdminPortDisconnect      %ws\n",lpszString);
         WriteFile(LogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
      }
      else
         PrintGetStringError(dwResult);
   }
}

//
// Function to reset port statistics
//
DWORD WINAPI RasAdminClearDisc(LPVOID param)
{
CHAR option= ' ';
WORD i = 0;
INT  k = 0;

   while (!Quit)
   {
      // If the user hits q or Q then this will exit gracefully.
      // It may take some time to exit though.
      printf("Select to Q - Quit\n");
      do
      {
         option=_getch();
         option =toupper(option);
         printf("%c\n",option);
      } while (((option) != 'q')&& ((option) != 'Q'));

      if (option == 'Q')
      {
         Quit = TRUE;
      }
   }
   return(0);
}

VOID ProcConnectionGetInfo(HANDLE hConnection)
{
RAS_CONNECTION_0 *pRasConnection0;
RAS_CONNECTION_1 *pRasConnection1;
LPBYTE lplpbBuffer;
DWORD dwBytesWritten = 0;
CHAR  szBuf[120];
WCHAR *lpszString;
WCHAR Buf[512];
lpszString = &Buf[0];

   // API Called: MprAdminConnectionGetInfo
   // Parameters:
   // IN  RAS_SERVER_HANDLE phRasServer
   // IN DWORD    dwLevel
   // IN HANDLE   hConnection
   // OUT LPBYTE * lplpbBuffer
   Status = MprAdminConnectionGetInfo(phRasServer,0,hConnection,&lplpbBuffer);
   if (Status != NO_ERROR)
   {
      sprintf(szBuf, "ERROR MprAdminConnectionGetInfo   %d\n",Status);
      WriteFile(LogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
      dwResult = MprAdminGetErrorString( Status, &lpszString);
      if (dwResult == NO_ERROR)
      {
         sprintf(szBuf,"ERROR  MprAdminConnectionGetInfo      %ws\n",lpszString);
         WriteFile(LogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
      }
      else
         PrintGetStringError(dwResult);
   }
   else
   {
      pRasConnection0 = (RAS_CONNECTION_0 *) lplpbBuffer;
      sprintf(szBuf, "\n\nDumping RpRasConnection0::Called from ProcConnectionGetinfo:->MprAdminConnectionGetInfo\n");
              WriteFile(cLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
      File_Print_Connection0(pRasConnection0);
      DbgPrint("\n\nDumping RpRasConnection0::Called from ProcConnectionGetinfo:->MprAdminConnectionGetInfo\n");
      Debug_Print_Connection0(pRasConnection0);
      MprAdminBufferFree(pRasConnection0);
   }

   Status = MprAdminConnectionGetInfo(phRasServer,1,hConnection,&lplpbBuffer);
   if (Status != NO_ERROR)
   {
      sprintf(szBuf, "ERROR MprAdminConnectionGetInfo   %d\n",Status);
      WriteFile(LogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
      dwResult = MprAdminGetErrorString( Status, &lpszString);
      if (dwResult == NO_ERROR)
      {
         sprintf(szBuf,"ERROR  MprAdminConnectionGetInfo      %ws\n",lpszString);
         WriteFile(LogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
      }
      else
         PrintGetStringError(dwResult);
   }
   else
   {
      pRasConnection1 = (RAS_CONNECTION_1 *) lplpbBuffer;
      sprintf(szBuf, "\n\nDumping RpRasConnection1::Called from ProcConnectionGetinfo:->MprAdminConnectionGetInfo\n");
      WriteFile(cLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
      File_Print_Connection1(pRasConnection1);
      DbgPrint("\n\nDumping RpRasConnection1::Called from ProcConnectionGetinfo:->MprAdminConnectionGetInfo\n");
      Debug_Print_Connection1(pRasConnection1);
      MprAdminBufferFree(pRasConnection1);
   }
}

//
// get port info
//
VOID ProcPortGetInfo(HANDLE hPort)
{
RAS_PORT_0 *pRasPort0;
RAS_PORT_1 *pRasPort1;
LPBYTE lplpbBuffer;
DWORD dwBytesWritten = 0;
CHAR  szBuf[120];
WCHAR *lpszString;
WCHAR Buf[512];
lpszString = &Buf[0];

   // API Called: MprAdminPortGetInfo
   // Parameters:
   // IN  RAS_SERVER_HANDLE phRasServer
   // IN DWORD    dwLevel
   // IN HANDLE   hPort
   // OUT LPBYTE * lplpbBuffer
   Status = MprAdminPortGetInfo(phRasServer,0,hPort,&lplpbBuffer);
   if (Status != NO_ERROR)
   {
      sprintf(szBuf, "ERROR MprAdminPortGetInfo   %d\n",Status);
      WriteFile(LogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
      dwResult = MprAdminGetErrorString( Status, &lpszString);
      if (dwResult == NO_ERROR)
      {
         sprintf(szBuf,"ERROR  MprAdminPortGetInfo      %ws\n",lpszString);
         WriteFile(LogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
      }
      else
         PrintGetStringError(dwResult);
   }
   else
   {
      pRasPort0 = (RAS_PORT_0 *) lplpbBuffer;
      sprintf(szBuf, "\n\nDumping RpRasPort0::Called from ProcPortGetInfo:-> MprAdminPortGetInfo\n");
      WriteFile(cLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
      File_Print_RAS_PORT0(pRasPort0);
      DbgPrint("\n\nDumping RpRasPort0::Called from ProcPortGetInfo:->MprAdminPortGetInfo\n");
      Debug_Print_RAS_PORT0(pRasPort0);
      MprAdminBufferFree(pRasPort0);
   }

   Status = MprAdminPortGetInfo(phRasServer,1,hPort,&lplpbBuffer);
   if (Status != NO_ERROR)
   {
      sprintf(szBuf, "ERROR MprAdminPortGetInfo   %d\n",Status);
      WriteFile(LogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
      dwResult = MprAdminGetErrorString( Status, &lpszString);
      if (dwResult == NO_ERROR)
      {
         sprintf(szBuf,"ERROR  MprAdminPortGetInfo      %ws\n",lpszString);
         WriteFile(LogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
      }
      else
         PrintGetStringError(dwResult);
   }
   else
   {
      pRasPort1 = (RAS_PORT_1 *) lplpbBuffer;
      sprintf(szBuf, "\n\nDumping pRasPort1::Called from ProcPortGetInfo:->MprAdminPortGetInfo\n");
      WriteFile(cLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
      File_Print_RAS_PORT1(pRasPort1);
      DbgPrint("\n\nDumping RpRasConnection1::Called from ProcPortGetInfo:->MprAdminPortGetInfo\n");
      Debug_Print_RAS_PORT1(pRasPort1);
      MprAdminBufferFree(pRasPort1);
   }
}

/*************************************************************************/
/***************** Start Main ********************************************/
/*************************************************************************/

void _cdecl main(int argc, char **argv)
{
INT LoopPriv = 0;
INT Error = 0;
CHAR szBuf[120];
DWORD dwBytesWritten = 0;
DWORD Dialin = 1;
WORD i = 0;
DWORD id = 0;
WCHAR *lpszString;
WCHAR Buf[512];
HANDLE ConnectDiscThread;
LPBYTE lplpbBuffer;
DWORD lpdwEntriesRead = 0;
DWORD lpdwTotalEntries = 0;
LPDWORD lpdwResumeHandle=NULL;
RAS_CONNECTION_0 *pRasConnection0;
LPBYTE plplpbBuffer;
DWORD plpdwPrefMaxLen = 0;
DWORD plpdwEntriesRead = 0;
DWORD plpdwTotalEntries = 0;
DWORD plpdwResumeHandle = 0;
RAS_PORT_0 *pRasPort0;
HANDLE hConnection = 0;
lpszString = &Buf[0];

 if (argc > 1)
 {
    mbstowcs(RasSrv, argv[1], 32) ;
 }
 Init1();
 Init2();
 Error=IsServiceRunning(RasSrv);
 if (!Error)
 {
    printf ("Ras Server is not started on the system");
    exit(0);
 }
 ConnectDiscThread = CreateThread (NULL,
                                  0,
                                  &RasAdminClearDisc,
                                  (LPVOID) id,
                                  0,
                                  &id);
 printf("Quit %d\n",Quit);
 while (!Quit)
 {
 // API Called:     MprAdminConnectionEnum
 // Parameters:
 //  IN RAS_SERVER_HANDLE phRasServer
 //  IN DWORD dwLevel
 //  OUT LPBYTE * lplpbBuffer    (RAS_CONNECTION_0 array)
 //  IN DWORD dwPrefMaxLen
 //  OUT LPDWORD lpdwEntriesRead
 //  OUT LPDWORD lpdwdTotalEntries
 //  IN  LPDWORD lpdwResumeHandle  OPTIONAL

 // Free up lplpbBuffer by calling MprAdminFreeBuffer API

   Status = MprAdminConnectionEnum(phRasServer,(DWORD) 0,&lplpbBuffer,(DWORD)-1,
                                   &lpdwEntriesRead,&lpdwTotalEntries,lpdwResumeHandle);
   if (Status != NO_ERROR)
   {
      sprintf(szBuf, "ERROR MprAdminConnectionEnum   %d\n",Status);
      WriteFile(LogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
      dwResult = MprAdminGetErrorString( Status, &lpszString);
      if (dwResult == NO_ERROR)
      {
         sprintf(szBuf,"ERROR  MprAdminConnectionEnum      %ws\n",lpszString);
         WriteFile(LogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
      }
      else
         PrintGetStringError(dwResult);
   }
   else
   {
      if (lpdwEntriesRead == lpdwTotalEntries)
      {
         pRasConnection0 = (RAS_CONNECTION_0 *) lplpbBuffer;
         for (i=0; i < (DWORD)lpdwTotalEntries;i++)
         {
            sprintf(szBuf, "\n\nDumping RpRasConnection0::Called from MprAdminConnectionEnum\n");
            WriteFile(cLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
            File_Print_Connection0(&pRasConnection0[i]);
            DbgPrint("\n\nDumping RpRasConnection0::Called from MprAdminConnectionEnum\n");
            Debug_Print_Connection0(&pRasConnection0[i]);
            ProcConnectionGetInfo(pRasConnection0[i].hConnection);
            // pass hConnection = handle from MprAdminConnectionEnum
            hConnection = pRasConnection0[i].hConnection;
            //alternatively, could pass hConnection = INVALID_HANDLE_VALUE;
            Sleep (500);
            // A good idea is to call UserPrivilege outside the loop. It doesn't really
            // make sense to go through the privilege change for every iteration.
            // However, this code is just intended to demonstrate the usage.
            UserPrivilege(pRasConnection0[i].wszLogonDomain,pRasConnection0[i].wszUserName);

            ProcConnectionClearStat(pRasConnection0[i].hConnection);
            // API Called:     MprAdminPortEnum
            // Parameters:
            //  IN RAS_SERVER_HANDLE phRasServer
            //  IN DWORD pdwLevel
            //  HANDLE hConnection
            //  OUT LPBYTE * plplpbBuffer    (RAS_PORT_0 array)
            //  IN DWORD pdwPrefMaxLen
            //  OUT LPDWORD plpdwEntriesRead
            //  OUT LPDWORD plpdwdTotalEntries
            //  IN  LPDWORD plpdwResumeHandle  OPTIONAL

            // Free up lplpbBuffer by calling MprAdminFreeBuffer API
            Status = MprAdminPortEnum(phRasServer,(DWORD) 0,hConnection, &plplpbBuffer,
                        (DWORD) -1, &plpdwEntriesRead, &plpdwTotalEntries, &plpdwResumeHandle);
            if (Status != NO_ERROR)
            {
               sprintf(szBuf, "ERROR MprAdminPortEnum   %d\n",Status);
               WriteFile(LogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
               dwResult = MprAdminGetErrorString( Status, &lpszString);
               if (dwResult == NO_ERROR)
               {
                  sprintf(szBuf,"ERROR  MprAdminPortEnum      %ws\n",lpszString);
                  WriteFile(LogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
               }
               else
                  PrintGetStringError(dwResult);
            }
            else
            {
               if (plpdwEntriesRead == plpdwTotalEntries)
               {
                  pRasPort0 = (RAS_PORT_0 *) plplpbBuffer;
                  for (i=0; i < (DWORD)plpdwTotalEntries;i++)
                  {
                     sprintf(szBuf, "\n\nDumping pRasPort0::Called from MprAdminPortEnum\n");
                     WriteFile(cLogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);
                     File_Print_RAS_PORT0(&pRasPort0[i]);
                     DbgPrint("\n\nDumping RpRasPort0::Called from MprAdminPortEnum\n");
                     Debug_Print_RAS_PORT0(&pRasPort0[i]);
                     ProcPortGetInfo(pRasPort0[i].hPort);
                     Sleep (500);
                     ProcPortClearStat(pRasPort0[i].hPort);
                     Sleep (500);
                     ProcPortReset(pRasPort0[i].hPort);
                     Sleep (500);
                     ProcPortDisconnect(pRasPort0[i].hPort);
                  }
               }
               MprAdminBufferFree(pRasPort0);
            }
         }
      }
      MprAdminBufferFree(pRasConnection0);
   }

   CloseHandle(LogFile);
   Sleep(sleeptime);
   Init1();

 } //end of loop: while (!Quit)

 WaitForSingleObject(ConnectDiscThread,INFINITE);

 //Calling MprAdminServerDisconnect (type VOID, returns nothing)
 // Parameters
 // IN RAS_SERVER_HANDLE * phRasServer
 MprAdminServerDisconnect(phRasServer);
 DbgPrint("info: called MprAdminServerDisconnect(hRasServer=0x%x)\n", phRasServer);
 sprintf(szBuf, "info: called MprAdminServerDisconnect(hRasServer=0x%x)\n", phRasServer);
 WriteFile(LogFile, (LPSTR)szBuf, strlen(szBuf), &dwBytesWritten, NULL);

 //
 // close logfiles
 //
 CloseHandle(LogFile);
 CloseHandle(cLogFile);
 CloseHandle(ErrLogFile);

} // Main End


README.TXT

Description:

This sample demonstrates how a special DLL may be created to allow an
administrator to exercise additional customized control over connection
acceptance and IP address assignment for dialin users connecting to an
NT Dial-Up Server/Router.  The server is configured via the registry
to use the special DLL (or DLL's), and if the DLL(s) are not present
the Dial-Up Server/Router will fail to start, and an error will be logged
in the server's eventlog.

This sample DLL exports the following API's

MprAdminAcceptNewConnection
MprAdminConnectionHangupNotification

MprAdminAcceptNewLink
MprAdminLinkHangupNotification

MprAdminGetIpAddressForUser
MprAdminReleaseIpAddress

Platform:  NT 5.0**
  ** or NT 4.0 + "Routing and Remote Access" add-on, which is available as a
     free download from www.microsoft.com

Source code:
admindll.c

Build instructions:
TBD

Requirments:

NT Server running Dial-Up Server/Router, configured with TCP/IP and dialin
ports.  The dialin ports can be any valid media (e.g. modem, ISDN, PPTP,
etc), as long as a user can make a RAS connection to the server with TCP/IP.

Usage Instructions:

To configure the Dial-Up Server/Router to use a custom admin DLL, the
registry must be edited as follows for each custom DLL:

HKEY_LOCAL_MACHINE
SOFTWARE
MICROSOFT
RAS

1. Add Key --> "<DllName>"  (e.g. "AdminDll")

2. Under "<DllName>" key, add a String Value called "DllPath"

3. Set the value of DllPath = "<full path to the dll>"
   e.g. "C:\TEST\ADMINDLL.DLL"

4. Restart the server to begin using the custom DLL(s).

When dialin users connect, RAS will call the appropriate API's exported by
the custom DLL, allowing customized control over connection and link
acceptance, and IP address assignment.

Behavior of the Sample Custom DLL

This sample DLL keeps a small database of 256 IP addresses, and will dole
out an IP address when the request comes in. It will mark the assigned IP
address as used.  In addition this database will store the user name and
port name.  When MprAdminReleaseIpAddress is called, it will check the port
name and username. If it matches then it will free up the IP address.

MprAdminAcceptNewConnection keeps a small database of the connected users
and their statistics and parameter information.  During
MprAdminConnectionHangupNotification, it will match the passed structures
with the stored information.  If the information doesn't match then the DLL
will generate an error. MprAdminAcceptNewConnection will only accept a
connection if the username is not "Administrator" and the account has no
callback privileges.  Also, this DLL will only allow a user to connect
with two or fewer links when making a Multilink connection.  This was an
arbitararily chosen scheme for demonstration purposes. The DLL developer
can keep their own database and let users connect based on time,
privliges, etc.

When the sample DLL's exported API are called, results and information are
written to debug output.  If a kernel debugger is not attached, you can
attach your debugger to the MPROUTER.EXE process to observe the debug output.


ADMINDLL.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
*       MprAdmin API usage and  is meant to help users with
*       MprAdmin API calling conventions. Use this code at your own risk.
*       Microsoft disclaims any implied warranty. This sample code can
*       be copied and distributed.
*
******************************************************************************/

/********************************************************************
*  admindll.c -- Sample program demonstrating the use of MprAdmin
*                     CallOut API's
*
*****************************************************************************/
/***************************************************************************
*  Functions:
*        Init(void);
*        Init2(void);
*        Debug_Print_RasConnection(
*                       IN RAS_CONNECTION_0 *   pRasConnection0,
*                       IN RAS_CONNECTION_1 *   pRasConnection1)
*
*        File_Print_RasConnection(
*                       IN RAS_CONNECTION_0 *   pRasConnection0,
*                       IN RAS_CONNECTION_1 *   pRasConnection1)
*
*        Debug_Print_RasPort(
*                       IN RAS_PORT_0 *   pRasPort0,
*                       IN RAS_PORT_1 *   pRasPort1)
*
*        File_Print_RasPort(
*                       IN RAS_PORT_0 *   pRasPort0,
*                       IN RAS_PORT_1 *   pRasPort1)
*****************************************************************************/

#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include <stdlib.h>
#include <lm.h>
#include <stdio.h>
#include <time.h>
#include <mprapi.h>
#include <memory.h>

//Functions
VOID
Debug_Print_RasConnection(
   IN RAS_CONNECTION_0 *   pRasConnection0,
   IN RAS_CONNECTION_1 *   pRasConnection1
   );

VOID
File_Print_RasConnection(IN RAS_CONNECTION_0 *   pRasConnection0,
   IN RAS_CONNECTION_1 *   pRasConnection1
   );

VOID
Debug_Print_RasPort(
   IN RAS_PORT_0 *   pRasPort0,
   IN RAS_PORT_1 *   pRasPort1
   );

VOID
File_Print_RasPort(
   IN RAS_PORT_0 *   pRasPort0,
   IN RAS_PORT_1 *   pRasPort1
);

VOID Init(void);
VOID Init2(void);

//Global Variables
#define Number_Of_Flags 6
#define Num_Ip_Address 254

DWORD  TempIpAddress = 0x010A0F0E;
HANDLE IPFile = NULL;
HANDLE ConDisFile = NULL;
HANDLE ErrLogFile = NULL;
HANDLE ErrLogFileIP = NULL;
BOOL IPFileInValid = FALSE;
BOOL ConnectDisconnectInValid = FALSE;
BOOL ErrorLogInValid = FALSE;
BOOL ErrLogIPInValid = FALSE;
BOOL First_Call = TRUE;

WCHAR*
StrDupWFromA(
    CHAR* psz );

static DWORD Multilink_Count = 0;
HANDLE Connection_Handle;
/* the above two variables are to control the number of ports a connection */
/* can use for multilink.  Multilink_Count can be set via regedit.exe to   */
/* make it little more portable */

typedef struct Static_Info_Database
   {
      BOOL bTaken;
      HANDLE hConnection;
      RAS_CONNECTION_0 pRasConnection0;
      RAS_CONNECTION_1 pRasConnection1;
   } Static_Info_Database;

Static_Info_Database  Static_Connection_Info[Num_Ip_Address];

typedef struct Static_Statistic
   {
      WCHAR wszUserName[UNLEN+1];
      WCHAR wszPortName[MAX_PORT_NAME];
      DWORD IpAddress;
      BOOL bTaken;
   } Static_Statistic;

Static_Statistic  Static_Check_Stats[Num_Ip_Address];

WCHAR*
StrDupWFromA(
    CHAR* psz )

    /* Returns heap block containing a copy of 0-terminated string 'psz' or
    ** NULL on error or if 'psz' is NULL.  The output string is converted to
    ** UNICODE.  It is caller's responsibility to Free the returned string.
    */
{
   WCHAR* pszNew = NULL;

   if (psz)
   {
      DWORD cb;

      cb = MultiByteToWideChar( CP_ACP, 0, psz, -1, NULL, 0 );
      pszNew = malloc( cb * sizeof(TCHAR) );
      if (!pszNew)
      {
         return NULL;
      }

      cb = MultiByteToWideChar( CP_ACP, 0, psz, -1, pszNew, cb );
      if (cb == 0)
      {
         free( pszNew );
         return NULL;
      }
   }

   return pszNew;

}

//
// Format and write debug information to OutputDebugString
//
ULONG
_cdecl
DbgPrint(
    PCH Format,
    ...
    )
{
    CHAR szbuffer[255];
    WCHAR buffer[255];
    WCHAR *temp=NULL;
    LPINT lpResult=0;
    va_list marker;
    va_start (marker,Format);
    if (IsTextUnicode(marker, sizeof(marker),lpResult))
    {
       temp=StrDupWFromA(Format);
       wvsprintfW (buffer,temp, marker);
       OutputDebugString (buffer);
       free(temp);
    }
    else
    {
       wvsprintfA (szbuffer,Format, marker);
       temp=StrDupWFromA(szbuffer);
       OutputDebugString (temp);
       free(temp);
    }
    return TRUE;

}

//
// Called from MprAdminGetIpAddressForUser, which itself gets called by RasMan
//
VOID Init(void)
{
INT i=0;

   IPFile = CreateFile(TEXT("IpAddress.log"),
                       GENERIC_READ|GENERIC_WRITE,
                       FILE_SHARE_READ,
                       (LPSECURITY_ATTRIBUTES) NULL,
                       CREATE_ALWAYS,
                       0,
                       (HANDLE) NULL);
   if (IPFile== INVALID_HANDLE_VALUE)
   {
      IPFileInValid = TRUE;
   }
   ErrLogFileIP = CreateFile(TEXT("IpAddError.log"),
                             GENERIC_READ|GENERIC_WRITE,
                             FILE_SHARE_READ,
                             (LPSECURITY_ATTRIBUTES) NULL,
                             CREATE_ALWAYS,
                             0,
                             (HANDLE) NULL);

   if (ErrLogFileIP== INVALID_HANDLE_VALUE)
   {
      ErrLogIPInValid = TRUE;
   }

}

//
// Called by MprAdminAcceptNewConnection, itself gets called by RAS Server
//
VOID Init2(void)
{
INT i=0;

   ConDisFile = CreateFile(TEXT("ConDis.log"),
                           GENERIC_READ|GENERIC_WRITE,
                           FILE_SHARE_READ,
                           (LPSECURITY_ATTRIBUTES) NULL,
                           OPEN_ALWAYS,
                           0,
                           (HANDLE) NULL);
   if (ConDisFile== INVALID_HANDLE_VALUE)
   {
      ConnectDisconnectInValid = TRUE;
   }

   ErrLogFile = CreateFile(TEXT("AdminErr.log"),
                           GENERIC_READ|GENERIC_WRITE,
                           FILE_SHARE_READ,
                           (LPSECURITY_ATTRIBUTES) NULL,
                           OPEN_ALWAYS,
                           0,
                           (HANDLE) NULL);

   if (ErrLogFile == INVALID_HANDLE_VALUE)
   {
      ErrorLogInValid = TRUE;
   }

   for (i=0;i < Num_Ip_Address; i++)
   {
      Static_Check_Stats[i].bTaken=FALSE;
      TempIpAddress += 0x01000000;

      if ((TempIpAddress | 0x0ff000000) == 0x0ff000000)
         TempIpAddress = 0x010A0F0E;

      Static_Check_Stats[i].IpAddress = TempIpAddress;
      Static_Connection_Info[i].bTaken = FALSE;
   }

}

BOOL DllMain(
    HINSTANCE hinstDLL,  // handle to DLL module
    DWORD fdwReason,     // reason for calling function
    LPVOID lpReserved )  // reserved
{
    // Perform actions based on the reason for calling.
    switch( fdwReason )
    {
       case DLL_PROCESS_ATTACH:
       // Initialize once for each new process.
       // Return FALSE to fail DLL load.
    break;

       case DLL_THREAD_ATTACH:
       // Do thread-specific initialization.
       break;

       case DLL_THREAD_DETACH:
       // Do thread-specific cleanup.
       break;

       case DLL_PROCESS_DETACH:
       // Perform any necessary cleanup.
       break;

    }

    return TRUE;  // Successful DLL_PROCESS_ATTACH.
}

DWORD  APIENTRY
MprAdminGetIpAddressForUser(
    IN WCHAR *      lpszUserName,
    IN WCHAR *      lpszPortName,
    IN OUT DWORD * pipAddress,
    OUT BOOL *      pfNotifyDLL
)
{
DWORD i=0;
DWORD dwBytesWritten=0;
CHAR buf[120];

   //to check if the call is first rasman call out. If yes, call Initialize routine
   if (First_Call)
   {
      Init();
      Init2();
      First_Call=FALSE;
   }
   DbgPrint("First_Call  %d\n",First_Call);
   DbgPrint("\nGetIpAddressForUser called UserName=%ws,Port=%ws,IpAddress=%d\n",
            lpszUserName,lpszPortName,(DWORD)*pipAddress );

   for (i=0;i < Num_Ip_Address;i++)
   {
      if (Static_Check_Stats[i].bTaken == FALSE)
      {
         wcscpy(Static_Check_Stats[i].wszUserName, lpszUserName);
         wcscpy(Static_Check_Stats[i].wszPortName, lpszPortName);
         *pipAddress = Static_Check_Stats[i].IpAddress;
         DbgPrint("Trying to get the IP Address   %d",(DWORD) *pipAddress);
         Static_Check_Stats[i].bTaken=TRUE;
         break;
      }
   }
   DbgPrint("GetIpAddressForUser setting IpAddress=%d\n",(DWORD)*pipAddress );
   sprintf(buf,"GetIpAddressForUser setting IpAddress=%d\n",(DWORD)*pipAddress );
   WriteFile(IPFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   *pfNotifyDLL = TRUE;

   return( NO_ERROR );
}

VOID APIENTRY
MprAdminReleaseIpAddress(
    IN WCHAR *      lpszUserName,
    IN WCHAR *      lpszPortName,
    IN OUT DWORD * pipAddress
)
{
DWORD dwBytesWritten=0;
CHAR buf[120];
int i=0;

    DbgPrint( "\nReleaseIpAddressr called UserName=%ws,Port=%ws,IpAddress=%d\n",
              lpszUserName,lpszPortName,(DWORD)*pipAddress );

    for (i=0;i < Num_Ip_Address;i++)
    {
       if (Static_Check_Stats[i].IpAddress == (DWORD) *pipAddress)
       {
          if (Static_Check_Stats[i].bTaken == TRUE)
          {
             if (wcscmp(Static_Check_Stats[i].wszUserName,lpszUserName) == 0)
             {
                if (wcscmp(Static_Check_Stats[i].wszPortName,lpszPortName) == 0)
                {
                   Static_Check_Stats[i].bTaken = FALSE;break;
                }
                else
                {
                   DbgPrint("ERROR ReleaseIP PortName, Cannot free IP Address    %ws\n",
                            Static_Check_Stats[i].wszPortName);
                   if (!(ErrLogIPInValid))
                   {
                      sprintf(buf,"ERROR ReleaseIP PortName, Cannot free IP Address   %ws\n",
                              Static_Check_Stats[i].wszPortName);
                      WriteFile(ErrLogFileIP, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
                      break;
                   }
                }
             }
             else
             {
                DbgPrint("ERROR ReleaseIP UserName Incorrect, Cannot free IP Address   %ws\n",
                         Static_Check_Stats[i].wszUserName);
                if (!(ErrLogIPInValid))
                {
                   sprintf(buf,"ERROR ReleaseIP UserName Incorrect, Cannot free IP Address   %ws\n",
                           Static_Check_Stats[i].wszUserName);
                   WriteFile(ErrLogFileIP, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
                }
                break;
             }
          }
          else
          {
             DbgPrint("ERROR ReleaseIP Wrong IP address, doesn't exist, Cannot free IP Address    %d\n",
                      Static_Check_Stats[i].IpAddress);
             if (!(ErrLogIPInValid))
             {
                sprintf(buf,"ERROR ReleaseIP Wrong IP Address, doesn't exist Cannot free IP Address   %d\n",
                        Static_Check_Stats[i].IpAddress);
                WriteFile(ErrLogFileIP, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
             }
             break;
          }
       }
    }

}

BOOL APIENTRY
MprAdminAcceptNewConnection(
   IN RAS_CONNECTION_0 *   pRasConnection0,
   IN RAS_CONNECTION_1 *   pRasConnection1
)
{
INT i=0;
CHAR buf[512];
DWORD dwBytesWritten=0;

   DbgPrint("MprAdminAcceptNewConnection Called\n");
   Debug_Print_RasConnection(pRasConnection0,pRasConnection1);
   WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf,"MprAdminAcceptNewConnection Called::dumping pRasConnection0\n\n");
   File_Print_RasConnection(pRasConnection0,pRasConnection1);

   if (( lstrcmp( pRasConnection0->wszUserName, TEXT("aDministrator") ) ) == 0)
   {
      DbgPrint("Do not Accept Connection\n");
      FlushFileBuffers(ErrLogFile);
      FlushFileBuffers(ConDisFile);
      return( FALSE );
   }
   else
   {
      for (i=0;i < Num_Ip_Address; i++)
         if (!(Static_Connection_Info[i].bTaken))
         {
            Static_Connection_Info[i].bTaken = TRUE;
            Static_Connection_Info[i].hConnection=pRasConnection0->hConnection;
            memcpy(&Static_Connection_Info[i].pRasConnection0,pRasConnection0, sizeof(RAS_CONNECTION_0));
            memcpy(&Static_Connection_Info[i].pRasConnection1,pRasConnection1, sizeof(RAS_CONNECTION_1));
            break;
         }

       DbgPrint("Accept Connection\n");
       FlushFileBuffers(ErrLogFile);
       FlushFileBuffers(ConDisFile);
       return( TRUE );
    }
    return( TRUE );
}

BOOL APIENTRY
MprAdminAcceptNewLink(
   IN RAS_PORT_0 *   pRasPort0,
   IN RAS_PORT_1 *   pRasPort1
)
{
CHAR  buf[512];
DWORD dwBytesWritten = 0;

   Multilink_Count++;
   /* Multilink_Count can be set through regedit.exe to make it little more portable */
   /* Currently it is hard coded in this sample, so that no connection can have more than 2 ports */
   if (Multilink_Count == 1)
      Connection_Handle = pRasPort0->hConnection;/* this is the first port */

   //to check if the call is first rassrv call out. If yes, call Initialize routine
   DbgPrint("MprAdminAcceptNewLink Called, times  %d\n",Multilink_Count);
   Debug_Print_RasPort(pRasPort0,pRasPort1);

   //dumping pRasPort0 to a file
   WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf,"MprAdminAcceptNewLink Called time %d::dumping pRasPort0 to file\n",Multilink_Count);
   File_Print_RasPort(pRasPort0,pRasPort1);

   /* if the Connection_Handle is not same then it is a new call, reset the counter to 1 */
   if (Connection_Handle != pRasPort0->hConnection)
   {
      if (Multilink_Count > 1)
      {
         Connection_Handle = pRasPort0->hConnection;
         Multilink_Count = 1;
         return TRUE;
      }
      else
         return FALSE;
   }
   /* Connection_Handle same as previous port then restrict the number of ports. */
   /* Just return FALSE */
   if (Multilink_Count > 2)
   {
      DbgPrint("MprAdminAcceptNewLink(): Multilink_Count=%d > 2  so return FALSE\n", Multilink_Count);
      return FALSE;
   }

   return TRUE;

}

VOID APIENTRY
MprAdminLinkHangupNotification(
   IN RAS_PORT_0 *   pRasPort0,
   IN RAS_PORT_1 *   pRasPort1
)
{
   char buf[512];
   DWORD dwBytesWritten = 0;

   DbgPrint("MprAdminLinkHangupNotification Called\n");
   Debug_Print_RasPort(pRasPort0,pRasPort1);
   //dumping pRasPort0 to a file
   WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf,"MprAdminLinkHangupNotification Called::dumping pRasPort0 to file\n");
   File_Print_RasPort(pRasPort0,pRasPort1);

}

VOID APIENTRY
MprAdminConnectionHangupNotification(
   IN RAS_CONNECTION_0 *   pRasConnection0,
   IN RAS_CONNECTION_1 *   pRasConnection1
)
{
INT i = 0;
DWORD dwBytesWritten = 0;
CHAR  buf[120];

   DbgPrint( "\nMprAdminConnectionHangupNotification called \n" );
   Debug_Print_RasConnection(pRasConnection0,pRasConnection1);
   WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf,"MprAdminConnectionHangupNotification called::dumping pRasConnection0\n\n");
   File_Print_RasConnection(pRasConnection0,pRasConnection1);

   //Sanity checks
   for (i=0; i < Num_Ip_Address; i++)
   {
      if ((Static_Connection_Info[i].bTaken) && (Static_Connection_Info[i].hConnection == pRasConnection0->hConnection))
      {
         Static_Connection_Info[i].bTaken = FALSE;
         if (ErrorLogInValid)
         {
            if (Static_Connection_Info[i].pRasConnection0.hConnection != pRasConnection0->hConnection)
               DbgPrint("ERROR Connection Handle differs   %d-> Stored,    %d->Passed\n",
                        Static_Connection_Info[i].pRasConnection0.hConnection, pRasConnection0->hConnection);

            if (Static_Connection_Info[i].pRasConnection0.hInterface != pRasConnection0->hInterface)
               DbgPrint("ERROR Interface Handle differs   %d-> Stored,    %d->Passed\n",
                        Static_Connection_Info[i].pRasConnection0.hInterface, pRasConnection0->hInterface);

            if (Static_Connection_Info[i].pRasConnection0.dwConnectDuration > pRasConnection0->dwConnectDuration)
               DbgPrint("ERROR Duration time is less the   %d-> Stored,    %d->Passed\n",
                        Static_Connection_Info[i].pRasConnection0.dwConnectDuration, pRasConnection0->dwConnectDuration);

            if (Static_Connection_Info[i].pRasConnection0.dwInterfaceType != pRasConnection0->dwInterfaceType)
               DbgPrint("ERROR Interface Type differs   %d-> Stored,    %d->Passed\n",
                        Static_Connection_Info[i].pRasConnection0.dwInterfaceType != pRasConnection0->dwInterfaceType);

            if (Static_Connection_Info[i].pRasConnection0.dwConnectionFlags != pRasConnection0->dwConnectionFlags)
               DbgPrint("ERROR Connection Flags differs   %d-> Stored,    %d->Passed\n",
                        Static_Connection_Info[i].pRasConnection0.dwConnectionFlags, pRasConnection0->dwConnectionFlags);

            if (wcscmp(Static_Connection_Info[i].pRasConnection0.wszInterfaceName,pRasConnection0->wszInterfaceName) == 0)
               DbgPrint("ERROR Interface Name Differs    %ws ->Stored,   %ws ->Passed\n",
                        Static_Connection_Info[i].pRasConnection0.wszInterfaceName,pRasConnection0->wszInterfaceName);

            if (wcscmp(Static_Connection_Info[i].pRasConnection0.wszUserName,pRasConnection0->wszUserName) != 0)
               DbgPrint("ERROR User Name Differs    %ws ->Stored,   %ws ->Passed\n",
                        Static_Connection_Info[i].pRasConnection0.wszUserName,pRasConnection0->wszUserName);

            if (wcscmp(Static_Connection_Info[i].pRasConnection0.wszLogonDomain,pRasConnection0->wszLogonDomain) != 0)
               DbgPrint("ERROR Logon Name Differs    %ws ->Stored,   %ws ->Passed\n",
                        Static_Connection_Info[i].pRasConnection0.wszLogonDomain,pRasConnection0->wszLogonDomain);

            if (wcscmp(Static_Connection_Info[i].pRasConnection0.wszRemoteComputer,pRasConnection0->wszRemoteComputer) != 0)
               DbgPrint("ERROR Remote Name Differs    %ws ->Stored,   %ws ->Passed\n",
                        Static_Connection_Info[i].pRasConnection0.wszRemoteComputer,pRasConnection0->wszRemoteComputer);
         }
         else
         {
            WriteFile(ErrLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
            if (Static_Connection_Info[i].pRasConnection0.hConnection != pRasConnection0->hConnection)
               sprintf(buf,"ERROR Connection Handle differs   %d-> Stored,    %d->Passed\n",
                       Static_Connection_Info[i].pRasConnection0.hConnection, pRasConnection0->hConnection);

            WriteFile(ErrLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
            if (Static_Connection_Info[i].pRasConnection0.hInterface != pRasConnection0->hInterface)
               sprintf(buf,"ERROR Interface Handle differs   %d-> Stored,    %d->Passed\n",
                       Static_Connection_Info[i].pRasConnection0.hInterface, pRasConnection0->hInterface);

            WriteFile(ErrLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
            if (Static_Connection_Info[i].pRasConnection0.dwConnectDuration <= pRasConnection0->dwConnectDuration)
               sprintf(buf,"ERROR Duration time is less the   %d-> Stored,    %d->Passed\n",
                       Static_Connection_Info[i].pRasConnection0.dwConnectDuration, pRasConnection0->dwConnectDuration);
            WriteFile(ErrLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);

            if (Static_Connection_Info[i].pRasConnection0.dwInterfaceType != pRasConnection0->dwInterfaceType)
               sprintf(buf,"ERROR Interface Type differs   %d-> Stored,    %d->Passed\n",
                       Static_Connection_Info[i].pRasConnection0.dwInterfaceType != pRasConnection0->dwInterfaceType);

            WriteFile(ErrLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);

            if (Static_Connection_Info[i].pRasConnection0.dwConnectionFlags != pRasConnection0->dwConnectionFlags)
               sprintf(buf,"ERROR Connection Flags differs   %d-> Stored,    %d->Passed\n",
                       Static_Connection_Info[i].pRasConnection0.dwConnectionFlags, pRasConnection0->dwConnectionFlags);

            WriteFile(ErrLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);

            if (wcscmp(Static_Connection_Info[i].pRasConnection0.wszInterfaceName,pRasConnection0->wszInterfaceName) == 0)
               sprintf(buf,"ERROR Interface Name Differs    %ws ->Stored,   %ws ->Passed\n",
                       Static_Connection_Info[i].pRasConnection0.wszInterfaceName,pRasConnection0->wszInterfaceName);

            WriteFile(ErrLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);

            if (wcscmp(Static_Connection_Info[i].pRasConnection0.wszUserName,pRasConnection0->wszUserName) != 0)
               sprintf(buf,"ERROR User Name Differs    %ws ->Stored,   %ws ->Passed\n",
                       Static_Connection_Info[i].pRasConnection0.wszUserName,pRasConnection0->wszUserName);

            WriteFile(ErrLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);

            if (wcscmp(Static_Connection_Info[i].pRasConnection0.wszLogonDomain,pRasConnection0->wszLogonDomain) != 0)
               sprintf(buf,"ERROR Logon Name Differs    %ws ->Stored,   %ws ->Passed\n",
                       Static_Connection_Info[i].pRasConnection0.wszLogonDomain,pRasConnection0->wszLogonDomain);

            WriteFile(ErrLogFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);

            if (wcscmp(Static_Connection_Info[i].pRasConnection0.wszRemoteComputer,pRasConnection0->wszRemoteComputer) != 0)
               sprintf(buf,"ERROR Remote Name Differs    %ws ->Stored,   %ws ->Passed\n",
                       Static_Connection_Info[i].pRasConnection0.wszRemoteComputer,pRasConnection0->wszRemoteComputer);
         }
      }
      FlushFileBuffers(ErrLogFile);
      FlushFileBuffers(ConDisFile);
   }

}

VOID Debug_Print_RasConnection(
   IN RAS_CONNECTION_0 *   pRasConnection0,
   IN RAS_CONNECTION_1 *   pRasConnection1
)
{
WCHAR *Interface_Type[] =
   {
   TEXT("ROUTER_IF_TYPE_CLIENT"),
   TEXT("ROUTER_IF_TYPE_HOME_ROUTER"),
   TEXT("ROUTER_IF_TYPE_FULL_ROUTER"),
   TEXT("ROUTER_IF_TYPE_DEDICATED"),
   TEXT("ROUTER_IF_TYPE_INTERNAL"),
   TEXT("ROUTER_IF_TYPE_INTERNAL")
   };

INT i = 0;
DWORD dwBytesWritten = 0;

   //dumping pRasConnection0
   DbgPrint("pRasConnection0->hConnection  %d\n",pRasConnection0->hConnection);
   DbgPrint("pRasConnection0->hInterface   %d\n",pRasConnection0->hInterface);
   DbgPrint("pRasConnection0->dwConnectDuration  %d\n",pRasConnection0->dwConnectDuration);
   DbgPrint("pRasConnection0->dwInterfaceType  %ws\n",Interface_Type[pRasConnection0->dwInterfaceType]);
   DbgPrint("pRasConnection0->dwConnectionFlags  %d\n",pRasConnection0->dwConnectionFlags);
   DbgPrint("pRasConnection0->wszInterfaceName  %ws\n",pRasConnection0->wszInterfaceName);
   DbgPrint("pRasConnection0->wszUserName  %ws\n",pRasConnection0->wszUserName);
   DbgPrint("pRasConnection0->wszLogonDomain  %ws\n",pRasConnection0->wszLogonDomain);
   DbgPrint("pRasConnection0->wszRemoteComputer  %ws\n",pRasConnection0->wszRemoteComputer);

   //dumping pRasConnection1
   DbgPrint( "\n\npRasConnection1->hConnection  %d\n",pRasConnection1->hConnection);
   DbgPrint( "pRasConnection1->hInterface   %d\n",pRasConnection1->hInterface);
   DbgPrint( "pRasConnection1->PppInfo.nbf.dwError %d\n",pRasConnection1->PppInfo.nbf.dwError);
   DbgPrint( "pRasConnection1->PppInfo.nbf.wszWksta %ws\n",pRasConnection1->PppInfo.nbf.wszWksta);
   DbgPrint( "pRasConnection1->PppInfo.ip.dwError %d\n",pRasConnection1->PppInfo.ip.dwError);
   DbgPrint( "pRasConnection1->PppInfo.ip.wszAddress %ws\n",pRasConnection1->PppInfo.ip.wszAddress);
   DbgPrint( "pRasConnection1->PppInfo.ip.wszRemoteAddress %ws\n",pRasConnection1->PppInfo.ip.wszRemoteAddress);
   DbgPrint( "pRasConnection1->PppInfo.ipx.dwError %d\n",pRasConnection1->PppInfo.ipx.dwError);
   DbgPrint( "pRasConnection1->PppInfo.ipx.wszAddress %ws\n",pRasConnection1->PppInfo.ipx.wszAddress);
   DbgPrint( "pRasConnection1->PppInfo.at.dwError  %d\n",pRasConnection1->PppInfo.at.dwError );
   DbgPrint( "pRasConnection1->PppInfo.at.wszAddress %ws\n",pRasConnection1->PppInfo.at.wszAddress);

   //connection stats
   DbgPrint( "pRasConnection1->dwBytesXmited=%d\n", pRasConnection1->dwBytesXmited );
   DbgPrint( "pRasConnection1->dwBytesRcved=%d\n", pRasConnection1->dwBytesRcved );
   DbgPrint( "pRasConnection1->dwFramesXmited=%d\n", pRasConnection1->dwFramesXmited );
   DbgPrint( "pRasConnection1->dwFramesRcved=%d\n", pRasConnection1->dwFramesRcved);
   DbgPrint( "pRasConnection1->dwCrcErr=%d\n", pRasConnection1->dwCrcErr);
   DbgPrint( "pRasConnection1->dwTimeoutErr=%d\n", pRasConnection1->dwTimeoutErr);
   DbgPrint( "pRasConnection1->dwAlignmentErr=%d\n", pRasConnection1->dwAlignmentErr);
   DbgPrint( "pRasConnection1->dwHardwareOverrunErr=%d\n", pRasConnection1->dwHardwareOverrunErr);
   DbgPrint( "pRasConnection1->dwFramingErr=%d\n", pRasConnection1->dwFramingErr);
   DbgPrint( "pRasConnection1->dwBufferOverrunErr=%d\n", pRasConnection1->dwBufferOverrunErr);
}

VOID File_Print_RasConnection(IN RAS_CONNECTION_0 *   pRasConnection0,
IN RAS_CONNECTION_1 *   pRasConnection1
)
{
   WCHAR *Interface_Type[] =
     {
     TEXT("ROUTER_IF_TYPE_CLIENT"),
     TEXT("ROUTER_IF_TYPE_HOME_ROUTER"),
     TEXT("ROUTER_IF_TYPE_FULL_ROUTER"),
     TEXT("ROUTER_IF_TYPE_DEDICATED"),
     TEXT("ROUTER_IF_TYPE_INTERNAL"),
     TEXT("ROUTER_IF_TYPE_INTERNAL")
     };

   INT  i = 0;
   CHAR buf[512];
   DWORD dwBytesWritten = 0;

   //write to file
   //dumping pRasConnection0
   WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf,"pRasConnection0->hConnection  %d\n",pRasConnection0->hConnection);
   WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf,"pRasConnection0->hInterface   %d\n",pRasConnection0->hInterface);
   WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf,"pRasConnection0->dwConnectDuration  %d\n",pRasConnection0->dwConnectDuration);

   WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf,"pRasConnection0->dwInterfaceType  %ws\n",Interface_Type[pRasConnection0->dwInterfaceType]);
   WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf,"pRasConnection0->dwConnectionFlags  %d\n",pRasConnection0->dwConnectionFlags);
   WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf,"pRasConnection0->wszInterfaceName  %ws\n",pRasConnection0->wszInterfaceName);
   WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf,"pRasConnection0->wszUserName  %ws\n",pRasConnection0->wszUserName);
   WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf,"pRasConnection0->wszLogonDomain  %ws\n",pRasConnection0->wszLogonDomain);
   WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf,"pRasConnection0->wszRemoteComputer  %ws\n",pRasConnection0->wszRemoteComputer);

   //dumping pRasConnection1
   WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf,"dumping pRasConnection1\n");
   WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf, "\n\npRasConnection1->hConnection  %d\n",pRasConnection1->hConnection);
   WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf, "pRasConnection1->hInterface   %d\n",pRasConnection1->hInterface);
   WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf, "pRasConnection1->PppInfo.nbf.dwError %d\n",pRasConnection1->PppInfo.nbf.dwError);
   WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf, "pRasConnection1->PppInfo.nbf.wszWksta %ws\n",pRasConnection1->PppInfo.nbf.wszWksta);
   WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf, "pRasConnection1->PppInfo.ip.dwError %d\n",pRasConnection1->PppInfo.ip.dwError);
   WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);

sprintf(buf, "pRasConnection1->PppInfo.ip.wszAddress %ws\n",pRasConnection1->PppInfo.ip.wszAddress);
   WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf, "pRasConnection1->PppInfo.ip.wszRemoteAddress %ws\n",pRasConnection1->PppInfo.ip.wszRemoteAddress);
   WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf, "pRasConnection1->PppInfo.ipx.dwError %d\n",pRasConnection1->PppInfo.ipx.dwError);
   WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf, "pRasConnection1->PppInfo.ipx.wszAddress %ws\n",pRasConnection1->PppInfo.ipx.wszAddress);
   WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf, "pRasConnection1->PppInfo.at.dwError  %d\n",pRasConnection1->PppInfo.at.dwError );
   WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf, "pRasConnection1->PppInfo.at.wszAddress %ws\n",pRasConnection1->PppInfo.at.wszAddress);

   //connection stats
   WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf, "pRasConnection1->dwBytesXmited=%d\n", pRasConnection1->dwBytesXmited );
   WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf, "pRasConnection1->dwBytesRcved=%d\n", pRasConnection1->dwBytesRcved );
   WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf, "pRasConnection1->dwFramesXmited=%d\n", pRasConnection1->dwFramesXmited );
   WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf, "pRasConnection1->dwFramesRcved=%d\n", pRasConnection1->dwFramesRcved);
   WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf, "pRasConnection1->dwCrcErr=%d\n", pRasConnection1->dwCrcErr);
   WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf, "pRasConnection1->dwTimeoutErr=%d\n", pRasConnection1->dwTimeoutErr);
   WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf, "pRasConnection1->dwAlignmentErr=%d\n", pRasConnection1->dwAlignmentErr);
   WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf, "pRasConnection1->dwHardwareOverrunErr=%d\n", pRasConnection1->dwHardwareOverrunErr);
   WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf, "pRasConnection1->dwFramingErr=%d\n", pRasConnection1->dwFramingErr);
   WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
   sprintf(buf, "pRasConnection1->dwBufferOverrunErr=%d\n", pRasConnection1->dwBufferOverrunErr);
   WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
}

VOID Debug_Print_RasPort(
IN RAS_PORT_0 *   pRasPort0,
IN RAS_PORT_1 *   pRasPort1
)
{
   WCHAR *Line_Condition[] =
      {
      TEXT("RAS_PORT_NON_OPERATIONAL"),
      TEXT("RAS_PORT_DISCONNECTED"),
      TEXT("RAS_PORT_CALLING_BACK"),
      TEXT("RAS_PORT_LISTENING"),
      TEXT("RAS_PORT_AUTHENTICATING"),
      TEXT("RAS_PORT_AUTHENTICATED"),
      TEXT("RAS_PORT_INITIALIZING")
      };

   WCHAR *Hdw_Error[] =
      {
      TEXT("RAS_HARDWARE_OPERATIONAL"),
      TEXT("RAS_HARDWARE_FAILURE")
      };

   DWORD dwBytesWritten = 0;

  //dumping pRasPort0
   DbgPrint("pRasPort0->hPort  %d\n",pRasPort0->hPort);
   DbgPrint("pRasPort0->hConnection   %d\n",pRasPort0->hConnection);
   DbgPrint("pRasPort0->dwPortCondition  %ws\n",Line_Condition[pRasPort0->dwPortCondition-1]);
   DbgPrint("pRasPort0->dwTotalNumberOfCalls  %d\n",pRasPort0->dwTotalNumberOfCalls);
   DbgPrint("pRasPort0->dwConnectDuration  %d\n",pRasPort0->dwConnectDuration);
   DbgPrint("pRasPort0->wszPortName  %ws\n",pRasPort0->wszPortName);
   DbgPrint("pRasPort0->wszMediaName  %ws\n",pRasPort0->wszMediaName);
   DbgPrint("pRasPort0->wszDeviceName  %ws\n",pRasPort0->wszDeviceName);
   DbgPrint("pRasPort0->wszDeviceType  %ws\n",pRasPort0->wszDeviceType);

   //dumping pRasPort1
   DbgPrint("pRasPort1->hPort  %d\n",pRasPort1->hPort);
   DbgPrint("pRasPort1->hConnection   %d\n",pRasPort1->hConnection);
   DbgPrint("pRasPort1->dwHardwareCondition  %ws\n",Hdw_Error[pRasPort1->dwHardwareCondition]);

   //connection stats
   DbgPrint( "pRasPort1->dwLineSpeed=%d\n", pRasPort1->dwLineSpeed );
   DbgPrint( "pRasPort1->dwBytesXmited=%d\n", pRasPort1->dwBytesXmited );
   DbgPrint( "pRasPort1->dwBytesRcved=%d\n", pRasPort1->dwBytesRcved );
   DbgPrint( "pRasPort1->dwFramesXmited=%d\n", pRasPort1->dwFramesXmited );
   DbgPrint( "pRasPort1->dwFramesRcved=%d\n", pRasPort1->dwFramesRcved);
   DbgPrint( "pRasPort1->dwCrcErr=%d\n", pRasPort1->dwCrcErr);
   DbgPrint( "pRasPort1->dwTimeoutErr=%d\n", pRasPort1->dwTimeoutErr);
   DbgPrint( "pRasPort1->dwAlignmentErr=%d\n", pRasPort1->dwAlignmentErr);
   DbgPrint( "pRasPort1->dwHardwareOverrunErr=%d\n", pRasPort1->dwHardwareOverrunErr);
   DbgPrint( "pRasPort1->dwFramingErr=%d\n", pRasPort1->dwFramingErr);
   DbgPrint( "pRasPort1->dwBufferOverrunErr=%d\n", pRasPort1->dwBufferOverrunErr);
}

VOID File_Print_RasPort(
IN RAS_PORT_0 *   pRasPort0,
IN RAS_PORT_1 *   pRasPort1
)
{
    WCHAR *Line_Condition[] =
    {
      TEXT("RAS_PORT_NON_OPERATIONAL"),
      TEXT("RAS_PORT_DISCONNECTED"),
      TEXT("RAS_PORT_CALLING_BACK"),
      TEXT("RAS_PORT_LISTENING"),
      TEXT("RAS_PORT_AUTHENTICATING"),
      TEXT("RAS_PORT_AUTHENTICATED"),
      TEXT("RAS_PORT_INITIALIZING")
     };

    WCHAR *Hdw_Error[] =
    {
      TEXT("RAS_HARDWARE_OPERATIONAL"),
      TEXT("RAS_HARDWARE_FAILURE")
     };

    CHAR  buf[512];
    DWORD dwBytesWritten = 0;

    //dumping pRasPort0 to a file
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf,"pRasPort0->hPort  %d\n",pRasPort0->hPort);
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf,"pRasPort0->hConnection   %d\n",pRasPort0->hConnection);
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf,"pRasPort0->dwPortCondition  %ws\n",Line_Condition[pRasPort0->dwPortCondition-1]);
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf,"pRasPort0->dwTotalNumberOfCalls  %d\n",pRasPort0->dwTotalNumberOfCalls);
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf,"pRasPort0->dwConnectDuration  %d\n",pRasPort0->dwConnectDuration);
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf,"pRasPort0->wszPortName  %ws\n",pRasPort0->wszPortName);
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf,"pRasPort0->wszMediaName  %ws\n",pRasPort0->wszMediaName);
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf,"pRasPort0->wszDeviceName  %ws\n",pRasPort0->wszDeviceName);
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf,"pRasPort0->wszDeviceType  %ws\n",pRasPort0->wszDeviceType);

    //dumping pRasPort1
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf,"\n dumping pRasPort1\n");
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf,"pRasPort1->hPort  %d\n",pRasPort1->hPort);
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf,"pRasPort1->hConnection   %d\n",pRasPort1->hConnection);
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf,"pRasPort1->dwHardwareCondition  %ws\n",Hdw_Error[pRasPort1->dwHardwareCondition]);

    //connection stats
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "pRasPort1->dwLineSpeed=%d\n", pRasPort1->dwLineSpeed );
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "pRasPort1->dwBytesXmited=%d\n", pRasPort1->dwBytesXmited );
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "pRasPort1->dwBytesRcved=%d\n", pRasPort1->dwBytesRcved );
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "pRasPort1->dwFramesXmited=%d\n", pRasPort1->dwFramesXmited );
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "pRasPort1->dwFramesRcved=%d\n", pRasPort1->dwFramesRcved);
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "pRasPort1->dwCrcErr=%d\n", pRasPort1->dwCrcErr);
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "pRasPort1->dwTimeoutErr=%d\n", pRasPort1->dwTimeoutErr);
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "pRasPort1->dwAlignmentErr=%d\n", pRasPort1->dwAlignmentErr);
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "pRasPort1->dwHardwareOverrunErr=%d\n", pRasPort1->dwHardwareOverrunErr);
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "pRasPort1->dwFramingErr=%d\n", pRasPort1->dwFramingErr);
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);
    sprintf(buf, "pRasPort1->dwBufferOverrunErr=%d\n", pRasPort1->dwBufferOverrunErr);
    WriteFile(ConDisFile, (LPSTR)buf, strlen(buf), &dwBytesWritten, NULL);

}


README.TXT

RAS Extensible Authentication API

See raseapif.h rasauth.h
~


EAP.H

/********************************************************************/
/**          Copyright(c) 1985-1997 Microsoft Corporation.         **/
/********************************************************************/

//***
//
// Filename:    eap.h
//
// Description: Sample Extensible Authentication Protocol header file
//
//

#ifndef _EAP_H_
#define _EAP_H_

//
// This protocols Type Id
//

#define PPP_EAP_PROTOCOL_ID 20

//
// Defines states within the this EAP protocol.
//

typedef enum _MYSTATE
{
    MYSTATE_Initial,
    MYSTATE_WaitForUserOK,
    MYSTATE_WaitForRequest,
    MYSTATE_ReqSent,
    MYSTATE_WaitForAuthenticationToComplete,
    MYSTATE_Done

}MYSTATE;

typedef struct _EAPCB
{
    MYSTATE     EapState;

    HANDLE      hPort;

    BOOL        fAuthenticator;

    LPVOID      pWorkBuffer;

    CHAR        szIdentity[ UNLEN + 1 ];

    DWORD       dwIdExpected;

    CHAR        szPassword[ PWLEN + 1 ];

    DWORD       dwResult;

    PBYTE       pUIContext;

    RAS_AUTH_ATTRIBUTE * pUserAttributes;

} EAPCB, *PEAPCB;

//
// Function Prototypes
//

DWORD APIENTRY
EapBegin(
    OUT VOID** ppWorkBuf,
    IN  VOID*  pInfo
);

DWORD APIENTRY
EapEnd(
    IN VOID* pWorkBuf
);

DWORD APIENTRY
EapMakeMessage(
    IN  VOID*               pWorkBuf,
    IN  PPP_EAP_PACKET*     pReceiveBuf,
    OUT PPP_EAP_PACKET*     pSendBuf,
    IN  DWORD               cbSendBuf,
    OUT PPP_EAP_OUTPUT*     pResult,
    IN  PPP_EAP_INPUT*      pInput
);

DWORD
AuthenticateeMakeMessage(
    IN  EAPCB*              pwb,
    IN  PPP_EAP_PACKET*     pReceiveBuf,
    OUT PPP_EAP_PACKET*     pSendBuf,
    IN  DWORD               cbSendBuf,
    IN PPP_EAP_INPUT*       pInput,
    OUT  PPP_EAP_OUTPUT*    pResult
);

DWORD
AuthenticatorMakeMessage(
    IN  EAPCB*              pwb,
    IN  PPP_EAP_PACKET*     pReceiveBuf,
    OUT PPP_EAP_PACKET*     pSendBuf,
    IN  DWORD               cbSendBuf,
    IN PPP_EAP_INPUT*       pInput,
    OUT  PPP_EAP_OUTPUT*    pResult
);

VOID
MakeResponseMessage(
    IN  EAPCB*              pwb,
    IN  PPP_EAP_PACKET*     pReceiveBuf,
    OUT PPP_EAP_PACKET *    pSendBuf,
    IN DWORD                cbSendBuf
);

VOID
MakeResultMessage(
    IN  DWORD              dwError,
    IN  BYTE               bId,
    OUT PPP_EAP_PACKET*    pSendBuf,
    IN  DWORD              cbSendBuf
);

DWORD
GetPasswordFromResponse(
    IN  PPP_EAP_PACKET*    pReceiveBuf,
    OUT CHAR*              pszUserName
);

DWORD
AuthenticateUser(
    IN CHAR *               szUserName,
    IN CHAR *               szPassword,
    IN EAPCB *              pwb
);

//
// Globals.
//

#ifdef RASEAPGLOBALS
#define GLOBALS
#define EXTERN
#else
#define EXTERN extern
#endif

//
// Next packet identifier to assign.
//
EXTERN DWORD dwNextId
#ifdef GLOBALS
    = 0
#endif
;

EXTERN
DWORD  (APIENTRY *FpRasAuthenticateClient)(
    IN  HANDLE                  hPort,
    IN  RAS_AUTH_ATTRIBUTE *    pInAttributes
)
#ifdef GLOBALS
    = NULL
#endif
;

#undef EXTERN
#undef GLOBALS

#endif // _EAP_H_


EAP.C

/********************************************************************/
/**          Copyright(c) 1985-1997 Microsoft Corporation.         **/
/********************************************************************/

//***
//
// Filename:    eap.c
//
// Description: Sample Extensible Authentication Protocol.
//              Here is a graphic of the EAP sample protocol:
//
//
//      Authenticator                       Authenticatee
//      -------------                       -------------
//
//                      "Send Password"
//                  ---------------------->
//                        EAP Request
//
//                        <password>
//                  <----------------------
//                        EAP Response
//
//
//                  ----------------------->
//                       Success/Failure
//
//

#include <windows.h>
#include <winuser.h>
#include <lmcons.h>
#include <string.h>
#include <stdlib.h>
#include <rasauth.h>
#include <raseapif.h>
#include <raserror.h>
#define SDEBUGGLOBALS
#define RASEAPGLOBALS
#include "eap.h"

/*---------------------------------------------------------------------------
** External entry points
**---------------------------------------------------------------------------
*/

BOOL
RasEapDllEntry(
    IN HANDLE hinstDll,
    IN DWORD  fdwReason,
    IN LPVOID lpReserved
)
    //
    // This routine is called by the system on various events such as the
    // process attachment and detachment.  See Win32 DllEntryPoint
    // documentation.
    //
    // Returns TRUE if successful, FALSE otherwise.
    //
{
    switch ( fdwReason )
    {
        case DLL_PROCESS_ATTACH:
        {
            DisableThreadLibraryCalls( hinstDll );

            break;
        }

        case DLL_PROCESS_DETACH:
        {
            break;
        }
    }

    return( TRUE );
}

DWORD APIENTRY
RasEapGetInfo (
    IN  DWORD         dwEapTypeId,
    OUT PPP_EAP_INFO* pInfo
)
    //
    // RasEapGetInfo entry point called by the EAP-PPP engine by name.
    //
{
    if ( dwEapTypeId != PPP_EAP_PROTOCOL_ID )
    {
        //
        // We only support PPP_EAP_PROTOCOL_ID eap type
        //

        return( ERROR_NOT_SUPPORTED );
    }

    ZeroMemory( pInfo, sizeof( PPP_EAP_INFO ) );

    //
    // Fill in the required information
    //

    pInfo->dwEapTypeId       = PPP_EAP_PROTOCOL_ID;
    pInfo->RasEapBegin       = EapBegin;
    pInfo->RasEapEnd         = EapEnd;
    pInfo->RasEapMakeMessage = EapMakeMessage;

    return( NO_ERROR );
}

DWORD APIENTRY
EapBegin(
    OUT VOID** ppWorkBuf,
    IN  VOID*  pInfo
)
    //
    // EapBegin entry point called by the EAP PPP engine thru the passed
    // address.
    //
{
    PPP_EAP_INPUT* pInput = (PPP_EAP_INPUT* )pInfo;
    EAPCB*         pwb;

    //
    // Allocate work buffer.
    //

    if ( ( pwb = (EAPCB* )LocalAlloc( LPTR, sizeof( EAPCB ) ) ) == NULL )
    {
        return( ERROR_NOT_ENOUGH_MEMORY );
    }

    //
    // Save information passed in, will be used later
    //

    pwb->fAuthenticator     = pInput->fAuthenticator;
    pwb->EapState           = MYSTATE_Initial;
    pwb->hPort              = pInput->hPort;
    FpRasAuthenticateClient = pInput->RasAuthenticateClient;

    //
    // Save the identity. On the authenticatee side, this is obtained by user
    // input, on the authenticator side this was obtained by the Identity
    // request message.
    //

    strcpy( pwb->szIdentity, pInput->pszIdentity );

    //
    // If we are an authenticatee, then use the password passed in
    //

    if ( !pwb->fAuthenticator )
    {
        strcpy( pwb->szPassword, pInput->pszPassword );
    }

    //
    // Register work buffer with engine.
    //

    *ppWorkBuf = pwb;

    return( NO_ERROR );
}

DWORD APIENTRY
EapEnd(
    IN VOID* pWorkBuf
)
    //
    // EapEnd entry point called by the PPP engine thru the passed address.
    // See EAP interface documentation.
    //
{
    if ( pWorkBuf != NULL )
    {
        //
        // Release all resources used by this authentication session.
        //

        EAPCB * pwb = ( EAPCB *)pWorkBuf;

        if ( pwb->pUIContext != NULL )
        {
            LocalFree( pwb->pUIContext );
        }

        ZeroMemory( pWorkBuf, sizeof(EAPCB) );

        LocalFree( pWorkBuf );
    }

    return( NO_ERROR );
}

DWORD APIENTRY
EapMakeMessage(
    IN  VOID*               pWorkBuf,
    IN  PPP_EAP_PACKET*     pReceiveBuf,
    OUT PPP_EAP_PACKET*     pSendBuf,
    IN  DWORD               cbSendBuf,
    OUT PPP_EAP_OUTPUT*     pResult,
    IN  PPP_EAP_INPUT*      pInput
)
    //
    // RasEapMakeMessage entry point called by the PPP engine thru the passed
    // address.
    //
{
    EAPCB * pwb = (EAPCB* )pWorkBuf;

    //
    // Call the appropriate routine to process the event.
    //

    if ( pwb->fAuthenticator )
    {
        return( AuthenticatorMakeMessage( pwb,
                                          pReceiveBuf,
                                          pSendBuf,
                                          cbSendBuf,
                                          pInput,
                                          pResult ) );
    }
    else
    {
        return( AuthenticateeMakeMessage( pwb,
                                          pReceiveBuf,
                                          pSendBuf,
                                          cbSendBuf,
                                          pInput,
                                          pResult ) );
    }
}

/*---------------------------------------------------------------------------
** Internal routines
**---------------------------------------------------------------------------
*/

VOID
HostToWireFormat16(
    IN     WORD  wHostFormat,
    IN OUT PBYTE pWireFormat
)
{
    //
    // Will convert a 16 bit integer from host format to wire format
    //

    *((PBYTE)(pWireFormat)+0) = (BYTE) ((DWORD)(wHostFormat) >>  8);
    *((PBYTE)(pWireFormat)+1) = (BYTE) (wHostFormat);
}

WORD
WireToHostFormat16(
    IN PBYTE pWireFormat
)
{
    //
    // Will convert a 16 bit integer from wire format to host format
    //

    WORD wHostFormat = ((*((PBYTE)(pWireFormat)+0) << 8) +
                        (*((PBYTE)(pWireFormat)+1)));

    return( wHostFormat );
}

DWORD
AuthenticateeMakeMessage(
    IN  EAPCB*            pwb,
    IN  PPP_EAP_PACKET*   pReceiveBuf,
    OUT PPP_EAP_PACKET*   pSendBuf,
    IN  DWORD             cbSendBuf,
    IN  PPP_EAP_INPUT*    pInput,
    OUT PPP_EAP_OUTPUT*   pResult
)
    //
    // Authenticatee side event handler
    //
{
    DWORD dwRetCode = NO_ERROR;

    switch( pwb->EapState )
    {
        case MYSTATE_Initial:

            //
            // Bring up interactive UI to notify user that he/she is being
            // authenticated via the sample EAP
            //

            pResult->fInvokeInteractiveUI = TRUE;

            pResult->dwSizeOfUIContextData =
                       strlen("You are being authenticated by a Sample EAP" )+1;

            pResult->pUIContextData = LocalAlloc(
                                              LPTR,
                                              pResult->dwSizeOfUIContextData);

            if ( pResult->pUIContextData == NULL )
            {
                return( ERROR_NOT_ENOUGH_MEMORY );
            }

            strcpy( pResult->pUIContextData,
                    "You are being authenticated by a Sample EAP"  );

            pwb->pUIContext = pResult->pUIContextData;

            pwb->EapState = MYSTATE_WaitForUserOK;

            break;

        case MYSTATE_WaitForUserOK:

            //
            // Wait for response from user
            //

            if ( pInput->fDataReceivedFromInteractiveUI )
            {
                if ( pwb->pUIContext != NULL )
                {
                    LocalFree( pwb->pUIContext );

                    pwb->pUIContext = NULL;
                }

                //
                // If user doesn't like this, then we hangup the line
                //

                if ( pInput->dwSizeOfDataFromInteractiveUI != (strlen("OK")+1) )
                {
                    dwRetCode = ERROR_ACCESS_DENIED;

                    break;
                }

                if (  strcmp( pInput->pDataFromInteractiveUI, "OK" ) != 0 )
                {
                    dwRetCode = ERROR_ACCESS_DENIED;

                    break;
                }

                pwb->EapState = MYSTATE_WaitForRequest;
            }
            else
            {
                //
                // Ignore all other events.
                //

                pResult->Action = EAPACTION_NoAction;
            }

            break;

        case MYSTATE_WaitForRequest:

            if ( pReceiveBuf != NULL )
            {
                //
                // If we received a request packet from the server then we
                // process it.
                //

                if ( pReceiveBuf->Code == EAPCODE_Request )
                {
                    //
                    // Build the response packet
                    //

                    MakeResponseMessage(pwb, pReceiveBuf, pSendBuf, cbSendBuf);

                    //
                    // Response packets should not be sent with any timeout
                    //

                    pResult->Action = EAPACTION_Send;

                    //
                    // We are done so we change to MYSTATE_Done
                    //

                    pwb->EapState = MYSTATE_Done;

                    break;
                }
                else
                {
                    //
                    // We shouldn't get any other packet in this state so
                    // we simply drop this invalid packet
                    //

                    pResult->Action = EAPACTION_NoAction;

                    dwRetCode = ERROR_PPP_INVALID_PACKET;

                    break;
                }
            }

            break;

        case MYSTATE_Done:
        {
            if ( pReceiveBuf == NULL )
            {
                //
                // If we did not receive a packet then we check to see if
                // the fSuccessPacketReceived flag is set
                //

                if ( ( pInput != NULL ) && ( pInput->fSuccessPacketReceived ) )
                {
                    //
                    // We are done
                    //

                    pResult->Action = EAPACTION_Done;
                    pwb->EapState   = MYSTATE_Done;
                }
                else
                {
                    //
                    // Otherwise we ignore this event
                    //

                    pResult->Action = EAPACTION_NoAction;
                }

                break;
            }

            if ( ( pReceiveBuf->Code == EAPCODE_Success ) ||
                 ( pReceiveBuf->Code == EAPCODE_Failure ) )
            {
                //
                // If we received success or failure, we are done
                //

                if ( pReceiveBuf->Id != pwb->dwIdExpected )
                {
                    //
                    // But first make sure the the Success/Failure packet ID
                    // matches that of the last response sent.
                    // If not silently discard the packet.
                    //

                    pResult->Action = EAPACTION_NoAction;
                    dwRetCode       = ERROR_PPP_INVALID_PACKET;
                }
                else
                {

                    pResult->Action = EAPACTION_Done;
                    pwb->EapState   = MYSTATE_Done;
                }

                break;
            }
            else if ( pReceiveBuf->Code == EAPCODE_Request )
            {
                //
                // We must always respond to requests
                //

                MakeResponseMessage(pwb, pReceiveBuf, pSendBuf, cbSendBuf);

                //
                // Response packets should not be sent with any timeout
                //

                pResult->Action = EAPACTION_Send;
            }
            else
            {
                //
                // Otherwise we received an illegal packet, wrong code set
                // So simply drop the packet.
                //

                pResult->Action = EAPACTION_NoAction;
                dwRetCode       = ERROR_PPP_INVALID_PACKET;
            }
        }
    }

    return( dwRetCode );
}

VOID
MakeResponseMessage(
    IN  EAPCB*           pwb,
    IN  PPP_EAP_PACKET * pReceiveBuf,
    OUT PPP_EAP_PACKET * pSendBuf,
    IN  DWORD            cbSendBuf
)
    //
    // Builds a response packet.  'pwb' is the address of the work
    // buffer associated with the port.
    //
{
    BYTE* pcbPassword;
    CHAR* pchPassword;

    (void )cbSendBuf;

    //
    // Fill in the password.
    //

    pcbPassword = pSendBuf->Data + 1;

    *pcbPassword = (BYTE )strlen( pwb->szPassword );

    pchPassword = pcbPassword + 1;

    strcpy( pchPassword, pwb->szPassword );

    //
    // Set the response code
    //

    pSendBuf->Code = (BYTE )EAPCODE_Response;

    //
    // The Reponse packet Id MUST match the Request packet Id.
    //

    pSendBuf->Id = pReceiveBuf->Id;

    //
    // The Success/Failure packet that we get must match the ID of the last
    // response sent
    //

    pwb->dwIdExpected = pSendBuf->Id;

    //
    // Set the EAP type ID
    //

    pSendBuf->Data[0] = (BYTE )PPP_EAP_PROTOCOL_ID;

    //
    // Set the length of the packet
    //

    HostToWireFormat16((WORD )(PPP_EAP_PACKET_HDR_LEN+1+*pcbPassword+1),
                       pSendBuf->Length );
}

VOID
MakeResultMessage(
    IN  DWORD           dwError,
    IN  BYTE            bId,
    OUT PPP_EAP_PACKET* pSendBuf,
    IN  DWORD           cbSendBuf
)

    //
    // Builds a result packet (Success or Failure) in caller's 'pSendBuf'
    // buffer. 'cbSendBuf' is the length of caller's buffer.
    // 'dwError' indicates whether an Success or Failure should be generated,
    // 'bId' is the Id of the Success of Failure packet.
    //
{
    (void )cbSendBuf;

    //
    // If there was no error then we send a Success packet, otherwise we send
    // a failure message
    //

    if ( dwError == NO_ERROR )
    {
        pSendBuf->Code = EAPCODE_Success;
    }
    else
    {
        pSendBuf->Code = EAPCODE_Failure;
    }

    //
    // Id must match the last response received
    //

    pSendBuf->Id = bId;

    //
    // Set the length
    //

    HostToWireFormat16((WORD)PPP_EAP_PACKET_HDR_LEN, (PBYTE )pSendBuf->Length);
}

VOID
MakeRequestMessage(
    IN  EAPCB*           pwb,
    OUT PPP_EAP_PACKET * pSendBuf,
    IN DWORD             cbSendBuf
)

    //
    // Will build a request packet
    //
{
    BYTE *pcbPeerMessage;
    CHAR *pchPeerMessage;

    pcbPeerMessage  = pSendBuf->Data + 1;

    *pcbPeerMessage = (BYTE)strlen("send password");

    pchPeerMessage  = pcbPeerMessage + 1;

    strcpy (pchPeerMessage,"send password");

    //
    // Set the Request Code
    //

    pSendBuf->Code = EAPCODE_Request;

    //
    // Set the Identifier
    //

    pSendBuf->Id = (BYTE) dwNextId++;

    //
    // Remember this Id since we need to send the Success/Failure packet with
    // this Id
    //

    pwb->dwIdExpected = pSendBuf->Id;

    //
    // Set the length
    //

    HostToWireFormat16((WORD)(PPP_EAP_PACKET_HDR_LEN+1+*pcbPeerMessage+1),
                              pSendBuf->Length );

    //
    // Set the EAP Type Id
    //

    pSendBuf->Data[0] = PPP_EAP_PROTOCOL_ID;

}

DWORD
AuthenticatorMakeMessage(
    IN  EAPCB*              pwb,
    IN  PPP_EAP_PACKET*     pReceiveBuf,
    OUT PPP_EAP_PACKET*     pSendBuf,
    IN  DWORD               cbSendBuf,
    IN  PPP_EAP_INPUT*      pInput,
    OUT PPP_EAP_OUTPUT*     pResult
)
    //
    // Authenticator side event handler
    //
{
    DWORD dwRetCode = NO_ERROR;

    switch( pwb->EapState )
    {
        case MYSTATE_ReqSent:

            if ( pReceiveBuf != NULL )
            {
                //
                // If we received a packet
                //

                if ( pReceiveBuf->Code == EAPCODE_Response )
                {
                    CHAR szPassword[PWLEN + 1];

                    //
                    // If we received a response to our identity request,
                    // then process it. There is no need to check the Id
                    // here since the PPP engine will only pass on packets
                    // whose Id matches those set with the
                    // EAPACTION_SendWithTimeout action.
                    //

                    dwRetCode = GetPasswordFromResponse( pReceiveBuf,
                                                         szPassword );

                    if ( dwRetCode != NO_ERROR )
                    {
                        if ( dwRetCode != ERROR_PPP_INVALID_PACKET )
                        {
                            //
                            // Fatal error, we fail the connection.
                            //

                            return( dwRetCode );
                        }

                        //
                        // Otherwise the packet is most likely corrupt.
                        // Fall thru to the initial state to resend the
                        // request with the same Id.
                        //

                        dwNextId = pwb->dwIdExpected;
                    }
                    else
                    {

                        //
                        // Request authentication provider to authenticate
                        // this user.
                        //

                        dwRetCode = AuthenticateUser( pwb->szIdentity,
                                                      szPassword,
                                                      pwb );

                        if ( dwRetCode != NO_ERROR )
                        {
                            return( dwRetCode );
                        }
                        else
                        {
                            //
                            // Authentication request completed successfully.
                            // This is an asynchronous call so we change state
                            // and wait for the provider to complete the
                            // authentication.
                            //

                            pwb->EapState =
                                    MYSTATE_WaitForAuthenticationToComplete;

                            pResult->Action = EAPACTION_NoAction;
                        }
                    }

                    break;
                }
                else
                {
                    //
                    // Otherwise silently drop the packet.
                    // We should only get requests
                    //

                    pResult->Action = EAPACTION_NoAction;

                    break;
                }
            }
            else
            {
                //
                // If in this state we get called with a NULL pReceiveBuf, this
                // means that we timed out waiting for a response from the
                // the authenticatee, we need to resend with the same Id.
                //

                dwNextId = pwb->dwIdExpected;

                //
                // Fall thru to resend the request packet
                //
            }

        case MYSTATE_Initial:

            //
            // Create Request packet
            //

            MakeRequestMessage( pwb, pSendBuf, cbSendBuf );

            //
            // Tell the PPP engine to drop all responses that do not mactch
            // this Id
            //

            pResult->dwIdExpected = (BYTE)pwb->dwIdExpected;

            //
            // Request messages must be sent with a timeout
            //

            pResult->Action = EAPACTION_SendWithTimeoutInteractive;

            //
            // Send we have sent a Request we change to the ReqSent state
            // where we will wait for a response
            //

            pwb->EapState = MYSTATE_ReqSent;

            break;

        case MYSTATE_WaitForAuthenticationToComplete:
        {
            if ( pInput != NULL )
            {
                //
                // Did the authentication provider complete the authentication?
                //

                if ( pInput->fAuthenticationComplete )
                {
                    //
                    // If there was a processing error, simply return this
                    // error.
                    //

                    if ( pInput->dwAuthError != NO_ERROR )
                    {
                        return( pInput->dwAuthError );
                    }

                    //
                    // If the authentication process completed successfully but
                    // the user failed to authenticate, save the failure code.
                    //

                    if ( pInput->dwAuthResultCode != NO_ERROR )
                    {
                        pwb->dwResult = pInput->dwAuthResultCode;
                    }

                    pResult->Action = EAPACTION_SendAndDone;
                    pwb->EapState   = MYSTATE_Done;

                    //
                    // fall thru to the MYSTATE_Done state where we will
                    // send a Success or Failure packet
                    //
                }
            }

            if ( ( pInput == NULL ) || ( !pInput->fAuthenticationComplete ) )
            {
                //
                // Ignore everything if authentication is not complete
                //

                pResult->Action = EAPACTION_NoAction;

                break;
            }

            //
            // ...fall thru to the MYSTATE_Done state where we will
            // send a Success or Failure packet
            //
        }

        case MYSTATE_Done:
        {
            //
            // Make Success or Failure packet.
            //

            MakeResultMessage( pwb->dwResult,
                               (BYTE)pwb->dwIdExpected,
                               pSendBuf,
                               cbSendBuf);

            strcpy( pResult->szIdentity, pwb->szIdentity );

            pResult->Action = EAPACTION_SendAndDone;

            pResult->dwAuthResultCode = pwb->dwResult;

            break;
        }

        default:

            break;
    }

    return( dwRetCode );

}

DWORD
GetPasswordFromResponse(
    IN  PPP_EAP_PACKET* pReceiveBuf,
    OUT CHAR*           pszPassword
)
    //
    // Fill caller's pszPassword' buffer with the password, in the request
    // packet.
    //
    // Returns NO_ERROR if successful., or ERROR_PPP_INVALID_PACKET if the
    // packet is misformatted in any way.
    //
{
    BYTE* pcbPassword;
    CHAR* pchPassword;
    WORD  cbPacket;

    cbPacket = WireToHostFormat16( pReceiveBuf->Length );

    //
    // Extract the password
    //

    if ( cbPacket < ( PPP_EAP_PACKET_HDR_LEN + 1 + 1 ) )
    {
        return( ERROR_PPP_INVALID_PACKET );
    }

    pcbPassword = pReceiveBuf->Data + 1;
    pchPassword = pcbPassword + 1;

    if (cbPacket < PPP_EAP_PACKET_HDR_LEN + 1 + 1 + *pcbPassword)
    {
        return ERROR_PPP_INVALID_PACKET;
    }

    CopyMemory( pszPassword, pchPassword, *pcbPassword );

    //
    // NULL terminate the password
    //

    pszPassword[ *pcbPassword ] = '\0';

    return( NO_ERROR );
}

DWORD
AuthenticateUser(
    IN CHAR *szUserName,
    IN CHAR *szPassword,
    IN EAPCB *pwb
)
    //
    // Will build user attributes and send them to the authentication provider
    // for authentication.
    //
{

    RAS_AUTH_ATTRIBUTE *pAttributes = NULL;
    DWORD dwRetCode;

    pAttributes = (RAS_AUTH_ATTRIBUTE *)
                 LocalAlloc(LPTR, sizeof (RAS_AUTH_ATTRIBUTE) * 3);

    if (pAttributes == NULL)
    {
        return (GetLastError());
    }

    //
    // for user name
    //

    pAttributes[0].raaType = raatUserName;
    pAttributes[0].dwLength =strlen(szUserName);
    pAttributes[0].Value = LocalAlloc(LPTR, (strlen(szUserName)+1));

    if (pAttributes == NULL)
    {
        LocalFree (pAttributes);
        return (GetLastError());
    }

    CopyMemory (pAttributes[0].Value,szUserName, strlen(szUserName));

    //
    // for password
    //

    pAttributes[1].raaType = raatUserPassword;
    pAttributes[1].dwLength =strlen(szPassword);
    pAttributes[1].Value = LocalAlloc(LPTR, (strlen(szPassword)+1));

    if (pAttributes == NULL)
    {
        LocalFree (pAttributes[0].Value);
        LocalFree (pAttributes);
        return (GetLastError());
    }

    CopyMemory (pAttributes[1].Value,szPassword, strlen(szPassword));

    //
    // For Termination
    //

    pAttributes[2].raaType  = raatMinimum;
    pAttributes[2].dwLength = 0;
    pAttributes[2].Value    = NULL;

    //
    // Call the authentication provider
    //

    dwRetCode = (*FpRasAuthenticateClient)( pwb->hPort, pAttributes );

    //
    // Free up Attributes
    //

    LocalFree (pAttributes[0].Value);
    LocalFree (pAttributes[1].Value);

    return( dwRetCode );
}

DWORD APIENTRY
RasEapInvokeInteractiveUI(
    IN  HWND            hWndParent,
    IN  PBYTE           pUIContextData,
    IN  DWORD           dwSizeofUIContextData,
    OUT PBYTE *         ppUserData              OPTIONAL,
    OUT DWORD *         lpdwSizeOfUserData
)
{
    if ( MessageBoxA( hWndParent,
                      pUIContextData,
                      "EAP sample",
                      MB_OKCANCEL ) == IDOK )
    {
        *lpdwSizeOfUserData = strlen( "OK" )+ 1;

        if ( ( *ppUserData = LocalAlloc( LPTR, strlen( "OK" )+ 1 ) ) == NULL )
        {
            return( ERROR_NOT_ENOUGH_MEMORY );
        }

        strcpy( *ppUserData, "OK" );
    }
    else
    {
        *ppUserData         = NULL;
        *lpdwSizeOfUserData = 0;
    }

    return( NO_ERROR );
}

DWORD APIENTRY
RasEapFreeUserData(
    IN  PBYTE           pUserData
)
{
    if ( pUserData != NULL )
    {
        LocalFree( pUserData );
    }

    return( NO_ERROR );
}


ALGO.C

/*++

Copyright (c) 1995  Microsoft Corporation

Module Name:

    net\ip\lookup\algo.c

Abstract:

Revision History:

--*/

DWORD g_dwBitMask = {0x00000001,
                     0x00000002,
                     0x00000004,
                     0x00000008,
                     0x00000010,
                     0x00000020,
                     0x00000040,
                     0x00000080,
                     0x00000100,
                     0x00000200,
                     0x00000400,
                     0x00000800,
                     0x00001000,
                     0x00002000,
                     0x00004000,
                     0x00008000,
                     0x00010000,
                     0x00020000,
                     0x00040000,
                     0x00080000,
                     0x00100000,
                     0x00200000,
                     0x00400000,
                     0x00800000,
                     0x01000000,
                     0x02000000,
                     0x04000000,
                     0x08000000,
                     0x10000000,
                     0x20000000,
                     0x40000000,
                     0x80000000};

DWORD g_dwPrefixMask = {0x00000001,
                        0x00000003,
                        0x00000007,
                        0x0000000F,
                        0x0000001F,
                        0x0000003F,
                        0x0000007F,
                        0x000000FF,
                        0x000001FF,
                        0x000003FF,
                        0x000007FF,
                        0x00000FFF,
                        0x00001FFF,
                        0x00003FFF,
                        0x00007FFF,
                        0x0000FFFF,
                        0x0001FFFF,
                        0x0003FFFF,
                        0x0007FFFF,
                        0x000FFFFF,
                        0x001FFFFF,
                        0x003FFFFF,
                        0x007FFFFF,
                        0x00FFFFFF,
                        0x01FFFFFF,
                        0x03FFFFFF,
                        0x07FFFFFF,
                        0x0FFFFFFF,
                        0x1FFFFFFF,
                        0x3FFFFFFF,
                        0x7FFFFFFF,
                        0xFFFFFFFF}

BYTE
DistPos(
    IN  PTRIE_KEY   ptkKey1,
    IN  PTRIE_KEY   ptkKey2
    )
/*++
  Routine Description
      Returns the position of the distinguishing bit for two keys. This is
      the first bit that differs between the two keys.  If one key is a prefix
      of another (strict or non strict), then the dist bit is 1+width of the smaller
      key (== length of smaller key)

      Notationally:
      DistBit(K1, K2) = Min{i|K1[i] != K2[i]}
      DistBit(K1, K2) = Length(K1) iff K1 is a prefix of K2

  Arguments

  Return Value
      NO_ERROR

--*/
{
    BYTE    byLength;

    byLength = MAX(Length(ptkKey1),
                   Length(ptkKey2));

    for(i = 0; i < byLength; i++)
    {
        if(ptkKey1->dwAddr & g_dwBitMask[i] isnot
           ptkKey2->dwAddr & g_dwBitMask[i])
        {
            break;
        }
    }

    return i;
}

PTRIE_KEY
GetKey(
    IN  PTRIE_NODE  ptnNode,
    IN  PTRIE_KEY   ptkKey,
    OUT PBYTE       pbyPosition
    )
/*++
  Routine Description
      Given a input key and node, returns the stored key in the node
      whose index bit matches with the input key

  Arguments

  Return Value
      NO_ERROR

--*/
{

    if(Width(ptkKey) < Index(ptnNode))
    {
        return NULL;
    }

    *pbyPosition = GetRelevantBit(ptkKey->dwAddr, Index(ptnNode));

#if DBG

    //
    // A little consistency check here
    //

    if(LeftKey(ptnNode))
    {
        ASSERT(ptnNode->byPosition is LEFT);
    }

    if(RightKey(ptnNode))
    {
        ASSERT(ptnNode->byPosition is RIGHT);
    }

#endif

    return GetKeyByPosition(ptnNode,
                            *pbyPosition);
}

PTRIE_NODE
GetSubTrie(
    IN  PTRIE_NODE  ptnNode,
    IN  PTRIE_KEY   ptkKey,
    OUT PBYTE       pbyPosition
    )
/*++
  Routine Description

  Locks

  Arguments

  Return Value
      NO_ERROR

--*/
{

    if(Width(ptkKey) < Index(ptnNode))
    {
        return NULL;
    }

    *pbyPosition = GetRelevantBit(ptkKey->dwAddr, Index(ptnNode));

#if DBG

    //
    // A little consistency check here
    //

    if(LeftSubTrie(ptnNode))
    {
        ASSERT(ptnNode->ptnTrie[LEFT]->byPosition is LEFT);
    }

    if(RightSubTrie(ptnNode))
    {
        ASSERT(ptnNode->ptnTrie[RIGHT]->byPosition is RIGHT);
    }

#endif

    return GetSubTrieByPosition(ptnNode,
                                *pbyPosition);

}

PTRIE_KEY
GetClosestKey(
    PTRIE_NODE  ptnNode,
    PTRIE_KEY   ptkKey
    )
/*++
  Routine Description
      If the length of the key is greater than or equal to the index, return
      any Key.
      else
         If a key with matching relevant bit is found, return that, else
         return the other key

  Locks

  Arguments

  Return Value
      NO_ERROR

--*/
{
    PTRIE_KEY   ptkTemp;
    BYTE        byPos;

    if(Width(ptkKey) >= Index(ptnNode))
    {
        ptkTemp = GetKey(ptnNode,
                         ptkKey,
                         &byPos);

        if(ptkTemp isnot NULL)
        {
            return ptkTemp;
        }
        else
        {
            return GetKeyByPosition(ptnNode,
                                    ComplementPosition(byPos))
        }
    }
    else
    {
        //
        // For now we return the left key first
        //

        ptkTemp = GetKeyByPosition(ptnNode,
                                   LEFT);

        if(ptkTemp)
        {
            return ptkTemp;
        }
        else
        {
            return GetKeyByPosition(ptnNode,
                                    RIGHT);
        }
    }
}

PTRIE_NODE
GetClosestSubTrie(
    PTRIE_NODE  ptnNode,
    PTRIE_KEY   ptkKey
    )
/*++
  Routine Description
      If the length of the key is greater than or equal to the index, return
      any sub trie.
      else
         If a trie with matching relevant bit is found, return that, else
         return the other trie

  Locks

  Arguments

  Return Value
      NO_ERROR

--*/
{
    PTRIE_NODE  ptnTemp;
    BYTE        byPos;

    if(Width(ptkKey) >= Index(ptnNode))
    {
        ptnTemp = GetSubTrie(ptnNode,
                             ptkKey,
                             &byPos);

        if(ptnTemp isnot NULL)
        {
            return ptnTemp;
        }
        else
        {
            return GetSubTrieByPosition(ptnNode,
                                        ComplementPosition(byPos))
        }
    }
    else
    {
        //
        // For now we return the left trie first
        //

        ptkTemp = GetSubTrieByPosition(ptnNode,
                                       LEFT);

        if(ptkTemp)
        {
            return ptkTemp;
        }
        else
        {
            return GetSubTrieByPosition(ptnNode,
                                        RIGHT);
        }
    }
}

PTRIE_NODE
CreateNode(
    IN  BYTE        byIndex,
    IN  PTRIE_KEY   ptkKey
    )
/*++
  Routine Description

  Locks

  Arguments

  Return Value
      NO_ERROR

--*/
{
    PTRIE_NODE  ptnNode;

    ptnNode = HeapAlloc(g_hPrivateHeap,
                        0,
                        sizeof(TRIE_NODE));

    if(ptnNode is NULL)
    {
        printf("Unable to allocate node. Error %d\n",
               GetLastError());

        return NULL;
    }

    ptnNode->ptnTrie[LEFT]  = NULL;
    ptnNode->ptnTrie[RIGHT] = NULL;

    ptnNode->ptnParent      = NULL;

    ptnNode->byIndex        = byIndex;

    //
    // See where the key would go into the allocated node
    //

    GetKey(ptnNode,
           ptkKey,
           &byPos);

    //
    // Since we are going to be inserting the key here, set
    // its position field also
    //

    ptkKey->byPos = byPos;

    ptnNode->rgptkKey[byPos]  = ptkKey;

    ptnNode->rgptkKey[ComplementPosition(byPos)] = NULL;

    return ptnNode;
}

DWORD
InsertKey(
    PTRIE_NODE  *pptnRoot,
    PTRIE_KEY   ptkKey
    )
/*++
  Routine Description

  Locks

  Arguments

  Return Value
      NO_ERROR

--*/
{
    PTRIE_NODE  ptnTempNode;

    if(*pptnRoot is NULL)
    {
        *pptnRoot = AllocateNode(Width(ptkKey),
                                 ptkKey);

        if(*pptnRoot is NULL)
        {
            return ERROR_NOT_ENOUGH_MEMORY;
        }

        return NO_ERROR;
    }

    ptnTempNode = *pptnRoot;

    //
    // Descend the tree, branching according to the Index bit
    // Stop when the node is a leaf OR
    // when the index is greater than the width of the key OR
    // when the prefix of the node is not the same as the key
    //
    // The prefix is found by (ptkKey->dwAddr & g_dwPrefixMask[Index])
    //

    while(!IsLeafNode(ptnTempNode) and
          (Width(ptkKey) > Index(ptnTempNode)) and
          (ptnTempNode->rgptkKey[ptnTempNode->byNonNullKey] & g_dwPrefixMask[Index(ptnTempNode)] is
           ptkKey[
    {
        ptnTempNode = ClosestSubTrie(ptnTempNode,
                                     ptkKey);
    }

    byDistPost = DistPos(key,
                         ClosestKey(ptnNode,ptkKey));

    byIndex = MIN(Lenght(Key), byDistPos);

    if(ptnTempNode is *pptnRoot)
    {
        InsertInOrAbove(pptnRoot,
                        ptnTempNode,
                        ptkKey,
                        byDistPos);
    }
    else
    {
        if(GetSubTrie(ptnNode, ptkKey) is NULL)
        {
            InsertWithEmptySubTrie(ptnNode,
                                   ptkKey,
                                   byDistPos);
        }
        else
        {
            InsertWithNonEmptySubTrie(ptnNode,
                                      ptkKey,
                                      byDistPos);
        }
    }

    return NO_ERROR;
}

DWORD
InsertInOrAbove(
    PTRIE_NODE  *pptnRoot,
    PTRIE_NODE  ptnNode,
    PTRIE_KEY   ptkKey,
    BYTE        byDistPos
    )
/*++
  Routine Description

  Locks

  Arguments

  Return Value
      NO_ERROR

--*/
{
    if(


STRUCTS.H

/*++

Copyright (c) 1995  Microsoft Corporation

Module Name:

    net\ip\lookup\structs.h

Abstract:

Revision History:

--*/

#ifndef __LOOKUP_STRUCTS_H__
#define __LOOKUP_STRUCTS_H__

#define LEFT    0
#define RIGHT   1

//
//  BYTE
//  ComplementPosition(
//      IN BYTE byPos
//      )
//

#define ComplementPosition(X)   ((X)^0x01)

typedef struct _TRIE_KEY
{
    DWORD   dwAddr;
    DWORD   dwMask;
    BYTE    byPosition;
    BYTE    byLength;
}TRIE_KEY, *PTRIE_KEY;

//
//  ULONG
//  Length(
//      IN PTRIE_KEY ptkKey
//      )
//

#define Length(X)    (X)->byLength

typedef struct _TRIE_NODE TRIE_NODE *PTRIE_NODE;

struct _TRIE_NODE
{
    PTRIE_NODE  ptnParent;
    PTRIE_NODE  ptnTrie[2];
    PTRIE_KEY   ptkKey[2];
    BYTE        byPosition;
    BYTE        byIndex;
};

//
//  BYTE
//  Index(
//      IN PTRIE_NODE ptnNode
//      )
//

#define Index(X)    (X)->byIndex

//
//  PTRIE_NODE
//  Parent(
//      IN PTRIE_NODE ptnNode
//      )
//

#define Parent(X)   (X)->ptnParent

//
//  PTRIE_NODE
//  LeftTrie(
//      IN PTRIE_NODE ptnNode
//      )
//

#define LeftTrie(X) (X)->ptnTrie[LEFT]

//
//  PTRIE_NODE
//  RightTrie(
//      IN PTRIE_NODE ptnNode
//      )
//

#define RightTrie(X) (X)->ptnTrie[RIGHT]

//
//  PTRIE_NODE
//  LeftKey(
//      IN PTRIE_NODE ptnNode
//      )
//

#define LeftKey(X) (X)->rgptkKey[LEFT]

//
//  PTRIE_NODE
//  RightKey(
//      IN PTRIE_NODE ptnNode
//      )
//

#define RightKey(X) (X)->rgptkKey[RIGHT]

//
//  BOOL
//  IsLeafNode(
//      IN PTRIE_NODE ptnNode
//      )
//

#define IsLeafNode(X)   ((RightTrie((X)) == NULL) && (LeftTrie((X)) == NULL))

//
//  BOOL
//  IsSingleKeyNode(
//      IN PTRIE_NODE ptnNode
//      )
//

#define IsSingleKeyNode(X) ((RightKey((X)) && (LeftKey((X)) == NULL)) ||    \
                            (LeftKey((X)) && (RightKey((X)) == NULL)))

//
//  BOOL
//  IsSingleKeyLeafNode(
//      IN PTRIE_NODE ptnNode
//      )
//

#define IsSingleKeyLeafNode(X) (IsLeafNode((X)) && IsSingleKeyNode((X)))

//
//  PTRIE_KEY
//  GetKeyByPosition(
//      IN PTRIE_NODE ptnNode,
//      IN ULONG      ulPosition
//      )
//

#define GetKeyByPosition(X,Y)   ((X)->rgptkKey[(Y)])

//
//  PTRIE_NODE
//  GetSubTrieByPosition(
//      IN PTRIE_NODE ptnNode,
//      IN ULONG      ulPosition
//      )
//

#define GetSubTrieByPosition(X,Y)  ((X)->ptnNode[(Y)])


README.TXT

========================================================================
OLE Control DLL : PLUGIN
========================================================================

ControlWizard has created this project for your PLUGIN OLE Control DLL,
which contains 1 control.

This skeleton project not only demonstrates the basics of writing an OLE
Control, but is also a starting point for writing the specific features
of your control.

This file contains a summary of what you will find in each of the files
that make up your PLUGIN OLE Control DLL.

plugin.mak
The Visual C++ project makefile for building your OLE Control.

plugin.h
This is the main include file for the OLE Control DLL.  It
includes other project-specific includes such as resource.h.

plugin.cpp
This is the main source file that contains code for DLL initialization,
termination and other bookkeeping.

plugin.rc
This is a listing of the Microsoft Windows resources that the project
uses.  This file can be directly edited with the Visual C++ resource
editor.

plugin.def
This file contains information about the OLE Control DLL that
must be provided to run with Microsoft Windows.

plugin.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 generate and edit message maps and dialog data
maps and to generate prototype member functions.

plugin.odl
This file contains the Object Description Language source code for the
type library of your control.

/////////////////////////////////////////////////////////////////////////////
Plugin control:

PluginC.h
This file contains the declaration of the CPluginCtrl C++ class.

PluginC.cpp
This file contains the implementation of the CPluginCtrl C++ class.

PluginP.h
This file contains the declaration of the CPluginPropPage C++ class.

PluginP.cpp
This file contains the implementation of the CPluginPropPage C++ class.

PluginC.bmp
This file contains a bitmap that a container will use to represent the
CPluginCtrl control when it appears on a tool palette.  This bitmap
is included by the main resource file plugin.rc.

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

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

resource.h
This is the standard header file, which defines new resource IDs.
The Visual C++ resource editor reads and updates this file.

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

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

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


PLUGIN.H

// plugin.h : main header file for PLUGIN.DLL

#if !defined( __AFXCTL_H__ )
#error include 'afxctl.h' before including this file
#endif

#include "resource.h"       // main symbols
#include "mprui.h"

/////////////////////////////////////////////////////////////////////////////
// CPluginApp : See plugin.cpp for implementation.

class CPluginApp : public COleControlModule
{
public:
BOOL InitInstance();
int ExitInstance();
};

extern const GUID CDECL _tlid;
extern const WORD _wVerMajor;
extern const WORD _wVerMinor;

/////////////////////////////////////////////////////////////////////////////
// CPluginObj command target

class CPluginObj : public CCmdTarget
{
DECLARE_DYNCREATE(CPluginObj)

CPluginObj();           // protected constructor used by dynamic creation

// Attributes
public:

// Operations
public:

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CPluginObj)
public:
virtual void OnFinalRelease();
//}}AFX_VIRTUAL

// Implementation
protected:
IMprUIHost *m_pIMprUIHost;
virtual ~CPluginObj();

// Generated message map functions
//{{AFX_MSG(CPluginObj)
// NOTE - the ClassWizard will add and remove member functions here.
//}}AFX_MSG

DECLARE_MESSAGE_MAP()
DECLARE_OLECREATE(CPluginObj)

 BEGIN_INTERFACE_PART(MprUIPlugin, IUnknown)
// Connect to router.  The plugin module should disconnect from the
//  router when the IMprUIPlugin is released.
STDMETHOD(ConnectToRouter)(LPCTSTR pRouterName, IMprUIHost *pMprUIHost);
// Returns the text for the given node.
//  Use CoTaskMemAlloc to allocate memory for ppszTitle
STDMETHOD(GetTitle)(PWSTR* ppszTitle);
// Returns a context menu for the given node.
//  Menu will be destroyed by host.
STDMETHOD(GetContextMenu)(HMENU* phMenu);
// Processes the command returned by the context menu
STDMETHOD(ProcessCommand)(WPARAM wParam, LPARAM lParam, HWND hWndParent);
// Returns CLSID for the ole control which displays right hand pane
//  Use CoTaskMemAlloc to allocate space for CLSID*
STDMETHOD(GetCLSID)(CLSID** ppClsid);
END_INTERFACE_PART(MprUIPlugin)

DECLARE_INTERFACE_MAP()
};

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


PLUGINC.CPP

// PluginC.cpp : Implementation of the CPluginCtrl OLE control class.

#include "stdafx.h"
#include "plugin.h"
#include "PluginC.h"
#include "PluginP.h"

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

IMPLEMENT_DYNCREATE(CPluginCtrl, COleControl)

/////////////////////////////////////////////////////////////////////////////
// Message map

BEGIN_MESSAGE_MAP(CPluginCtrl, COleControl)
//{{AFX_MSG_MAP(CPluginCtrl)
// NOTE - ClassWizard will add and remove message map entries
//    DO NOT EDIT what you see in these blocks of generated code !
//}}AFX_MSG_MAP
ON_OLEVERB(AFX_IDS_VERB_PROPERTIES, OnProperties)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// Dispatch map

BEGIN_DISPATCH_MAP(CPluginCtrl, COleControl)
//{{AFX_DISPATCH_MAP(CPluginCtrl)
DISP_FUNCTION(CPluginCtrl, "MprUISetContext", MprUISetContext, VT_I4, VTS_I4 VTS_I4)
DISP_FUNCTION(CPluginCtrl, "MprUIRefresh", MprUIRefresh, VT_I4, VTS_NONE)
//}}AFX_DISPATCH_MAP
END_DISPATCH_MAP()

/////////////////////////////////////////////////////////////////////////////
// Event map

BEGIN_EVENT_MAP(CPluginCtrl, COleControl)
//{{AFX_EVENT_MAP(CPluginCtrl)
// NOTE - ClassWizard will add and remove event map entries
//    DO NOT EDIT what you see in these blocks of generated code !
//}}AFX_EVENT_MAP
END_EVENT_MAP()

/////////////////////////////////////////////////////////////////////////////
// Property pages

// TODO: Add more property pages as needed.  Remember to increase the count!
BEGIN_PROPPAGEIDS(CPluginCtrl, 1)
PROPPAGEID(CPluginPropPage::guid)
END_PROPPAGEIDS(CPluginCtrl)

/////////////////////////////////////////////////////////////////////////////
// Initialize class factory and guid

IMPLEMENT_OLECREATE_EX(CPluginCtrl, "PLUGIN.PluginCtrl.1",
0x85df114, 0x10e, 0x11d0, 0xaa, 0x6b, 0, 0x80, 0x5f, 0xc, 0x92, 0x32)

/////////////////////////////////////////////////////////////////////////////
// Type library ID and version

IMPLEMENT_OLETYPELIB(CPluginCtrl, _tlid, _wVerMajor, _wVerMinor)

/////////////////////////////////////////////////////////////////////////////
// Interface IDs

const IID BASED_CODE IID_DPlugin =
{ 0x85df112, 0x10e, 0x11d0, { 0xaa, 0x6b, 0, 0x80, 0x5f, 0xc, 0x92, 0x32 } };
const IID BASED_CODE IID_DPluginEvents =
{ 0x85df113, 0x10e, 0x11d0, { 0xaa, 0x6b, 0, 0x80, 0x5f, 0xc, 0x92, 0x32 } };

/////////////////////////////////////////////////////////////////////////////
// Control type information

static const DWORD BASED_CODE _dwPluginOleMisc =
OLEMISC_ACTIVATEWHENVISIBLE |
OLEMISC_SETCLIENTSITEFIRST |
OLEMISC_INSIDEOUT |
OLEMISC_CANTLINKINSIDE |
OLEMISC_RECOMPOSEONRESIZE;

IMPLEMENT_OLECTLTYPE(CPluginCtrl, IDS_PLUGIN, _dwPluginOleMisc)

/////////////////////////////////////////////////////////////////////////////
// CPluginCtrl::CPluginCtrlFactory::UpdateRegistry -
// Adds or removes system registry entries for CPluginCtrl

BOOL CPluginCtrl::CPluginCtrlFactory::UpdateRegistry(BOOL bRegister)
{
// TODO: Verify that your control follows apartment-model threading rules.
// Refer to MFC TechNote 64 for more information.
// If your control does not conform to the apartment-model rules, then
// you must modify the code below, changing the 6th parameter from
// afxRegApartmentThreading to 0.

if (bRegister)
return AfxOleRegisterControlClass(
AfxGetInstanceHandle(),
m_clsid,
m_lpszProgID,
IDS_PLUGIN,
IDB_PLUGIN,
afxRegApartmentThreading,
_dwPluginOleMisc,
_tlid,
_wVerMajor,
_wVerMinor);
else
return AfxOleUnregisterClass(m_clsid, m_lpszProgID);
}

/////////////////////////////////////////////////////////////////////////////
// CPluginCtrl::CPluginCtrl - Constructor

CPluginCtrl::CPluginCtrl()
{
InitializeIIDs(&IID_DPlugin, &IID_DPluginEvents);

// TODO: Initialize your control's instance data here.
}

/////////////////////////////////////////////////////////////////////////////
// CPluginCtrl::~CPluginCtrl - Destructor

CPluginCtrl::~CPluginCtrl()
{
// TODO: Cleanup your control's instance data here.
}

/////////////////////////////////////////////////////////////////////////////
// CPluginCtrl::OnDraw - Drawing function

void CPluginCtrl::OnDraw(
CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid)
{
// TODO: Replace the following code with your own drawing code.
pdc->FillRect(rcBounds, CBrush::FromHandle((HBRUSH)GetStockObject(WHITE_BRUSH)));
pdc->Ellipse(rcBounds);
}

/////////////////////////////////////////////////////////////////////////////
// CPluginCtrl::DoPropExchange - Persistence support

void CPluginCtrl::DoPropExchange(CPropExchange* pPX)
{
ExchangeVersion(pPX, MAKELONG(_wVerMinor, _wVerMajor));
COleControl::DoPropExchange(pPX);

// TODO: Call PX_ functions for each persistent custom property.

}

/////////////////////////////////////////////////////////////////////////////
// CPluginCtrl::OnResetState - Reset control to default state

void CPluginCtrl::OnResetState()
{
COleControl::OnResetState();  // Resets defaults found in DoPropExchange

// TODO: Reset any other control state here.
}

/////////////////////////////////////////////////////////////////////////////
// CPluginCtrl message handlers

long CPluginCtrl::MprUISetContext(long lNode, long lContext)
{
// TODO: Add your dispatch handler code here

return 0;
}

long CPluginCtrl::MprUIRefresh()
{
// TODO: Add your dispatch handler code here

return 0;
}


PLUGINC.H

// PluginC.h : Declaration of the CPluginCtrl OLE control class.

/////////////////////////////////////////////////////////////////////////////
// CPluginCtrl : See PluginC.cpp for implementation.

class CPluginCtrl : public COleControl
{
DECLARE_DYNCREATE(CPluginCtrl)

// Constructor
public:
CPluginCtrl();

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CPluginCtrl)
public:
virtual void OnDraw(CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid);
virtual void DoPropExchange(CPropExchange* pPX);
virtual void OnResetState();
//}}AFX_VIRTUAL

// Implementation
protected:
~CPluginCtrl();

DECLARE_OLECREATE_EX(CPluginCtrl)    // Class factory and guid
DECLARE_OLETYPELIB(CPluginCtrl)      // GetTypeInfo
DECLARE_PROPPAGEIDS(CPluginCtrl)     // Property page IDs
DECLARE_OLECTLTYPE(CPluginCtrl)// Type name and misc status

// Message maps
//{{AFX_MSG(CPluginCtrl)
// NOTE - 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()

// Dispatch maps
//{{AFX_DISPATCH(CPluginCtrl)
afx_msg long MprUISetContext(long lNode, long lContext);
afx_msg long MprUIRefresh();
//}}AFX_DISPATCH
DECLARE_DISPATCH_MAP()

// Event maps
//{{AFX_EVENT(CPluginCtrl)
//}}AFX_EVENT
DECLARE_EVENT_MAP()

// Dispatch and event IDs
public:
enum {
//{{AFX_DISP_ID(CPluginCtrl)
dispidMprUISetContext = 1L,
dispidMprUIRefresh = 2L,
//}}AFX_DISP_ID
};
};


PLUGINP.CPP

// PluginP.cpp : Implementation of the CPluginPropPage property page class.

#include "stdafx.h"
#include "plugin.h"
#include "PluginP.h"

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

IMPLEMENT_DYNCREATE(CPluginPropPage, COlePropertyPage)

/////////////////////////////////////////////////////////////////////////////
// Message map

BEGIN_MESSAGE_MAP(CPluginPropPage, COlePropertyPage)
//{{AFX_MSG_MAP(CPluginPropPage)
// NOTE - ClassWizard will add and remove message map entries
//    DO NOT EDIT what you see in these blocks of generated code !
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// Initialize class factory and guid

IMPLEMENT_OLECREATE_EX(CPluginPropPage, "PLUGIN.PluginPropPage.1",
0x85df115, 0x10e, 0x11d0, 0xaa, 0x6b, 0, 0x80, 0x5f, 0xc, 0x92, 0x32)

/////////////////////////////////////////////////////////////////////////////
// CPluginPropPage::CPluginPropPageFactory::UpdateRegistry -
// Adds or removes system registry entries for CPluginPropPage

BOOL CPluginPropPage::CPluginPropPageFactory::UpdateRegistry(BOOL bRegister)
{
if (bRegister)
return AfxOleRegisterPropertyPageClass(AfxGetInstanceHandle(),
m_clsid, IDS_PLUGIN_PPG);
else
return AfxOleUnregisterClass(m_clsid, NULL);
}

/////////////////////////////////////////////////////////////////////////////
// CPluginPropPage::CPluginPropPage - Constructor

CPluginPropPage::CPluginPropPage() :
COlePropertyPage(IDD, IDS_PLUGIN_PPG_CAPTION)
{
//{{AFX_DATA_INIT(CPluginPropPage)
// NOTE: ClassWizard will add member initialization here
//    DO NOT EDIT what you see in these blocks of generated code !
//}}AFX_DATA_INIT
}

/////////////////////////////////////////////////////////////////////////////
// CPluginPropPage::DoDataExchange - Moves data between page and properties

void CPluginPropPage::DoDataExchange(CDataExchange* pDX)
{
//{{AFX_DATA_MAP(CPluginPropPage)
// NOTE: ClassWizard will add DDP, DDX, and DDV calls here
//    DO NOT EDIT what you see in these blocks of generated code !
//}}AFX_DATA_MAP
DDP_PostProcessing(pDX);
}

/////////////////////////////////////////////////////////////////////////////
// CPluginPropPage message handlers


PLUGINP.H

// PluginP.h : Declaration of the CPluginPropPage property page class.

////////////////////////////////////////////////////////////////////////////
// CPluginPropPage : See PluginP.cpp.cpp for implementation.

class CPluginPropPage : public COlePropertyPage
{
DECLARE_DYNCREATE(CPluginPropPage)
DECLARE_OLECREATE_EX(CPluginPropPage)

// Constructor
public:
CPluginPropPage();

// Dialog Data
//{{AFX_DATA(CPluginPropPage)
enum { IDD = IDD_PROPPAGE_PLUGIN };
// NOTE - ClassWizard will add data members here.
//    DO NOT EDIT what you see in these blocks of generated code !
//}}AFX_DATA

// Implementation
protected:
virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support

// Message maps
protected:
//{{AFX_MSG(CPluginPropPage)
// NOTE - 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()

};


PLUGIN.CPP

// plugin.cpp : Implementation of CPluginApp and DLL registration.

#include "stdafx.h"
#include <initguid.h>
#include "plugin.h"
#include "pluginc.h"
#include "afxpriv.h"

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

CPluginApp NEAR theApp;

const GUID CDECL BASED_CODE _tlid =
{ 0x85df111, 0x10e, 0x11d0, { 0xaa, 0x6b, 0, 0x80, 0x5f, 0xc, 0x92, 0x32 } };
const WORD _wVerMajor = 1;
const WORD _wVerMinor = 0;

////////////////////////////////////////////////////////////////////////////
// CPluginApp::InitInstance - DLL initialization

BOOL CPluginApp::InitInstance()
{
BOOL bInit = COleControlModule::InitInstance();

if (bInit)
{
// TODO: Add your own module initialization code here.
}

return bInit;
}

////////////////////////////////////////////////////////////////////////////
// CPluginApp::ExitInstance - DLL termination

int CPluginApp::ExitInstance()
{
// TODO: Add your own module termination code here.

return COleControlModule::ExitInstance();
}

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

STDAPI DllRegisterServer(void)
{
AFX_MANAGE_STATE(_afxModuleAddrThis);

if (!AfxOleRegisterTypeLib(AfxGetInstanceHandle(), _tlid))
return ResultFromScode(SELFREG_E_TYPELIB);

if (!COleObjectFactoryEx::UpdateRegistryAll(TRUE))
return ResultFromScode(SELFREG_E_CLASS);

return NOERROR;
}

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

STDAPI DllUnregisterServer(void)
{
AFX_MANAGE_STATE(_afxModuleAddrThis);

if (!AfxOleUnregisterTypeLib(_tlid, _wVerMajor, _wVerMinor))
return ResultFromScode(SELFREG_E_TYPELIB);

if (!COleObjectFactoryEx::UpdateRegistryAll(FALSE))
return ResultFromScode(SELFREG_E_CLASS);

return NOERROR;
}
/////////////////////////////////////////////////////////////////////////////
// CPluginObj

IMPLEMENT_DYNCREATE(CPluginObj, CCmdTarget)

CPluginObj::CPluginObj()
{
EnableAutomation();

m_pIMprUIHost = NULL;

// To keep the application running as long as an OLE automation
//object is active, the constructor calls AfxOleLockApp.
AfxOleLockApp();
}

CPluginObj::~CPluginObj()
{
// To terminate the application when all objects created with
// with OLE automation, the destructor calls AfxOleUnlockApp.
AfxOleUnlockApp();
}

void CPluginObj::OnFinalRelease()
{
// When the last reference for an automation object is released
// OnFinalRelease is called.  The base class will automatically
// deletes the object.  Add additional cleanup required for your
// object before calling the base class.

CCmdTarget::OnFinalRelease();
}

BEGIN_MESSAGE_MAP(CPluginObj, CCmdTarget)
//{{AFX_MSG_MAP(CPluginObj)
// NOTE - the ClassWizard will add and remove mapping macros here.
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

BEGIN_INTERFACE_MAP(CPluginObj, CCmdTarget)
INTERFACE_PART(CPluginObj, IID_IMprUIPlugin, MprUIPlugin)
END_INTERFACE_MAP()

// {085DF11A-010E-11D0-AA6B-00805F0C9232}
IMPLEMENT_OLECREATE(CPluginObj, "PLUGIN.PLUGINOBJ", 0x85df11a, 0x10e, 0x11d0, 0xaa, 0x6b, 0x0, 0x80, 0x5f, 0xc, 0x92, 0x32)

/////////////////////////////////////////////////////////////////////////////
// CPluginObj message handlers

HRESULT CPluginObj::XMprUIPlugin::QueryInterface(REFIID riid, LPVOID FAR* ppvObj)
{
METHOD_PROLOGUE(CPluginObj, MprUIPlugin);
return (HRESULT)pThis->ExternalQueryInterface(&riid, ppvObj);
}

ULONG CPluginObj::XMprUIPlugin::AddRef()
{
METHOD_PROLOGUE(CPluginObj, MprUIPlugin);
return pThis->ExternalAddRef();
}

ULONG CPluginObj::XMprUIPlugin::Release()
{
METHOD_PROLOGUE(CPluginObj, MprUIPlugin);
return pThis->ExternalRelease();
}

HRESULT CPluginObj::XMprUIPlugin::ConnectToRouter
(LPCTSTR pRouterName, IMprUIHost *pIMprUIHost)
{
METHOD_PROLOGUE(CPluginObj, MprUIPlugin);
ASSERT(pRouterName && pIMprUIHost);

pThis->m_pIMprUIHost = pIMprUIHost;
return S_OK;
}

HRESULT CPluginObj::XMprUIPlugin::GetTitle(PWSTR* ppszTitle)
{
METHOD_PROLOGUE(CPluginObj, MprUIPlugin);
ASSERT(ppszTitle);
CString str;
WCHAR *pwc;

VERIFY(str.LoadString(IDS_TITLE));
*ppszTitle = (WCHAR*)::CoTaskMemAlloc((str.GetLength() + 1) * sizeof(WCHAR));
if (*ppszTitle == NULL)
return E_OUTOFMEMORY;

#ifndef _UNICODE
USES_CONVERSION;
pwc = A2W(str);
#else
pwc = (LPTSTR)(LPCTSTR)str;
#endif

wcscpy(*ppszTitle, pwc);
return S_OK;
}

HRESULT CPluginObj::XMprUIPlugin::GetContextMenu(HMENU* phMenu)
{
METHOD_PROLOGUE(CPluginObj, MprUIPlugin);
ASSERT(phMenu);
CMenu menu;

VERIFY(menu.LoadMenu(IDR_PLUGIN));
*phMenu = menu.Detach();

return S_OK;
}

HRESULT CPluginObj::XMprUIPlugin::ProcessCommand
(WPARAM wParam, LPARAM lParam, HWND hWndParent)
{
METHOD_PROLOGUE(CPluginObj, MprUIPlugin);

if (LOWORD(wParam) != ID_CREATEFLOATER)
return E_INVALIDARG;

if (pThis->m_pIMprUIHost == NULL)
return E_FAIL;

CString str;
VERIFY(str.LoadString(IDS_FLOAT));
WCHAR *pwc;

#ifndef _UNICODE
USES_CONVERSION;
pwc = A2W(str);
#else
pwc = (LPTSTR)(LPCTSTR)str;
#endif

HRESULT hr = pThis->m_pIMprUIHost->FloatWindow(
CPluginObj::guid,              // CLSID representing the object
CPluginCtrl::guid,             // CLSID of Ole Control to create
(IMprUIPlugin*)this,           // Plugin in node making request
0x00000001,                    // constant context passed back to Ole Control
pwc                            // Title of floating window
);

ASSERT(hr == S_OK);

return hr;
}

HRESULT CPluginObj::XMprUIPlugin::GetCLSID(CLSID** ppClsid)
{
METHOD_PROLOGUE(CPluginObj, MprUIPlugin);
ASSERT(ppClsid);
HRESULT hr = S_OK;

*ppClsid = (CLSID*)CoTaskMemAlloc(sizeof(CLSID));
if (*ppClsid == NULL)
return E_OUTOFMEMORY;

memcpy(*ppClsid, &CPluginCtrl::guid, sizeof(CLSID));

return S_OK;
}


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by plugin.rc
//
#define IDS_PLUGIN                      1
#define IDB_PLUGIN                      1
#define IDS_PLUGIN_PPG                  2
#define IDS_PLUGIN_PPG_CAPTION          100
#define IDD_PROPPAGE_PLUGIN             100
#define IDS_TITLE                       101
#define IDS_FLOAT                       102
#define IDR_PLUGIN                      201
#define ID_CREATEFLOATER                32768

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        202
#define _APS_NEXT_COMMAND_VALUE         32769
#define _APS_NEXT_CONTROL_VALUE         201
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif


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"


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

#include <afxctl.h>         // MFC support for OLE Controls

// Delete the two includes below if you do not wish to use the MFC
//  database classes
#ifndef _UNICODE
#include <afxdb.h>// MFC database classes
#include <afxdao.h>// MFC DAO database classes
#endif //_UNICODE


README.TXT

                Sample IP Routing Protocol
            -----------------------------------

This code provides a sample implementation of a IP routing protocol.  It
illustrates the interaction with the IP Router Manager and the Route Table
Manager.  It also provides guidelines to efficient use of the event logging
and tracing facilities provided with the Routing SDK. The code uses WinSock2
functionality for optimized execution.  It also contains comments about the
preferred usage of NT and Routing functionalities (such as worker threads etc)

To run the sample, the developer needs to create an application that will
manage and install the protocol.  This is better described in the PlugIn
documentation.


DEFS.H

/*++

Copyright (c) 1997  Microsoft Corporation

Module Name:

    sample\ip\api.c

Abstract:
    Functions exported by the Sample Routing Protocol DLL

Revision History:

--*/

#include "logtrdef.h"

#define PROTOCOL_STATE_STOPPED      0
#define PROTOCOL_STATE_STOPPING     1
#define PROTOCOL_STATE_RUNNING      2

#define NUM_STATES                  3

#define BINDING_DOWN            0
#define BINDING_UP              1

#define QUERY_TYPE_GET          0
#define QUERY_TYPE_GET_NEXT     1
#define QUERY_TYPE_GET_FIRST    2
#define QUERY_TYPE_SET          3
#define QUERY_TYPE_CREATE       4
#define QUERY_TYPE_DELETE       5

#define NUM_WAIT_EVENTS         3

//
// Define this to be your compiler's ASSERTs
//

#define ASSERT( exp )

//
//  VOID
//  InitializeListHead(
//      PLIST_ENTRY ListHead
//      );
//

#define InitializeListHead(ListHead) (\
    (ListHead)->Flink = (ListHead)->Blink = (ListHead))

//
//  BOOLEAN
//  IsListEmpty(
//      PLIST_ENTRY ListHead
//      );
//

#define IsListEmpty(ListHead) \
    ((ListHead)->Flink == (ListHead))

//
//  PLIST_ENTRY
//  RemoveHeadList(
//      PLIST_ENTRY ListHead
//      );
//

#define RemoveHeadList(ListHead) \
    (ListHead)->Flink;\
    {RemoveEntryList((ListHead)->Flink)}

//
//  PLIST_ENTRY
//  RemoveTailList(
//      PLIST_ENTRY ListHead
//      );
//

#define RemoveTailList(ListHead) \
    (ListHead)->Blink;\
    {RemoveEntryList((ListHead)->Blink)}

//
//  VOID
//  RemoveEntryList(
//      PLIST_ENTRY Entry
//      );
//

#define RemoveEntryList(Entry) {\
    PLIST_ENTRY _EX_Blink;\
    PLIST_ENTRY _EX_Flink;\
    _EX_Flink = (Entry)->Flink;\
    _EX_Blink = (Entry)->Blink;\
    _EX_Blink->Flink = _EX_Flink;\
    _EX_Flink->Blink = _EX_Blink;\
    }

//
//  VOID
//  InsertTailList(
//      PLIST_ENTRY ListHead,
//      PLIST_ENTRY Entry
//      );
//

#define InsertTailList(ListHead,Entry) {\
    PLIST_ENTRY _EX_Blink;\
    PLIST_ENTRY _EX_ListHead;\
    _EX_ListHead = (ListHead);\
    _EX_Blink = _EX_ListHead->Blink;\
    (Entry)->Flink = _EX_ListHead;\
    (Entry)->Blink = _EX_Blink;\
    _EX_Blink->Flink = (Entry);\
    _EX_ListHead->Blink = (Entry);\
    }

//
//  VOID
//  InsertHeadList(
//      PLIST_ENTRY ListHead,
//      PLIST_ENTRY Entry
//      );
//

#define InsertHeadList(ListHead,Entry) {\
    PLIST_ENTRY _EX_Flink;\
    PLIST_ENTRY _EX_ListHead;\
    _EX_ListHead = (ListHead);\
    _EX_Flink = _EX_ListHead->Flink;\
    (Entry)->Flink = _EX_Flink;\
    (Entry)->Blink = _EX_ListHead;\
    _EX_Flink->Blink = (Entry);\
    _EX_ListHead->Flink = (Entry);\
    }

//
// Macros called each time an api is called and exited. This is to
// facilitate StopProtocol() functionality.
//

#define EnterProtocolApi()                                      \
{                                                               \
    EnterCriticalSection(&g_csProtocolStateLock);               \
                                                                \
    if(g_dwProtocolState == PROTOCOL_STATE_RUNNING)             \
    {                                                           \
        g_dwProtocolRefCount++;                                 \
        LeaveCriticalSection(&g_csProtocolStateLock);           \
    }                                                           \
    else                                                        \
    {                                                           \
        LeaveCriticalSection(&g_csProtocolStateLock);           \
        return ERROR_CAN_NOT_COMPLETE;                          \
    }                                                           \
}

#define ExitProtocolApi()                                       \
{                                                               \
    EnterCriticalSection(&g_csProtocolStateLock);               \
    g_dwProtocolRefCount++;                                     \
    LeaveCriticalSection(&g_csProtocolStateLock);               \
}

#define PRINT_ADDRESS(x)    \
    ((x)&0x000000FF),(((x)&0x0000FF00)>>8),(((x)&0x00FF0000)>>16),(((x)&0xFF000000)>>24)

//
// VOID
// PrintRoute(PRTM_IP_ROUTE pRoute)
//

#define PrintRoute(FL,pRoute)   \
{                               \
    Trace4(FL,                  \
           "Dest: %d.%d.%d.%d(%d.%d.%d.%d) Nexthop: %d.%d.%d.%d(%d.%d.%d.%d)", \
           PRINT_ADDRESS((pRoute)->RR_Network.N_NetNumber),         \
           PRINT_ADDRESS((pRoute)->RR_Network.N_NetMask),           \
           PRINT_ADDRESS((pRoute)->RR_NextHopAddress.N_NetNumber),  \
           PRINT_ADDRESS((pRoute)->RR_NextHopAddress.N_NetMask));   \
    Trace5(FL,"I/F: %d Proto: %d Metric: %d Priority: %d Validity: %s.\n",    \
           (pRoute)->RR_InterfaceID,                       \
           (pRoute)->RR_RoutingProtocol,                   \
           (pRoute)->RR_FamilySpecificData.FSD_Metric,     \
           (pRoute)->RR_FamilySpecificData.FSD_Priority,   \
           (IsRouteValid((pRoute)))?"TRUE":"FALSE");       \
}


EXTERN.H

/*++

Copyright (c) 1997  Microsoft Corporation

Module Name:

    sample\ip\extern.h

Abstract:
    External declarations included by all modules

Revision History:

--*/

#ifndef __EXTERN_H__
#define __EXTERN_H__

extern DWORD  g_dwLogLevel;
extern DWORD  g_dwNumIf;
extern DWORD  g_dwTrace;
extern HANDLE g_hLogHandle;
extern HANDLE g_hPrivateHeap;
extern HANDLE g_hRtmHandle;
extern HANDLE g_hRtmEvent;
extern HANDLE g_hMgrNotifyEvent;
extern HANDLE g_hSocketEvent;
extern HANDLE g_hStopProtocolEvent;
extern DWORD  g_dwProtocolState;
extern DWORD  g_dwProtocolRefCount;
extern PCHAR  g_ppszStateStrings[NUM_STATES];

extern LIST_ENTRY   g_leIfListHead;
extern LIST_ENTRY   g_leMsgQueue;
extern PMGR_MSG     g_pmmStopMsg;

extern PSUPPORT_FUNCTIONS   g_psfnSupportFunctions;
extern CRITICAL_SECTION     g_csIfListLock;
extern CRITICAL_SECTION     g_csProtocolStateLock;
extern CRITICAL_SECTION     g_csGlobalInfoLock;
extern CRITICAL_SECTION     g_csQueueLock;
extern CRITICAL_SECTION     g_csQueueLock;

#endif // __EXTERN_H__


GLOBAL.C

/*++

Copyright (c) 1997  Microsoft Corporation

Module Name:

    sample\ip\global.c

Abstract:
    Start/Stop and Global Info related functions exported by the
    Sample Routing Protocol DLL.

Revision History:

--*/

#include "sampinc.h"

BOOL
DllStartup();
BOOL
DllCleanup();
VOID
InitializeProtocolState();
VOID
DeleteProtocolState();
BOOL
ValidateGlobalInfo(
    PSAMPLE_PROTOCOL_GLOBAL_INFO pInfo
    );
DWORD WINAPI
MainThread(
    PVOID   pvContext
    );

PNT_IF
GetIfBlockGivenIndex(
    DWORD   dwIndex
    );

VOID
APIENTRY
UpdateRoutes(
    PVOID   pvContext
    );
VOID
InitializeCriticalSections();
VOID
DeleteCriticalSections();
DWORD
CreateEvents();
VOID
DestroyEvents();
DWORD
CreateHeaps();
DWORD
DestroyHeaps();
VOID
CleanupProtocol(
    BOOL bWinsockLoaded
    );

DWORD  g_dwLogLevel;
DWORD  g_dwNumIf;
DWORD  g_dwTrace;
HANDLE g_hLogHandle;
HANDLE g_hPrivateHeap;
HANDLE g_hRtmHandle;
HANDLE g_hRtmEvent;
HANDLE g_hMgrNotifyEvent;
HANDLE g_hStopProtocolEvent;
HANDLE g_hSocketEvent;
DWORD  g_dwProtocolState;
DWORD  g_dwProtocolRefCount;
PCHAR  g_ppszStateStrings[NUM_STATES];

LIST_ENTRY  g_leIfListHead;
LIST_ENTRY  g_leMsgQueue;
PMGR_MSG    g_pmmStopMsg;

PSUPPORT_FUNCTIONS  g_psfnSupportFunctions;
CRITICAL_SECTION    g_csIfListLock;
CRITICAL_SECTION    g_csProtocolStateLock;
CRITICAL_SECTION    g_csGlobalInfoLock;
CRITICAL_SECTION    g_csQueueLock;

DWORD
QueueAsyncFunction(
    WORKERFUNCTION  pfnFunction,
    PVOID           pvContext,
    BOOL            bAlertable
    )
/*++
  Routine Description
      Wrapper around function that queues work to a thread

  Arguments

  Return Value
      NO_ERROR

--*/
{
    DWORD   dwResult;

    EnterCriticalSection(&g_csProtocolStateLock);

    if (g_dwProtocolState == PROTOCOL_STATE_RUNNING)
    {
        g_dwProtocolRefCount++;

        LeaveCriticalSection(&g_csProtocolStateLock);
    }
    else
    {
        LeaveCriticalSection(&g_csProtocolStateLock);

        return ERROR_CAN_NOT_COMPLETE;
    }

    dwResult = QueueWorkItem(pfnFunction,
                             pvContext,
                             bAlertable);

    //
    // If we successfully queued the item, dont decrement the count
    //

    if(dwResult != NO_ERROR)
    {
        EnterCriticalSection(&g_csProtocolStateLock);

        g_dwProtocolRefCount--;

        LeaveCriticalSection(&g_csProtocolStateLock);
    }

    return dwResult;
}

BOOL
WINAPI
DLLMain(
    IN HINSTANCE   hInstance,
    IN DWORD       dwReason,
    IN PVOID       pvUnused
    )
/*++
  Routine Description
      This is the DLL entrypoint handler. It calls DllStartup
      to initialize all data structures that need to be present
      even before the StartProtocol() function is called
      It calls DllCleanup to free resources that are not freed when
      StopProtocol() is called

  Arguments
      hInstance     Handle of the DLL
      dwReason      Reason why the function is being invoked

  Return Value
      TRUE      If the function succeeds
      FALSE     otherwise

--*/
{
    BOOL bErr;

    bErr = FALSE;

    switch(dwReason)
    {
        case DLL_PROCESS_ATTACH:
        {
            //
            // Since we dont need to handle the THREAD_ATTACH and THREAD_DETACH
            // reasons, we ask the loader not to invoke our function when a
            // thread is created or destroyed in the process. This can be a
            // significant performance improvement
            //

            DisableThreadLibraryCalls(hInstance);

            g_dwTrace    = INVALID_TRACEID;
            g_hLogHandle = NULL;

            bErr = DllStartup();

            break;
        }

        case DLL_PROCESS_DETACH:
        {
            bErr = DllCleanup();

            break;
        }

        default:
        {
            bErr = TRUE;

            break;
        }
    }

    return bErr;
}

BOOL
DllStartup()
/*++
  Routine Description
      Initializes the tracing and event logging handles.
      Initializes the state of the protocol to begin tracking threads that
      are executing code in the DLL

  Arguments
      None

  Return Value
      TRUE  if function succeeds
      FALSE otherwise

--*/
{
    g_dwTrace    = TraceRegister(SAMPLE_PROTOCOL_NAME);
    g_hLogHandle = RouterLogRegister(SAMPLE_PROTOCOL_NAME);

    InitializeProtocolState();

    return TRUE;
}

BOOL
DllCleanup()
/*++
  Routine Description
      Deletes the tracing and event handles
      Frees resources used to track threads in the DLL

  Arguments

  Return Value
      TRUE

--*/
{
    DeleteProtocolState();

    if(g_hLogHandle)
    {
        RouterLogDeregister(g_hLogHandle);
    }

    if(g_dwTrace != INVALID_TRACEID)
    {
        TraceDeregister(g_dwTrace);
    }

    return TRUE;
}

VOID
InitializeProtocolState()
/*++
  Routine Description
      Since the routing protocol is a DLL that may be unloaded, we need
      to track all activity occuring in the DLL

  Arguments

  Return Value
      NO_ERROR

--*/
{
    InitializeCriticalSection(&g_csProtocolStateLock);

    InitializeCriticalSection(&g_csQueueLock);

    g_dwProtocolState    = PROTOCOL_STATE_STOPPED;
    g_dwProtocolRefCount = 0;
    g_dwLogLevel         = PROTO_LOGGING_ERROR;
}

VOID
DeleteProtocolState()
/*++
  Routine Description

  Arguments

  Return Value
      NO_ERROR

--*/
{
    ASSERT(g_dwProtocolState == PROTOCOL_STATE_STOPPED);
    ASSERT(g_dwProtocolRefCount == 0);

    DeleteCriticalSection(&g_csQueueLock);

    DeleteCriticalSection(&g_csProtocolStateLock);
}

DWORD
APIENTRY
RegisterProtocol(
    IN OUT PMPR_ROUTING_CHARACTERISTICS pRoutingChar,
    IN OUT PMPR_SERVICE_CHARACTERISTICS pServiceChar
    )
/*++
Routine Description
    This is the first function called by the IP Router Manager.
    The Router Manager tells the routing protocol it version and capabilities
    It also tells the our DLL, the ID of the protocol it expects us to
    register.  This allows one DLL to support multiple routing protocols.
    We return the functionality we support and a pointer to our functions.

Arguments

Return Value
    NO_ERROR

--*/
{
    //
    // The Router Manager should be calling us to register our protocol.
    // The Router Manager must be atleast the version we are compiled with
    // The Router Manager must support routing and demand update
    //

    if(pRoutingChar->dwProtocolId != SAMPLE_PROTOCOL_ROUTE_ID)
    {
        return ERROR_NOT_SUPPORTED;
    }

    if(pRoutingChar->dwVersion < MS_ROUTER_VERSION)
    {
        return ERROR_NOT_SUPPORTED;
    }

    if(pRoutingChar->fSupportedFunctionality != (ROUTING | DEMAND_UPDATE_ROUTES))
    {
        return ERROR_NOT_SUPPORTED;
    }

    //
    // Since we are not a service advertiser (an IPX thing)
    //

    //
    // We setup our characteristics and function pointers
    //

    pServiceChar->fSupportedFunctionality = 0;

    pRoutingChar->fSupportedFunctionality = (ROUTING | DEMAND_UPDATE_ROUTES);

    pRoutingChar->pfnStartProtocol    = StartProtocol;
    pRoutingChar->pfnStopProtocol     = StopProtocol;
    pRoutingChar->pfnAddInterface     = AddInterface;
    pRoutingChar->pfnDeleteInterface  = DeleteInterface;
    pRoutingChar->pfnGetEventMessage  = GetEventMessage;
    pRoutingChar->pfnGetInterfaceInfo = GetInterfaceConfigInfo;
    pRoutingChar->pfnSetInterfaceInfo = SetInterfaceConfigInfo;
    pRoutingChar->pfnBindInterface    = BindInterface;
    pRoutingChar->pfnUnbindInterface  = UnBindInterface;
    pRoutingChar->pfnEnableInterface  = EnableInterface;
    pRoutingChar->pfnDisableInterface = DisableInterface;
    pRoutingChar->pfnGetGlobalInfo    = GetGlobalInfo;
    pRoutingChar->pfnSetGlobalInfo    = SetGlobalInfo;
    pRoutingChar->pfnMibCreateEntry   = MibCreate;
    pRoutingChar->pfnMibDeleteEntry   = MibDelete;
    pRoutingChar->pfnMibGetEntry      = MibGet;
    pRoutingChar->pfnMibSetEntry      = MibSet;
    pRoutingChar->pfnMibGetFirstEntry = MibGetFirst;
    pRoutingChar->pfnMibGetNextEntry  = MibGetNext;
    pRoutingChar->pfnUpdateRoutes     = DoUpdateRoutes;

    return NO_ERROR;
}

DWORD
APIENTRY
StartProtocol(
    IN HANDLE               hMgrNotifyEvent,
    IN PSUPPORT_FUNCTIONS   pSupportFunctions,
    IN PVOID                pvConfig
    )
/*++
  Routine Description
      After the protocol has been registered, the IP Router Manager calls
      this function to tell the protocol to start. Most of the startup
      code is executed here

  Arguments
      hMgrNotifyEvent   Event to Set if the IP Router Manager needs to
                        be notified to take any action on our behalf
      pSupportFunctions Some functions exported by IP Router Manager
      pvConfig          Our global configuration which was setup by our
                        setup/admin DLL

  Return Value
      NO_ERROR

--*/
{
    WSADATA wsaData;
    DWORD   dwResult, dwThread;
    HANDLE  hThread;
    BOOL    bWinsockLoaded;

    PSAMPLE_PROTOCOL_GLOBAL_INFO    pGlobalInfo;

    EnterProtocolApi();

    TraceEnter("StartProtocol");

    if(pvConfig == NULL)
    {
        Trace0(ERR,
               "StartProtocol: Called with NULL config");

        TraceLeave("StartProtocol");

        ExitProtocolApi();

        return ERROR_INVALID_PARAMETER;
    }

    pGlobalInfo = (PSAMPLE_PROTOCOL_GLOBAL_INFO)pvConfig;

    if(!ValidateGlobalInfo(pGlobalInfo))
    {
        TraceLeave("StartProtocol");

        ExitProtocolApi();

        return ERROR_INVALID_DATA;
    }

    g_hPrivateHeap       = NULL;
    g_hRtmEvent          = NULL;
    g_hMgrNotifyEvent    = NULL;
    g_hRtmHandle         = NULL;
    g_hStopProtocolEvent = NULL;
    g_hSocketEvent       = NULL;
    g_pmmStopMsg         = NULL;
    bWinsockLoaded       = FALSE;

    g_psfnSupportFunctions = pSupportFunctions;

    do
    {
        InitializeCriticalSections();

        //
        // We need Winsock2.0 or better
        //

        dwResult = (DWORD)WSAStartup(MAKEWORD(2,0), &wsaData);

        if(dwResult != 0)
        {

            Trace1(ERR,
                   "StartProtocol: Error %d starting WinSock.", dwResult);

            LogErr0(WINSOCK_FAIL,
                    dwResult);
            break;
        }

        bWinsockLoaded = TRUE;

        dwResult = CreateHeaps();

        if(dwResult != NO_ERROR)
        {
            break;
        }

        dwResult = CreateEvents();

        if(dwResult != NO_ERROR)
        {
            break;
        }

        //
        // The message that will be used to notify the stopping
        // of this protocol. If we cant create this, we wont be
        // able to stop
        //

        g_pmmStopMsg = HeapAlloc(g_hPrivateHeap,
                                 0,
                                 sizeof(MGR_MSG));

        if(g_pmmStopMsg == NULL)
        {
            dwResult = GetLastError();

            Trace1(ERR,
                   "StartProtocol: Couldnt allocate stop message. Error %d",
                   dwResult);

            break;
        }

        //
        // Register as an RTM client. We will not keep multiple routes
        // to one destination so we set the flags to denote this
        //

        g_hRtmHandle = RtmRegisterClient(RTM_PROTOCOL_FAMILY_IP,
                                         SAMPLE_PROTOCOL_ROUTE_ID,
                                         g_hRtmEvent,
                                         RTM_PROTOCOL_SINGLE_ROUTE);

        if(g_hRtmHandle == NULL)
        {
            dwResult = GetLastError();

            Trace1(ERR,
                   "StartProtocol: Error %d registering with RTM",
                   dwResult);

            LogErr0(RTM_REGISTER_FAILED,
                    dwResult);

            break;
        }

        //
        // Create the main thread. This executes the MainThread() function
        //

        hThread = CreateThread(NULL, 0,
                               MainThread,
                               NULL,
                               0,
                               &dwThread);

        if (hThread == NULL)
        {
            dwResult = GetLastError();

            Trace1(ERR,
                   "StartProtocol: Error %d starting main thread",
                   dwResult);

            LogErr0(CREATE_THREAD_FAILED,
                    dwResult);

            break;
        }

        //
        // We will never refer to the thread using the handle
        // so close it and avoid a leak
        //

        CloseHandle(hThread);

    }while(FALSE);

    if(dwResult != NO_ERROR)
    {
        //
        // Something bad happened
        //

        if(g_pmmStopMsg)
        {
            HeapFree(g_hPrivateHeap,
                     0,
                     g_pmmStopMsg);
        }

        CleanupProtocol(bWinsockLoaded);

    }
    else
    {
        g_hMgrNotifyEvent = hMgrNotifyEvent;

        g_dwProtocolState = PROTOCOL_STATE_RUNNING;
    }

    TraceLeave("StartProtocol");

    ExitProtocolApi();

    return NO_ERROR;
}

DWORD
APIENTRY
StopProtocol()
/*++
  Routine Description
     This function is called by the IP Router Manager to tell the protocol
     to stop.  We set the protocol state to prevent us from servicing
     any more requests. Then we notify the MainThread to stop and
     return PENDING to the IP Router Manager. If we had no thread and no
     work functions in our implementation, we could have stopped
     synchronously.

  Arguments

  Return Value
     PENDING

--*/
{
    EnterCriticalSection(&g_csProtocolStateLock);

    TraceEnter("StopProtocol");

    //
    // Cannot stop if already stopped
    //

    if(g_dwProtocolState != PROTOCOL_STATE_RUNNING)
    {
        Trace1(GLOBAL,
               "StopProtocol: Protocol state is %s",
               g_ppszStateStrings[g_dwProtocolState]);

        TraceLeave("StopProtocol");

        LeaveCriticalSection(&g_csProtocolStateLock);

        ExitProtocolApi();

        return ERROR_CAN_NOT_COMPLETE;
    }

    //
    // Dont release the state lock, so that the other threads
    // won't cleanup under us
    //

    //
    // Tell the main thread to stop. That will clean out everything
    //

    Trace0(GLOBAL,
           "StopProtocol: Signalling main thread to stop");

    SetEvent(g_hStopProtocolEvent);

    //
    // Set state to STOPPING;
    //

    g_dwProtocolState = PROTOCOL_STATE_STOPPING;

    Trace1(GLOBAL,
           "StopProtocol: %d threads are still active",
           g_dwProtocolRefCount);

    TraceLeave("StopProtocol");

    LeaveCriticalSection(&g_csProtocolStateLock);

    return ERROR_PROTOCOL_STOP_PENDING;
}

DWORD
APIENTRY
GetGlobalInfo(
    IN OUT PVOID    pvConfig,
    IN OUT PDWORD   pdwSize
    )
/*++
  Routine Description
      The function is called by the IP Router Manager, usually in because of
      a query by the admin utility.  We see if we have space enough to
      return our global config. If we do we return it, otherwise we return the
      size needed

  Arguments
      pvConfig  Pointer to allocated buffer to store our config
      pdwSize   Size of config.

  Return Value
      ERROR_INSUFFICIENT_BUFFER If the size of the buffer is too small
      ERROR_INVALID_PARAMETER
      ERROR_INVALID_DATA
      NO_ERROR

--*/
{
    PSAMPLE_PROTOCOL_GLOBAL_INFO    pGlobalInfo;

    EnterProtocolApi();

    TraceEnter("GetGlobalInfo");

    if(pdwSize == NULL)
    {
        Trace0(ERR,
               "GetGlobalInfo: Router Manager called us with NULL size");

        TraceLeave("GetGlobalInfo");

        ExitProtocolApi();

        return ERROR_INVALID_PARAMETER;
    }

    if((*pdwSize < sizeof(SAMPLE_PROTOCOL_GLOBAL_INFO))||
       (pvConfig == NULL))
    {
        //
        // Either the size was too small or there was no
        // storage
        //

        *pdwSize = sizeof(SAMPLE_PROTOCOL_GLOBAL_INFO);

        Trace3(GLOBAL,
               "GetGlobalInfo: Router Manager called us with size %d and info %x. Info size should be %d",
               *pdwSize,
               pvConfig,
               sizeof(SAMPLE_PROTOCOL_GLOBAL_INFO));

        TraceLeave("GetGlobalInfo");

        ExitProtocolApi();

        return ERROR_INSUFFICIENT_BUFFER;
    }

    *pdwSize = sizeof(SAMPLE_PROTOCOL_GLOBAL_INFO);

    //
    // Ok, so we have a good buffer to write our info into
    //

    pGlobalInfo = (PSAMPLE_PROTOCOL_GLOBAL_INFO)pvConfig;

    //
    // Currently that is all the info we have
    //

    EnterCriticalSection(&g_csGlobalInfoLock);

    pGlobalInfo->dwLogLevel = g_dwLogLevel;

    LeaveCriticalSection(&g_csGlobalInfoLock);

    TraceLeave("GetGlobalInfo");

    ExitProtocolApi();

    return NO_ERROR;
}

DWORD
APIENTRY
SetGlobalInfo(
    IN PVOID pvConfig
    )
/*++
  Routine Description
      Called by the IP Router Manager usually in response to an admin
      utility changing the config.  We verify the parameters and the
      info and set it

  Arguments

  Return Value
      ERROR_INVALID_PARAMETER
      ERROR_INVALID_DATA
      NO_ERROR

--*/
{
    PSAMPLE_PROTOCOL_GLOBAL_INFO    pGlobalInfo;

    EnterProtocolApi();

    TraceEnter("SetGlobalInfo");

    if(pvConfig == NULL)
    {
        Trace0(ERR,
               "SetGlobalInfo: Router Manager called us with NULL info");

        TraceLeave("SetGlobalInfo");

        ExitProtocolApi();

        return ERROR_INVALID_PARAMETER;
    }

    pGlobalInfo = (PSAMPLE_PROTOCOL_GLOBAL_INFO)pvConfig;

    if(!ValidateGlobalInfo(pGlobalInfo))
    {
        //
        // Bad global info
        //

        TraceLeave("SetGlobalInfo");

        ExitProtocolApi();

        return ERROR_INVALID_DATA;
    }

    //
    // Good info
    //

    EnterCriticalSection(&g_csGlobalInfoLock);

    g_dwLogLevel = pGlobalInfo->dwLogLevel;

    LeaveCriticalSection(&g_csGlobalInfoLock);

    TraceLeave("SetGlobalInfo");

    ExitProtocolApi();

    return ERROR_INVALID_DATA;

}

DWORD
APIENTRY
DoUpdateRoutes(
    IN DWORD dwIndex
    )
/*++
  Routine Description
      This function is called by the IP Router Manger to ask us to
      update routes over a Demand Dial link.  The link has already been
      brought up so should be in ENABLED-BOUND state.  After we are done
      we need to set the g_hMgrNotifyEvent to inform the Router Manager
      that we have a message in our queue to be delivered to it. The
      Router Manager will call our GetEventMessage() function in which
      we will inform it that we are done with update routes (and the routes
      have been stored in RTM). The Router Manager will "freeze" these routes
      by converting them to AUTOSTATIC.

  Arguments

  Return Value
      NO_ERROR/PENDING  For Success

--*/
{
    DWORD   dwResult;

    //
    // We will fire off a worker function to do the "real work"
    // That will set the event
    //

    dwResult = QueueAsyncFunction(UpdateRoutes,
                                  (PVOID)dwIndex,
                                  TRUE);

    return dwResult;
}

BOOL
ValidateGlobalInfo(
    PSAMPLE_PROTOCOL_GLOBAL_INFO pInfo
    )
/*++
  Routine Description
      Checks to see if the global info is OK. It is good practice to do this
      because a corrupt registry can change configuration causing all sorts of
      debugging headaches if it is not found early

  Arguments
      pInfo     The Global Info to check

  Return Value
      TRUE      If the info is good
      FALSE     otherwise

--*/
{
    //
    // Ensure that the logging level is within bounds
    //

    if((pInfo->dwLogLevel <= PROTO_LOGGING_INFO) &&
       (pInfo->dwLogLevel >= PROTO_LOGGING_NONE))
    {
        return TRUE;
    }

    return FALSE;
}

DWORD
APIENTRY
GetEventMessage(
    OUT ROUTING_PROTOCOL_EVENTS *pEvent,
    OUT PMESSAGE                pResult
    )
/*++
  Routine Description
      This is called by the IP Router Manager if we indicate that we have
      a message in our queue to be delivered to it (by setting the
      g_hMgrNotifyEvent)

  Arguments

  Return Value
      NO_ERROR

--*/
{
    PLIST_ENTRY pleNode;
    PMGR_MSG    pmmMsg;

    //
    // IMPORTANT: This can be called after the protocol is stopped
    // so we dont do ref counting for this API.
    //

    EnterCriticalSection(&g_csQueueLock);

    if(IsListEmpty(&g_leMsgQueue))
    {
        LeaveCriticalSection(&g_csQueueLock);

        return ERROR_NO_MORE_ITEMS;
    }

    pleNode = RemoveHeadList(&g_leMsgQueue);

    LeaveCriticalSection(&g_csQueueLock);

    pmmMsg = CONTAINING_RECORD(pleNode,
                               MGR_MSG,
                               leMsgLink);

    *pEvent  = pmmMsg->rreEvent;
    *pResult = pmmMsg->mResult;

    HeapFree(g_hPrivateHeap,
             0,
             pmmMsg);

    return NO_ERROR;
}

VOID
APIENTRY
UpdateRoutes(
    PVOID   pvContext
    )
/*++
  Routine Description

  Arguments

  Return Value
      NO_ERROR

--*/
{
    DWORD       dwIndex;
    PNT_IF      pIf;
    PMGR_MSG    pmmMsg;

    //
    // We dont EnterProtocolApi() here since it has been done for us
    // when we were queued for execution
    //

    TraceEnter("UpdateRoutes");

    dwIndex = (DWORD)pvContext;

    EnterCriticalSection(&g_csIfListLock);

    pIf = GetIfBlockGivenIndex(dwIndex);

    if(pIf == NULL)
    {
        //
        // This can happen because the interface could have been deleted
        // between the now and the time this function was queued for
        // execution
        //

        LeaveCriticalSection(&g_csIfListLock);

        TraceLeave("UpdateRoutes");

        //
        // We must decrement the ref count for the protocol
        // though
        //

        ExitProtocolApi();

        return;
    }

    //
    // Here we do the protocol processing stuff
    // In the sample we wont do anything
    //

    LeaveCriticalSection(&g_csIfListLock);

    //
    // We allocate the memory for a message here and free it in
    // GetEventMessage()
    //

    pmmMsg = HeapAlloc(g_hPrivateHeap,
                       0,
                       sizeof(MGR_MSG));

    if(pmmMsg == NULL)
    {
        Trace1(ERR,
               "UpdateRoutes: Unable to allocate memory for message. Error %d",
               GetLastError());

        TraceLeave("UpdateRoutes");

        ExitProtocolApi();

        return;
    }

    pmmMsg->rreEvent = UPDATE_COMPLETE;

    pmmMsg->mResult.UpdateCompleteMessage.InterfaceIndex = dwIndex;
    pmmMsg->mResult.UpdateCompleteMessage.UpdateType     = DEMAND_UPDATE_ROUTES;
    pmmMsg->mResult.UpdateCompleteMessage.UpdateStatus   = NO_ERROR;

    EnterCriticalSection(&g_csQueueLock);

    InsertHeadList(&g_leMsgQueue,
                   &pmmMsg->leMsgLink);

    LeaveCriticalSection(&g_csQueueLock);

    SetEvent(g_hMgrNotifyEvent);

    TraceLeave("UpdateRoutes");

    ExitProtocolApi();

    return;
}

VOID
InitializeCriticalSections()
/*++
  Routine Description

  Arguments

  Return Value
      NO_ERROR

--*/
{
    InitializeCriticalSection(&g_csIfListLock);
    InitializeCriticalSection(&g_csProtocolStateLock);
    InitializeCriticalSection(&g_csGlobalInfoLock);
}

VOID
DeleteCriticalSections()
/*++
  Routine Description

  Arguments

  Return Value
      NO_ERROR

--*/
{
    DeleteCriticalSection(&g_csIfListLock);
    DeleteCriticalSection(&g_csProtocolStateLock);
    DeleteCriticalSection(&g_csGlobalInfoLock);
}

DWORD
CreateEvents()
/*++
  Routine Description
      Create all the events necessary

  Arguments

  Return Value
      None

--*/
{
    DWORD   dwResult;

    //
    // Create the event used to get notifications from RTM about new routes
    // It has default security, is auto reset since only one thread will wait
    // on it. Initially set to FALSE. For debug versions we name the event
    // This helps in detecting leaks
    //

#if DBG
#define EVENT_NAME  "RtmEventForSampleProtocol"
#else
#define EVENT_NAME  NULL
#endif

    g_hRtmEvent = CreateEvent(NULL,
                              FALSE,
                              FALSE,
                              EVENT_NAME);

#undef EVENT_NAME

    if(g_hRtmEvent == NULL)
    {
        dwResult = GetLastError();

        Trace1(ERR,
               "CreateEvents: Error %d creating RTM Event",
               dwResult);

        LogErr0(EVENT_FAIL,dwResult);

        return dwResult;
    }

#if DBG
#define EVENT_NAME  "SocketEventForSampleProtocol"
#else
#define EVENT_NAME  NULL
#endif

    g_hSocketEvent = CreateEvent(NULL,
                                 FALSE,
                                 FALSE,
                                 EVENT_NAME);

#undef EVENT_NAME

    if(g_hSocketEvent == NULL)
    {
        dwResult = GetLastError();

        Trace1(ERR,
               "CreateEvents: Error %d creating Socket Event",
               dwResult);

        LogErr0(EVENT_FAIL,dwResult);

        return dwResult;
    }

#if DBG
#define EVENT_NAME  "StopEventForSampleProtocol"
#else
#define EVENT_NAME  NULL
#endif

    g_hStopProtocolEvent = CreateEvent(NULL,
                                       FALSE,
                                       FALSE,
                                       EVENT_NAME);

#undef EVENT_NAME

    if(g_hStopProtocolEvent == NULL)
    {
        dwResult = GetLastError();

        Trace1(ERR,
               "CreateEvents: Error %d creating Stop Protocol Event",
               dwResult);

        LogErr0(EVENT_FAIL,dwResult);

        return dwResult;
    }

    return NO_ERROR;
}

VOID
DestroyEvents()
/*++
  Routine Description
      Close all the event handles

  Arguments

  Return Value
      NO_ERROR

--*/
{
    if(g_hRtmEvent != NULL)
    {
        CloseHandle(g_hRtmEvent);
    }

    if(g_hStopProtocolEvent != NULL)
    {
        CloseHandle(g_hStopProtocolEvent);
    }

    if(g_hSocketEvent != NULL)
    {
        CloseHandle(g_hSocketEvent);
    }
}

DWORD
CreateHeaps()
/*++
  Routine Description

  Arguments

  Return Value
      NO_ERROR

--*/
{
    DWORD dwResult;

    g_hPrivateHeap = HeapCreate(0,0,0);

    if(g_hPrivateHeap == NULL)

{
        dwResult = GetLastError();

        Trace1(ERR,
               "CreateHeaps: Error %d creating heap",
               dwResult);

        return dwResult;
    }

    return NO_ERROR;
}

DWORD
DestroyHeaps()
/*++
  Routine Description

  Arguments

  Return Value
      NO_ERROR

--*/
{
    if(g_hPrivateHeap != NULL)
    {
        HeapDestroy(g_hPrivateHeap);
    }

    return NO_ERROR;
}

VOID
CleanupProtocol(
    BOOL bWinsockLoaded
    )
/*++
  Routine Description

  Locks

  Arguments

  Return Value
      NO_ERROR

--*/
{

    if(bWinsockLoaded)
    {
        if(WSACleanup() != NO_ERROR)
        {
            Trace1(ERR,
                   "CleanupProtocol: Error %d cleaning up WinSock",
                   WSAGetLastError());
        }
    }

    if(g_hRtmHandle)
    {
        RtmDeregisterClient(g_hRtmHandle);
    }

    DestroyEvents();
    DeleteCriticalSections();
    DestroyHeaps();

    return;
}


IF.C

/*++

Copyright 1997 - 1998 Microsoft Corporation

Module Name:

    sample\ip\if.c

Abstract:
    Interface related functions exported by the Sample Routing Protocol DLL and
    other code dealing with interfaces

Revision History:

--*/

#include "sampinc.h"

BOOL
ValidateInterfaceInfo(
    PSAMPLE_PROTOCOL_INTERFACE_INFO pInfo
    );
DWORD
ActivateBinding(
    PINTRNL_IF  pBind
    );
DWORD
DeactivateBinding(
    PINTRNL_IF  pBind
    );
PNT_IF
GetIfBlockGivenIndex(
    DWORD   dwIndex
    );
PINTRNL_IF
GetBindingGivenAddress(
    PNT_IF pIf,
    DWORD dwAddress
    );
DWORD
CreateSocket(
    IN  DWORD  dwAddress,
    OUT SOCKET *psSocket
    );

DWORD
APIENTRY
AddInterface(
    IN PWCHAR               pwszInterfaceName,
    IN DWORD                dwIndex,
    IN NET_INTERFACE_TYPE   dwIfType,
    IN PVOID                pvConfig
    )
/*++
  Routine Description
      Called by the IP Router Manager to add an interface when
      it finds our information block (header) within the interface's
      configuration.
      We verify the information and create the structure for the
      interface.  Then we see all the configured addresses for
      the interface and create a binding structure for each address
      The interface comes up as UNBOUND-DISABLED

  Arguments
      pwszInterfaceName The name of the interface. We store it for logging
      dwIndex           The positive integer this interface will be referred to by
      dwIfType          The type of the interface (WAN/LAN etc)
      pvConfig          Our config for this interface

  Return Value
      ERROR_INVALID_PARAMETER
      ERROR_INVALID_DATA
      NO_ERROR

--*/
{
    ULONG       ulNameLen,i;
    DWORD       dwResult;
    PWCHAR      pwszName;
    PINTRNL_IF  pBind;
    PNT_IF      pIf,pPrev;
    PLIST_ENTRY pleNode;

    PSAMPLE_PROTOCOL_INTERFACE_INFO pIfInfo;

    EnterProtocolApi();

    TraceEnter("AddInterface");

    Trace3(INTF, "AddInterface called for: %S %d %d ",
           pwszInterfaceName,
           dwIndex,
           dwIfType);

    ulNameLen = wcslen(pwszInterfaceName);

    if((pwszInterfaceName == NULL) ||
       (pvConfig == NULL) ||
       (ulNameLen == 0))
    {
        Trace0(ERR,
               "AddInterface: Router Manager called us with NULL info");

        TraceLeave("AddInterface");

        ExitProtocolApi();

        return ERROR_INVALID_PARAMETER;
    }

    pIfInfo = (PSAMPLE_PROTOCOL_INTERFACE_INFO)pvConfig;

    if(!ValidateInterfaceInfo(pIfInfo))
    {
        TraceLeave("AddInterface");

        ExitProtocolApi();

        return ERROR_INVALID_DATA;
    }

    EnterCriticalSection(&g_csIfListLock);

    //
    // Create the interface block and the blocks for the binding
    //

    dwResult = NO_ERROR;

    __try
    {
        pIf = HeapAlloc(g_hPrivateHeap,
                        HEAP_ZERO_MEMORY,
                        sizeof(NT_IF));

        if(pIf == NULL)
        {
            dwResult = GetLastError();

            Trace1(ERR,
                   "AddInterface: Error %d allocating memory for i/f",
                   dwResult);

            __leave;
        }

        InitializeListHead(&pIf->leInternalIfHead);

        pwszName = HeapAlloc(g_hPrivateHeap,
                             HEAP_ZERO_MEMORY,
                             (ulNameLen + 1)*sizeof(WCHAR));

        if(pwszName == NULL)
        {
            dwResult = GetLastError();

            Trace1(ERR,
                   "AddInterface: Error %d allocating memory for name",
                   dwResult);

            __leave;
        }

        wcscpy(pwszName,
               pwszInterfaceName);

        pIf->pwszIfName = pwszName;

        ClearNtState(pIf);

        pIf->dwType = dwIfType;

        pIf->ulNumBindings = pIfInfo->ulNumBindings;

        for(i = 0; i < pIfInfo->ulNumBindings; i++)
        {
            pBind = HeapAlloc(g_hPrivateHeap,
                                HEAP_ZERO_MEMORY,
                                sizeof(INTRNL_IF));

            if(pBind == NULL)
            {
                dwResult = GetLastError();

                Trace1(ERR,
                       "AddInterface: Error %d allocating memory for internal i/f",
                       dwResult);

                __leave;
            }

            InsertHeadList(&(pIf->leInternalIfHead),
                           &(pBind->leInternalIfLink));

            //
            // Copy out binding info
            //

            pBind->dwAddress    = pIfInfo->rgbiInfo[i].dwAddress;
            pBind->dwMask       = pIfInfo->rgbiInfo[i].dwMask;
            pBind->bEnabled     = pIfInfo->rgbiInfo[i].bEnabled;
            pBind->dwState      = BINDING_DOWN;
            pBind->sSocket      = INVALID_SOCKET;

            if(pIfInfo->ulNumBindings == 1)
            {
                //
                // If we have only one address, we set that to 0.0.0.0
                // for now even if our config has screwed up
                //

                pBind->dwAddress    = 0x00000000;
                pBind->dwMask       = 0x00000000;
            }
        }
    }
    __finally
    {
        if(dwResult != NO_ERROR)
        {
            //
            // Clean up all resources
            //

            if(pIf)
            {
                if(pIf->pwszIfName)
                {
                    HeapFree(g_hPrivateHeap,
                             0,
                             pIf->pwszIfName);
                }

                pleNode  = pIf->leInternalIfHead.Flink;

                while(pleNode != &pIf->leInternalIfHead)
                {
                    pBind = CONTAINING_RECORD(pleNode,
                                              INTRNL_IF,
                                              leInternalIfLink);

                    pleNode = pleNode->Flink;

                    HeapFree(g_hPrivateHeap,
                             0,
                             pBind);
                }
            }
        }
        else
        {
            //
            // Insert the interface into our link
            //

            for(pleNode  = g_leIfListHead.Flink;
                pleNode != &g_leIfListHead;
                pleNode  = pleNode->Flink)
            {
                pPrev = CONTAINING_RECORD(pleNode,
                                          NT_IF,
                                          leNtIfLink);

                if(pPrev->dwNtIndex > dwIndex)
                {
                    break;
                }
            }

            //
            // pPrev now points to the interface with a higher index
            // pPrev->Blink < dwIndex so put our interface after
            // pPrev->Blink. This way we get an ordered list
            //

            InsertHeadList(pPrev->leNtIfLink.Blink,
                           &(pIf->leNtIfLink));

        }
    }

    LeaveCriticalSection(&g_csIfListLock);

    TraceLeave("AddInterface");

    ExitProtocolApi();

    return dwResult;
}

DWORD
APIENTRY
DeleteInterface(
    IN DWORD dwIndex
    )
/*++
  Routine Description
      Called by the IP Router Manager to delete an interface. If the interface is
      running we shut it down and free all the resources used by it

  Arguments
      dwIndex   The index of the interface to be deleted

  Return Value
      ERROR_INVALID_PARAMETER   If an interface with the given index was not found
      NO_ERROR

--*/
{
    PNT_IF      pIf;
    PLIST_ENTRY pleNode;
    PINTRNL_IF  pBind;

    EnterProtocolApi();

    TraceEnter("DeleteInterface");

    Trace1(ERR,
           "DeleteInterface: Called for interface %d",
           dwIndex);

    EnterCriticalSection(&g_csIfListLock);

    pIf = GetIfBlockGivenIndex(dwIndex);

    if(pIf == NULL)
    {
        //
        // Did not have an interface corresponding to
        // this index
        //

        LeaveCriticalSection(&g_csIfListLock);

        Trace1(ERR,
               "DeleteInterface: Interface %d does not exist",
               dwIndex);

        TraceLeave("DeleteInterface");

        ExitProtocolApi();

        return ERROR_INVALID_PARAMETER;
    }

    //
    // Remove it from the list of NT Interfaces
    //

    RemoveEntryList(&(pIf->leNtIfLink));

    ASSERT(pIf->dwNtIndex == dwIndex);
    ASSERT(pIf->pwszIfName);

    //
    // Shut down each binding on the interface. We may have already
    // gotten Unbind() calls so they may have already been shut down
    //

    pleNode = pIf->leInternalIfHead.Flink;

    while(pleNode != &(pIf->leInternalIfHead))
    {
        pBind = CONTAINING_RECORD(pleNode, INTRNL_IF, leInternalIfLink);

        pleNode = pleNode->Flink;

        if(pBind->dwState == BINDING_UP)
        {
            //
            // So we did not get an unbind or disable for this
            //

            ASSERT(pBind->sSocket != INVALID_SOCKET);

            if(closesocket(pBind->sSocket) != NO_ERROR)
            {
                Trace3(ERR,
                       "DeleteInterface: Error %d closing socket %d on interface %S",
                       WSAGetLastError(),
                       pBind->sSocket,
                       pIf->pwszIfName);
            }
        }

        HeapFree(g_hPrivateHeap,
                 0,
                 pBind);
    }

    //
    // Free other resources
    //

    HeapFree(g_hPrivateHeap,
             0,
             pIf->pwszIfName);

    //
    // Lastly free the interface itself
    //

    HeapFree(g_hPrivateHeap,
             0,
             pIf);

    LeaveCriticalSection(&g_csIfListLock);

    TraceLeave("AddInterface");

    ExitProtocolApi();

    return NO_ERROR;
}

DWORD
APIENTRY
GetInterfaceConfigInfo(
    IN     DWORD  dwIndex,
    IN OUT PVOID  pvConfig,
    IN OUT PDWORD pdwSize
    )
/*++
  Routine Description
      Called by the IP Router Manager to retrieve an interface's configuration.
      Usually this is because an admin utility is displaying this information.
      The Router Manager calls us with a NULL config and ZERO size. We return
      the required size to it.  It then allocates the needed memory and calls
      us a second time with a valid buffer.  We validate parameters each time
      and copy out our config if we can

  Arguments
      dwIndex   Index of the interface being queried
      pvConfig  Pointer to buffer to store the config
      pdwSize   Size of the buffer

  Return Value
      ERROR_INSUFFICIENT_BUFFER If the size of the buffer is too small
      ERROR_INVALID_PARAMETER
      ERROR_INVALID_DATA
      NO_ERROR

--*/
{
    PNT_IF      pIf;
    PLIST_ENTRY pleNode;
    PINTRNL_IF  pBind;
    ULONG       i;

    PSAMPLE_PROTOCOL_INTERFACE_INFO pIfInfo;

    EnterProtocolApi();

    TraceEnter("GetInterfaceConfigInfo");

    if(pdwSize == NULL)
    {
        Trace0(ERR,
               "GetInterfaceConfigInfo: Router Manager called us with NULL size");

        TraceLeave("GetInterfaceConfigInfo");

        ExitProtocolApi();

        return ERROR_INVALID_PARAMETER;
    }

    EnterCriticalSection(&g_csIfListLock);

    pIf = GetIfBlockGivenIndex(dwIndex);

    if(pIf == NULL)
    {
        LeaveCriticalSection(&g_csIfListLock);

        Trace1(ERR,
               "GetInterfaceConfigInfo: Interface %d does not exist",
               dwIndex);

        TraceLeave("GetInterfaceConfigInfo");

        ExitProtocolApi();

        return ERROR_INVALID_PARAMETER;
    }

    if(*pdwSize < SIZEOF_PROTO_IF_INFO(pIf->ulNumBindings))
    {
        *pdwSize = SIZEOF_PROTO_IF_INFO(pIf->ulNumBindings);

        LeaveCriticalSection(&g_csIfListLock);

        TraceLeave("GetInterfaceConfigInfo");

        ExitProtocolApi();

        return ERROR_INSUFFICIENT_BUFFER;
    }

    *pdwSize = SIZEOF_PROTO_IF_INFO(pIf->ulNumBindings);

    if(pvConfig == NULL)
    {
        LeaveCriticalSection(&g_csIfListLock);

        Trace0(ERR,
               "GetInterfaceConfigInfo: Router Manager called us with NULL config buffer");

        TraceLeave("GetInterfaceConfigInfo");

        ExitProtocolApi();

        return ERROR_INVALID_PARAMETER;
    }

    //
    // So we have a good buffer of the right size
    // Copy out the info
    //

    pIfInfo = (PSAMPLE_PROTOCOL_INTERFACE_INFO)pvConfig;

    pIfInfo->ulNumBindings = pIf->ulNumBindings;

    for(pleNode  = pIf->leInternalIfHead.Flink, i = 0;
        pleNode != &pIf->leInternalIfHead;
        pleNode  = pleNode->Flink, i++)
    {
        pBind = CONTAINING_RECORD(pleNode, INTRNL_IF, leInternalIfLink);

        pIfInfo->rgbiInfo[i].dwAddress  = pBind->dwAddress;
        pIfInfo->rgbiInfo[i].dwMask     = pBind->dwMask;
        pIfInfo->rgbiInfo[i].bEnabled   = pBind->bEnabled;
    }

    ASSERT(i == pIf->ulNumBindings);

    //
    // If we had only one address we will return 0.0.0.0 as the address
    // even if we know what the current address is
    //

    if(pIf->ulNumBindings == 1)
    {
        pIfInfo->rgbiInfo[0].dwAddress  = 0x00000000;
        pIfInfo->rgbiInfo[0].dwMask     = 0x00000000;
    }

    LeaveCriticalSection(&g_csIfListLock);

    TraceLeave("GetInterfaceConfigInfo");

    ExitProtocolApi();

    return NO_ERROR;
}

DWORD
APIENTRY
SetInterfaceConfigInfo(
    IN DWORD dwIndex,
    IN PVOID pvConfig
    )
/*++
  Routine Description

  Arguments

  Return Value
      NO_ERROR

--*/
{
    PNT_IF      pIf;

    PSAMPLE_PROTOCOL_INTERFACE_INFO pIfInfo;

    EnterProtocolApi();

    TraceEnter("SetInterfaceConfigInfo");

    if(pvConfig == NULL)
    {
        //
        // A NULL config means no change
        //

        TraceLeave("SetInterfaceConfigInfo");

        ExitProtocolApi();

        return NO_ERROR;
    }

    pIfInfo = (PSAMPLE_PROTOCOL_INTERFACE_INFO)pvConfig;

    if(!ValidateInterfaceInfo(pIfInfo))
    {
        TraceLeave("SetInterfaceConfigInfo");

        ExitProtocolApi();

        return ERROR_INVALID_DATA;
    }

    EnterCriticalSection(&g_csIfListLock);

    pIf = GetIfBlockGivenIndex(dwIndex);

    if(pIf == NULL)
    {
        Trace1(ERR,
               "SetInterfaceConfigInfo: Interface %d does not exist",
               dwIndex);

        TraceLeave("SetInterfaceConfigInfo");

        ExitProtocolApi();

        return ERROR_INVALID_PARAMETER;
    }

    //
    // Do what needs to be done with the info
    //

    return NO_ERROR;
}

DWORD
APIENTRY
BindInterface(
    IN DWORD dwIndex,
    IN PVOID pBinding
    )
/*++
  Routine Description
      Called by the IP Router Manager once it learns the address(es) on an
      interface.  This may happen as soon as the router starts (after the
      interface is added, ofcourse) when the interface has a static address
      or may happen when an interface acquires a DHCP address or may happen
      when IPCP acquire the address for a dial up link
      The binding may consist of one or more addresses.  We walk the addresses
      given and find the corresponding block in our config.  We then update
      any information and if the interface has been enabled, we activate the
      binding, i.e. begin running on it.

  Arguments
      dwIndex   Index of the interface in question
      pBinding  Pointer to IP_ADAPTER_BINDING_INFO containing info about
                the addresses on the interface

  Return Value
      ERROR_INVALID_PARAMETER
      NO_ERROR

--*/
{
    PNT_IF      pIf;
    PLIST_ENTRY pleNode;
    PINTRNL_IF  pBind;
    ULONG       i;
    DWORD       dwResult;

    PIP_ADAPTER_BINDING_INFO pBindingInfo;

    EnterProtocolApi();

    TraceEnter("BindInterface");

    if(pBinding == NULL)
    {
        Trace0(ERR,
               "BindInterface: Router Manager called us with NULL binding");

        TraceLeave("BindInterface");

        ExitProtocolApi();

        return ERROR_INVALID_PARAMETER;
    }

    pBindingInfo = (PIP_ADAPTER_BINDING_INFO)pBinding;

    if(pBindingInfo->NumAddresses < 1)
    {
        Trace0(ERR,
               "BindInterface: Router Manager called us with NO binding");

        TraceLeave("BindInterface");

        ExitProtocolApi();

        return ERROR_INVALID_PARAMETER;
    }

    EnterCriticalSection(&g_csIfListLock);

    pIf = GetIfBlockGivenIndex(dwIndex);

    if(pIf == NULL)
    {
        LeaveCriticalSection(&g_csIfListLock);

        Trace1(ERR,
               "BindInterface: Interface %d does not exist",
               dwIndex);

        TraceLeave("BindInterface");

        ExitProtocolApi();

        return ERROR_INVALID_PARAMETER;
    }

    if(IsNtBound(pIf))
    {
        //
        // Can not receive two bind notifications without an
        // Unbind in the middle
        //

        Trace1(ERR,
               "BindInterface: Interface %S is already bound",
               pIf->pwszIfName);

        LeaveCriticalSection(&g_csIfListLock);

        TraceLeave("BindInterface");

        ExitProtocolApi();

        return ERROR_INVALID_PARAMETER;
    }

    SetNtBound(pIf);

    for(i = 0; i < pBindingInfo->NumAddresses; i++)
    {
        pBind = GetBindingGivenAddress(pIf,
                                       pBindingInfo->Address[i].IPAddress);

        if(pBind == NULL)
        {
            //
            // Our config is bad, we will try and go through all the blocks
            // and see if we can atleast bind on the others.
            //

            Trace3(ERR,
                   "BindInterface: Couldnt find config block for %d.%d.%d.%d/%d.%d.%d.%d over %S",
                   PRINT_ADDRESS(pBindingInfo->Address[i].IPAddress),
                   PRINT_ADDRESS(pBindingInfo->Address[i].Mask),
                   pIf->pwszIfName);

            continue;
        }

        if(pBindingInfo->NumAddresses == 1)
        {
            //
            // If we have only one address, then the addresses
            // may not match, so update them
            //

            pBind->dwAddress = pBindingInfo->Address[i].IPAddress;
            pBind->dwMask    = pBindingInfo->Address[i].Mask;
        }
    }

#if DBG

    //
    // Check to make sure we got bindings for all the configured addresses
    //

    if(pIf->ulNumBindings > 1)
    {
        for(pleNode  = pIf->leInternalIfHead.Flink;
            pleNode != &pIf->leInternalIfHead;
            pleNode  = pleNode->Flink)
        {
            bFound = FALSE;

            pBind = CONTAINING_RECORD(pleNode, INTRNL_IF, leInternalIfLink);

            for(i = 0; i < pBindingInfo->NumAddresses; i++)
            {
                if(pBind->dwAddress == pBindingInfo->Address[i].IPAddress)
                {
                    ASSERT(pBind->dwMask == pBindingInfo->Address[i].Mask);

                    bFound = TRUE;
                    break;
                }
            }

            if(!bFound)
            {
                Trace3(ERR,
                       "BindInterface: Binding not indicated for %d.%d.%d.%d/%d.%d.%d.%d over %S",
                       PRINT_ADDRESS(pBind->dwAddress),
                       PRINT_ADDRESS(pBind->dwMask),
                       pIf->pwszIfName);
            }
        }
    }

#endif // DBG

    //
    // OK now we have the bindings all set up
    //

    if(IsNtEnabled(pIf))
    {
        //
        // So the interface is bound and enabled => we are all set
        // to go
        //

        for(pleNode  = pIf->leInternalIfHead.Flink;
            pleNode != &pIf->leInternalIfHead;
            pleNode  = pleNode->Flink)
        {
            pBind = CONTAINING_RECORD(pleNode, INTRNL_IF, leInternalIfLink);

            if(pBind->bEnabled)
            {
                //
                // Our config tells us that we are enabled for this address
                //

                ASSERT(pBind->dwState != BINDING_UP);

                //
                // This will set the state to BINDING_UP
                //

                dwResult = ActivateBinding(pBind);

                if(dwResult != NO_ERROR)
                {
                    Trace2(ERR,
                           "BindInterface: Unable to activate %d.%d.%d.%d over %S",
                           PRINT_ADDRESS(pBind->dwAddress),
                           pIf->pwszIfName);
                }
            }
        }
    }

    LeaveCriticalSection(&g_csIfListLock);

    TraceLeave("BindInterface");

    ExitProtocolApi();

    return NO_ERROR;
}

DWORD
APIENTRY
UnBindInterface(
    IN DWORD dwIndex
    )
/*++
  Routine Description
      Called when the interface loses its IP Address(es). This may
      happen when the interface is shutting down. It may be because an
      admin Disabled the interface with IP (as opposed to just disabling
      or protocol on the interface). It can happen when the admin releases
      a DHCP acquired interface or when a dial up link disconnects
      We see if we were running on the binding and if so, we shut it down.
      We dont release any memory associated with the binding because
      we can get bound again

  Arguments
      dwIndex   The index of the interface being unbound

  Return Value
      ERROR_INVALID_PARAMETER
      NO_ERROR

--*/
{
    PNT_IF      pIf;
    PLIST_ENTRY pleNode;
    PINTRNL_IF  pBind;
    DWORD       dwResult;

    EnterProtocolApi();

    TraceEnter("UnbindInterface");

    EnterCriticalSection(&g_csIfListLock);

    pIf = GetIfBlockGivenIndex(dwIndex);

    if(pIf == NULL)
    {
        LeaveCriticalSection(&g_csIfListLock);

        Trace1(ERR,
               "UnbindInterface: Interface %d does not exist",
               dwIndex);

        TraceLeave("UnbindInterface");

        ExitProtocolApi();

        return ERROR_INVALID_PARAMETER;
    }

    if(!IsNtBound(pIf))
    {
        //
        // Nothing new happening
        //

        Trace1(INTF,
               "UnbindInterface: Unbind received for %S which was never bound",
               pIf->pwszIfName);

        LeaveCriticalSection(&g_csIfListLock);

        TraceLeave("UnbindInterface");

        ExitProtocolApi();

        return NO_ERROR;
    }

    ClearNtBound(pIf);

    if(IsNtEnabled(pIf))
    {
        //
        // Since it is enabled and was bound, deactivate the
        // bindings
        //

        for(pleNode  = pIf->leInternalIfHead.Flink;
            pleNode != &pIf->leInternalIfHead;
            pleNode  = pleNode->Flink)
        {
            pBind = CONTAINING_RECORD(pleNode, INTRNL_IF, leInternalIfLink);

            if(pBind->dwState == BINDING_UP)
            {
                //
                // This binding had been successfully activated
                //

                dwResult = DeactivateBinding(pBind);

                if(dwResult != NO_ERROR)
                {
                    Trace2(ERR,
                           "UnbindInterface: Unable to deactivate %d.%d.%d.%d over %S",
                           PRINT_ADDRESS(pBind->dwAddress),
                           pIf->pwszIfName);
                }
            }
        }

        if(pIf->ulNumBindings == 1)
        {
            //
            // Clear out the old address
            //

            pBind = CONTAINING_RECORD(pIf->leInternalIfHead.Flink,
                                      INTRNL_IF,
                                      leInternalIfLink);

            pBind->dwAddress = 0x00000000;
            pBind->dwMask    = 0x00000000;
        }
    }

    LeaveCriticalSection(&g_csIfListLock);

    TraceLeave("UnbindInterface");

    ExitProtocolApi();

    return NO_ERROR;

}

DWORD
APIENTRY
EnableInterface(
    IN DWORD dwIndex
    )
/*++
  Routine Description
      An interface comes up in a DISABLED state. This function is
      called by the router manager to enable the interface.  It can also
      be called when the interface is being reenabled after being disabled
      by the admin
      The bindings on an interface are kept across and Enable-Disable, so
      we heck to see if we were already bound, and if so we activate the
      protocol over the bindings.

  Arguments
      dwIndex   Interface to be enabled

  Return Value
      NO_ERROR

--*/
{
    PNT_IF      pIf;
    PLIST_ENTRY pleNode;
    PINTRNL_IF  pBind;
    DWORD       dwResult;

    EnterProtocolApi();

    TraceEnter("EnableInterface");

    EnterCriticalSection(&g_csIfListLock);

    pIf = GetIfBlockGivenIndex(dwIndex);

    if(pIf == NULL)
    {
        LeaveCriticalSection(&g_csIfListLock);

        Trace1(ERR,
               "EnableInterface: Interface %d does not exist",
               dwIndex);

        TraceLeave("EnableInterface");

        ExitProtocolApi();

        return ERROR_INVALID_PARAMETER;
    }

    if(IsNtEnabled(pIf))
    {
        //
        // Nothing new happening
        //

        Trace1(INTF,
               "EnableInterface: Enable received for %S which is already enabled",
               pIf->pwszIfName);

        LeaveCriticalSection(&g_csIfListLock);

        TraceLeave("EnableInterface");

        ExitProtocolApi();

        return NO_ERROR;
    }

    SetNtEnabled(pIf);

    if(IsNtBound(pIf))
    {
        //
        // Since it is enabled and bound, activate the bindings
        //

        for(pleNode  = pIf->leInternalIfHead.Flink;
            pleNode != &pIf->leInternalIfHead;
            pleNode  = pleNode->Flink)
        {
            pBind = CONTAINING_RECORD(pleNode, INTRNL_IF, leInternalIfLink);

            ASSERT(pBind->dwState == BINDING_DOWN);

            if(pBind->bEnabled)
            {
                dwResult = ActivateBinding(pBind);

                if(dwResult != NO_ERROR)
                {
                    Trace2(ERR,
                           "EnableInterface: Unable to activate %d.%d.%d.%d over %S",
                           PRINT_ADDRESS(pBind->dwAddress),
                           pIf->pwszIfName);
                }
            }
        }
    }

    LeaveCriticalSection(&g_csIfListLock);

    TraceLeave("EnableInterface");

    ExitProtocolApi();

    return NO_ERROR;
}

DWORD
APIENTRY
DisableInterface(
    IN DWORD dwIndex
    )
/*++
  Routine Description
      Called by the IP Router Manger to disable an interface.  This is
      usually in response to an admin setting the AdminStatus in IP to DOWN.
      This is different from an admin just disabling the bEnabled flag in our
      config because that is opaque to IP. That is a routing protocol specific
      disable and is conveyed to us via SetInterfaceConfig() calls.  THIS IS
      AN IMPORTANT DISTINCTION. A ROUTING PROTOCOL NEEDS TO MANTAIN TWO STATES -
      AN NT STATE AND A PROTOCOL SPECIFIC STATE.

  Arguments
      dwIndex   The index of the interface to disable

  Return Value
      ERROR_INVALID_PARAMETER
      NO_ERROR

--*/
{
    PNT_IF      pIf;
    PLIST_ENTRY pleNode;

PINTRNL_IF  pBind;
    DWORD       dwResult;

    EnterProtocolApi();

    TraceEnter("DisableInterface");

    EnterCriticalSection(&g_csIfListLock);

    pIf = GetIfBlockGivenIndex(dwIndex);

    if(pIf == NULL)
    {
        LeaveCriticalSection(&g_csIfListLock);

        Trace1(ERR,
               "DisableInterface: Interface %d does not exist",
               dwIndex);

        TraceLeave("DisableInterface");

        ExitProtocolApi();

        return ERROR_INVALID_PARAMETER;
    }

    if(!IsNtEnabled(pIf))
    {
        //
        // Nothing new happening
        //

        Trace1(INTF,
               "DisableInterface: Disable received for %S which was never enabled",
               pIf->pwszIfName);

        LeaveCriticalSection(&g_csIfListLock);

        TraceLeave("DisableInterface");

        ExitProtocolApi();

        return NO_ERROR;
    }

    ClearNtEnabled(pIf);

    if(IsNtBound(pIf))
    {
        //
        // Since it was enabled and is bound, it must have been
        // activated, so deactivate the bindings now
        //

        for(pleNode  = pIf->leInternalIfHead.Flink;
            pleNode != &pIf->leInternalIfHead;
            pleNode  = pleNode->Flink)
        {
            pBind = CONTAINING_RECORD(pleNode, INTRNL_IF, leInternalIfLink);

            if(pBind->dwState == BINDING_UP)
            {
                //
                // This binding had been successfully activated
                //

                dwResult = DeactivateBinding(pBind);

                if(dwResult != NO_ERROR)
                {
                    Trace2(ERR,
                           "DisableInterface: Unable to deactivate %d.%d.%d.%d over %S",
                           PRINT_ADDRESS(pBind->dwAddress),
                           pIf->pwszIfName);
                }
            }
        }
    }

    LeaveCriticalSection(&g_csIfListLock);

    TraceLeave("DisableInterface");

    ExitProtocolApi();

    return NO_ERROR;

}

BOOL
ValidateInterfaceInfo(
    PSAMPLE_PROTOCOL_INTERFACE_INFO pInfo
    )
/*++
  Routine Description

  Locks

  Arguments

  Return Value
      NO_ERROR

--*/
{
    return TRUE;
}

PNT_IF
GetIfBlockGivenIndex(
    DWORD   dwIndex
    )
/*++
  Routine Description

  Locks
      Assumes the IF List lock is held

  Arguments

  Return Value
      NO_ERROR

--*/
{
    PLIST_ENTRY pleNode;
    PNT_IF      pIf;

    for(pleNode  = g_leIfListHead.Flink;
        pleNode != &g_leIfListHead;
        pleNode  = pleNode->Flink)
    {
        pIf = CONTAINING_RECORD(pleNode,
                                NT_IF,
                                leNtIfLink);

        if(pIf->dwNtIndex == dwIndex)
        {
            return pIf;
        }

        if(pIf->dwNtIndex > dwIndex)
        {
            //
            // Since the list is ordered, we wont find this further on
            //

            return NULL;
        }
    }

    return NULL;
}

PINTRNL_IF
GetBindingGivenAddress(
    PNT_IF pIf,
    DWORD dwAddress
    )
/*++
  Routine Description

  Locks
      Assumes the IF List lock is held

  Arguments

  Return Value
      NO_ERROR

--*/
{

    return NULL;
}

DWORD
ActivateBinding(
    PINTRNL_IF  pBind
    )
/*++
  Routine Description
      Called when the interface needs to be brought up on this Address
      We create a socket and fire it off

  Locks
      Assumes the IF List lock is held

  Arguments

  Return Value
      NO_ERROR

--*/
{
    DWORD   dwResult;

    ASSERT(pBind->dwState == BINDING_DOWN);
    ASSERT(pBind->sSocket == INVALID_SOCKET);

    ASSERT(pBind->dwAddress != 0x00000000);
    ASSERT(pBind->dwMask    != 0x00000000);

    dwResult = CreateSocket(pBind->dwAddress,
                            &pBind->sSocket);

    if(dwResult == NO_ERROR)
    {
        pBind->dwState = BINDING_UP;
    }

    return dwResult;
}

DWORD
DeactivateBinding(
    PINTRNL_IF  pBind
    )
/*++
  Routine Description

  Locks
      Assumes the IF List lock is held

  Arguments

  Return Value
      NO_ERROR

--*/
{
    ASSERT(pBind->dwState == BINDING_UP);
    ASSERT(pBind->sSocket != INVALID_SOCKET);

    ASSERT(pBind->dwAddress != 0x00000000);
    ASSERT(pBind->dwMask    != 0x00000000);

    if(WSAEventSelect(pBind->sSocket,
                      (WSAEVENT)NULL,
                      0) == SOCKET_ERROR)
    {
        Trace2(ERR,
               "DeactivateBinding: Error %d clearing socket (%d.%d.%d.%d) association with event",
               WSAGetLastError(),
               PRINT_ADDRESS(pBind->dwAddress));

    }

    closesocket(pBind->sSocket);

    pBind->sSocket = INVALID_SOCKET;

    return NO_ERROR;
}

DWORD
CreateSocket(
    IN  DWORD   dwAddress,
    OUT SOCKET  *psSocket
    )
/*++
  Routine Description
     Activates router discovery messages on an interface. The interface must already
     be bound

  Locks
     ICB_LIST lock must be held as WRITER

  Arguments
      picb          The ICB of the interface to activate

  Return Value
      NO_ERROR or some error code
--*/
{
    DWORD           dwResult, dwBytesReturned;
    LINGER          lLinger;
    BOOL            bOption, bLoopback;
    SOCKADDR_IN     sinSockAddr;
    INT             iScope;

    TraceEnter("CreateSocket");

    //
    // Create the sockets for the interface
    //

    *psSocket = INVALID_SOCKET;

    do
    {
        *psSocket = WSASocket(AF_INET,
                              SOCK_RAW,
                              SAMPLE_PROTOCOL_IP_ID,
                              NULL,
                              0,
                              WSA_FLAG_MULTIPOINT_C_LEAF|WSA_FLAG_MULTIPOINT_D_LEAF);

        if(*psSocket == INVALID_SOCKET)
        {
            dwResult = WSAGetLastError();

            Trace2(ERR,
                   "CreateSocket Couldnt create socket on %d.%d.%d.%d . Error %d",
                   PRINT_ADDRESS(dwAddress),
                   dwResult);

            break;
        }

        //
        // Set SO_LINGER to off
        //

        lLinger.l_onoff  = 0;
        lLinger.l_linger = 0;

        if(setsockopt(*psSocket,
                      SOL_SOCKET,
                      SO_LINGER,
                      (const char *)&lLinger,
                      sizeof(LINGER)) == SOCKET_ERROR)
        {
            Trace2(ERR,
                   "CreateSocket: Couldnt set linger option on %d.%d.%d.%d - continuing. Error %d",
                   PRINT_ADDRESS(dwAddress),
                   WSAGetLastError());
        }

        //
        // Set to SO_REUSEADDR
        //

        bOption = TRUE;

        if(setsockopt(*psSocket,
                      SOL_SOCKET,
                      SO_REUSEADDR,
                      (const char FAR*)&bOption,
                      sizeof(BOOL)) == SOCKET_ERROR)
        {
            Trace2(ERR,
                   "CreateSocket: Couldnt set reuse option on %d.%d.%d.%d - continuing. Error %d",
                   PRINT_ADDRESS(dwAddress),
                   WSAGetLastError());
        }

        if(WSAEventSelect(*psSocket,
                          (WSAEVENT)g_hSocketEvent,
                          FD_READ) == SOCKET_ERROR)
        {
            dwResult = WSAGetLastError();

            Trace2(ERR,
                   "CreateSocket: WSAEventSelect() failed for socket on %d.%d.%d.%d.Error %d",
                   PRINT_ADDRESS(dwAddress),
                   dwResult);

            closesocket(*psSocket);

            *psSocket = INVALID_SOCKET;

            break;
        }

        //
        // Bind to the addresses on the interface
        //

        sinSockAddr.sin_family      = AF_INET;
        sinSockAddr.sin_addr.s_addr = dwAddress;
        sinSockAddr.sin_port        = 0;

        if(bind(*psSocket,
                (const struct sockaddr FAR*)&sinSockAddr,
                sizeof(SOCKADDR_IN)) == SOCKET_ERROR)
        {
            dwResult = WSAGetLastError();

            Trace2(ERR,
                   "CreateSocket: Couldnt bind to %d.%d.%d.%d.  Error %d",
                   PRINT_ADDRESS(dwAddress),
                   dwResult);

            closesocket(*psSocket);

            *psSocket = INVALID_SOCKET;

            break;
        }

        //
        // Set scope/TTL to 1 since we always multicast the responses
        // Also set Loopback to ignore self generated packets. Loopback
        // d==card doesnt work currently on NT, so we still have code to
        // filter our own multicasts in the receive path
        //

        bLoopback   = FALSE;

        dwResult = WSAIoctl(*psSocket,
                            SIO_MULTIPOINT_LOOPBACK,
                            (PVOID)&bLoopback,
                            sizeof(BOOL),
                            NULL,
                            0,
                            &dwBytesReturned,
                            NULL,
                            NULL);

        if(dwResult == SOCKET_ERROR)
        {
            Trace2(ERR,
                   "CreateSocket: Error %d setting loopback to FALSE on %d.%d.%d.%d",
                   WSAGetLastError(),
                   PRINT_ADDRESS(dwAddress));
        }

        iScope  = 1;

        dwResult = WSAIoctl(*psSocket,
                            SIO_MULTICAST_SCOPE,
                            (PVOID)&iScope,
                            sizeof(INT),
                            NULL,
                            0,
                            &dwBytesReturned,
                            NULL,
                            NULL);

        if(dwResult == SOCKET_ERROR)
        {
            Trace2(ERR,
                   "CreateSocket: Error %d setting multicast scope to 1 on %d.%d.%d.%d",
                   WSAGetLastError(),
                   PRINT_ADDRESS(dwAddress));
        }

        //
        // Join the multicast session on SAMPLE_PROTOCOL_MULTICAST_GROUP1
        //

        sinSockAddr.sin_family      = AF_INET;
        sinSockAddr.sin_addr.s_addr = SAMPLE_PROTOCOL_MULTICAST_GROUP1;
        sinSockAddr.sin_port        = 0;

        if(WSAJoinLeaf(*psSocket,
                       (const struct sockaddr FAR*)&sinSockAddr,
                       sizeof(SOCKADDR_IN),
                       NULL,
                       NULL,
                       NULL,
                       NULL,
                       JL_BOTH) == INVALID_SOCKET)
        {
            dwResult = WSAGetLastError();

            Trace2(ERR,
                   "CreateSocket: Couldnt join multicast group1 on socket for %d.%d.%d.%d. Error %d",
                   PRINT_ADDRESS(dwAddress),
                   dwResult);

            closesocket(*psSocket);

            *psSocket = INVALID_SOCKET;

            break;
        }

        //
        // Join the multicast session on SAMPLE_PROTOCOL_MULTICAST_GROUP2
        //

        sinSockAddr.sin_family      = AF_INET;
        sinSockAddr.sin_addr.s_addr = SAMPLE_PROTOCOL_MULTICAST_GROUP2;
        sinSockAddr.sin_port        = 0;

        if(WSAJoinLeaf(*psSocket,
                       (const struct sockaddr FAR*)&sinSockAddr,
                       sizeof(SOCKADDR_IN),
                       NULL,
                       NULL,
                       NULL,
                       NULL,
                       JL_BOTH) == INVALID_SOCKET)
        {
            dwResult = WSAGetLastError();

            Trace2(ERR,
                   "CreateSocket: Couldnt join multicast group2 on socket on %d.%d.%d.%d. Error %d",
                   PRINT_ADDRESS(dwAddress),
                   dwResult);

            closesocket(*psSocket);

            *psSocket = INVALID_SOCKET;

            break;
        }

    }while(FALSE);

    TraceLeave("CreateSocket");

    return dwResult;
}


IPRTSAMP.H

/*++

Copyright (c) 1997  Microsoft Corporation

Module Name:

    sample\ip\saminfo.h

Abstract:
    Data structures exposed by the Sample Routing Protocol.  These
    include the Global and Interface Info and the structures needed
    to query the MIB.

Revision History:

--*/

#ifndef __IPRTSAMP_H__
#define __IPRTSAMP_H__

typedef struct _NT_IF
{
    LIST_ENTRY  leNtIfLink;
    DWORD       dwNtIndex;
    DWORD       fNtState;
    ULONG       ulNumBindings;
    PWCHAR      pwszIfName;
    DWORD       dwType;
    LIST_ENTRY  leInternalIfHead;
}NT_IF, *PNT_IF;

#define NT_STATE_ENABLED_BIT    0x00000001
#define NT_STATE_BOUND_BIT      0x00000002

#define NT_STATE_ENABLED_BOUND  (NT_STATE_ENABLED_BIT|NT_STATE_BOUND_BIT)

#define ClearNtState(X)     \
    ((X)->fNtState |= 0x00000000)

#define SetNtEnabled(X)     \
    ((X)->fNtState |= NT_STATE_ENABLED_BIT)

#define ClearNtEnabled(X)   \
    ((X)->fNtState &= ~NT_STATE_ENABLED_BIT)

#define IsNtEnabled(X)     \
    ((X)->fNtState | NT_STATE_ENABLED_BIT)

#define SetNtBound(X)     \
    ((X)->fNtState |= NT_STATE_BOUND_BIT)

#define ClearNtBound(X)   \
    ((X)->fNtState &= ~NT_STATE_BOUND_BIT)

#define IsNtBound(X)     \
    ((X)->fNtState | NT_STATE_BOUND_BIT)

#define IsNtEnabledAndBound(X)     \
    (((X)->fNtState & NT_STATE_ENABLED_BOUND) == NT_STATE_ENABLED_BOUND)

typedef struct _INTRNL_IF
{
    LIST_ENTRY  leInternalIfLink;
    DWORD       dwAddress;
    DWORD       dwMask;
    BOOL        bEnabled;
    DWORD       dwState;
    SOCKET      sSocket;
}INTRNL_IF, *PINTRNL_IF;

typedef struct _MGR_MSG
{
    LIST_ENTRY              leMsgLink;
    ROUTING_PROTOCOL_EVENTS rreEvent;
    MESSAGE                 mResult;
}MGR_MSG, *PMGR_MSG;

#define MAX_PACKET_SIZE     512

typedef struct _RECEIVE_BUFFER
{
    WSABUF  wbBuffer;
    BYTE    rgbyData[MAX_PACKET_SIZE];
}RECEIVE_BUFFER, *PRECEIVE_BUFFER;

#endif


LOGTRDEF.H

/*++

Copyright (c) 1995  Microsoft Corporation

Module Name:

    net\ip\rtrmgr\logtrdefs.c

Abstract:
    IP Router Manager defines for tracing and logging

Revision History:

    Gurdeep Singh Pall          6/16/95  Created

--*/

#ifndef __LOGTRDEF_H__
#define __LOGTRDEF_H__

//
// constants and macros used for tracing
//

#define SAMP_TRACE_ANY              ((DWORD)0xFFFF0000 | TRACE_USE_MASK)
#define SAMP_TRACE_ERR              SAMP_TRACE_ANY
#define SAMP_TRACE_ENTER            ((DWORD)0x00010000 | TRACE_USE_MASK)
#define SAMP_TRACE_INTF             ((DWORD)0x00020000 | TRACE_USE_MASK)
#define SAMP_TRACE_GLOBAL           ((DWORD)0x00040000 | TRACE_USE_MASK)
#define SAMP_TRACE_ROUTE            ((DWORD)0x00080000 | TRACE_USE_MASK)

#define TRACEID         g_dwTrace

#define Trace0(l,a)             \
            TracePrintfEx(TRACEID, SAMP_TRACE_ ## l, a)
#define Trace1(l,a,b)           \
            TracePrintfEx(TRACEID, SAMP_TRACE_ ## l, a, b)
#define Trace2(l,a,b,c)         \
            TracePrintfEx(TRACEID, SAMP_TRACE_ ## l, a, b, c)
#define Trace3(l,a,b,c,d)       \
            TracePrintfEx(TRACEID, SAMP_TRACE_ ## l, a, b, c, d)
#define Trace4(l,a,b,c,d,e)     \
            TracePrintfEx(TRACEID, SAMP_TRACE_ ## l, a, b, c, d, e)
#define Trace5(l,a,b,c,d,e,f)   \
            TracePrintfEx(TRACEID, SAMP_TRACE_ ## l, a, b, c, d, e, f)
#define Trace6(l,a,b,c,d,e,f,g) \
            TracePrintfEx(TRACEID, SAMP_TRACE_ ## l, a, b, c, d, e, f, g)
#define Trace7(l,a,b,c,d,e,f,g,h) \
            TracePrintfEx(TRACEID, SAMP_TRACE_ ## l, a, b, c, d, e, f, g, h)

#if DBG

#define TraceEnter(X)   TracePrintfEx(TRACEID, SAMP_TRACE_ENTER, "Entered: "X)
#define TraceLeave(X)   TracePrintfEx(TRACEID, SAMP_TRACE_ENTER, "Leaving: "X"\n")

#else   // DBG

#define TraceEnter(X)
#define TraceLeave(X)

#endif // DBG

//
// Event logging macros
//

#define LOGLEVEL        g_dwLogLevel
#define LOGHANDLE       g_hLogHandle

#define LOGERR          RouterLogError
#define LOGERRDATA      RouterLogErrorData
#define LOGWARN         RouterLogWarning
#define LOGWARNDATA     RouterLogWarningData
#define LOGINFO         RouterLogInformation
#define LOGINFODATA     RouterLogInformationData

//
// Error logging
//

//
// VOID
// Log{Err|Warn|Info}{NumArgs}(
//                             DWORD    dwLogMsgId,
//                             PSTR     pszInsertString1,
//                             ....
//                             PSTR     pszInsertString{NumArgs},
//                             DWORD    dwErrorCode
//                             );
//
// VOID
// Log{Err|Warn|Info}Data{NumArgs}(
//                                 DWORD    dwLogMsgId,
//                                 PSTR     pszInsertString1,
//                                 ....
//                                 PSTR     pszInsertString{NumArgs},
//                                 DWORD    dwDataSize,
//                                 PBYTE    pbData
//                                 );
//

#define LogErr0(msg,err)                                    \
if (LOGLEVEL >= PROTO_LOGGING_ERROR)                    \
            LOGERR(LOGHANDLE,SAMPLOG_ ## msg,0,NULL,(err))
#define LogErr1(msg,a,err) \
        if (LOGLEVEL >= PROTO_LOGGING_ERROR) \
            LOGERR(LOGHANDLE,SAMPLOG_ ## msg,1,&(a),(err))
#define LogErr2(msg,a,b,err) \
        if (LOGLEVEL >= PROTO_LOGGING_ERROR) { \
            LPSTR _asz[2] = { (a), (b) }; \
            LOGERR(LOGHANDLE,SAMPLOG_ ## msg,2,_asz,(err)); \
        }
#define LogErr3(msg,a,b,c,err) \
        if (LOGLEVEL >= PROTO_LOGGING_ERROR) { \
            LPSTR _asz[3] = { (a), (b), (c) }; \
            LOGERR(LOGHANDLE,SAMPLOG_ ## msg,3,_asz,(err)); \
        }
#define LogErr4(msg,a,b,c,d,err) \
        if (LOGLEVEL >= PROTO_LOGGING_ERROR) { \
            LPSTR _asz[4] = { (a), (b), (c), (d) }; \
            LOGERR(LOGHANDLE,SAMPLOG_ ## msg,4,_asz,(err)); \
        }

// Warning logging

#define LogWarn0(msg,err) \
        if (LOGLEVEL >= PROTO_LOGGING_WARN) \
            LOGWARN(LOGHANDLE,SAMPLOG_ ## msg,0,NULL,(err))
#define LogWarn1(msg,a,err) \
        if (LOGLEVEL >= PROTO_LOGGING_WARN) \
            LOGWARN(LOGHANDLE,SAMPLOG_ ## msg,1,&(a),(err))
#define LogWarn2(msg,a,b,err) \
        if (LOGLEVEL >= PROTO_LOGGING_WARN) { \
            LPSTR _asz[2] = { (a), (b) }; \
            LOGWARN(LOGHANDLE,SAMPLOG_ ## msg,2,_asz,(err)); \
        }
#define LogWarn3(msg,a,b,c,err) \
        if (LOGLEVEL >= PROTO_LOGGING_WARN) { \
            LPSTR _asz[3] = { (a), (b), (c) }; \
            LOGWARN(LOGHANDLE,SAMPLOG_ ## msg,3,_asz,(err)); \
        }
#define LogWarn4(msg,a,b,c,d,err) \
        if (LOGLEVEL >= PROTO_LOGGING_WARN) { \
            LPSTR _asz[4] = { (a), (b), (c), (d) }; \
            LOGWARN(LOGHANDLE,SAMPLOG_ ## msg,4,_asz,(err)); \
        }

#define LogWarnData2(msg,a,b,dw,buf) \
        if (LOGLEVEL >= PROTO_LOGGING_WARN) { \
            LPSTR _asz[2] = { (a), (b) }; \
            LOGWARNDATA(LOGHANDLE,SAMPLOG_ ## msg,2,_asz,(dw),(buf)); \
        }

#define LogWarnData5(msg,a,b,c,d,e,dw,buf) \
        if (LOGLEVEL >= PROTO_LOGGING_WARN) { \
            LPSTR _asz[5] = { (a), (b), (c), (d), (e)}; \
            LOGWARNDATA(LOGHANDLE,SAMPLOG_ ## msg,5,_asz,(dw),(buf)); \
        }

// Information logging

#define LogInfo0(msg,err) \
        if (LOGLEVEL >= PROTO_LOGGING_INFO) \
            LOGINFO(LOGHANDLE,SAMPLOG_ ## msg,0,NULL,(err))
#define LogInfo1(msg,a,err) \
        if (LOGLEVEL >= PROTO_LOGGING_INFO) \
            LOGINFO(LOGHANDLE,SAMPLOG_ ## msg,1,&(a),(err))
#define LogInfo2(msg,a,b,err) \
        if (LOGLEVEL >= PROTO_LOGGING_INFO) { \
            LPSTR _asz[2] = { (a), (b) }; \
            LOGINFO(LOGHANDLE,SAMPLOG_ ## msg,2,_asz,(err)); \
        }
#define LogInfo3(msg,a,b,c,err) \
        if (LOGLEVEL >= PROTO_LOGGING_INFO) { \
            LPSTR _asz[3] = { (a), (b), (c) }; \
            LOGINFO(LOGHANDLE,SAMPLOG_ ## msg,3,_asz,(err)); \
        }
#define LogInfo4(msg,a,b,c,d,err) \
        if (LOGLEVEL >= PROTO_LOGGING_INFO) { \
            LPSTR _asz[4] = { (a), (b), (c), (d) }; \
            LOGINFO(LOGHANDLE,SAMPLOG_ ## msg,4,_asz,(err)); \
        }

#endif // __LOGTRDEF_H__


MIB.C

/*++

Copyright (c) 1997  Microsoft Corporation

Module Name:

    sample\ip\mib.c

Abstract:
    MIB Functions exported by the Sample Routing Protocol DLL and related code

Revision History:

--*/

#include "sampinc.h"

DWORD
LocateInterface(
    DWORD               dwQueryType,
    PPROTO_MIB_QUERY    pQuery,
    ULONG               ulNumIndices,
    PPROTO_MIB_RESPONSE pResponse,
    PDWORD              pdwOutputSize
    );

//
// As mentioned in saminfo.h we do not support Creates, Sets or Deletes
//

DWORD
APIENTRY
MibCreate(
    IN DWORD dwInputSize,
    IN PVOID pInputData
    )
/*++
  Routine Description

  Arguments

  Return Value
      NO_ERROR

--*/
{
    //
    // Not supported
    //

    return NO_ERROR;
}

DWORD
APIENTRY
MibDelete(
    IN DWORD dwInputSize,
    IN PVOID pInputData
    )
/*++
  Routine Description

  Arguments

  Return Value
      NO_ERROR

--*/
{
    //
    // Not supported
    //

    return NO_ERROR;
}

DWORD
APIENTRY
MibSet(
    IN DWORD dwInputSize,
    IN PVOID pInputData
    )
/*++
  Routine Description

  Arguments

  Return Value
      NO_ERROR

--*/
{
    //
    // Not supported
    //

    return NO_ERROR;
}

DWORD
APIENTRY
MibGet(
    IN     DWORD  dwInputSize,
    IN     PVOID  pInputData,
    IN OUT PDWORD pdwOutputSize,
       OUT PVOID  pOutputData
    )
/*++
  Routine Description
      Called by an admin (SNMP) utility.  It actually passes through the
      IP Router Manager, but all that does is demux the call to the
      desired routing protocol
      We verify the parameters and return the information if we can

  Arguments

  Return Value
      ERROR_INSUFFICIENT_BUFFER
      ERROR_INVALID_PARAMETER
      ERROR_NO_DATA
      ERROR_NO_MORE_ITEMS
      ERROR_INVALID_INDEX
      NO_ERROR

--*/
{
    PPROTO_MIB_QUERY    pQuery;
    PPROTO_MIB_RESPONSE pResponse;
    ULONG               ulNumIndices;
    DWORD               dwResult;

    EnterProtocolApi();

    TraceEnter("MibGet");

    if(dwInputSize < sizeof(DWORD))
    {
        //
        // Need atleas the OID to do the get
        //

        TraceLeave("MibGet");

        ExitProtocolApi();

        return ERROR_INVALID_PARAMETER;
    }

    //
    // The input to the function is the query
    //

    pQuery      = (PPROTO_MIB_QUERY)pInputData;
    pResponse   = (PPROTO_MIB_RESPONSE)pOutputData;

    //
    // It would be really bad if someone messed the sizes up
    //

    ASSERT(dwInputSize%sizeof(DWORD) == 0);

    ulNumIndices = NUM_INDICES(dwInputSize);

    dwResult = NO_ERROR;

    switch(pQuery->dwOid)
    {
        case PROTO_MIB_GLOBAL_ID:
        {
            //
            // The output size needed is the PROTO_MIB_RESPONSE + just enough for
            // the structure we need. We dont need space for the whole union
            //

            if(*pdwOutputSize < FIELD_OFFSET(PROTO_MIB_RESPONSE, mgGlobal) + sizeof(PROTO_MIB_GLOBAL))
            {
                *pdwOutputSize = FIELD_OFFSET(PROTO_MIB_RESPONSE, mgGlobal) + sizeof(PROTO_MIB_GLOBAL);

                dwResult = ERROR_INSUFFICIENT_BUFFER;

                break;
            }

            *pdwOutputSize = FIELD_OFFSET(PROTO_MIB_RESPONSE, mgGlobal) + sizeof(PROTO_MIB_GLOBAL);

            pResponse->dwOid = PROTO_MIB_GLOBAL_ID;

            EnterCriticalSection(&g_csGlobalInfoLock);

            pResponse->mgGlobal.dwLogLevel = g_dwLogLevel;

            LeaveCriticalSection(&g_csGlobalInfoLock);

            break;
        }
        case PROTO_MIB_INTF_ID:
        {
            EnterCriticalSection(&g_csIfListLock);

            dwResult = LocateInterface(QUERY_TYPE_GET,
                                       pQuery,
                                       ulNumIndices,
                                       pResponse,
                                       pdwOutputSize);

            LeaveCriticalSection(&g_csIfListLock);

            break;
        }
        default:
        {
            dwResult = ERROR_INVALID_PARAMETER;

            break;
        }
    }

    TraceLeave("MibGet");

    ExitProtocolApi();

    return dwResult;
}

DWORD
APIENTRY
MibGetFirst(
    IN     DWORD  dwInputSize,
    IN     PVOID  pInputData,
    IN OUT PDWORD pdwOutputSize,
       OUT PVOID  pOutputData
    )
/*++
  Routine Description

  Locks

  Arguments

  Return Value
      NO_ERROR

--*/
{
    PPROTO_MIB_QUERY    pQuery;
    PPROTO_MIB_RESPONSE pResponse;
    ULONG               ulNumIndices;
    DWORD               dwResult;

    EnterProtocolApi();

    TraceEnter("MibGet");

    if(dwInputSize < sizeof(DWORD))
    {
        //
        // Need atleas the OID to do the get
        //

        TraceLeave("MibGet");

        ExitProtocolApi();

        return ERROR_INVALID_PARAMETER;
    }

    //
    // The input to the function is the query
    //

    pQuery      = (PPROTO_MIB_QUERY)pInputData;
    pResponse   = (PPROTO_MIB_RESPONSE)pOutputData;

    //
    // It would be really bad if someone messed the sizes up
    //

    ASSERT(dwInputSize%sizeof(DWORD) == 0);

    ulNumIndices = NUM_INDICES(dwInputSize);

    dwResult = NO_ERROR;

    switch(pQuery->dwOid)
    {
        case PROTO_MIB_GLOBAL_ID:
        {
            //
            // Only support GET_FIRST for tables
            //

            dwResult = ERROR_INVALID_PARAMETER;

            break;
        }
        case PROTO_MIB_INTF_ID:
        {
            EnterCriticalSection(&g_csIfListLock);

            dwResult = LocateInterface(QUERY_TYPE_GET_FIRST,
                                       pQuery,
                                       ulNumIndices,
                                       pResponse,
                                       pdwOutputSize);

            LeaveCriticalSection(&g_csIfListLock);

            break;
        }
    }

    TraceLeave("MibGet");

    ExitProtocolApi();

    return dwResult;
}

DWORD
APIENTRY
MibGetNext(
    IN     DWORD  dwInputSize,
    IN     PVOID  pInputData,
    IN OUT PDWORD pdwOutputSize,
       OUT PVOID  pOutputData
    )
/*++
  Routine Description

  Locks

  Arguments

  Return Value
      NO_ERROR

--*/
{
    PPROTO_MIB_QUERY    pQuery;
    PPROTO_MIB_RESPONSE pResponse;
    ULONG               ulNumIndices;
    DWORD               dwResult;

    EnterProtocolApi();

    TraceEnter("MibGet");

    if(dwInputSize < sizeof(DWORD))
    {
        //
        // Need atleas the OID to do the get
        //

        TraceLeave("MibGet");

        ExitProtocolApi();

        return ERROR_INVALID_PARAMETER;
    }

    //
    // The input to the function is the query
    //

    pQuery      = (PPROTO_MIB_QUERY)pInputData;
    pResponse   = (PPROTO_MIB_RESPONSE)pOutputData;

    //
    // It would be really bad if someone messed the sizes up
    //

    ASSERT(dwInputSize%sizeof(DWORD) == 0);

    ulNumIndices = NUM_INDICES(dwInputSize);

    dwResult = NO_ERROR;

    switch(pQuery->dwOid)
    {
        case PROTO_MIB_GLOBAL_ID:
        {
            //
            // Only support GET_NEXT for tables
            //

            dwResult = ERROR_INVALID_PARAMETER;

            break;
        }
        case PROTO_MIB_INTF_ID:
        {
            EnterCriticalSection(&g_csIfListLock);

            dwResult = LocateInterface(QUERY_TYPE_GET_FIRST,
                                       pQuery,
                                       ulNumIndices,
                                       pResponse,
                                       pdwOutputSize);

            LeaveCriticalSection(&g_csIfListLock);

            break;
        }
    }

    TraceLeave("MibGet");

    ExitProtocolApi();

    return dwResult;
}

DWORD
LocateInterface(
    DWORD               dwQueryType,
    PPROTO_MIB_QUERY    pQuery,
    ULONG               ulNumIndices,
    PPROTO_MIB_RESPONSE pResponse,
    PDWORD              pdwOutputSize
    )
/*++
  Routine Description
      This locates the correct interface for the MIB query

  Locks
      The IF List lock should be held

  Arguments
      dwQueryType
      pQuery
      ulNumIndices
      pResponse
      pdwOutputSize

  Return Value
      ERROR_NO_DATA             If there are no interfaces
      ERROR_INVALID_PARAMETER   If the index is bad
      ERROR_INVALID_INDEX       If the index does not exist
      ERROR_NO_MORE_ITEMS       If there are no more interfaces. Unlike SNMP
                                we dont walk to the next variable. This keeps
                                our code easy, and doesnt take away any functionality
                                since the NT SNMP agent will try the next variable
                                automatically on getting this error
      NO_ERROR

--*/
{
    PNT_IF          pIf, pRetIf;
    PINTRNL_IF      pBind;
    BOOL            bModified,bNext;
    PLIST_ENTRY     pleNode;
    DWORD           dwResult,dwIndex,i;
    PPROTO_MIB_INTF pMibIf;

    TraceEnter("LocateInterface");

    if(IsListEmpty(&g_leIfListHead))
    {
        TraceLeave("LocateInterface");

        return ERROR_NO_DATA;
    }

    if((ulNumIndices < 1) && (dwQueryType == QUERY_TYPE_GET))
    {
        //
        // For a get we need the index
        //

        TraceLeave("LocateInterface");

        return ERROR_INVALID_PARAMETER;
    }

    if(dwQueryType == QUERY_TYPE_GET_FIRST)
    {
        //
        // Since the list is ordered
        //

        pIf = CONTAINING_RECORD(g_leIfListHead.Flink, NT_IF, leNtIfLink);

        if(*pdwOutputSize < FIELD_OFFSET(PROTO_MIB_RESPONSE,miIntf) +
                             SIZEOF_MIB_INTF_INFO(pIf->ulNumBindings))
        {

            *pdwOutputSize = FIELD_OFFSET(PROTO_MIB_RESPONSE,miIntf) +
                SIZEOF_MIB_INTF_INFO(pIf->ulNumBindings);

            TraceLeave("LocateInterface");

            return ERROR_INSUFFICIENT_BUFFER;
        }
    }

    //
    // Now a little magic to figure out which interface to return.
    // We initialize the index to 0. If we have not gotten an index we
    // set th bModified to TRUE, meaning the index has been initialized
    // and we have not actually got a 0.
    //

    if(ulNumIndices > 0)
    {
        dwIndex     = pQuery->rgdwIndex[0];
        bModified   = FALSE;
    }
    else
    {
        dwIndex     = 0;
        bModified   = TRUE;
    }

    //
    // Now we will take the next interface if the query type is GET_NEXT
    // and the index was not modified. Otherwise we will pick the interface
    // == dwIndex
    //

    bNext = ((dwQueryType == QUERY_TYPE_GET_NEXT) && (bModified == FALSE));

    pRetIf   = NULL;
    dwResult = ERROR_NO_MORE_ITEMS;

    for(pleNode  = g_leIfListHead.Flink;
        pleNode != &g_leIfListHead;
        pleNode  = pleNode->Flink)
    {
        pIf = CONTAINING_RECORD(pleNode,
                                NT_IF,
                                leNtIfLink);

        if((dwIndex == pIf->dwNtIndex) && !bNext)
        {
            //
            // Found an if with matchin index and we were not
            // meant to get the "next" one
            //

            pRetIf = pIf;

            break;
        }

        if(dwIndex < pIf->dwNtIndex)
        {
            if(dwQueryType == QUERY_TYPE_GET_NEXT)
            {
                //
                // Found the first one greater than the given
                // and the query type is GET_NEXT
                //

                pRetIf = pIf;

                break;
            }
            else
            {
                //
                // We were meant to get an exact match but
                // since the list is ordered, we wont find it now
                //

                dwResult = ERROR_INVALID_INDEX;
            }
        }
    }

    if(pRetIf != NULL)
    {
        //
        // So we did find an interface
        //

        if(*pdwOutputSize < FIELD_OFFSET(PROTO_MIB_RESPONSE,miIntf) +
                             SIZEOF_MIB_INTF_INFO(pRetIf->ulNumBindings))
        {

            *pdwOutputSize = FIELD_OFFSET(PROTO_MIB_RESPONSE,miIntf) +
                SIZEOF_MIB_INTF_INFO(pRetIf->ulNumBindings);

            dwResult = ERROR_INSUFFICIENT_BUFFER;
        }
        else
        {
            //
            // So we found the interface AND have the space
            //

            *pdwOutputSize = FIELD_OFFSET(PROTO_MIB_RESPONSE,miIntf) +
                SIZEOF_MIB_INTF_INFO(pRetIf->ulNumBindings);

            pResponse->dwOid = PROTO_MIB_INTF_ID;

            pMibIf = (PPROTO_MIB_INTF)(&(pResponse->miIntf));

            pMibIf->dwIfIndex     = pRetIf->dwNtIndex;
            pMibIf->ulNumAddress  = pRetIf->ulNumBindings;

            for(pleNode  = pRetIf->leInternalIfHead.Flink, i = 0;
                pleNode != &pIf->leInternalIfHead;
                pleNode  = pleNode->Flink, i++)
            {
                pBind = CONTAINING_RECORD(pleNode, INTRNL_IF, leInternalIfLink);

                pMibIf->rgmbBindInfo[i].dwAddress = pBind->dwAddress;
                pMibIf->rgmbBindInfo[i].dwMask    = pBind->dwMask;
                pMibIf->rgmbBindInfo[i].bEnabled  = pBind->bEnabled;
            }

            ASSERT(i == pRetIf->ulNumBindings);

            dwResult = NO_ERROR;
        }
    }

    TraceLeave("LocateInterface");

    return dwResult;
}


API.H

#ifndef __SAMPLE_API_H__
#define __SAMPLE_API_H__

DWORD
APIENTRY
RegisterProtocol(
    IN OUT PMPR_ROUTING_CHARACTERISTICS pRoutingChar,
    IN OUT PMPR_SERVICE_CHARACTERISTICS pServiceChar
    );

DWORD
APIENTRY
StartProtocol(
    IN HANDLE hMgrNotifyEvent,
    IN PSUPPORT_FUNCTIONS pSupportFunctions,
    IN PVOID pConfig
    );

DWORD
APIENTRY
StopProtocol(
    VOID
    );

DWORD
APIENTRY
GetGlobalInfo(
    IN OUT PVOID pConfig,
    IN OUT PDWORD pdwSize
    );

DWORD
APIENTRY
SetGlobalInfo(
    IN PVOID pConfig
    );

DWORD
APIENTRY
AddInterface(
    IN PWCHAR pwszInterfaceName,
    IN DWORD dwIndex,
    IN NET_INTERFACE_TYPE dwIfType,
    IN PVOID pConfig
    );

DWORD
APIENTRY
DeleteInterface(
    IN DWORD dwIndex
    );

DWORD
APIENTRY
GetEventMessage(
    OUT ROUTING_PROTOCOL_EVENTS *pEvent,
    OUT MESSAGE *pResult
    );

DWORD
APIENTRY
GetInterfaceConfigInfo(
    IN DWORD dwIndex,
    IN OUT PVOID pConfig,
    IN OUT PDWORD pdwSize
    );

DWORD
APIENTRY
SetInterfaceConfigInfo(
    IN DWORD dwIndex,
    IN PVOID pConfig
    );

DWORD
APIENTRY
BindInterface(
    IN DWORD dwIndex,
    IN PVOID pBinding
    );

DWORD
APIENTRY
UnBindInterface(
    IN DWORD dwIndex
    );

DWORD
APIENTRY
EnableInterface(
    IN DWORD dwIndex
    );

DWORD
APIENTRY
DisableInterface(
    IN DWORD dwIndex
    );

DWORD
APIENTRY
DoUpdateRoutes(
    IN DWORD dwIndex
    );

DWORD
APIENTRY
MibCreate(
    IN DWORD dwInputSize,
    IN PVOID pInputData
    );

DWORD
APIENTRY
MibDelete(
    IN DWORD dwInputSize,
    IN PVOID pInputData
    );

DWORD
APIENTRY
MibGet(
    IN DWORD dwInputSize,
    IN PVOID pInputData,
    IN OUT PDWORD pdwOutputSize,
    OUT PVOID pOutputData
    );

DWORD
APIENTRY
MibSet(
    IN DWORD dwInputSize,
    IN PVOID pInputData
    );

DWORD
APIENTRY
MibGetFirst(
    IN DWORD dwInputSize,
    IN PVOID pInputData,
    IN OUT PDWORD pdwOutputSize,
    OUT PVOID pOutputData
    );

DWORD
APIENTRY
MibGetNext(
    IN DWORD dwInputSize,
    IN PVOID pInputData,
    IN OUT PDWORD pdwOutputSize,
    OUT PVOID pOutputData
    );

#endif // __SAMPLE_API_H__


SAMINFO.H

/*++

Copyright (c) 1997  Microsoft Corporation

Module Name:

    sample\saminfo.h

Abstract:
    Public defines and structures exposed by the Sample Routing Protocol DLL

Revision History:

--*/

#ifndef __SAMPLE_INFO_H__
#define __SAMPLE_INFO_H__

#define ANY_SIZE    1

#define SAMPLE_PROTOCOL_NAME        "Sample IP Routing Protocol"

//
// The IP Protocol number for this protocol. This is assuming that this
// protocol runs over Raw IP
//

#define SAMPLE_PROTOCOL_ROUTE_ID    100

//
// Assuming this is an IETF protocol with protocol ID 200
//

#define SAMPLE_PROTOCOL_IP_ID   \
    PROTOCOL_ID(PROTO_TYPE_UNICAST, 0, 0, 200)

//////////////////////////////////////////////////////////////////////////////
//                                                                          //
// It is highly recommended that implementors provide their own versioning  //
// scheme.  This sample uses information structures whose first DWORD is    //
// the version number.  This allows extensibility in the future             //
//                                                                          //
//////////////////////////////////////////////////////////////////////////////

#define SAMPLE_PROTOCOL_VERSION     0x00000001

#define SAMPLE_PROTOCOL_MULTICAST_GROUP1    ((DWORD)0x640000E0) // 224.0.0.100
#define SAMPLE_PROTOCOL_MULTICAST_GROUP2    ((DWORD)0x650000E0) // 224.0.0.101

//////////////////////////////////////////////////////////////////////////////
//                                                                          //
// The Global Info                                                          //
//                                                                          //
//////////////////////////////////////////////////////////////////////////////

#define PROTO_LOGGING_NONE  ((DWORD) 0)
#define PROTO_LOGGING_ERROR ((DWORD) 1)
#define PROTO_LOGGING_WARN  ((DWORD) 2)
#define PROTO_LOGGING_INFO  ((DWORD) 3)

typedef struct _SAMPLE_PROTOCOL_GLOBAL_INFO
{
    DWORD   dwVersion;
    DWORD   dwLogLevel;
}SAMPLE_PROTOCOL_GLOBAL_INFO, *PSAMPLE_PROTOCOL_GLOBAL_INFO;

//////////////////////////////////////////////////////////////////////////////
//                                                                          //
// An interface an have mutliple addresses. The Sample Routing Protocol     //
// allows the admin. to configure parameters for each address.              //
// Each Address (or rather <address,mask>) is called a BINDING - for        //
// reasons that will be obvious in the code.  When there are multiple       //
// bindings on an interface, then the IP Addresses are known before         //
// hand and do not change while the router is running.  If there is         //
// only one address, it may or may not be known before the router           //
// starts, hence is assumed to be 0.0.0.0. The actual address only          //
// becomes available when the IP Router Manager calls the protocol's        //
// BindInterface() function.  Also such address MAY be dynamic - we         //
// say MAY because if the interface has a single static IP Address,         //
// it will not change while the router is running. On the other hand,       //
// if the interface has a DHCP address (which can only be a SINGLE          //
// address) then an ipconfig /release /renew could change the address       //
// while the router is running. Since by looking at a single IP address     //
// we can not say whether it is dynamic or not, we say that it MAY          //
// change.                                                                  //
// This fact (that address's change) has a bearing on SNMP MIB indices.     //
// If the interface has a single IP address, your protocol should be        //
// able to handle the information being indexed via the actual address      //
// or via 0.0.0.0.  This means that the ADDRESS BY ITSELF CAN NOT BE        //
// AN INDEX.  That is, the interface index MUST be one of the indices       //
//                                                                          //
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//                                                                          //
// The only information per binding is whether it is enabled or not         //
// Hey, this is a SAMPLE, what did you expect - OSPF?                       //
//                                                                          //
//////////////////////////////////////////////////////////////////////////////

typedef struct _BINDING_INFO
{
    DWORD   dwAddress;
    DWORD   dwMask;
    BOOL    bEnabled;
}BINDING_INFO, *PBINDING_INFO;

//////////////////////////////////////////////////////////////////////////////
//                                                                          //
// So the per interface information consists of the information             //
// that applies across bindings and information for each binding            //
//                                                                          //
//////////////////////////////////////////////////////////////////////////////

typedef struct _SAMPLE_PROTOCOL_INTERFACE_INFO
{
    DWORD           dwVersion;
    ULONG           ulNumBindings;
    BINDING_INFO    rgbiInfo[ANY_SIZE];
}SAMPLE_PROTOCOL_INTERFACE_INFO, *PSAMPLE_PROTOCOL_INTERFACE_INFO;

#define SIZEOF_PROTO_IF_INFO(X)                                     \
    (FIELD_OFFSET(SAMPLE_PROTOCOL_INTERFACE_INFO, rgbiInfo[0])  +   \
     ((X) * sizeof(BINDING_INFO)))

//////////////////////////////////////////////////////////////////////////////
//                                                                          //
// MIB related information                                                  //
//                                                                          //
// The Sample Routing Protocol only supports queries (Get/GetFirst/GetNext) //
// via the MIB calls. No Sets/Deletes/Creates are supported.  The           //
// SetInterface/GlobalInfo() calls should be used for that.  The reason     //
// is to avoid exposing two different interfaces for sets since keeping     //
// those in synch is generally difficult. This is a VERY GOOD guideline to  //
// follow.  EITHER DONT ALLOW ANY SETS THROUGH MIB CALLS OR ONLY ALLOW      //
// THOSE VARIABLES TO BE SET THAT ARE NOT SET VIA INTERFACE/GLOBAL INFO     //
// CALLS.  A case in point being the TTL field exposed via MIB-II. The      //
// IP Router Manager allows this field to be set since it is not exposed    //
// via the other calls.  IN GENERAL USE THE MIB CALLS FOR STATISTICS.       //
// This will make coding the admin DLL for the protocol a lot easier.       //
//                                                                          //
//////////////////////////////////////////////////////////////////////////////

#define PROTO_MIB_GLOBAL_ID     0
#define PROTO_MIB_INTF_ID       PROTO_MIB_GLOBAL_ID + 1

//////////////////////////////////////////////////////////////////////////////
//                                                                          //
// The following structure is used to query the MIB. The Oid field is       //
// one of the IDs #defined above.  The index is stored in a variable        //
// sized array.  All the indices are DWORDs for this protocol.  This is     //
// generally a good thing.  It wouldn't be the brightest thing to have      //
// a string as an index.                                                    //
// The number of indices are calculated from the size of the query          //
// structure that is passed in to the MibXXX calls.                         //
//                                                                          //
//////////////////////////////////////////////////////////////////////////////

typedef struct _PROTO_MIB_QUERY
{
    DWORD   dwOid;
    DWORD   rgdwIndex[ANY_SIZE];
}PROTO_MIB_QUERY, *PPROTO_MIB_QUERY;

//////////////////////////////////////////////////////////////////////////////
//                                                                          //
// Macro to figure out the number of indices given the query size           //
//                                                                          //
//////////////////////////////////////////////////////////////////////////////

#define NUM_INDICES(X)  ((X)/sizeof(DWORD) - 1)

//////////////////////////////////////////////////////////////////////////////
//                                                                          //
// The following structures are the information returned by the protocol in //
// response to a MIB query.  Currently they are the same as the             //
// Interface/Global structures. However if this were a real protocol, the   //
// statistics would go in here and would not be present in the other        //
// structures.                                                              //
//                                                                          //
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//                                                                          //
// The Global info is a scalar and has no index.  Unlike SNMP we dont       //
// expect a scalar to be indexed by 0                                       //
//                                                                          //
//////////////////////////////////////////////////////////////////////////////

typedef struct _PROTO_MIB_GLOBAL
{
    DWORD   dwLogLevel;
}PROTO_MIB_GLOBAL, *PPROTO_MIB_GLOBAL;

//////////////////////////////////////////////////////////////////////////////
//                                                                          //
// The Interface info is indexed by the ifIndex                             //
//                                                                          //
//////////////////////////////////////////////////////////////////////////////

typedef struct _MIB_BIND
{
    DWORD   dwAddress;
    DWORD   dwMask;
    BOOL    bEnabled;
}MIB_BIND, *PMIB_BIND;

typedef struct _PROTO_MIB_INTF
{
    DWORD       dwIfIndex;
    ULONG       ulNumAddress;
    MIB_BIND    rgmbBindInfo[ANY_SIZE];
}PROTO_MIB_INTF, *PPROTO_MIB_INTF;

#define SIZEOF_MIB_INTF_INFO(X)     \
    (FIELD_OFFSET(PROTO_MIB_INTF, rgmbBindInfo[0])  + \
     ((X) * sizeof(MIB_BIND)))

typedef struct _PROTO_MIB_RESPONSE
{
    DWORD   dwOid;
    union
    {
        PROTO_MIB_GLOBAL    mgGlobal;
        PROTO_MIB_INTF      miIntf;
    };
}PROTO_MIB_RESPONSE, *PPROTO_MIB_RESPONSE;

#endif // __SAMPLE_INFO_H__


SAMPINC.H

/*++

Copyright (c) 1995  Microsoft Corporation

Module Name:

    net\ip\rtrmgr\allinc.h

Abstract:
    IP Router Manager header for all includes

Revision History:

    Gurdeep Singh Pall          6/8/95  Created

--*/

#ifndef __PCHSAMP_H__
#define __PCHSAMP_H__

#define _WINSOCKAPI_

#include <windef.h>
#include <windows.h>
#include <winnt.h>
#include <winbase.h>

#undef _WINSOCKAPI_

#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
#include <TCHAR.H>
#include <rtutils.h>
#include <routprot.h>
#include <mprerror.h>
#include <rtm.h>
#include <winsock2.h>
#include <ws2tcpip.h>

#include "defs.h"
#include "saminfo.h"
#include "iprtsamp.h"
#include "logtrdef.h"
#include "extern.h"
#include "samplog.h"
#include "api.h"
#endif // __PCHSAMP_H__


THREAD.C

/*++

Copyright 1997 - 1998 Microsoft Corporation

Module Name:

    sample\ip\thread.c

Abstract:
    Functions related to the main processing thread

Revision History:

--*/

#include "sampinc.h"

VOID
Shutdown();

VOID
ProcessRouteChange();

VOID
ProcessReadNotification();

VOID
CleanupProtocol(
    BOOL bWinsockLoaded
    );

DWORD WINAPI
MainThread(
    PVOID   pvContext
    )
/*++
  Routine Description
      This is the main thread created by the protocol.  In general, an
      implementor should avoid creating too many threads.  A dedicated
      thread should only be created if there is regular work that the
      protocol needs to be doing.  If the protocol is more "interrupt driven",
      it is much better to use the WorkerFunction routines provided in
      rtutils.h
      The

  Arguments
      pvContext     Unused

  Return Value
      NO_ERROR

--*/
{
    DWORD   dwRet, dwTimeOut;
    ULONG   ulPoll;
    HANDLE  hOwnModule;
    HANDLE  rgWaitObjects[NUM_WAIT_EVENTS] = {g_hStopProtocolEvent,
                                              g_hRtmEvent,
                                              g_hSocketEvent};

    //
    // The parameter is never used. The following macro
    // kills lint/compiler warnings
    //

    UNREFERENCED_PARAMETER(pvContext);

    //
    // We dont need to EnterProtocolApi() for this thread
    // since this is the thread that shuts the DLL down
    //

    //
    // Now we try and load ourself. This is an NT trick to stop the OS loader
    // from cleaning out the DLL from under you after you have SetEvent()
    // Doing SetEvent() may cause another thread to execute which may then
    // unload you. However you still have some epilogue code after the SetEvent()
    // that needs to be executed.  Putting a ref count on your DLL is a
    // way of stopping this from happening
    //

    hOwnModule  = NULL;

    hOwnModule  = LoadLibraryEx("IPRTSAMP.DLL",
                                NULL,
                                0);

    if(hOwnModule == NULL)
    {
        Trace1(ERR,
               "MainThread: Unable to load itself. May cause problems at stop. Error %d",
               GetLastError());
    }

    //
    // We initially block on the wait call. When we are told
    // to stop, we go into a polling mode
    //

    dwTimeOut = INFINITE;
    ulPoll    = 0;

    while(TRUE)
    {
        dwRet = WaitForMultipleObjectsEx(NUM_WAIT_EVENTS,
                                        rgWaitObjects,
                                        FALSE,
                                        dwTimeOut,
                                        TRUE);

        switch(dwRet)
        {
            case WAIT_IO_COMPLETION:
            {
                break;
            }

            case WAIT_OBJECT_0: // Stop Protocol Event
            {
                //
                // See if the number of threads in the DLL are down to 0
                //

                EnterCriticalSection(&g_csProtocolStateLock);

                if(g_dwProtocolRefCount > 0)
                {
                    //
                    // There is someone other than us in the DLL.
                    // We go into polling mode
                    //

                    dwTimeOut = 1000;

                    LeaveCriticalSection(&g_csProtocolStateLock);

                    break;
                }

                Shutdown();

                LeaveCriticalSection(&g_csProtocolStateLock);

                break;
            }

            case (WAIT_OBJECT_0 + 1): // RTM Event
            {
                ProcessRouteChange();

                break;
            }

            case (WAIT_OBJECT_0 + 2): // Socket Event
            {
                ProcessReadNotification();

                break;
            }

            case WAIT_TIMEOUT:
            {
                EnterCriticalSection(&g_csProtocolStateLock);

                if(g_dwProtocolRefCount > 0)
                {
                    ulPoll++;

                    if(ulPoll > 5)
                    {
                        //
                        // 5 seconds have gone by but there are still
                        // threads executing in our DLL. This is usually
                        // a sign of deadlock, or infinite looping
                        //

                        Trace1(ERR,
                               "MainThread: Ref count is not decreasing. Count is %d",
                               g_dwProtocolRefCount);
                    }

                    LeaveCriticalSection(&g_csProtocolStateLock);

                    break;
                }

                Shutdown();

                LeaveCriticalSection(&g_csProtocolStateLock);

                break;
            }
        }
    }

    if(hOwnModule)
    {
        //
        // This is an atomic call that frees the library and
        // returns from the thread.
        //

        FreeLibraryAndExitThread(hOwnModule,
                                 NO_ERROR);
    }

    return NO_ERROR;
}

VOID
Shutdown()
/*++
  Routine Description

  Arguments

  Return Value
      NO_ERROR

--*/
{

    TraceEnter("Shutdown");

    CleanupProtocol(TRUE);

    g_pmmStopMsg->rreEvent = ROUTER_STOPPED;

    EnterCriticalSection(&g_csQueueLock);

    InsertHeadList(&g_leMsgQueue,
                   &g_pmmStopMsg->leMsgLink);

    LeaveCriticalSection(&g_csQueueLock);

    g_dwProtocolState = PROTOCOL_STATE_STOPPED;

    SetEvent(g_hMgrNotifyEvent);

    TraceLeave("ShutDown");
}

VOID
ProcessRouteChange()
{
    DWORD   dwResult, dwFlags;

    RTM_IP_ROUTE ripCurr, ripPrev;

    dwResult = RtmDequeueRouteChangeMessage(g_hRtmHandle,
                                            &dwFlags,
                                            &ripCurr,
                                            &ripPrev);

    if(dwResult != NO_ERROR)
    {
        Trace1(ERR,
               "ProcessRouteChange: Error %d dequeueing message from RTM",
               dwResult);

        return;
    }

    switch(dwFlags)
    {
        case RTM_ROUTE_ADDED:
        {
            Trace0(ROUTE,
                   "ProcessRouteChange: New route was added");

            PrintRoute(ROUTE, &ripCurr);

            break;
        }

        case RTM_ROUTE_DELETED:
        {
            Trace0(ROUTE,
                   "ProcessRouteChange: Old route was deleted");

            PrintRoute(ROUTE, &ripPrev);

            break;
        }

        case RTM_ROUTE_CHANGED:
        {
            Trace0(ROUTE,
                   "ProcessRouteChange: A best route was changed");

            Trace0(ROUTE,
                   "Old route--");

            PrintRoute(ROUTE, &ripPrev);

            Trace0(ROUTE,
                   "New route--");

            PrintRoute(ROUTE, &ripCurr);

            break;
        }
    }
}

VOID
ProcessReadNotification()
{
    PNT_IF          pIf;
    PINTRNL_IF      pBind;
    PRECEIVE_BUFFER prbBuffer;
    DWORD           dwFlags,dwBytesRead;
    INT             iRecvAddrLen;
    PLIST_ENTRY     pleNode1, pleNode2;

    WSANETWORKEVENTS    wsaNetworkEvents;
    struct sockaddr_in  siFrom;

    EnterCriticalSection(&g_csIfListLock);

    for(pleNode1  = g_leIfListHead.Flink;
        pleNode1 != &g_leIfListHead;
        pleNode1  = pleNode1->Flink)
    {
        pIf = CONTAINING_RECORD(pleNode1,
                                NT_IF,
                                leNtIfLink);

        if(!IsNtEnabledAndBound(pIf))
        {
            continue;
        }

        for(pleNode2  = pIf->leInternalIfHead.Flink;
            pleNode2 != &pIf->leInternalIfHead;
            pleNode2  = pleNode2->Flink)
        {
            pBind = CONTAINING_RECORD(pleNode2, INTRNL_IF, leInternalIfLink);

            if(pBind->sSocket == INVALID_SOCKET)
            {
                ASSERT(pBind->dwState != BINDING_UP);

                continue;
            }
        }

        if(WSAEnumNetworkEvents(pBind->sSocket,
                                (WSAEVENT)NULL,
                                &wsaNetworkEvents) == SOCKET_ERROR)
        {
            Trace1(ERR,
                   "ProcessRead: WSAEnumNetworkEvents() returned %d",
                   WSAGetLastError());

            continue;
        }

        if(!(wsaNetworkEvents.lNetworkEvents & FD_READ))
        {
            //
            // Read bit isnot set and we arent interested in
            // anything else
            //

            continue;
        }

        if(wsaNetworkEvents.iErrorCode[FD_READ_BIT] != NO_ERROR)
        {
            Trace2(ERR,
                   "ProcessRead: Error %d associated with socket %d.%d.%d.%d for FD_READ",
                   wsaNetworkEvents.iErrorCode[FD_READ_BIT],
                   PRINT_ADDRESS(pBind->dwAddress));

            continue;
        }

        //
        // Create a buffer to receive data
        //

        prbBuffer = HeapAlloc(g_hPrivateHeap,
                              0,
                              sizeof(RECEIVE_BUFFER));

        if(prbBuffer == NULL)
        {
            Trace1(ERR,
                   "ProcessRead: Error %d allocating memory for read buffer",
                   GetLastError());

            continue;
        }

        prbBuffer->wbBuffer.buf = prbBuffer->rgbyData;

        iRecvAddrLen = sizeof(struct sockaddr_in);

        if(WSARecvFrom(pBind->sSocket,
                       &(prbBuffer->wbBuffer),
                       1,
                       &dwBytesRead,
                       &dwFlags,
                       (struct sockaddr *)&siFrom,
                       &iRecvAddrLen,
                       NULL,
                       NULL) != NO_ERROR)
        {
            Trace1(ERR,
                   "ProcessRead. Error %d reading data",
                   WSAGetLastError());

            HeapFree(g_hPrivateHeap,
                     0,
                     prbBuffer);

            continue;
        }

        //ValidateAndQueuePacket();

    }

}


APING.C

/*****************************************************************************
 *
 *  MODULE NAME: APING.C
 *
 *  COPYRIGHTS:
 *             This module contains code made available by IBM
 *             Corporation on an AS IS basis.  Any one receiving the
 *             module is considered to be licensed under IBM copyrights
 *             to use the IBM-provided source code in any way he or she
 *             deems fit, including copying it, compiling it, modifying
 *             it, and redistributing it, with or without
 *             modifications.  No license under any IBM patents or
 *             patent applications is to be implied from this copyright
 *             license.
 *
 *             A user of the module should understand that IBM cannot
 *             provide technical support for the module and will not be
 *             responsible for any consequences of use of the program.
 *
 *             Any notices, including this one, are not to be removed
 *             from the module without the prior written consent of
 *             IBM.
 *
 *  AUTHOR:    Peter J. Schwaller
 *             VNET:     PJS at RALVM6           Tie Line: 444-4376
 *             Internet: pjs@ralvm6.vnet.ibm.com     (919) 254-4376
 *
 *  FUNCTION:  Perform an echo test to a specified destination.
 *             APING can be used when you are first installing APPC on
 *             your computer to make sure you can connect to another
 *             computer in the network.  APING can also be used to
 *             get an estimate of the delay time or throughput to another
 *             computer in the network.
 *
 *  AVAILABILITY:
 *             These sample programs and source are also available on
 *             CompuServe through the APPC Information Exchange.  To get
 *             to the APPC forum just type 'GO APPC' from any CompuServe
 *             prompt.  The samples are available in the Sample Programs
 *             library section.  Just search on the keyword CPICPGMS to
 *             find all the samples in this series.
 *
 *             Updates for the sample programs and support for many more
 *             CPI-C platforms will also be made available on CompuServe.
 *
 *  RELATED FILES:
 *             See APING.DOC for detailed information.
 *
 *  PORTABILIITY NOTES:
 *             The APING client program is completely portable.  In fact,
 *             all of the source modules can be compiled without #define-ing
 *             any platform constant value.
 *
 *             To take advantage of a performance optimization on the
 *             OS/2 platform, the alloc_cpic_buffer() is used.  If the
 *             OS/2 platform is specified (#define of OS2, FAPI, or OS2_20)
 *             alloc_cpic_buffer() will return a shared memory buffer.
 *             If not, a buffer allocated with malloc() will be returned.
 *
 *             If you are porting to a platform that can take advantage
 *             of a specially allocated memory buffer, you should
 *             add this support to the alloc_cpic_buffer() routine in the
 *             CPICPORT.C file.
 *
 *             The get_time() routine is used to determine the current time,
 *             which is in turn used to calculate the elapsed time of the
 *             data transfer.  The get_time() routine is used to hide
 *             differences between operating system timer facilties that
 *             are available.  Since the C library routine to query the
 *             current time only returns the time in seconds, it is
 *             advantageous to return the current time in milliseconds
 *             if it is available.
 *
 *             If you are porting to a platform that support a timer
 *             with a resolution better than one second, you should
 *             modify the get_time() routine in the CPICPORT.C file.
 *
 *  CHANGE HISTORY:
 *  Date       Description
 *  06/15/92   NS/DOS accepts version 2.02 into system test.
 *  08/05/92   Version 2.31 released to CompuServe
 *             This version was also distributed at the APPC/APPN Platform
 *             Developer's Conference held in Raleigh, NC.
 *  08/13/92   Changed all printf and fprintf calls to use a write_*() call.
 *  08/17/92   Made security handling code conditionally compiled.
 *  08/23/92   Removed call to cpicinit_default_destination().
 *  08/24/92   Version 2.32 released to CompuServe.
 *  08/28/92   Changed message about invalid size parm to go to write_error().
 *  09/08/92   Added check for buffer==NULL.  Initialized block to zeros.
 *  09/22/92   Version 2.33 released to CompuServe.
 *  11/17/92   Added support for printing remote operating system name.
 *             Version 2.34 released to CompuServe
 *  01/07/93   Version 2.35
 *             Fixed a number of problems when compiling with IBM C Set/2
 *                password input was displayed
 *                timer resolution was 1 second
 *
 *****************************************************************************/

/*****************************************************************************
 * OVERVIEW of APING CPI-C Flows
 *
 *   Client (APING)                      Server (APINGD)
 *   --------------                      ---------------
 * Set up conversation
 *   Allocate       -------------------> Accept Conversation
 * Exchange Version Numbers
 *   Send Data      -------------------> Receive
 *   Receive        <------------------- Send Data
 * For number of iterations (i)
 *  For number of consecutive packets (c)
 *     Send Data    -------------------> Receive
 *  For number of consecutive packets (c)
 *     Receive      <------------------- Send Data
 * Shut down the conversation
 *   Deallocate (FLUSH)
 *****************************************************************************/

#if defined(WIN32) || defined(WINDOWS)                                 /*WIN32*/
#include <windows.h>                                                   /*WIN32*/
#endif                                                                 /*WIN32*/

#include "wincpic.h"

/* Set up constant declarations */
#include "cpicdefs.h"

/* Collection of routines with special ported version for each platform */
#include "cpicport.h"

/* standard C include files */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* CPI-C error handling routines */
/* This file is supplied with APING */
#include "cpicerrs.h"

/* CPI-C conversation startup routines */
/* This file is supplied with APING */
#include "cpicinit.h"

/* Argument processing procedure */
/* This file is supplied with APING */
#include "getopt.h"

/* CPI-C error handling info */
CPICERR * cpicerr;

/*
 * Max size of a data buffer.  This is the largest size buffer that can
 * be specified on a call to CPI-C.
 */
#define  MAX_SIZE (32763)

/* These are the defaults to be used if the user does not provide arguments  */
/* to override these values.                                                 */
#define  DEFAULT_TP_NAME   "APINGD"
#define  DEFAULT_MODE_NAME "#INTER"
#define  DEFAULT_SYM_DEST  "APINGD"

/* Define these here so we can make changes throughout the code. */
/*
 * The PROGRAM_INFO string should be kept in sync with the
 * MAJOR_VERSION and MINOR_VERSION constants.  Although the
 * cpicerr_exchange_version() call will support values up to 255,
 * values for MINOR_VERSION should be from 00-99 to maintain the
 * two character format in the version string.
 */
#define  PROGRAM_NAME      "APING"
#define  PROGRAM_INFO      "version 2.35"
#define  MAJOR_VERSION     (2)
#define  MINOR_VERSION     (35)
#define  LOG_FILE_NAME     "aping.err"
#define  LOG_FILE_PATH     "$LOGPATH"

/*
 * Define the first APING version to support one way data transfer.
 * (Version 2.02).  After we exchange version numbers, we can determine
 * whether or not our partner can handle one way data or not.
 */
#define  ONEWAY_MAJOR_VERSION    (2)
#define  ONEWAY_MINOR_VERSION    (2)

typedef struct flags {
    int    one_way_flag;                    /* Was one way data requested    */
                                            /* value of 2 means two way echo */
                                            /* value of 1 means one way      */
    unsigned long size;                     /* size of data sends            */
    unsigned long number_iterations;        /* times through the loop        */
    unsigned long number_concurrent;        /* sends by each side per loop   */
} FLAGS;

/* function prototypes for procedures in this file */
void     do_aping(int argc,
                  char *argv[]);
void     process_arguments(int argc,
                           char *argv[],
                           CPICINIT * cpicinit,
                           FLAGS * flags);

/*
 * Message displayed with show_info() when APING is started.
 */
char * intro[] = {
    PROGRAM_NAME " " PROGRAM_INFO " APPC echo test with timings.",
    "  by Peter J. Schwaller (pjs@ralvm6.vnet.ibm.com)",
    NULL
    };

/*
 * Message displayed with show_info() when usage information is requested or
 * after an invalid flag was specified.
 */
char * usage[] = {
    "",
    "APING [flags] destination",
    "  destination",
    "\tmay be either a symbolic destination name or a partner LU name",
    "  -m mode_name",
    "\tmode name (default: " DEFAULT_MODE_NAME ")",
    "  -t tp_name",
    "\tthe TP to start on the server (default: " DEFAULT_TP_NAME ")",
    "  -s N",
    "\tN is the size of the packet transmitted (default: 100 bytes)",
    "  -i N",
    "\tN is the number of iterations (default: 2)",
    "  -c N",
    "\tN is the number of consecutive packets sent by each side (default: 1)",
    "  -1",
    "\tOnly send data from client to server (No echo)",
#if defined(SUPPORTS_SETTING_SECURITY)
    "  -u userid",
    "  -p password",
    "\tSecurity parameters.  If a userid is specified without a password,",
    "\tyou will be prompted for the password.",
    "  -n",
    "\tDo not use any security (SECURITY=NONE).",
#endif
    "",
    "The minimum time, maximum time, and average time will be shown.",
    NULL
    };

void _cdecl
main(int argc, char *argv[])
{

    do_aping(argc, argv);

}

void
do_aping( int argc, char *argv[])
{
    /* Variables used for CPI-C calls */
    unsigned char cm_conv_id[8];            /* CPI-C conversation ID         */
    CM_INT32    cm_rc;                      /* CPI-C return code             */
    CM_INT32    length;                     /* generic length variable       */
    CM_INT32    rts_received;               /* request to send received      */
    CM_INT32    max_receive_len;            /* Max receive length on CMRCV   */
    CM_INT32    data_received;              /* Data received parm from CMRCV */
    CM_INT32    received_len;               /* Amount of data rcvd on CMRCV  */
    CM_INT32    status_received;            /* Status from CMRCV             */

    /* Data buffer for send and receive */
    unsigned char CM_PTR buffer;            /* CPIC data buffer              */

#ifdef WIN32                                                          /*WIN32*/
    unsigned short WinCPICVERSION = 0x0001;                           /*WIN32*/
    WCPICDATA CPICData;                                               /*WIN32*/
#endif                                                                /*WIN32*/

    /* Destination information */
    CPICINIT * cpicinit;

    unsigned long start_time = 0;           /* when a ping starts            */
    unsigned long end_time;                 /* when a ping ends              */
    unsigned long elapsed_time;             /* used for time calculations    */

    char          partner_major_version;
    char          partner_minor_version;
    char          opsys_string[64];

    unsigned long curr_iteration;           /* which iteration is active     */
    unsigned long curr_concurrent;          /* which send/recv is active     */
    unsigned long total_time = 0;           /* used to calculate averages    */

    unsigned long min_time = 0xFFFFFFFF;    /* used for min elapsed time     */
    unsigned long max_time = 0;             /* used for max elapsed time     */
                                            /* min and max are initialized   */
                                            /* to the extreme opposite values*/
                                            /* to avoid doing special        */
                                            /* processing the first time thru*/

    double data_rate;                       /* Variable used to calculate    */
                                            /* the data rate                 */

    FLAGS  flags;                           /* Command line flags            */

    flags.one_way_flag = 2;
    flags.size = 100;
    flags.number_iterations = 2;
    flags.number_concurrent = 1;

    /*
     * Make sure all output is seen as soon as possible.
     */
    setbuf(stdout, NULL);

    show_info(intro);                       /* Show program information      */

#if (defined(WIN32) || defined(WINDOWS))                              /*WIN32*/
       /****************************************************************WIN32*/
       /* Initialisation for WinCPIC                                   *WIN32*/
       /****************************************************************WIN32*/
    if (WinCPICStartup(WinCPICVERSION,&CPICData))                     /*WIN32*/
    {                                                                 /*WIN32*/
       return ;                                                    /*WIN32*/
    }                                                                 /*WIN32*/
#endif                                                                /*WIN32*/

    /*
     * Create a new CPICINIT structure and initialize values.
     * The procedures are in CPICINIT.C
     */

    cpicinit = cpicinit_new();
    cpicinit_default_tp_name(cpicinit, DEFAULT_TP_NAME);
    cpicinit_default_mode_name(cpicinit, DEFAULT_MODE_NAME);
    cpicinit_default_sym_dest_name(cpicinit, DEFAULT_SYM_DEST);

    /*
     * Process all of the command line arguments.  All of the conversation
     * setup arguments are stored in the cpicinit object.  The block size
     * and other loop parameters are set in the global variables.
     */
    process_arguments(argc, argv, cpicinit, &flags);

    if (cpicinit->set_destination == NOT_SET) {
        write_error( "\n\aYou must specify a destination.\n");
        show_info(usage);
        do_exit(EXIT_FAILURE);
    }

    if (cpicinit_query_password_needed(cpicinit)) {
        /* get a password from the user */
        cpicinit_get_password(cpicinit);
    }

    buffer = (unsigned char CM_PTR)alloc_cpic_buffer((unsigned int)flags.size);
                                            /* allocate an APPC buffer       */

    if (buffer == NULL) {
        write_error("Error allocating memory buffer.\nExiting...\n");
        do_exit(EXIT_FAILURE);
    }

    memset(buffer, 0, (unsigned int)flags.size);  /* Set block to zeros      */

    /*
     * Initialize the CPICERR structure.  This is done before the CMINIT
     * call so that we can use CPICERR for help with errors on CMINIT.
     * The procedure is in CPICERR.C
     */
    cpicerr = cpicerr_new();
    cpicerr_set_program_name(cpicerr, PROGRAM_NAME);
    cpicerr_set_program_info(cpicerr, PROGRAM_INFO);
    cpicerr_set_major_version(cpicerr, MAJOR_VERSION);
    cpicerr_set_minor_version(cpicerr, MINOR_VERSION);
    cpicerr_set_log_file_name(cpicerr, LOG_FILE_NAME);
    cpicerr_set_log_file_path(cpicerr, LOG_FILE_PATH);

    /*
     * Setup a CPI-C conversation.  This routine issues all of the
     * CPI-C calls from CMINIT() through just before you would
     * issue the CMALLC() call.  The conversation id is returned
     * for use by the program.
     */
    cpicinit_setup_conversation(cpicinit, cm_conv_id, cpicerr);

    /*
     * Fill in conversation information for CPI-C error reporting.
     */
    cpicerr_set_conv_id(cpicerr, cm_conv_id);

    {
    CM_SYNC_LEVEL sync_level = CM_CONFIRM;
    cmssl(cm_conv_id,                       /* Set sync level                */
          &sync_level,
          &cm_rc);
    /* The only expected return code is CM_OK */
    if (cm_rc != CM_OK) cpicerr_handle_rc(cpicerr, MSG_CMSSL, cm_rc);
    }

    {
    CM_PREPARE_TO_RECEIVE_TYPE prep_to_receive = CM_PREP_TO_RECEIVE_FLUSH;
    cmsptr(cm_conv_id,                      /* Set prepare to receive type   */
           &prep_to_receive,
           &cm_rc);
    /* The only expected return code is CM_OK */
    if (cm_rc != CM_OK) cpicerr_handle_rc(cpicerr, MSG_CMSPTR, cm_rc);
    }

    /*
     * Fill in conversation information for CPI-C error reporting.
     */
    cpicerr_set_conv_id(cpicerr, cm_conv_id);

    start_time = get_time();            /* let's time the allocate       */

    cmallc(cm_conv_id,
           &cm_rc);

    end_time = get_time();                  /* stop the timer                */

    /* The only expected return code is CM_OK */
    if (cm_rc != CM_OK) cpicerr_handle_rc(cpicerr, MSG_CMALLC, cm_rc);

                                            /* show the allocate time        */
    write_output("\nAllocate duration:                     %8lu ms\n",
                                                       end_time - start_time);

    start_time = get_time();

    cpicerr_exchange_version_plus(cpicerr,
                                  cm_conv_id,
                                  CM_SEND_STATE,
                                  &partner_major_version,
                                  &partner_minor_version,
                                  opsys_string,
                                  sizeof(opsys_string));

    end_time = get_time();                  /* stop the timer                */

    /*
     * Display the operating system string returned in the exchange.
     * The string length will be 0 if the operating system string
     * wasn't available.
     */

    if (strlen(opsys_string) != 0) {
        write_output("\nConnected to a partner running on: %s\n",
                     opsys_string);
    }

    /*
     * If the one_way_flag has been set, we have to make sure that our
     * partner is at the correct version to accept one way data.  If
     * the partner is not at the right level, we'll turn off the one
     * way flag and the partner will echo data (normal operating mode).
     *
     * Trying to use one way data with a partner that can't handle the
     * CONFIRM status indicator would result in an error on the partner
     * side.  On this side, we would see DEALLOCATE_ABEND.
     */
    if ((flags.one_way_flag == 1)  &&
        !(partner_major_version > ONEWAY_MAJOR_VERSION ||
        (partner_major_version == ONEWAY_MAJOR_VERSION &&
         partner_minor_version >= ONEWAY_MINOR_VERSION))) {
        write_output("\nOne way data transfer is not supported by partner.\n");
        write_output("Partner will echo data.\n");
        flags.one_way_flag = 2;
    }

                                            /* show the startup time         */
    write_output("\nProgram startup and Confirm duration:  %8lu ms\n\n",
                                                       end_time - start_time);

    write_output(
    "        Duration        Data Sent        Data Rate        Data Rate\n");
    write_output(
    "        (msec)          (bytes)          (KB/s)           (Mb/s)   \n");
    write_output(
    "        --------        ---------        ---------        ---------\n");

    /*
     * Set the amount of data we will send on each CMSEND() call.
     */
    length = flags.size;

    /*
     * Start current at zero so if a BREAK occurs, it will contain the number
     * of iterations completed.  We take a slight risk in assuming that
     * the curr_iteration++ operation is atomic.
     *
     * This loop encompasses both a send loop and a receive loop.  Both
     * the send a receive loop are executed the number of times specified
     * by the number_iterations variable (set by the -i argument).
     */
    for (curr_iteration = 0;
         curr_iteration<flags.number_iterations;
         curr_iteration++ ) {
        {
        CM_SEND_TYPE send_type = CM_BUFFER_DATA;
        cmsst(cm_conv_id,
              &send_type,
              &cm_rc);
        if (cm_rc != CM_OK) cpicerr_handle_rc(cpicerr, MSG_CMSST, cm_rc);
        }

        start_time = get_time();

        for (curr_concurrent = 1;           /* Start current at one so we    */
             curr_concurrent<flags.number_concurrent ;/* do one LESS send in */
             curr_concurrent++ ) {          /* loop than the specifed number */

            cmsend(cm_conv_id,
                   buffer,
                   &length,
                   &rts_received,
                   &cm_rc);

            /* The only expected return code is CM_OK */
            if (cm_rc != CM_OK) cpicerr_handle_rc(cpicerr, MSG_CMSEND, cm_rc);

        }

        /*
         * For the final send in the number of concurrent sends, set the send
         * type to do a send and a prepare to receive.  This will send both
         * the data and the send permission indicator to our partner all at
         * once.
         *
         * If the one_way_flag has been set, we will issue a Confirm along
         * with the Send_Data.  This will allow us to know when the partner
         * has actually received all the data so we can get an accurate
         * timing.
         *
         * On the partner side, if Send status is received, the partner will
         * know to echo the data.  If Confirm status is received, the partner
         * will know to issued Confirmed and then get ready to receive
         * more data, since the partner won't be echoing.
         */
        {
        CM_SEND_TYPE send_type;
        if (flags.one_way_flag != 1) {
            send_type = CM_SEND_AND_PREP_TO_RECEIVE;
        } else {
            send_type = CM_SEND_AND_CONFIRM;
        }
        cmsst(cm_conv_id,
              &send_type,
              &cm_rc);
        if (cm_rc != CM_OK) cpicerr_handle_rc(cpicerr, MSG_CMSST, cm_rc);
        }

        cmsend(cm_conv_id,
               buffer,
               &length,
               &rts_received,
               &cm_rc);
        /* The only expected return code is CM_OK */
        if (cm_rc != CM_OK) cpicerr_handle_rc(cpicerr, MSG_CMSEND, cm_rc);

        if (flags.one_way_flag != 1) {
            max_receive_len = flags.size;
            do {

                cmrcv (cm_conv_id,          /* Receive Data                  */
                       buffer,              /* Data Pointer                  */
                       &max_receive_len,    /* Size of Data Buffer           */
                       &data_received,      /* returned - data received      */
                       &received_len,       /* returned - length of data     */
                       &status_received,    /* returned - status received    */
                       &rts_received,       /* returned - request to send    */
                       &cm_rc);
                /* The only expected return code is CM_OK */
                if (cm_rc != CM_OK) {
                    cpicerr_handle_rc(cpicerr, MSG_CMRCV, cm_rc);
                }

                if (data_received != CM_NO_DATA_RECEIVED) {
                    curr_concurrent--;
                }

            } while ((status_received !=  CM_SEND_RECEIVED));
            /* Repeat the receive loop until SEND permission has been rcvd. */

            if (curr_concurrent != 0) {
                write_error(
                "ERROR.\n");
                write_error(
                    "Partner did not send the expected number of records.\n");
            }
        } else {
        }

        end_time = get_time();              /* stop timer                    */

        elapsed_time = end_time - start_time; /* calculate elapsed time      */

        write_output("%16ld", elapsed_time);
        write_output("%17lu", flags.size * flags.number_concurrent *
                              flags.one_way_flag);

        if (elapsed_time) {
            data_rate =                     /* in KBytes / 0.1 Sec           */
                              (double)
              ( ( (((double)flags.size * (double)flags.number_concurrent) /
                       (double)1024) *
                   (double)1 * (double)1000 * (double)flags.one_way_flag)
                           /    /* divided by */
                 ( (double)elapsed_time  / (double)10)  );

            data_rate = data_rate / (double)10;

            write_output("%17.1f", data_rate);
            write_output("%17.3f\n", (data_rate * (double)8) / (double)1000);
            {
            /* this code was for integer arithmetic */
            unsigned long data_rate;
            unsigned long modifier = 100000L;
            unsigned long bytes = flags.size * flags.number_concurrent
                                  * flags.one_way_flag;

            while ( bytes > (0xFFFFFFFF / modifier) && (modifier > 10)) {
                modifier = modifier / 10;
            } /* endwhile */

            data_rate = (unsigned long)
                           ((bytes * modifier))
                                    /
               (((unsigned long)elapsed_time * 1024L) / (100000L / modifier) );
            }

        } else {
            write_output("\n");             /* Make sure we move to next line*/
        }

        total_time += elapsed_time;         /* accumulate the elapsed time   */

        if (elapsed_time > max_time) {
            max_time = elapsed_time;        /* set the max time              */
        }
        if (elapsed_time < min_time) {
            min_time = elapsed_time;        /* set the min time              */
        }

    }

    {
    CM_DEALLOCATE_TYPE deallocate_type = CM_DEALLOCATE_FLUSH;
    cmsdt(cm_conv_id,
          &deallocate_type,
          &cm_rc);
    /* The only expected return code is CM_OK */
    if (cm_rc != CM_OK) cpicerr_handle_rc(cpicerr, MSG_CMSDT, cm_rc);
    }

    cmdeal(cm_conv_id,
           &cm_rc);
    /* The only expected return code is CM_OK */
    if (cm_rc != CM_OK) cpicerr_handle_rc(cpicerr, MSG_CMDEAL, cm_rc);

    /* destroy the object we created with cpicinit_new() */
    cpicinit_destroy(cpicinit);

    /* destroy the object we created with cpicerr_new() */
    cpicerr_destroy(cpicerr);

    /*
     * Print out the grand totals and max/min times.
     */
    {

        double data_rate;

        if (total_time > 10) {

            data_rate =                 /* in KBytes / 0.1 Sec           */
                              (double)
              ( ( (((double)flags.size * (double)flags.number_concurrent) /
                         (double)1024) *
                (double)curr_iteration * (double)1000 *
                (double)flags.one_way_flag)
                           /    /* divided by */
                 ( (double)total_time  / (double)10)  );

            data_rate = data_rate / 10;

            write_output("Totals:%9lu", total_time);
            write_output("%17lu", flags.size *
                                  flags.number_concurrent *
                                  curr_iteration *
                                  flags.one_way_flag);
            write_output("%17.1f", data_rate);
            write_output("%17.3f\n", (data_rate * (double)8) /
                                               (double)1000);
        } else {
            write_output("Totals:%9lu", total_time);
            write_output("%17lu\n", flags.size *
                                    flags.number_concurrent *
                                    curr_iteration *
                                    flags.one_way_flag);
        }
        if (curr_iteration > 0) {
            write_output(
            "\nDuration statistics:   Min = %lu   Ave = %lu   Max = %lu\n",
                                             min_time,
                                             total_time / curr_iteration,
                                             max_time);
        }

    }
    do_exit(EXIT_SUCCESS);

}

void
process_arguments(int argc,
                  char *argv[],
                  CPICINIT * cpicinit,
                  FLAGS * flags )
{
    int         c;                          /* flag specifed, used w/getopt  */

    /*
     * GETOPT is an easy way to parse command line arguments
     * Each parameter which can have a flag is passed in the third argument
     * to getopt.  Getopt returns the character of the flag on the command
     * line and sets optarg to point to the value associated with the flag.
     * optind is the index of the argument that getopt is currently processing.
     */
    while (optind != argc) {
        c = getopt(argc, argv, "?1c:t:m:i:s:u:p:C:T:M:I:S:U:P:nNqQ");

switch (c) {
        case EOF:
            optarg = argv[optind];
            if (optarg[0] == '?') {
               show_info(usage);
               do_exit(EXIT_FAILURE);
            }
            optind++;
            if (cpicinit->set_destination == NOT_SET) {
                cpicinit_set_destination(cpicinit, optarg);
            } else {
                write_error("Only one destination may be specified.\n");
            }
            break;
        case 'M':
        case 'm':
            cpicinit_set_mode_name(cpicinit, optarg);
            break;
        case 'T':
        case 't':
            cpicinit_set_tp_name(cpicinit,optarg);
            break;
#if defined(SUPPORTS_SETTING_SECURITY)
        case 'U':
        case 'u':
            cpicinit_set_userid(cpicinit, optarg);
            break;
        case 'P':
        case 'p':
            cpicinit_set_password(cpicinit, optarg);
            break;
        case 'N':
        case 'n':
            cpicinit_set_security_type(cpicinit, CM_SECURITY_NONE);
            break;
#endif
        case 'S':
        case 's':
            flags -> size = atol(optarg);

            if (flags->size > MAX_SIZE) {   /* check bounds                  */
                write_error("Size (-s) of %lu too large.\n", flags->size);
                write_error("Setting size to %lu\n", (unsigned long)MAX_SIZE);
                flags->size = MAX_SIZE;     /* reset to MAX                  */
            }
            break;
        case 'Q':
        case 'q':
            /* quiet processing is done in main() */
            break;
        case 'I':
        case 'i':
            flags->number_iterations = atol(optarg);
            break;
        case 'C':
        case 'c':
            flags->number_concurrent = atol(optarg);
            break;
        case '1':
            flags->one_way_flag = 1;
            break;
        case '?':
            show_info(usage);
            do_exit(EXIT_FAILURE);
            break;
        default:
            write_output("Invalid flag.  Use %s -? for usage\n", PROGRAM_NAME);
            do_exit(EXIT_FAILURE);
        }
    }
}


APINGD.C

/*****************************************************************************
 *
 *  MODULE NAME: APINGD.C
 *
 *  COPYRIGHTS:
 *             This module contains code made available by IBM
 *             Corporation on an AS IS basis.  Any one receiving the
 *             module is considered to be licensed under IBM copyrights
 *             to use the IBM-provided source code in any way he or she
 *             deems fit, including copying it, compiling it, modifying
 *             it, and redistributing it, with or without
 *             modifications.  No license under any IBM patents or
 *             patent applications is to be implied from this copyright
 *             license.
 *
 *             A user of the module should understand that IBM cannot
 *             provide technical support for the module and will not be
 *             responsible for any consequences of use of the program.
 *
 *             Any notices, including this one, are not to be removed
 *             from the module without the prior written consent of
 *             IBM.
 *
 *  AUTHOR:    Peter J. Schwaller
 *             VNET:     PJS at RALVM6           Tie Line: 444-4376
 *             Internet: pjs@ralvm6.vnet.ibm.com     (919) 254-4376
 *
 *  FUNCTION:  Perform an echo test to a specified LU.
 *             APING can be used when you are first installing APPC on
 *             your computer to make sure you can connect to another
 *             computer in the network.  APING can also be used to
 *             get an estimate of the delay time or throughput to another
 *             computer in the network.
 *
 *             APINGD echos whatever is sent by APING.
 *                Keep receiving until you get permission to send
 *                Send the same number of same size records
 *
 *  AVAILABILITY:
 *             These sample programs and source are also available on
 *             CompuServe through the APPC Information Exchange.  To get
 *             to the APPC forum just type 'GO APPC' from any CompuServe
 *             prompt.  The samples are available in the Sample Programs
 *             library section.  Just search on the keyword CPICPGMS to
 *             find all the samples in this series.
 *
 *             Updates for the sample programs and support for many more
 *             CPI-C platforms will also be made available on CompuServe.
 *
 *  RELATED FILES:
 *             See APING.DOC for usage instructions.
 *
 *  PORTABILIITY NOTES:
 *             The APINGD server program is completely portable.  In fact,
 *             all of the source modules can be compiled without #define-ing
 *             any platform constant value.
 *
 *             To take advantage of a performance optimization on the
 *             OS/2 platform, the alloc_cpic_buffer() is used.  If the
 *             OS/2 platform is specified (#define of OS2, FAPI, or OS2_20)
 *             alloc_cpic_buffer() will return a shared memory buffer.
 *             If not, a buffer allocated with malloc() will be returned.
 *
 *             If you are porting to a platform that can take advantage
 *             of a specially allocated memory buffer, you should
 *             add this support to the alloc_cpic_buffer() call in the
 *             CPICPORT.C file.
 *
 *  CHANGE HISTORY:
 *  Date       Description
 *  06/15/92   NS/DOS accepts version 2.02 into system test.
 *  08/05/92   Version 2.31 released to CompuServe
 *             This version was also distributed at the APPC/APPN Platform
 *             Developer's Conference held in Raleigh, NC.
 *  08/13/92   Changed all printf and fprintf calls to use a write_*() call.
 *  08/24/92   Version 2.32 released to CompuServe.
 *  09/22/92   Version 2.33 released to CompuServe.
 *  11/17/92   Supports sending operating system string - see CPICERR.C
 *             Version 2.34 released to CompuServe
 *  01/07/93   Version 2.35
 *             Fixed a number of problems when compiling with IBM C Set/2
 *                password input was displayed
 *                timer resolution was 1 second
 *
 *****************************************************************************/

#ifdef WIN32                                                           /*WIN32*/
#include <windows.h>                                                   /*WIN32*/
SERVICE_STATUS_HANDLE stat_hand;                                       /*WIN32*/
SERVICE_STATUS servstat;                                               /*WIN32*/
#endif                                                                 /*WIN32*/

#include "wincpic.h"

/* standard C include files */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* Set up constant declarations */
#include "cpicdefs.h"

/* Collection of routines with special ported version for each platform */
#include "cpicport.h"

/* CPI-C error handling routines */
/* This file is supplied with APINGD */
#include "cpicerrs.h"

/* CPI-C initialization routines */
/* This file is supplied with APINGD */
#include "cpicinit.h"

/* Argument processing procedure */
/* This file is supplied with APINGD */
#include "getopt.h"

/* CPI-C error handling info */
CPICERR * cpicerr;

/*
 * Max size of a data buffer.  This is the largest size buffer that can
 * be specified on a call to CPI-C.
 */
#define  MAX_SIZE (0x7FFF)

/* Define these here so we can make changes throughout the code. */
/*
 * The PROGRAM_INFO string should be kept in sync with the
 * MAJOR_VERSION and MINOR_VERSION constants.  Although the
 * cpicerr_exchange_version() call will support values up to 255,
 * values for MINOR_VERSION should be from 00-99 to maintain the
 * two character format in the version string.
 */
#define  PROGRAM_NAME      "APINGD"
#define  PROGRAM_INFO      "version 2.35"
#define  MAJOR_VERSION     (2)
#define  MINOR_VERSION     (35)
#define  LOG_FILE_NAME     "apingd.err"
#define  LOG_FILE_PATH     "$LOGPATH"

/*
 * Message displayed with show_info() when APINGD is started.
 */
char * intro[] = {
    PROGRAM_NAME " " PROGRAM_INFO " - APPC loopback server",
    NULL
    };

char * log_file_name = NULL;

void TPStart( DWORD argc, LPSTR *argv)                                /*WIN32*/
{
    /* Variables used for CPI-C calls */
    unsigned char cm_conv_id[8];            /* CPI-C conversation ID         */
    CM_INT32    temp;                                                 /*WIN32*/
    CM_INT32    cm_rc;                      /* CPI-C return code             */
    CM_INT32    length;                     /* generic length variable       */
    CM_INT32    rts_received;               /* request to send received      */
    CM_INT32    max_receive_len;            /* Max receive length on CMRCV   */
    CM_INT32    data_received;              /* Data received parm from CMRCV */
    CM_INT32    received_len;               /* Amount of data rcvd on CMRCV  */
    CM_INT32    status_received;            /* Status from CMRCV             */

    /* Data buffer for send and receive */
    unsigned char CM_PTR buffer;            /* CPIC data buffer              */

    char        destination[MAX_DESTINATION];/* Partner destination          */
    unsigned int max_size;                  /* size to receive               */
    int         c;                          /* flag specifed, used w/getopt  */

    char partner_major_version;
    char partner_minor_version;

    show_info(intro);                       /* display intro information     */

#ifdef WIN32
    {
       /**********************************************************************/
       /* Initialisation for WinCPIC                                         */
       /**********************************************************************/
       unsigned short WinCPICVERSION = 0x0001;
       WCPICDATA CPICData;
       if (WinCPICStartup(WinCPICVERSION,&CPICData))
       {
         return;
       }
       /**********************************************************************/
       /* Set our local TP Name                                              */
       /**********************************************************************/
       temp=6;
       cmsltp("APINGD",&temp,&cm_rc);
    }
#endif

    while ((DWORD)optind != argc) {
        c = getopt(argc, argv, "?l:");
        switch (c) {
        case EOF:
            optind++;
            break;
        case 'l':
        case 'L':
            write_output("\nIncoming partner LU names will be logged to: %s\n",
                                                                optarg);
            log_file_name = optarg;
            break;
        } /* endswitch */
    } /* endwhile */

    /*
     * Initialize the CPICERR structure.  This is done before the CMACCP
     * call so that we can use CPICERR for help with errors on CMACCP.
     * The procedure is in CPICERR.C
     */
    cpicerr = cpicerr_new();
    cpicerr_set_program_name(cpicerr, PROGRAM_NAME);
    cpicerr_set_program_info(cpicerr, PROGRAM_INFO);
    cpicerr_set_major_version(cpicerr, MAJOR_VERSION);
    cpicerr_set_minor_version(cpicerr, MINOR_VERSION);
    cpicerr_set_log_file_name(cpicerr, LOG_FILE_NAME);
    cpicerr_set_log_file_path(cpicerr, LOG_FILE_PATH);

    cmaccp(cm_conv_id,                      /* Accept Conversation           */
           &cm_rc);
    /*
     * Note that as we have used cmsltp to specify our local TP name,
     * cmaccp may return asynchronously, so we must do a cmwait
     */
    if (cm_rc == CM_OPERATION_INCOMPLETE)                             /*WIN32*/
    {                                                                 /*WIN32*/
      cmwait(cm_conv_id, &cm_rc, &temp);                              /*WIN32*/
    }                                                                 /*WIN32*/

    /*
     * Fill in conversation information for CPI-C error reporting.
     */
    cpicerr_set_conv_id(cpicerr, cm_conv_id);

    if (cm_rc != CM_OK) {
        cpicerr_handle_rc(cpicerr, MSG_CMACCP, cm_rc);
    } else {
        CM_INT32 pln_length;
        /*
         * Extract the partner LU name and display it.
         */
        cmepln(cm_conv_id,
               (unsigned char *)destination,
               &pln_length,
               &cm_rc );

        destination[(int)pln_length] = '\0';
        write_output("\nContacted by partner: %s\n", destination);

        if (log_file_name != NULL) {
            FILE * file;
            file = fopen(log_file_name, "a");
            if (file != NULL) {
                write_log(file, "\nContacted by partner: %s\n", destination);
                fclose(file);
            }
        }

    }

    {
    CM_PREPARE_TO_RECEIVE_TYPE prep_to_receive = CM_PREP_TO_RECEIVE_FLUSH;
    cmsptr(cm_conv_id,                       /* Set prepare to receive type  */
           &prep_to_receive,
           &cm_rc);
    if (cm_rc != CM_OK) cpicerr_handle_rc(cpicerr, MSG_CMSPTR, cm_rc);
    }

    max_receive_len = max_size = MAX_SIZE;

    buffer = (unsigned char CM_PTR)alloc_cpic_buffer(max_size);
                                            /* allocate a buffer             */

    cpicerr_exchange_version(cpicerr,
                             cm_conv_id,
                             CM_RECEIVE_STATE,
                             &partner_major_version,
                             &partner_minor_version);

    do {
        unsigned long count;                /* number of consecutive         */
                                            /* sends or receives             */
        count = 0;                          /* initialize count of recvs     */
        do {
           cmrcv (cm_conv_id,               /* Receive Data                  */
                  buffer,                   /* Data Pointer                  */
                  &max_receive_len,         /* Size of Data Buffer           */
                  &data_received,           /* returned - data received      */
                  &received_len,            /* returned - length of data     */
                  &status_received,         /* returned - status received    */
                  &rts_received,            /* returned - request to send    */
                  &cm_rc);

            if (data_received != CM_NO_DATA_RECEIVED) {
                count++;                    /* keep track of receives        */
            }
        } while ( (status_received != CM_SEND_RECEIVED) &&
                  (status_received != CM_CONFIRM_RECEIVED) &&
                   !cm_rc);
        /*
         * loop until we get permission to send data or until error
         */

        if (cm_rc != CM_OK) {
            if (cm_rc == CM_DEALLOCATED_NORMAL) {
                do_exit(EXIT_SUCCESS);
            } else {
                cpicerr_handle_rc(cpicerr, MSG_CMRCV, cm_rc);
            }
        }

        if (status_received != CM_CONFIRM_RECEIVED) {
            /*
             * count is now equal to the number of data blocks we received
             * now we will send back the same number of data blocks of equal
             * size
             */
            {
            CM_SEND_TYPE send_type = CM_BUFFER_DATA;
            cmsst(cm_conv_id,
                  &send_type,
                  &cm_rc);
            if (cm_rc != CM_OK) cpicerr_handle_rc(cpicerr, MSG_CMSST, cm_rc);
            }

                                 /* send back the same number except for one */
            for ( count--; count && !cm_rc; count-- ) {
                length = received_len;
                cmsend(cm_conv_id,
                       buffer,
                       &length,
                       &rts_received,
                       &cm_rc);
                if (cm_rc != CM_OK) {
                    cpicerr_handle_rc(cpicerr, MSG_CMSEND, cm_rc);
                }
            }

            /*
             * Set the send type to do a send and a prepare to receive.
             * This will send both the data and the send permission indicator
             * to our partner all at once.
             */
            {
            CM_SEND_TYPE send_type = CM_SEND_AND_PREP_TO_RECEIVE;
            cmsst(cm_conv_id,
                  &send_type,
                  &cm_rc);
            if (cm_rc != CM_OK) cpicerr_handle_rc(cpicerr, MSG_CMSST, cm_rc);
            }

            length = received_len;
            cmsend(cm_conv_id,
                   buffer,
                   &length,
                   &rts_received,
                   &cm_rc);
            if (cm_rc != CM_OK) cpicerr_handle_rc(cpicerr, MSG_CMSEND, cm_rc);
        } else {
            /*
             * The partner has requested one way data transfer only.
             * We'll just issue Confirmed, then go back up to receive
             * more data.
             */
            cmcfmd(cm_conv_id,
                   &cm_rc);
            if (cm_rc != CM_OK) cpicerr_handle_rc(cpicerr, MSG_CMCFMD, cm_rc);
        }
    } while (cm_rc == CM_OK);

    /* destroy the object we created with cpicerr_new() */
    cpicerr_destroy(cpicerr);

    do_exit(EXIT_SUCCESS);
}

#ifdef WIN32
/*****************************************************************************/
/* The following code makes this TP invokable as an NT service. There are 3  */
/* routines.                                                                 */
/*                                                                           */
/* 1. main. This is the entry point for the process, it sets up a service    */
/*          table entry and then calls StartServiceCtrlDispatcher. This call */
/*          doesn't return, but uses the thread which called it as a         */
/*          control dispatcher for all the services implemented by this      */
/*          process (in this case, just the TP itself).                      */
/*                                                                           */
/* 2. ServiceMain. This is the main entry point for the service itself, the  */
/*          service control dispatcher creates a thread to start at this     */
/*          routine. It must register a service control handler for the      */
/*          service which will be called by the control dispatcher when it   */
/*          has control instructions for the service. It then informs the    */
/*          service control manager that the service is running and finally  */
/*          calls the start of the TP itself. This routine should not return */
/*          until the service is ready to die.                               */
/*                                                                           */
/* 3. ControlHandler. This routine is called by the control dispatcher when  */
/*          it has instructions for the service. We do not respond to any    */
/*          of the instructions as this service should be transitory and not */
/*          actually run for more than a few seconds so we don't need to do  */
/*          anything with the STOP or SHUTDOWN requests.                     */
/*          Note that we MUST call SetServiceStatus, even if the status      */
/*          hasn't changed.                                                  */
/*****************************************************************************/

void __cdecl main( DWORD argc, LPSTR * argv);
void WINAPI ServiceMain(DWORD dwNumServiceArgs, LPTSTR * lpServiceArgs);
VOID WINAPI ControlHandler(DWORD dwControl);
SERVICE_STATUS_HANDLE stat_hand;
SERVICE_STATUS servstat;

void __cdecl main( DWORD argc, LPSTR * argv)
{
  SERVICE_TABLE_ENTRY  stab[2];

  /***************************************************************************/
  /* Start the control dispatcher. This call gives the SCManager this        */
  /* thread for the entire period that this service is running, so that it   */
  /* can call us back with service controls. It will spawn a new thread to   */
  /* run the service itself, starting at entrypoint ServiceMain.             */
  /***************************************************************************/
  stab[0].lpServiceName = "APINGD\0";
  stab[0].lpServiceProc = ServiceMain;

  stab[1].lpServiceName = NULL;
  stab[1].lpServiceProc = NULL;

  StartServiceCtrlDispatcher(stab);

}

/*****************************************************************************/
/* This routine is the entry-point for the service itself the service        */
/* control dispatcher creates a thread to start here when we issue           */
/* StartServiceControlDispatcher.                                            */
/*                                                                           */
/* Inputs:  number of arguments to services, array of strings.               */
/*                                                                           */
/* Outputs: none                                                             */
/*                                                                           */
/*****************************************************************************/
void WINAPI ServiceMain(DWORD dwNumServiceArgs, LPTSTR * lpServiceArgs)
{

  DWORD rc;

  stat_hand = RegisterServiceCtrlHandler("APINGD\0", ControlHandler);
  if (stat_hand == (SERVICE_STATUS_HANDLE)NULL)
  {
    rc = GetLastError();
    DebugBreak();
  }

  /***************************************************************************/
  /* Let the SCManager know that we are running.                             */
  /***************************************************************************/
  servstat.dwServiceType              = SERVICE_WIN32;
  servstat.dwCurrentState             = SERVICE_RUNNING;
  servstat.dwControlsAccepted         = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
  servstat.dwWin32ExitCode            = NO_ERROR;
  servstat.dwServiceSpecificExitCode  = NO_ERROR;
  servstat.dwCheckPoint               = 0;
  servstat.dwWaitHint                 = 0;

  rc = SetServiceStatus(stat_hand, &servstat);
  if (!rc)
  {
     rc = GetLastError();
     DebugBreak();
  }

  TPStart(dwNumServiceArgs, lpServiceArgs);

}

/*****************************************************************************/
/* This routine is the callback from the SCManager to handle specific        */
/* service control requests. It MUST call SetServiceStatus before it         */
/* returns, regardless of whether the status has changed.                    */
/*                                                                           */
/* Inputs: service control requested                                         */
/*                                                                           */
/* Outputs: none                                                             */
/*                                                                           */
/*****************************************************************************/
VOID WINAPI ControlHandler(DWORD dwControl)
{
  DWORD rc;

  switch (dwControl)
  {
    case SERVICE_CONTROL_STOP :
      servstat.dwCurrentState = SERVICE_STOP_PENDING;
      servstat.dwWaitHint     = 24000;
      break;

    case SERVICE_CONTROL_PAUSE :
    case SERVICE_CONTROL_CONTINUE :
    case SERVICE_CONTROL_INTERROGATE :
      servstat.dwWaitHint     = 0;
      break;

 case SERVICE_CONTROL_SHUTDOWN:
      servstat.dwCurrentState = SERVICE_STOP_PENDING;
      servstat.dwWaitHint     = 10000;
break;
  }

  rc=SetServiceStatus(stat_hand, &servstat);
  if (!rc)
  {
     rc=GetLastError();
  }

}

#endif


SENDRECV.C

/* sendrecv.c */
/* (C) COPYRIGHT DATA CONNECTION LIMITED 1993 */

/*****************************************************************************/
/* Change History                                                            */
/*                                                                           */
/*       04/05/93 NGR Created.                                               */
/* TPED  26/05/93 SW  RECVTP must issue TP_ENDED after each conversation     */
/* PERF  24/06/93 SW  Don't regenerate data for each SEND verb               */
/* FILE  02/07/93 NGR Don't attempt to close NULL file handle                */
/* PATH  10/08/93 NGR Gord's changes to get config from current directory    */
/*****************************************************************************/

/*****************************************************************************/
/*                                                                           */
/* ROUTINE : SENDRECV                                                        */
/*                                                                           */
/* FUNCTION: This file contains the main routines for a simple bulk data     */
/*           sending and receiving TPs SENDTP and RECVTP                     */
/*                                                                           */
/* INPUTS  : SENDTP.CFG (file) (documented below)                            */
/*           RECVTP.CFG (file) (documented below)                            */
/*                                                                           */
/* OUTPUTS : SENDTP.OUT                                                      */
/*           RECVTP.OUT                                                      */
/*                                                                           */
/*****************************************************************************/

/*****************************************************************************/
/* Operation:                                                                */
/*                                                                           */
/* SENDTP                                                                    */
/* DOS and OS/2. Simple command line TP which simply allocates, does the     */
/* specified number of send_data and then deallocates. Done via a while loop */
/* which makes calls to IssueNextVerb.                                       */
/*                                                                           */
/* RECVTP                                                                    */
/* As SENDTP, but it receive_allocates, then receive_and_waits until it gets */
/* deallocated. Repeat this NumConversation times.                           */
/*                                                                           */
/* NT and Win16. Simple windows apps which use the async verbs and control   */
/* operation via the window proc of a minimised window. This proc makes      */
/* calls to IssueNextVerb on receipt of an async_complete message            */
/*                                                                           */
/* IssueNextVerb uses the opcode of the verb which just completed to         */
/* decide what to issue next, and calls the appropriate do_ routine to fill  */
/* in the vcb, it then calls the appropriate APPC entry point.               */
/*                                                                           */
/* Send TP will initially issue a SEND_CONVERSATION verb in order to send    */
/* the number of conversations to be done to the Recv TP which expect this   */
/* to be sent to it.                                                         */
/*                                                                           */
/* The applications will terminate if they encounter an error and do not     */
/* produce diagnostic output so the APPC API trace is the best place to look */
/*****************************************************************************/

/*****************************************************************************/
/* Configuration files:                                                      */
/*                                                                           */
/* Configuration file is C:\SENDTP.CFG or RECVTP.CFG which contains          */
/* the following, one per line in any order. If not present then the given   */
/* default is assumed.                                                       */
/*                                                                           */
/* ResultFile = <Name of file for results, default C:\(SEND/RECV)TP.OUT>     */
/* LocalTPName = <Name to be used for TP started, default SENDTP/RECVTP>     */
/*                                                                           */
/* The following only apply to SENDTP                                        */
/*                                                                           */
/* NumConversations = <Number of conversations to be done, default = 1>      */
/* LocalLUAlias = <Alias for local LU (!), default SENDLU>                   */
/* RemoteLUAlias = <Alias for remote LU (!!), default RECVLU>                */
/* ModeName = <!?! default #INTER>                                           */
/* RemoteTPName = <name of the TP, default RECVTP>                           */
/* NumSends = <number of SEND_DATA verbs per conversation, default = 2>      */
/* ConfirmEvery = <number of SEND_DATA verbs between CONFIRMs, default = 1>  */
/* SendSize = <number of bytes per SEND_DATA, default = 1024>                */
/* SendConversation = <use the SEND_CONVERSATION verb, default = no>         */
/*                                                                           */
/* Note that if SendConversation is used then NumSends is irrelevant, the TP */
/* will simply do NumConversations SEND_CONVERSATION verbs                   */
/* If NumConversations is zero, then the TPs will do an infinite number of   */
/* conversations.                                                            */
/* If NumSends is zero, then SENDTP will never DEALLOCATE the first          */
/* conversation.                                                             */
/* If ConfirmEvery is zero, then SENDTP will not issue CONFIRM verbs.        */
/*****************************************************************************/
#if (defined(WINDOWS)||defined(WIN32))
#include <windows.h>
HINSTANCE hInst;
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winappc.h>
#include <wincsv.h>
#include "sendrecv.h"

#if defined(DOS5) || defined(DOS)
#define SYNC
#endif

#ifdef WINDOWS
#define memset(x,y,z) _fmemset(x,y,z)
#define memchr(x,y,z) _fmemchr(x,y,z)
#define strupr(x) _fstrupr(x)
#define strcmpi(x,y) _fstricmp(x,y)
#define strncpy(x,y,z) _fstrncpy(x,y,z)
#endif

#ifdef SYNC
/*****************************************************************************/
/* main - reads initialisation info and creates threads                      */
/*        DOS & OS\2 version                                                 */
/*****************************************************************************/
void main( int argc, char FAR * argv[])
{

#if defined(WIN32) || defined(WINDOWS)
  WAPPCDATA APPCData;
  WNDCLASS class;
  #define WinAPPCVERSION  0x0001

  /**************************************************************************/
  /* Startup WinAPPC                                                        */
  /**************************************************************************/
  if (WinAPPCStartup(WinAPPCVERSION,&APPCData))
  {
     return (FALSE);
  }
#endif

  printf("\n\n\n\n\n\n\n");
#ifdef SENDTP
  printf("SENDTP - APPC Bulk data send program to be used with RECVTP\n");
#else
  printf("RECVTP - APPC Bulk data recv program to be used with SENDTP\n");
#endif
  printf("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n\n");

  InitialiseMain();
  ReadConfig();

  while (!TPDead)
  {
     IssueNextVerb();
  }
#ifdef SENDTP
  OutputResults();
#endif

#if defined(WIN32) || defined(WINDOWS)
  WinAPPCCleanup();
#endif

  printf("Finished");

}
#else
/*****************************************************************************/
/* WinMain - reads initialisation info and controls message loop             */
/*           NT and Win16 version                                            */
/*****************************************************************************/
int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine, int nCmdShow)
{

  MSG msg;

  hInst = hInstance;

  InitialiseMain();

  if (!InitialiseWinMain(hInstance))
  {
     return (FALSE);
  }

  ReadConfig();

  while(GetMessage(&msg,NULL,0,0))
  {
     TranslateMessage(&msg);
     DispatchMessage(&msg);
  }

  return msg.wParam;         /* save exit parameter for return               */

}

/*****************************************************************************/
/* InitialiseWinMain - does the windows bits of initialisation               */
/*****************************************************************************/
BOOL InitialiseWinMain(HINSTANCE hInstance)
{
   WAPPCDATA APPCData;
   WNDCLASS class;
   #define WinAPPCVERSION  0x0001

   /**************************************************************************/
   /* Startup WinAPPC                                                        */
   /**************************************************************************/
   if (WinAPPCStartup(WinAPPCVERSION,&APPCData))
   {
      return (FALSE);
   }

   if ( (ASYNC_COMPLETE =
         RegisterWindowMessage(WIN_APPC_ASYNC_COMPLETE_MESSAGE)) == 0 )
   {
      return (0);
   }

   async_corr=0;

   /**************************************************************************/
   /* Register Window Class for our icon                                     */
   /**************************************************************************/

   class.style = 0;
   class.lpfnWndProc   = (WNDPROC)TPWndProc;
   class.cbClsExtra    = (DWORD)0;
   class.cbWndExtra    = (DWORD)0;
   class.hInstance     = hInstance;
   class.hIcon         = LoadIcon(hInstance,"MainIcon");
   class.hCursor       = LoadCursor(NULL, IDC_ARROW);
   class.hbrBackground = GetStockObject(WHITE_BRUSH);
   class.lpszMenuName  = (LPSTR) NULL;
   class.lpszClassName = (LPSTR) "SENDRECV\0";

   if (!RegisterClass(&class))
   {
     return (FALSE);
   }

   /**************************************************************************/
   /* Create the window                                                      */
   /**************************************************************************/
#ifdef SENDTP
   sprintf(title,"APPC Send TP\0");
#else
   sprintf(title,"APPC Receive TP\0");
#endif

   if ((hWndMain = CreateWindow("SENDRECV\0",      /* window class           */
       title,                                      /* window name            */
       WS_MINIMIZE|WS_OVERLAPPEDWINDOW,            /* window style           */
       0,                                          /* x position             */
       0,                                          /* y position             */
       10,                                         /* width                  */
       10,                                         /* height                 */
       NULL,                                       /* parent handle          */
       NULL,                                       /* menu or child ID       */
       hInstance,                                  /* instance               */
       NULL))                                      /* additional info        */
       == NULL)
   {
      return (FALSE);
   }

   ShowWindow(hWndMain, SW_MINIMIZE);

   return(TRUE);

}

/*****************************************************************************/
/* Window proc for the iconised window                                       */
/*****************************************************************************/
LONG FAR PASCAL TPWndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
{
   if (message == ASYNC_COMPLETE)
   {
      if (wParam != (WPARAM) async_corr)
      {
         /*****************************************************************/
         /* This is not the correlator we were expecting, so we must die. */
         /*****************************************************************/
         TPDead = TRUE;
#ifdef WIN32
         DebugBreak();
#endif
      }
      else
      {
#ifdef SENDTP
         IssueNextVerb();
#else
         if (!GotNumConv)
         {
            /*****************************************************************/
            /* We are still in the process of getting the number of convs to */
            /* be done, so do the next stage of this. Note if all is not OK  */
            /* then we just die.                                             */
            /*****************************************************************/
            Get_Run_Details();
            if (TPDead)
            {
               PostMessage(hWndMain, WM_CLOSE, 0, 0);
            }
         }
         else
         {
            IssueNextVerb();
         }
#endif
      }
   }
   else
   {
      switch (message)
      {
         case WM_CREATE:
            /*****************************************************************/
            /* Post a message to ourselves to kick off the first verb        */
            /*****************************************************************/
            vcb.hdr.opcode = 0x0000;
            async_corr = 0;
            PostMessage(hWnd, ASYNC_COMPLETE, (WPARAM)async_corr,
                                                 (LPARAM)(char FAR *)(vcbptr));
            break;

         case WM_QUERYOPEN:
            /*****************************************************************/
            /* Prevent the window being opened                               */
            /*****************************************************************/
            break;

         case WM_CLOSE:
            TPDead = TRUE;
            WinAPPCCleanup();
#ifdef SENDTP
            OutputResults();
#endif
            return DefWindowProc(hWnd, message, wParam, lParam);
            break;

         case WM_DESTROY:
if( DataPtr != NULL )
{
#ifdef WINDOWS
GlobalUnlock(LOWORD(GlobalHandle(SELECTOROF(DataPtr))));
GlobalFree(LOWORD(GlobalHandle(SELECTOROF(DataPtr))));
#else
 #ifdef WIN32
 free(DataPtr);
 #else
 DosFreeSeg(selector);
 #endif
#endif
}
            PostQuitMessage(0);
            break;

         default:
            return DefWindowProc(hWnd, message, wParam, lParam);
            break;
      }
   }
   return 0l;
}

#endif

/*****************************************************************************/
/* InitialiseMain - blanks out variables.                                    */
/*****************************************************************************/
void InitialiseMain()
{
   NumConversations=0;
   FirstConv=TRUE;
   memset(LocalTPName,0,64);
   ConvCount=0;
   Convid=0;
   SendSize=0;
   memset(TPid,0,8);
   TPDead=FALSE;
   vcbptr=(char *)&vcb;
   cnvtptr=(char *)&cnvt;

#ifdef SENDTP
   NumSends=0;
   SendConversation=0;
   ConfirmEvery=0;
   SendCount=0;
   ConfirmCount=0;
   datach='A';

   memset(LocalLUAlias,0,8);
   memset(RemoteLUAlias,0,8);
   memset(ModeName,0,8);
   memset(RemoteTPName,0,64);
#else
   GotNumConv = FALSE;
   GetStage = 0;
#endif

}

/*****************************************************************************/
/* IssueNextVerb - looks at the verb which has just completed and does the   */
/*                 next one                                                  */
/*****************************************************************************/
void IssueNextVerb()
{
   if (vcb.hdr.opcode != 0x0000)
   {
      ProcessReturns();
   }
   if (!TPDead)
   {
      switch (vcb.hdr.opcode)
      {
         case 0x0000:
#ifdef SENDTP
            Build_TP_STARTED();

            /**************************************************************/
            /*PERF* Don't re-generate data each time we issue a send!     */
            /**************************************************************/
            GenerateData();

#else
            Build_RECEIVE_ALLOCATE();
#endif
            break;

#ifdef SENDTP
         case AP_TP_STARTED:
            if (ConvCount == 0)
            {
               Send_Run_Details();
            }
            if (!TPDead)
            {
               NewConversation();
               if (SendConversation)
               {
                  Build_MC_SEND_CONVERSATION();
               }
               else
               {
                  Build_MC_ALLOCATE();
               }
            }
            break;

         case AP_M_ALLOCATE:
            Build_MC_SEND_DATA();
            break;

         case AP_M_SEND_DATA:
            SendCount++;
            ConfirmCount++;
            if ((NumSends != 0) && (SendCount == NumSends))
            {
               Build_MC_DEALLOCATE();
            }
            else if ((ConfirmEvery != 0) && (ConfirmCount == ConfirmEvery))
            {
               Build_MC_CONFIRM();
            }
            else
            {
               Build_MC_SEND_DATA();
            }
            break;

         case AP_M_SEND_CONVERSATION:
            if ((NumConversations != 0) && (ConvCount == NumConversations))
            {
               NewConversation();
               Build_TP_ENDED();
            }
            else
            {
               NewConversation();
               Build_MC_SEND_CONVERSATION();
            }
            break;

         case AP_M_CONFIRM:
            ConfirmCount=0;
            Build_MC_SEND_DATA();
            break;

         case AP_M_DEALLOCATE:
            if ((NumConversations !=0) && (ConvCount == NumConversations))
            {
               NewConversation();
               Build_TP_ENDED();
            }
            else
            {
               NewConversation();
               Build_MC_ALLOCATE();
            }
            break;

         case AP_TP_ENDED:
            /**************************************************************/
            /*TPED* Quit out and die if SENDTP                            */
            /**************************************************************/
            TPDead = TRUE;
            break;
#else
         case AP_RECEIVE_ALLOCATE:
            NewConversation();
            Build_MC_RECEIVE_AND_WAIT();
            break;

         case AP_M_RECEIVE_AND_WAIT:
            if ((vcb.rcvwait.primary_rc == AP_DEALLOC_NORMAL))
            {
               /*************************************************************/
               /*TPED* Issue TP_ENDED every time conversation deallocated   */
               /*************************************************************/
               Build_TP_ENDED();
            }
            else if ((vcb.rcvwait.what_rcvd == AP_CONFIRM_WHAT_RECEIVED) ||
                     (vcb.rcvwait.what_rcvd == AP_DATA_COMPLETE_CONFIRM))
            {
               Build_MC_CONFIRMED();
               Deallocated = FALSE;
            }
            else if ((vcb.rcvwait.what_rcvd == AP_CONFIRM_DEALLOCATE) ||
                     (vcb.rcvwait.what_rcvd == AP_DATA_COMPLETE_CONFIRM_DEALL))
            {
               Build_MC_CONFIRMED();
               Deallocated = TRUE;
            }
            else
            {
               Build_MC_RECEIVE_AND_WAIT();
            }
            break;

         case AP_M_CONFIRMED:
            if (Deallocated)
            {
               /*************************************************************/
               /*TPED* Issue TP_ENDED every time conversation deallocated   */
               /*************************************************************/
               Build_TP_ENDED();
            }
            else
            {
               Build_MC_RECEIVE_AND_WAIT();
            }
            break;

         case AP_TP_ENDED:
            /**************************************************************/
            /*TPED* If not completed all conversations,  issue RCV_ALLOC  */
            /**************************************************************/
            if ((NumConversations != 0) && (ConvCount < NumConversations))
            {
               Build_RECEIVE_ALLOCATE();
            }
            else
            {
               TPDead = TRUE;
            }
            break;
#endif

         default:
            /*****************************************************************/
            /* What is this verb then ??                                     */
            /*****************************************************************/
            TPDead = TRUE;
#ifdef WIN32
            DebugBreak();
#endif
            break;

      } /* Op-code switch */

   } /* TPDead after previous verb */

   /**************************************************************************/
   /* Now go ahead and issue the verb, if we're not dead.                    */
   /**************************************************************************/
   if (!TPDead)
   {
#ifdef SYNC
      APPC((long)(vcbptr));
#else
      async_corr=WinAsyncAPPC(hWndMain, (long)(char FAR *)(vcbptr));
#endif
   }
#ifndef SYNC
   else
   {
      PostMessage(hWndMain, WM_CLOSE, 0, 0);
   }
#endif
} /* Issue next verb */

#ifdef SENDTP
void Build_TP_STARTED()
{
   CLEARVCB

   vcb.tpstart.opcode = AP_TP_STARTED;
   memcpy(&(vcb.tpstart.lu_alias), LocalLUAlias, 8);
   memcpy(&(vcb.tpstart.tp_name), LocalTPName, 64);

}

void Build_MC_ALLOCATE()
{
   CLEARVCB

   vcb.allocate.opcode = AP_M_ALLOCATE;
   vcb.allocate.opext = AP_MAPPED_CONVERSATION;
   memcpy(&(vcb.allocate.tp_id),TPid, 8);
   vcb.allocate.sync_level = AP_CONFIRM_SYNC_LEVEL;
   vcb.allocate.rtn_ctl = AP_WHEN_SESSION_ALLOCATED;
   memcpy(&(vcb.allocate.plu_alias), RemoteLUAlias, 8);
   memcpy(&(vcb.allocate.mode_name), ModeName, 8);
   memcpy(&(vcb.allocate.tp_name), RemoteTPName, 64);
   vcb.allocate.security = AP_NONE;

}

void Build_MC_DEALLOCATE()
{
   CLEARVCB

   vcb.deallocate.opcode = AP_M_DEALLOCATE;
   vcb.deallocate.opext = AP_MAPPED_CONVERSATION;
   memcpy(&(vcb.deallocate.tp_id), TPid, 8);
   vcb.deallocate.conv_id = Convid;
   if (ConfirmEvery == 0)
   {
      vcb.deallocate.dealloc_type = AP_FLUSH;
   }
   else
   {
      vcb.deallocate.dealloc_type = AP_SYNC_LEVEL;
   }

}

void Build_MC_SEND_DATA()
{
   CLEARVCB

   //  PERF - GenerateData();

   vcb.snddata.opcode = AP_M_SEND_DATA;
   vcb.snddata.opext = AP_MAPPED_CONVERSATION;
   memcpy(&(vcb.snddata.tp_id), TPid, 8);
   vcb.snddata.conv_id = Convid;
   vcb.snddata.dlen = SendSize;
   vcb.snddata.dptr = DataPtr;
   vcb.snddata.type = AP_NONE;
}

void Build_MC_SEND_CONVERSATION()
{
   CLEARVCB

   // PERF - GenerateData();

   vcb.sndconv.opcode = AP_M_SEND_CONVERSATION;
   vcb.sndconv.opext = AP_MAPPED_CONVERSATION;
   memcpy(&(vcb.sndconv.tp_id), TPid, 8);
   vcb.sndconv.rtn_ctl = AP_WHEN_SESSION_ALLOCATED;
   memcpy(&(vcb.sndconv.plu_alias), RemoteLUAlias, 8);
   memcpy(&(vcb.sndconv.mode_name), ModeName, 8);
   memcpy(&(vcb.sndconv.tp_name), RemoteTPName, 64);
   vcb.sndconv.security = AP_NONE;
   vcb.sndconv.dlen = SendSize;
   vcb.sndconv.dptr = DataPtr;
}

void Build_MC_CONFIRM()
{
   CLEARVCB

   vcb.confirm.opcode = AP_M_CONFIRM;
   vcb.confirm.opext = AP_MAPPED_CONVERSATION;
   memcpy(&(vcb.confirm.tp_id), TPid, 8);
   vcb.confirm.conv_id = Convid;
}
#else

void Build_RECEIVE_ALLOCATE()
{
   CLEARVCB

   vcb.rcvalloc.opcode = AP_RECEIVE_ALLOCATE;
   memcpy(&(vcb.rcvalloc.tp_name), LocalTPName, 64);
}

void Build_MC_RECEIVE_AND_WAIT()
{
   CLEARVCB

   vcb.rcvwait.opcode = AP_M_RECEIVE_AND_WAIT;
   vcb.rcvwait.opext = AP_MAPPED_CONVERSATION;
   memcpy(&(vcb.rcvwait.tp_id),TPid,8);
   vcb.rcvwait.conv_id = Convid;
   vcb.rcvwait.rtn_status = AP_YES;
   vcb.rcvwait.max_len = 0xFFFF;
   vcb.rcvwait.dptr = DataPtr;
}

void Build_MC_CONFIRMED()
{
   CLEARVCB

   vcb.confirmed.opcode = AP_M_CONFIRMED;
   vcb.confirmed.opext = AP_MAPPED_CONVERSATION;
   memcpy(&(vcb.confirmed.tp_id),TPid,8);
   vcb.confirmed.conv_id = Convid;
}
#endif

void Build_TP_ENDED()
{
   CLEARVCB

   vcb.tpend.opcode = AP_TP_ENDED;
   memcpy(&(vcb.tpend.tp_id), TPid, 8);
   vcb.tpend.type = AP_SOFT;
}

#ifdef SENDTP
void Send_Run_Details()
{
   /**************************************************************************/
   /* Issues a Send_Conversation verb to send the number of conversations to */
   /* be done to the other side.                                             */
   /**************************************************************************/
   CLEARVCB

   vcb.sndconv.opcode = AP_M_SEND_CONVERSATION;
   vcb.sndconv.opext = AP_MAPPED_CONVERSATION;
   memcpy(&(vcb.sndconv.tp_id), TPid, 8);
   vcb.sndconv.rtn_ctl = AP_WHEN_SESSION_ALLOCATED;
   memcpy(&(vcb.sndconv.plu_alias), RemoteLUAlias, 8);
   memcpy(&(vcb.sndconv.mode_name), ModeName, 8);
   memcpy(&(vcb.sndconv.tp_name), RemoteTPName, 64);
   vcb.sndconv.security = AP_NONE;
   vcb.sndconv.dlen = sizeof(unsigned short);
   vcb.sndconv.dptr = DataPtr;
#ifdef WIN32
   *((unsigned short UNALIGNED *) DataPtr) = NumConversations;
#else
   *((unsigned short far *)DataPtr) = NumConversations;
#endif

   APPC((long)(vcbptr));
   if (vcb.sndconv.primary_rc != AP_OK)
   {
      TPDead = TRUE;
   }
}
#else
void Get_Run_Details()
{
   /**************************************************************************/
   /* Issues a Receive_Allocate and Receive_and_wait verb to get the number  */
   /* of conversations to be done.                                           */
   /* Returns true if this was successful, false otherwise.                  */
   /**************************************************************************/
   switch (GetStage)
   {
      case 0:

         CLEARVCB
         vcb.rcvalloc.opcode = AP_RECEIVE_ALLOCATE;
         memcpy(&(vcb.rcvalloc.tp_name), LocalTPName, 64);
         async_corr=WinAsyncAPPC(hWndMain, (long)(char FAR *)(vcbptr));
         GetStage = 1;
         break;

      case 1:

         if (vcb.rcvalloc.primary_rc != AP_OK)
         {
            TPDead = TRUE;
         }
         else
         {
            memcpy(TPid,&(vcb.rcvalloc.tp_id),8);
            Convid = vcb.rcvalloc.conv_id;
            CLEARVCB
            vcb.rcvwait.opcode = AP_M_RECEIVE_AND_WAIT;
            vcb.rcvwait.opext = AP_MAPPED_CONVERSATION;
            memcpy(&(vcb.rcvwait.tp_id),TPid,8);
            vcb.rcvwait.conv_id = Convid;
            vcb.rcvwait.rtn_status = AP_YES;
            vcb.rcvwait.max_len = sizeof(unsigned short);
            vcb.rcvwait.dptr = DataPtr;
            async_corr=WinAsyncAPPC(hWndMain, (long)(char FAR *)(vcbptr));
            GetStage = 2;
         }
         break;

      case 2:

         if (vcb.rcvwait.primary_rc != AP_DEALLOC_NORMAL)
         {
            TPDead = TRUE;
         }
         else
         {
#ifdef WIN32
            NumConversations = *((unsigned short UNALIGNED *)DataPtr);
#else
            NumConversations = *((unsigned short far *)DataPtr);
#endif
            CLEARVCB
            vcb.tpend.opcode = AP_TP_ENDED;
            memcpy(&(vcb.tpend.tp_id), TPid, 8);
            vcb.tpend.type = AP_SOFT;
            async_corr=WinAsyncAPPC(hWndMain, (long)(char FAR *)(vcbptr));
            GetStage = 3;
         }
         break;

      case 3:

         if (vcb.tpend.primary_rc != AP_OK)
         {
            TPDead = TRUE;
         }
         else
         {
            GotNumConv = TRUE;
            vcb.hdr.opcode = 0x0000;
            async_corr = 0;
            PostMessage(hWndMain, ASYNC_COMPLETE, (WPARAM)async_corr,
                                                 (LPARAM)(char FAR *)(vcbptr));
         }
         break;
   }
}
#endif

/*****************************************************************************/
/* ProcessReturns - Checks return codes from the last verb to complete and   */
/*                  saves off any useful information. If the return code is  */
/*                  bad then we just die.                                    */
/*****************************************************************************/

void ProcessReturns()
{
   if ( (  vcb.hdr.primary_rc != AP_OK) &&
       !( (vcb.hdr.opcode == AP_M_RECEIVE_AND_WAIT)&&
          (vcb.hdr.primary_rc == AP_DEALLOC_NORMAL)  )  )
   {
      TPDead = TRUE;
   }
   else
   {
      switch (vcb.hdr.opcode)
      {
         case AP_TP_ENDED:
            break;
#ifdef SENDTP
         case AP_TP_STARTED:
            memcpy(TPid,&(vcb.tpstart.tp_id),8);
            break;

         case AP_M_SEND_CONVERSATION:
            break;

         case AP_M_ALLOCATE:
            Convid = vcb.allocate.conv_id;
            break;

         case AP_M_SEND_DATA:
            break;

         case AP_M_DEALLOCATE:
            Convid = 0;
            break;

         case AP_M_CONFIRM:
            break;
#else
         case AP_RECEIVE_ALLOCATE:
            memcpy(TPid,&(vcb.rcvalloc.tp_id),8);
            Convid = vcb.rcvalloc.conv_id;
            break;

         case AP_M_RECEIVE_AND_WAIT:
            break;

         case AP_M_CONFIRMED:
            break;
#endif
         default:
            TPDead = TRUE;
#ifdef WIN32
            DebugBreak();
#endif
            break;
      }
   }
}

/*****************************************************************************/
/* ReadConfig - Reads config info from SENDTP.CFG also allocates buffer for  */
/*              sending                                                      */
/*****************************************************************************/
void ReadConfig()
{
#ifdef SENDTP
   char buffer[200];
#endif
#ifdef DOS5
   unsigned short selector;
#endif

   SendSize = 0xFFFF;

   if (!ReadString("LocalTPName",LocalTPName,64))
   {
#ifdef SENDTP
      strncpy(LocalTPName,"SENDTP",6);
#else
      strncpy(LocalTPName,"RECVTP",6);
#endif
   }
   PadString(LocalTPName,64);
   CONV_A_TO_E(LocalTPName,64);

#ifdef SENDTP
   if (!ReadString("ResultFile",FileName,60))
   {
      strcpy(FileName,"C:\\SENDTP.OUT");
   }
   NumConversations=1;
   if (ReadString("NumConversations",buffer,200))
   {
      NumConversations=atoi(buffer);
   }
   if (!ReadString("LocalLUAlias",LocalLUAlias,8))
   {
      strncpy(LocalLUAlias,"SENDLU",8);
   }
   PadString(LocalLUAlias,8);
   if (!ReadString("RemoteLUAlias",RemoteLUAlias,8))
   {
      strncpy(RemoteLUAlias,"RECVLU",8);
   }
   PadString(RemoteLUAlias,8);
   if (!ReadString("ModeName",ModeName,8))
   {
      strncpy(ModeName,"#INTER",8);
   }
   PadString(ModeName,8);
   CONV_A_TO_E(ModeName,8);
   if (!ReadString("RemoteTPName",RemoteTPName,64))
   {
      strncpy(RemoteTPName,"RECVTP",6);
   }
   PadString(RemoteTPName,64);
   CONV_A_TO_E(RemoteTPName,64);
   NumSends=2;
   if (ReadString("NumSends",buffer,200))
   {
      NumSends=atoi(buffer);
   }
   ConfirmEvery=1;
   if (ReadString("ConfirmEvery",buffer,200))
   {
      ConfirmEvery=atoi(buffer);
   }
   SendSize=1024;
   if (ReadString("SendSize",buffer,200))
   {
      SendSize=atoi(buffer);
   }
   SendConversation=FALSE;
   if (ReadString("SendConversation",buffer,200))
   {
      SendConversation = (*(strupr(buffer)) == 'Y');
   }
#endif
#ifdef DOS5
   DosAllocSeg(SendSize,&selector,1);
   DataPtr = MAKEP(selector,0);
#else
 #ifdef WIN32
   DataPtr = malloc(SendSize);
 #else
   DataPtr = (char far *)GlobalLock(GlobalAlloc(GPTR,SendSize));
 #endif
#endif
   ResultBuf = (NumConversations == 0) ?
               NULL : malloc(NumConversations * sizeof(RESULT));
   ResultPtr = ResultBuf;
}

/*****************************************************************************/
/* CONV_A_TO_E - ASCII to EBCDIC conversion routine.                         */
/*****************************************************************************/
void CONV_A_TO_E(char FAR * string,int length)
{
   memset(cnvtptr,0,sizeof(cnvt));

   cnvt.opcode       = SV_CONVERT;
   cnvt.direction    = SV_ASCII_TO_EBCDIC;
   cnvt.char_set     = SV_AE;

   cnvt.len          = length;
   cnvt.source       = string;
   cnvt.target       = string;

   ACSSVC_C((long)(char far *) (cnvtptr));      /* Call ACSSVC - go convert! */
}

/*****************************************************************************/
/* CONV_E_TO_A - EBCDIC to ASCII conversion routine.                         */
/*****************************************************************************/
void CONV_E_TO_A(char FAR * string,int length)
{
   memset(cnvtptr,0,sizeof(cnvt));

   cnvt.opcode       = SV_CONVERT;
   cnvt.direction    = SV_EBCDIC_TO_ASCII;
   cnvt.char_set     = SV_AE;
   cnvt.len          = length;
   cnvt.source       = string;
   cnvt.target       = string;

   ACSSVC_C((long)(char FAR *) (cnvtptr));      /* Call ACSSVC - go convert! */
}

/*****************************************************************************/
/* NewConversation - Reset and record timers for this conversation.          */
/*****************************************************************************/
void NewConversation()
{
   RESULT NewTime;

#ifdef SENDTP
   SendCount = 0;
   ConfirmCount =0;
#endif

   if (FirstConv)
   {
      FirstConv = FALSE;
      ConvStarted = GetTickCount();
   }
   else if (ResultPtr != NULL)
   {
      *ResultPtr++ = ((NewTime = GetTickCount()) - ConvStarted);
      ConvStarted = NewTime;
   }
   ConvCount++;
   OUTPUTNUMBER
}

#ifdef SENDTP
/*****************************************************************************/
/* GenerateData    - Fill in data buffer                                     */
/*****************************************************************************/
void GenerateData()
{
   int i;
   int div;
   int rem;
   char FAR * dptr;

   dptr = DataPtr;
   div = SendSize / 5;
   rem = SendSize % 5;

   for (; div--;)
   {
      for (i=4; i--; *dptr++ = datach);
      *dptr++ = '.';
   }
   for (; rem--; *dptr++ = datach);

   datach = (datach=='Z' ? 'A' : datach + 1);
}

/*****************************************************************************/
/* OutputResults - dump the times of conversations to file                   */
/*****************************************************************************/
void OutputResults()
{
  FILE *h = NULL;
  RESULT FAR * ptr = NULL;
  unsigned short i = 0;
  RESULT TotalTime=0;

  h = fopen(FileName,"w");
  if (h != NULL)
  {
    fprintf(h,"SENDTP Results\n--------------\n\n");
    CONV_E_TO_A(LocalTPName,64);
    fprintf(h,"Local TP Name           = %.64s\n",LocalTPName);
    CONV_E_TO_A(RemoteTPName,64);
    fprintf(h,"Remote TP Name          = %.64s\n",RemoteTPName);
    fprintf(h,"Local LU Alias          = %.8s\n",LocalLUAlias);
    fprintf(h,"Remote LU Alias         = %.8s\n",RemoteLUAlias);
    CONV_E_TO_A(ModeName,8);
    fprintf(h,"ModeName                = %.8s\n",ModeName);
    fprintf(h,"Number of conversations = %d\n",NumConversations);
    if (!SendConversation)
    {
       fprintf(h,"Sends per conversation  = %d\n",NumSends);
       fprintf(h,"Sends between confirms  = %d\n",ConfirmEvery);
    }
    fprintf(h,"Bytes per send          = %d\n",SendSize);
    fprintf(h,"Use SEND_CONVERSATION   = %s\n",(SendConversation ? "Yes":"No"));
    fprintf(h,"\n");

    ptr = ResultBuf;
    while (ptr < ResultPtr)
    {
TotalTime += *ptr;
      fprintf(h,"Conversation number %d, time = %.3f seconds\n",i++,
                                                (((float) *ptr++) / 1000.0 ));

    }
fprintf( h,"Total Time in Conversation = %.3f seconds\n",(((float)TotalTime)/1000.0) );
    fclose(h);                                                        /*FILE*/
  }
}

#endif

/*****************************************************************************/
/* ReadString - Get a line of text from the config file.                     */
/*****************************************************************************/
int ReadString(char FAR * lpValueName,char FAR * lpData, int maxlen)
{
   char       buffer[200];
   char      *p = NULL;
   FILE      *h = NULL;
   BOOL       match = FALSE;
   BOOL       eof   = FALSE;
   int        rc = 0;
   int        ch = 0;
   int        i = 0;
   BOOL       gotdata = FALSE;
   char       separators[] = " =\t\n";

#if (defined(WINDOWS)||defined(WIN32))                                 /*PATH*/
                                                                       /*PATH*/
GetModuleFileName( hInst, buffer, sizeof(buffer) );                /*PATH*/
lstrcpy( buffer+lstrlen(buffer) - 4, ".CFG" );                     /*PATH*/
h = fopen( buffer, "r" );                                          /*PATH*/
buffer[0] = '\0';                                                  /*PATH*/
                                                                       /*PATH*/
#else                                                                  /*PATH*/
#ifdef SENDTP
   h = fopen("C:\\sendtp.cfg", "r");
#else
   h = fopen("C:\\recvtp.cfg", "r");
#endif
#endif                                                                 /*PATH*/

   lpValueName=strupr(lpValueName);

   if (h != NULL)
   {
      while ((!match) && (!eof))
      {
         /********************************************************************/
         /* Use fgetc to read a line of text from the file.                  */
         /********************************************************************/
         for (i=0; (i<sizeof(buffer))     &&
                   ((ch=getc(h)) != EOF)  &&
                   ((char)ch != '\n');
                                      i++)
         {
            buffer[i] = (char)ch;
         }
         if ((char)ch == '\n')
         {
            buffer[i++] = (char)ch;
         }
         if (ch == EOF)
         {
            eof = TRUE;
         }
         else
         {
            /*****************************************************************/
            /* Compare the 1st token in the line read with the requested     */
            /* param.                                                        */
            /*****************************************************************/
            if (!strcmpi(strupr(strtok(buffer, separators)), lpValueName))
            {
               match = TRUE;
               /**************************************************************/
               /* Get a pointer to the 2nd token (the value we want)         */
               /**************************************************************/
               p = strtok(NULL, separators);

               /**************************************************************/
               /* Copy the data IF there is some.                            */
               /**************************************************************/
               if (p != NULL)
               {
                  /***********************************************************/
                  /* Force a NULL after the 2nn token                        */
                  /***********************************************************/
                  strtok(NULL, separators);

                  /***********************************************************/
                  /* Copy the data                                           */
                  /***********************************************************/
                  strncpy(lpData, p, maxlen);
                  gotdata = TRUE;
               }
               else
               {
                  gotdata = FALSE;
               }
            }
         }
      }

      if (gotdata)
      {
         rc = 1;
      }

      fclose(h);

   }

return(rc);
}

/*****************************************************************************/
/* PadString - Remove terminating NULL  and pad on the right with spaces     */
/*****************************************************************************/
void PadString(char FAR * string,int length)
{
   char FAR * p;
   if ((p=memchr(string,'\0',length)) != NULL)
   {
      while (p < string+length)
      {
         *p++=' ';
      }
   }
}


SENDRECV.H

/* sendrecv.h */
/* (C) COPYRIGHT DATA CONNECTION LIMITED 1993 */

/*****************************************************************************/
/* Change History                                                            */
/*                                                                           */
/*       04/05/93 NGR Created.                                               */
/* OUT1  02/07/93 NGR Change output string                                   */
/*****************************************************************************/

/* Global variables */

#ifdef DOS5
  #include <os2.h>
  #include <time.h>

  #define GetTickCount clock

#endif

typedef unsigned long RESULT;

#ifdef SENDTP
union
{
   struct appc_hdr             hdr;
   struct tp_started           tpstart;
   struct mc_send_conversation sndconv;
   struct mc_allocate          allocate;
   struct mc_send_data         snddata;
   struct mc_deallocate        deallocate;
   struct mc_confirm           confirm;
   struct tp_ended             tpend;
} vcb;
#else
union
{
   struct appc_hdr             hdr;
   struct receive_allocate     rcvalloc;
   struct mc_receive_and_wait  rcvwait;
   struct mc_confirmed         confirmed;
   struct tp_ended             tpend;
} vcb;
#endif
char FAR * vcbptr;

struct convert cnvt;
char FAR * cnvtptr;

char LocalTPName[64];
unsigned short SendSize;
unsigned short NumConversations;
BOOL TPDead;
BOOL FirstConv;
char FAR * DataPtr;
RESULT ConvStarted;
RESULT FAR * ResultPtr;
RESULT FAR * ResultBuf;
char FileName[60];
unsigned short ConvCount;
char TPid[8];
unsigned long Convid;

#ifdef SENDTP
char LocalLUAlias[8];
char RemoteLUAlias[8];
char ModeName[8];
char RemoteTPName[64];
unsigned short NumSends;
unsigned short ConfirmEvery;
BOOL SendConversation;
char datach;
unsigned short SendCount;
unsigned short ConfirmCount;
#else
BOOL Deallocated;
BOOL GotNumConv;
int GetStage;
#endif

#if (defined(WINDOWS)||defined(WIN32))
HWND hWndMain;
HANDLE async_corr;
unsigned short ASYNC_COMPLETE;
char title[30]={'\0'};
#endif

/* Function prototypes */

#ifndef SYNC
int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine, int nCmdShow);
BOOL InitialiseWinMain(HINSTANCE hInstance);
LONG FAR PASCAL TPWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
#else
void main( int argc, char FAR * argv[]);
#endif
void InitialiseMain(void);
void IssueNextVerb(void);
void ProcessReturns(void);
void ReadConfig(void);
void CONV_A_TO_E(char FAR * string,int length);
void CONV_E_TO_A(char FAR * string,int length);
void NewConversation(void);
int ReadString(char FAR * lpValueName, char FAR * lpData, int maxlen);
void PadString(char FAR * string, int length);
void OutputResults(void);
void Build_TP_ENDED(void);
#ifdef SENDTP
void Build_TP_STARTED(void);
void Build_MC_ALLOCATE(void);
void Build_MC_DEALLOCATE(void);
void Build_MC_SEND_DATA(void);
void Build_MC_SEND_CONVERSATION(void);
void Build_MC_CONFIRM(void);
void GenerateData(void);
void Send_Run_Details(void);
#else
void Build_RECEIVE_ALLOCATE(void);
void Build_MC_RECEIVE_AND_WAIT(void);
void Build_MC_CONFIRMED(void);
void Get_Run_Details(void);
#endif

/* Macros */

#define CLEARVCB memset(vcbptr,0,sizeof(vcb));
#ifdef SYNC
#define OUTPUTNUMBER printf("Conversation %d\n",ConvCount);
#else
#ifdef SENDTP
#define OUTPUTNUMBER sprintf(title,"APPC Send TP %d",ConvCount); \
                     SetWindowText(hWndMain,title);
#else
#define OUTPUTNUMBER sprintf(title,"APPC Receive TP %d",ConvCount); \
                     SetWindowText(hWndMain,title);
#endif
#endif


APPCLIB.C

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winappc.h>
#include <wincsv.h>
#include "appclib.h"
#include "queue.h"

#define memalloc(size) LocalAlloc(LMEM_FIXED, size)
#define memfree(pointer) LocalFree(pointer)

// --- internal functions used by the module ---
void appcerror(vcb_t vcb);
BOOL callappc(vcb_t *vcb);
void conv_a_to_e(char *string, int length);
void memcpypad(char *dest, TCHAR *source, int length);
void memcpya2e(char *dest, TCHAR *source, int length);
int do_receive_and_wait(char *tp_id, unsigned long int conv_id,
    void *data, int maxlen, BOOL *dealloc);
void do_send_data(char *tp_id, unsigned long int conv_id, void *data, int len);
void do_confirm(char *tp_id, unsigned long int conv_id);
void do_prepare_to_receive(char *tp_id, unsigned long int conv_id);
void do_get_fqplu_name(char *tp_id, unsigned long int conv_id,
   char *fqplu_name);
void do_tp_started(TCHAR *lu_alias, TCHAR *tp_name, char *tp_id);
unsigned long int do_mc_allocate(TCHAR *lu_alias, TCHAR *tp_name,
     TCHAR *mode_name, char *tp_id);
void do_deallocate(char *tp_id, unsigned long int conv_id);
void do_tp_ended(char *tp_id);

queue       *pairq;                     // queue of sessions that have
                                        // both read and write established
HANDLE      hPairqSem;                  // semaphore of number of objects
                                        // in pairq;
HANDLE      hListenThread = NULL;       // handle to listenthread()
HANDLEhShutdownEvent = NULL;// event to signal that the application
// is shutting down
intoutstanding;// number of outstanding APPC reqs
HANDLEhOutstandingZero = NULL;// event set when outstanding goes to
// zero
CRITICAL_SECTION    csPairq;            // critical section for pairq

/*
 * convert string from ascii to ebcdic (overwriting the original string).
 */
void conv_a_to_e(char *string, int length) {
    struct convert cnvt;

    memset(&cnvt, 0, sizeof(cnvt));
    cnvt.opcode = SV_CONVERT;
    cnvt.direction = SV_ASCII_TO_EBCDIC;
    cnvt.char_set = SV_AE;
    cnvt.len = length;
    cnvt.source = string;
    cnvt.target = string;

    ACSSVC_C((long) &cnvt);
}

/*
 * copy a string from <source> to <dest>, padding the end of it with spaces so
 * it takes <length> bytes.
 *
 * input is assumed to be Unicode if UNICODE is defined, output is always Ansi
 */
void memcpypad(char *dest, TCHAR *source, int length) {
#ifdef UNICODE
    char ansisrc[1024];

    // convert the source string from Unicode to ANSI
    wcstombs(ansisrc, source, 1024);

    // copy the ANSI string into the destination
    memset(dest, ' ', length);
    memcpy(dest, ansisrc, strlen(ansisrc));
#else
    memset(dest, ' ', length);
    memcpy(dest, source, strlen(source));
#endif
}

/*
 * copy a string from <source> to <dest>, padding the end of it with spaces
 * so it takes <length> bytes.  convert the <dest> string from ascii to ebcdic
 *
 * input is assumed to be Unicode if UNICODE is defined, output is always Ansi
 */
void memcpya2e(char *dest, TCHAR *source, int length) {
#ifdef UNICODE
    char ansisrc[1024];

    // convert the source string from Unicode to ANSI
    wcstombs(ansisrc, source, 1024);

    // copy the ANSI string into the destination
    memset(dest, ' ', length);
    memcpy(dest, ansisrc, strlen(ansisrc));

    // convert ansi to ebcdic
    conv_a_to_e(dest, length);
#else
    memset(dest, ' ', length);
    memcpy(dest, source, strlen(source));
    conv_a_to_e(dest, length);
#endif
}

/*
 * a standard error handler for APPC errors.  prints out the last verb and the
 * two return codes
 */
void appcerror(vcb_t vcb) {
    char errorbuf[1024];

    printf("\nAPPC Error: Verb = 0x%0x   PriRC = 0x%0x   SecRC = 0x%0x\n",
        APPC_FLIPI(vcb.hdr.opcode), APPC_FLIPI(vcb.hdr.primary_rc),
        APPC_FLIPL(vcb.hdr.secondary_rc));

    GetAppcReturnCode((struct appc_hdr *) &(vcb.hdr), 1024, errorbuf);

    printf("\n%s\n", errorbuf);

appcdestroy();

    ExitProcess(1);
}

/*
 * initialize APPC.  all programs should call this before using any
 * APPC functions
 */
int appcinit(void) {
    WAPPCDATA APPCData;

// create the shutdown event
hShutdownEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (hShutdownEvent == NULL) return -1;

// create the event that is triggered when outstanding goes to zero
// this only gets set when hShutdownEvent has been set and all requests
// are being cancelled
hOutstandingZero = CreateEvent(NULL, FALSE, FALSE, NULL);
if (hOutstandingZero == NULL) return -1;

    return WinAPPCStartup(WINAPPCVERSION, &APPCData);
}

/*
 * kill APPC.  this should be called when a program is done using APPC
 */
BOOL WINAPI appcdestroy(void) {
// if there are any outstanding requests
if (outstanding > 0) {
// tell all threads to cancel their outstanding requests.  when the last
// one cancels its request it will set hOutstandingZero.
SetEvent(hShutdownEvent);
// wait up to 60 seconds for all outstanding requests to be canceled
while
((WaitForSingleObject(hOutstandingZero, 20000) == WAIT_OBJECT_0) &&
 (outstanding > 0));
}

// tell WinAPPC that we're done with it
    return WinAPPCCleanup();
}

// a wrapper around APPC which handles hShutdownEvent for clean shutdowns
BOOL callappc(vcb_t *vcb) {
HANDLE hAsync, objs[2];
DWORD event;

// create an event handle
objs[0] = CreateEvent(NULL, FALSE, FALSE, NULL);

InterlockedIncrement(&outstanding);

// make the APPC call
ASYNCAPPC(objs[0], vcb, hAsync);
// see if the async APPC call failed
if (hAsync == 0) {
InterlockedDecrement(&outstanding);
SetLastError(APPCLIB_ASYNC_FAILED);
return FALSE;
}

// wait for either the APPC call to complete or the hShutdownEvent to be
// triggered
objs[1] = hShutdownEvent;
event = WaitForMultipleObjects(2, objs, FALSE, INFINITE) - WAIT_OBJECT_0;
switch (event) {
case 0:// APPC call completed
InterlockedDecrement(&outstanding);
break;
case 1:// shutdown event triggered
// cancel the outstanding request
WinAPPCCancelAsyncRequest(hAsync);
// if this is the last cancel than let appcdestroy() know that
if (InterlockedDecrement(&outstanding) <= 0) {
SetEvent(hOutstandingZero);
}
break;
case WAIT_FAILED:
SetLastError(APPCLIB_NO_INIT);
return FALSE;
}
CloseHandle(objs[0]);
CloseHandle(hAsync);

return TRUE;
}

/*
 * execute a MC_RECEIVE_AND_WAIT verb with the given TP ID and Conversation ID
 * data is copied into the buffer at <data> for a maximum of <maxlen> bytes.
 * Returns the number of bytes actually read.
 *
 * If the sender requests confirmation a MC_CONFIRM verb is send to them.
 * if the conversation gets deallocated then dealloc is set to true.
 */
int do_receive_and_wait(char *tp_id, unsigned long int conv_id,
                        void *data, int maxlen, BOOL *dealloc) {
    vcb_t vcb;
    int read;

    CLEARVCB(vcb);

    *dealloc = FALSE;

    vcb.rcvwait.opcode = AP_M_RECEIVE_AND_WAIT;
    vcb.rcvwait.opext = AP_MAPPED_CONVERSATION;
    memcpy(&(vcb.rcvwait.tp_id), tp_id, 8);
    vcb.rcvwait.conv_id = conv_id;
    vcb.rcvwait.rtn_status = AP_YES;
    vcb.rcvwait.max_len = maxlen;
    vcb.rcvwait.dptr = data;

    callappc(&vcb);
    APPCDBG(printf("vcb.rcvwait.what_rcvd = 0x%x\n", APPC_FLIPI(vcb.rcvwait.what_rcvd)));
    APPCDBG(printf("vcb.rcvwait.primary_rc = 0x%x\n", APPC_FLIPI(vcb.rcvwait.primary_rc)));
    APPCDBG(printf("vcb.rcvwait.dlen = %i\n", vcb.rcvwait.dlen));
    read = vcb.rcvwait.dlen;

    if ((vcb.rcvwait.primary_rc == AP_DEALLOC_NORMAL) ||
        (vcb.rcvwait.primary_rc == AP_DEALLOC_ABEND)) {
        do_tp_ended(tp_id);
        *dealloc = TRUE;
    } else if (vcb.rcvwait.primary_rc != AP_OK) {
        appcerror(vcb);
    } else if ((vcb.rcvwait.what_rcvd == AP_CONFIRM_DEALLOCATE) ||
               (vcb.rcvwait.what_rcvd == AP_DATA_COMPLETE_CONFIRM_DEALL)) {
        /* confirm deallocation */
        CLEARVCB(vcb);
        vcb.confirmed.opcode = AP_M_CONFIRMED;
        vcb.confirmed.opext = AP_MAPPED_CONVERSATION;
        memcpy(vcb.confirmed.tp_id, tp_id, 8);
        vcb.confirmed.conv_id = conv_id;
        callappc(&vcb);
        if (vcb.confirmed.primary_rc != AP_OK) appcerror(vcb);

        do_tp_ended(tp_id);
        *dealloc = TRUE;
    } else if ((vcb.rcvwait.what_rcvd == AP_CONFIRM_WHAT_RECEIVED) ||
               (vcb.rcvwait.what_rcvd == AP_CONFIRM_SEND) ||
               (vcb.rcvwait.what_rcvd == AP_DATA_COMPLETE_CONFIRM) ||
               (vcb.rcvwait.what_rcvd == AP_DATA_CONFIRM)) {
        /* do a confirmation if one was requested */
        CLEARVCB(vcb);
        vcb.confirmed.opcode = AP_M_CONFIRMED;
        vcb.confirmed.opext = AP_MAPPED_CONVERSATION;
        memcpy(vcb.confirmed.tp_id, tp_id, 8);
        vcb.confirmed.conv_id = conv_id;
        callappc(&vcb);
        if (vcb.confirmed.primary_rc != AP_OK) appcerror(vcb);
    }

    return read;
}

/*
 * do a MC_SEND_DATA verb with the given TP ID, Conversation ID and <len>
 * bytes of data at the <data> pointer.  asks for a confirmation upon
 * sending.
 */
void do_send_data(char *tp_id, unsigned long int conv_id, void *data, int len) {
    vcb_t vcb;

    CLEARVCB(vcb);

    vcb.snddata.opcode = AP_M_SEND_DATA;
    vcb.snddata.opext = AP_MAPPED_CONVERSATION;
    memcpy(vcb.snddata.tp_id, tp_id, 8);
    vcb.snddata.conv_id = conv_id;
    vcb.snddata.dlen = len;
    vcb.snddata.dptr = data;
    vcb.snddata.type = AP_SEND_DATA_CONFIRM;

    callappc(&vcb);

    if (vcb.snddata.primary_rc != AP_OK) appcerror(vcb);
}

/*
 * ask for a confirmation on a given TP ID and Conversation ID.  used for
 * syncronizing data flow.
 */
void do_confirm(char *tp_id, unsigned long int conv_id) {
    vcb_t vcb;

    CLEARVCB(vcb);
    vcb.confirm.opcode = AP_M_CONFIRM;
    vcb.confirm.opext = AP_MAPPED_CONVERSATION;
    memcpy(vcb.confirm.tp_id, tp_id, 8);
    vcb.confirm.conv_id = conv_id;

    callappc(&vcb);

    if (vcb.confirm.primary_rc != AP_OK) appcerror(vcb);
}

/*
 * switch the conversation into a receive state and ask the partner TP
 * to confirm this.
 */
void do_prepare_to_receive(char *tp_id, unsigned long int conv_id) {
    vcb_t vcb;

    CLEARVCB(vcb);
    vcb.torec.opcode = AP_M_PREPARE_TO_RECEIVE;
    vcb.torec.opext = AP_MAPPED_CONVERSATION;
    vcb.torec.ptr_type = AP_SYNC_LEVEL;
    vcb.torec.locks = AP_SHORT;
    memcpy(vcb.torec.tp_id, tp_id, 8);
    vcb.torec.conv_id = conv_id;

    callappc(&vcb);

    if (vcb.confirm.primary_rc != AP_OK) appcerror(vcb);
}

/*
 * wait on a TP name for a MC_ALLOCATE request.  the TP ID is written into
 * the buffer at <tp_id>, the conversation ID is returned.
 */
void do_get_fqplu_name(char *tp_id, unsigned long int conv_id,
                       char *fqplu_name) {
    vcb_t vcb;

    CLEARVCB(vcb);
    vcb.getattrib.opcode = AP_M_GET_ATTRIBUTES;
    vcb.getattrib.opext = AP_MAPPED_CONVERSATION;
    memcpy(vcb.getattrib.tp_id, tp_id, 8);
    vcb.getattrib.conv_id = conv_id;

    callappc(&vcb);
    if (vcb.rcvalloc.primary_rc != AP_OK) appcerror(vcb);

    memcpy(fqplu_name, vcb.getattrib.fqplu_name, 17);
}

/*
 * start a new TP.  this registers the LU alias and TP name for your TP
 * to use and returns a TP ID
 */
void do_tp_started(TCHAR *lu_alias, TCHAR *tp_name, char *tp_id) {
    vcb_t vcb;

    CLEARVCB(vcb);
    vcb.tpstart.opcode = AP_TP_STARTED;
    memcpypad(vcb.tpstart.lu_alias, lu_alias, 8);
    memcpya2e(vcb.tpstart.tp_name, tp_name, 64);

    callappc(&vcb);
    if (vcb.tpstart.primary_rc != AP_OK) appcerror(vcb);

    memcpy(tp_id, vcb.tpstart.tp_id, 8);
}

/*
 * allocate a conversation with a partner TP.  This takes the partner TP's
 * alias, tp name, mode name, and your TP ID and returns a conversation ID.
 *
 * it does a MC_CONFIRM directly afterwords to see if the conversation was
 * really allocated.  if it wasn't it sleeps for 100ms and tries again for
 * up to 20 retries (about 2 seconds).
 */
unsigned long int do_mc_allocate(TCHAR *lu_alias, TCHAR *tp_name,
                                 TCHAR *mode_name, char *tp_id) {
    vcb_t vcb;
    int tries = 0;
    unsigned long int conv_id;

    do {
        CLEARVCB(vcb);
        vcb.allocate.opcode = AP_M_ALLOCATE;
        vcb.allocate.opext = AP_MAPPED_CONVERSATION;
        memcpy(vcb.allocate.tp_id, tp_id, 8);
        vcb.allocate.sync_level = AP_CONFIRM_SYNC_LEVEL;
        vcb.allocate.rtn_ctl = AP_WHEN_SESSION_ALLOCATED;

        memcpypad(vcb.allocate.plu_alias, lu_alias, 8);
        memcpya2e(vcb.allocate.mode_name, mode_name, 8);
        memcpya2e(vcb.allocate.tp_name, tp_name, 64);
        vcb.allocate.security = AP_NONE;

        callappc(&vcb);
        conv_id = vcb.allocate.conv_id;
        if (vcb.allocate.primary_rc != AP_OK) appcerror(vcb);

        if (vcb.allocate.primary_rc == AP_ALLOCATION_ERROR) Sleep(100);
    } while ((tries++ < 20) &&
             (vcb.allocate.primary_rc == AP_ALLOCATION_ERROR));

    if (vcb.allocate.primary_rc != AP_OK) appcerror(vcb);

    return conv_id;
}

/*
 * deallocate a conversation.  should only be called with a TP ID and
 * conversation ID that are in the send state.
 */
void do_deallocate(char *tp_id, unsigned long int conv_id) {
    vcb_t vcb;

    CLEARVCB(vcb);
    vcb.dealloc.opcode = AP_M_DEALLOCATE;
    vcb.dealloc.opext = AP_MAPPED_CONVERSATION;
    memcpy(vcb.confirm.tp_id, tp_id, 8);
    vcb.confirm.conv_id = conv_id;
    vcb.dealloc.dealloc_type = AP_SYNC_LEVEL;

    callappc(&vcb);

    if (vcb.dealloc.primary_rc != AP_OK) appcerror(vcb);
}

void do_tp_ended(char *tp_id) {
    vcb_t vcb;

    CLEARVCB(vcb);
    vcb.tpend.opcode = AP_TP_ENDED;
    vcb.tpend.type = AP_SOFT;
    memcpy(vcb.confirm.tp_id, tp_id, 8);

    callappc(&vcb);

    if (vcb.dealloc.primary_rc != AP_OK) appcerror(vcb);
}

DWORD WINAPI listenthread(TCHAR *tpname) {
    vcb_t       vcb_r, vcb_w;               // verb control block for each sess
    TCHAR       tpname_r[9], tpname_w[9];   // tp name for each session
    HANDLE      h_event_r, h_event_w;       // event handles for each session
    HANDLE      h_async_r, h_async_w;       // async task handles for each sess
    HANDLE      objs[3];                    // array of handles to wait for
    DWORD       event;                      // which event returned
    tpconvid_t  tpconv;                     // temporary tpconv pointer
    name_time_t name_time;                  // logical unit of work & time key
    int         dread;
    BOOL        dealloc;                    // deallocation flag on session
    queue       *readq;                     // queue of sessions that have
                                            // read establish, but not write
    readnode_t  readnode;                   // node for readq
    unsigned long int w_conv_id;            // conversation id for write sess
    unsigned char w_tp_id[8];               // tp_id for write session
    qnode       *n;                         // temp node for searching queues

    // create the event handles for each session
    h_event_r = CreateEvent(NULL, FALSE, FALSE, NULL);
    h_event_w = CreateEvent(NULL, FALSE, FALSE, NULL);
    if ((h_event_r == NULL) || (h_event_w == NULL)) {
        printf("appclib: CreateEvent() failed near line %i\n", __LINE__);
        return FALSE;
    }

    // create the q
    readq = newq();
    if (readq == NULL) {
        printf("appclib: newq() failed near line %i\n", __LINE__);
        return FALSE;
    }

    // we actually use two different TP names, one for each session.
    // the name that the server reads on ends in a R, the name that the
    // server writes on ends in a W

//
// last minute change to ensure we work as an autostarted TP where
// the tp name we issue in the rcv_alloc must match the TP name we
// registered as with Service Control Manager.  On the Server read
// tp we do not alter the name.  The server write TP is still altered.
// will most likely not work if the TP name is 8 chars long and ends
// in a W.
//
    memcpy(tpname_r, tpname, 8 * sizeof(TCHAR));
//    tpname_r[7] = 0;
//    lstrcat(tpname_r, TEXT("R"));

    memcpy(tpname_w, tpname, 7 * sizeof(TCHAR));
    tpname_w[7] = 0;
    lstrcat(tpname_w, TEXT("W"));

    // start an receive_allocate on the read tp
InterlockedIncrement(&outstanding);
    CLEARVCB(vcb_r);
    vcb_r.rcvalloc.opcode = AP_RECEIVE_ALLOCATE;
    memcpya2e(vcb_r.rcvalloc.tp_name, tpname_r, 64);
    ASYNCAPPC(h_event_r, &vcb_r, h_async_r);
    if (h_async_r == 0) {
InterlockedDecrement(&outstanding);
        return FALSE;
    }

    // start an receive_allocate on the write tp
InterlockedIncrement(&outstanding);
    CLEARVCB(vcb_w);
    vcb_w.rcvalloc.opcode = AP_RECEIVE_ALLOCATE;
    memcpya2e(vcb_w.rcvalloc.tp_name, tpname_w, 64);
    ASYNCAPPC(h_event_w, &vcb_w, h_async_w);
    if (h_async_w == 0) {
InterlockedDecrement(&outstanding);
        printf("appclib: WinAsyncAPPCEx failed with verb 0x%x near line %i\n",
            APPC_FLIPI(vcb_r.rcvalloc.opcode), __LINE__);
        return FALSE;
    }

    while (TRUE) {
        objs[0] = h_event_r;
        objs[1] = h_event_w;
objs[2] = hShutdownEvent;
        event = WaitForMultipleObjects(3, objs, FALSE, INFINITE)-WAIT_OBJECT_0;
        switch (event) {
            case 0:// someone connected to reader
InterlockedDecrement(&outstanding);

// check the APPC return codes
if (vcb_r.rcvalloc.primary_rc != AP_OK) appcerror(vcb_r);

                // allocate a new readnode struct and the tpconv structure
                // inside it.  save the tp_id and conv_id into the tpconv
                // structure
                readnode = (readnode_t) memalloc(sizeof(struct readnode_st));
                readnode->tpconv =
(tpconvid_t) memalloc(sizeof(struct tpconvid_st));
                memcpy(readnode->tpconv->r_tp_id, vcb_r.rcvalloc.tp_id, 8);
                readnode->tpconv->r_conv_id = vcb_r.rcvalloc.conv_id;

                // start another receive_allocate on the read TP
InterlockedIncrement(&outstanding);
                CLEARVCB(vcb_r);
                vcb_r.rcvalloc.opcode = AP_RECEIVE_ALLOCATE;
                memcpya2e(vcb_r.rcvalloc.tp_name, tpname_r, 64);
                ASYNCAPPC(h_event_r, &vcb_r, h_async_r);
    if (h_async_r == 0) {
InterlockedDecrement(&outstanding);
        return FALSE;
    }

                // receive_and_wait for the name_time key for this session
                dread = do_receive_and_wait(readnode->tpconv->r_tp_id,
                    readnode->tpconv->r_conv_id, &(readnode->name_time),
                    sizeof(name_time_t), &dealloc);
                if ((dread != sizeof(name_time_t)) || (dealloc)) {
                    // close session
memfree(readnode->tpconv);
memfree(readnode);
                    break;
                }

                // add all of this to the readq
                addtoq(readq, (void *) readnode);
                break;
            case 1:// someone connected to writer
InterlockedDecrement(&outstanding);

// check the APPC return codes
if (vcb_w.rcvalloc.primary_rc != AP_OK) appcerror(vcb_w);

                // save the tp id and conversation id for this session
                memcpy(w_tp_id, vcb_w.rcvalloc.tp_id, 8);
                w_conv_id = vcb_w.rcvalloc.conv_id;

                // start another receive_allocate on the write TP
InterlockedIncrement(&outstanding);
                CLEARVCB(vcb_w);
                vcb_w.rcvalloc.opcode = AP_RECEIVE_ALLOCATE;
                memcpya2e(vcb_w.rcvalloc.tp_name, tpname_w, 64);
                ASYNCAPPC(h_event_w, &vcb_w, h_async_w);
    if (h_async_w == 0) {
InterlockedDecrement(&outstanding);
        return FALSE;
    }

                // receive and wait for the name_time key for this session
                dread = do_receive_and_wait(w_tp_id, w_conv_id, &name_time,
                    sizeof(name_time_t), &dealloc);
                if ((dread != sizeof(name_time_t)) || (dealloc)) {
                    // close session
                    break;
                }

                // find the matching name_time key in the readq
                for (n = readq->head; n != NULL; n = n->next) {
                    if (memcmp(&name_time,
                        &(((readnode_t) (n->ptr))->name_time),
                        sizeof(name_time_t)) == 0) break;
                }
                if (n == NULL) {
                    // close session
                    break;
                }

                readnode = (readnode_t) removeqnodefromq(readq, n);
                tpconv = readnode->tpconv;
                memfree(readnode);
                memcpy(tpconv->w_tp_id, w_tp_id, 8);
                tpconv->w_conv_id = w_conv_id;
                tpconv->r_valid = TRUE;
                tpconv->w_valid = TRUE;

                // receive and wait to confirm data turnaround
                do_receive_and_wait(tpconv->w_tp_id, tpconv->w_conv_id,
NULL, 0, &dealloc);

                // add it to the pairq and signal that there is one more
                // pair available
                EnterCriticalSection(&csPairq);
                addtoq(pairq, (void *) tpconv);
                ReleaseSemaphore(hPairqSem, 1, NULL);
                LeaveCriticalSection(&csPairq);
                break;
case 2:// shutdown event
WinAPPCCancelAsyncRequest(h_async_r);
InterlockedDecrement(&outstanding);
WinAPPCCancelAsyncRequest(h_async_w);
if (InterlockedDecrement(&outstanding) <= 0)
SetEvent(hOutstandingZero);
return FALSE;
        }
    }
}

/*
 * ---- functions to setup easy two-way APPC communication ----
 */

/*
 * listen on a TP name for incoming conversations.
 *
 * each TP can only listen to one tpname at a time (because this only
 * spawns one listenthread).  If a TP needs to be written that can handle
 * listening to multiple TP names in one run then this will need to be
 * modified to make one listenthread() for each appclisten() that gets
 * called with a unique TP name.
 *
 * returns a tpconvid that can be used with appcread, appcwrite and
 * appcclose.
 */
tpconvid_t appclisten(TCHAR *tpname) {
    tpconvid_t tpconv;
    DWORD tid;

    APPCDBG(printf("appclisten(%ws) starting\n", tpname));

    if (hListenThread == NULL) {
        APPCDBG(printf("appclisten: creating listenthread\n"));
        // create the semaphore for the pairq
        hPairqSem = CreateSemaphore(NULL, 0, 100000, NULL);
        if (hPairqSem == NULL) return NULL;

        // create the pairq
        pairq = newq();
        if (pairq == NULL) return NULL;

        // create a critical section for dealing with the pairq
        InitializeCriticalSection(&csPairq);

        // create the listen thread
        hListenThread = CreateThread(NULL, 0,
            (LPTHREAD_START_ROUTINE) listenthread, tpname, 0, &tid);
        if (hListenThread == NULL) return NULL;
    }

    // wait for the listen thread to put a tpconv pair into the pairq
    WaitForSingleObject(hPairqSem, INFINITE);

    // remove the first tpconv pair from the pairq and return it
    EnterCriticalSection(&csPairq);
    tpconv = removeheadfromq(pairq);
    LeaveCriticalSection(&csPairq);

    APPCDBG(printf("appclisten done\n", tpname));

    return tpconv;
}

/*
 * connect to a remote server.  this allocates two conversations, one
 * to write on and one to read on.
 *
 * the LU's should be listed as partners under SNA Admin with a mode name of
 * <mode_name> between them.
 *
 * returns a tpconvid which can be used with appcread, appcwrite, and
 * appcclose.
 */
tpconvid_t appcconnect(TCHAR *lu_alias, TCHAR *plu_alias,
                       TCHAR *tp_name, TCHAR *loc_tp_name, TCHAR *mode_name) {
    tpconvid_t  tpconv;
    name_time_t name_time;
    TCHAR       tpname_w[9], tpname_r[9];
    TCHAR       loctpname_w[9], loctpname_r[9];

    APPCDBG(printf("appcconnect(%ws, %ws, %ws, %ws) starting\n", lu_alias, plu_alias, tp_name, mode_name));

    // we actually use two different TP names, one for each session.
    // the name that the server reads on ends in a R, the name that the
    // server writes on ends in a W
    memcpy(tpname_r, tp_name, 8 * sizeof(TCHAR));
//    tpname_r[7] = 0;
//    lstrcat(tpname_r, TEXT("R"));

    memcpy(tpname_w, tp_name, 7 * sizeof(TCHAR));
    tpname_w[7] = 0;
    lstrcat(tpname_w, TEXT("W"));

    memcpy(loctpname_r, loc_tp_name, 8 * sizeof(TCHAR));
//    loctpname_r[7] = 0;
//    lstrcat(loctpname_r, TEXT("R"));

    memcpy(loctpname_w, loc_tp_name, 7 * sizeof(TCHAR));
loctpname_w[7] = 0;
    lstrcat(loctpname_w, TEXT("W"));

    tpconv = (tpconvid_t) memalloc(sizeof(struct tpconvid_st));
    if (tpconv == NULL) {
        printf("appclib: couldn't allocate memory for tpconvid_t\n");
        exit(1);
    }

    // connect the first session
    do_tp_started(lu_alias, loctpname_w, tpconv->w_tp_id);
    tpconv->w_conv_id = do_mc_allocate(plu_alias, tpname_r, mode_name,
        tpconv->w_tp_id);
    // fill in the logical unit of work/time structure
    do_get_fqplu_name(tpconv->w_tp_id, tpconv->w_conv_id, name_time.fqplu_name);
    name_time.time = GetTickCount();
    // send the fqplu name/time information
    do_send_data(tpconv->w_tp_id, tpconv->w_conv_id, (void *) &name_time,
        sizeof(name_time_t));
    tpconv->w_valid = TRUE;

    // connect the second session
    do_tp_started(lu_alias, loctpname_r, tpconv->r_tp_id);
    tpconv->r_conv_id = do_mc_allocate(plu_alias, tpname_w, mode_name,
        tpconv->r_tp_id);
    // send the fqplu name/time information
    do_send_data(tpconv->r_tp_id, tpconv->r_conv_id, (void *) &name_time,
        sizeof(name_time_t));
    // reverse the conversation
    do_prepare_to_receive(tpconv->r_tp_id, tpconv->r_conv_id);
    tpconv->r_valid = TRUE;

    APPCDBG(printf("appcconnect done\n"));

    return tpconv;
}

/*
 * read from a conversation setup by appcconnect() or appclisten().
 * will read at most <maxlen> bytes into the buffer at <data>.
 */
int appcread(tpconvid_t tpconv, void *data, int maxlen) {
    BOOL dealloc;
    int read;

    APPCDBG(printf("appcread(tpconv, data, %i) starting\n", maxlen));

    if (!tpconv->r_valid) return -1;
    read = do_receive_and_wait(tpconv->r_tp_id, tpconv->r_conv_id, data, maxlen,
        &dealloc);
    if (dealloc) tpconv->r_valid = FALSE;

    APPCDBG(printf("appcread done, returning %i, dealloc = %i, tpconv->r_valid = %i\n", read, dealloc, tpconv->r_valid));

    return read;
}

/*
 * write to a conversation setup by appcconnect() or appclisten().
 * will write at most <maxlen> bytes into the buffer at <data>
 */
void appcwrite(tpconvid_t tpconv, void *data, int len) {
    APPCDBG(printf("appcwrite(tpconv, data, %i) starting\n", len));
    do_send_data(tpconv->w_tp_id, tpconv->w_conv_id, data, len);
    APPCDBG(printf("appcwrite done\n"));
}

int appcvalid(tpconvid_t tpconv) {
    int rc;

    APPCDBG(printf("appcvalid starting and done, returning %i\n", (tpconv->r_valid && tpconv->w_valid)));
    rc = (tpconv->r_valid && tpconv->w_valid);
    return rc;
}

void appcclose(tpconvid_t tpconv) {
    APPCDBG(printf("appcclose(tpconv) starting\n"));

    /* deallocate the write channel */
    if (tpconv->w_valid) {
        do_deallocate(tpconv->w_tp_id, tpconv->w_conv_id);
        do_tp_ended(tpconv->w_tp_id);
        tpconv->w_valid = FALSE;
    }

    APPCDBG(printf("appcclose done\n"));
}


APPCLIB.H

#define CLEARVCB(vcb) memset(&vcb, 0, sizeof(vcb))

#undef DEBUG
#undef DEBUGCALLS

#ifdef DEBUGCALLS
#define ASYNCAPPC(handle, vcbp, hasync) { \
printf("asyncappc: %x . . . ", APPC_FLIPI(vcbp->hdr.opcode)); \
hasync = WinAsyncAPPCEx(handle, (long) vcbp); \
printf("done\n");\
}
#else
#define ASYNCAPPC(handle, vcbp, hasync) \
hasync = WinAsyncAPPCEx(handle, (long) vcbp)
#endif

#ifdef DEBUG
#define APPCDBG(x) x
#else
#define APPCDBG(x)
#endif

#define WINAPPCVERSION 0x0001

typedef union vcb_u {
struct appc_hdr             hdr;
    struct receive_allocate     rcvalloc;
    struct mc_receive_and_wait  rcvwait;
    struct tp_ended             tpend;
struct tp_startedtpstart;
struct mc_allocateallocate;
struct mc_send_datasnddata;
struct mc_flushflush;
struct mc_prepare_to_receivetorec;
struct mc_confirmconfirm;
struct mc_confirmedconfirmed;
struct mc_deallocatedealloc;
struct mc_get_attributesgetattrib;
} vcb_t;

typedef struct tpconvid_st {
/* TP ID and Conversation ID for read conversation */
unsigned charr_tp_id[8];
unsigned long int r_conv_id;
unsigned charr_valid;
/* TP ID and Conversation ID for write conversation */
unsigned charw_tp_id[8];
unsigned long intw_conv_id;
unsigned char w_valid;
} *tpconvid_t;

// this has the fully qualified partner lu name and time used for identifing
// incoming sessions
typedef struct name_time_st {
unsigned charfqplu_name[17];
DWORDtime;
} name_time_t;

typedef struct readnode_st {
name_time_tname_time;
tpconvid_ttpconv;
} *readnode_t;

// --- error codes ---
#define APPCLIB_ERROR_BASE 0x20000000// bit 29 is user error

#define APPCLIB_ASYNC_FAILED APPCLIB_ERROR_BASE + 0// WinAPPCAsyncEx failed
#define APPCLIB_NO_INIT APPCLIB_ERROR_BASE + 1// appcinit() wasn't called

// --- exported function prototypes ---
int appcinit(void);
BOOL WINAPI appcdestroy(void);
tpconvid_t appclisten(TCHAR *tpname);
tpconvid_t appcconnect(TCHAR *lu_alias, TCHAR *plu_alias,
   TCHAR *tp_name, TCHAR *loc_tp_name, TCHAR *mode_name);
int appcread(tpconvid_t tpconv, void *data, int maxlen);
void appcwrite(tpconvid_t tpconv, void *data, int len);
int appcvalid(tpconvid_t tpconv);
void appcclose(tpconvid_t tpconv);


AREMOTE.C

/*
Copyright (c) 1994, 1993 Microsoft Corporation

Module Name:
    Remote.c

Abstract:
    This module contains the main() entry point for Remote.
    Calls the Server or the Client depending on the first parameter.

Author:
    Rajivendra Nath (rajnath) 2-Jan-1993

Environment:
    Console App. User mode.

Revision History:
Alex Wetmore (t-alexwe) 6-Jun-1994
- converted remote to use APPC with Windows SNA Server instead of named
  pipes
*/

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <winappc.h>
#include "appclib.h"
#include "aremote.h"

TCHAR   HostName[HOSTNAMELEN];
TCHAR   *ChildCmd;
TCHAR   *TPName = TEXT("AREMOTE");
TCHAR   *locTPName = TEXT("AREMOTC");
TCHAR   *LocalLU = TEXT("AREMOTE");
TCHAR   *ServerLU;
TCHAR   *mode_name = TEXT("#INTER");
HANDLE MyOutHandle;
BOOL   IsAdvertise=TRUE;
DWORD  ClientToServerFlag;
BOOL   AutoStarted;

TCHARChildCmdBuffer[1024];

TCHAR* ColorList[]={TEXT("black") ,TEXT("blue") ,TEXT("green") ,TEXT("cyan") ,TEXT("red") ,TEXT("purple") ,TEXT("yellow") ,TEXT("white"),
                   TEXT("lblack"),TEXT("lblue"),TEXT("lgreen"),TEXT("lcyan"),TEXT("lred"),TEXT("lpurple"),TEXT("lyellow"),TEXT("lwhite")};

int __cdecl main(void);

WORD GetColorNum(TCHAR* color);
VOID SetColor(WORD attr);

CONSOLE_SCREEN_BUFFER_INFO csbiOriginal;

/*************************************************************/
VOID ErrorExit(TCHAR *str) {
    WRITEF((VBuff,TEXT("Error-%d:%s\n"),GetLastError(),str));
    ExitProcess(1);
}
/*************************************************************/
DWORD ReadFixBytes(HANDLE hRead, TCHAR*  Buffer, DWORD  ToRead,
    DWORD  TimeOut) {   //ignore timeout for timebeing
    DWORD xyzBytesRead=0;
    DWORD xyzBytesToRead=ToRead;
    TCHAR* xyzbuff=Buffer;

    while(xyzBytesToRead!=0)
    {
        if (!ReadFile(hRead,xyzbuff,xyzBytesToRead,&xyzBytesRead,NULL))
        {
            return(xyzBytesToRead);
        }

        xyzBytesToRead-=xyzBytesRead;
        xyzbuff+=xyzBytesRead;
    }
    return(0);

}
/*************************************************************/
VOID DisplayClientHlp() {
    WRITEF((VBuff,TEXT("\n   To Start the CLIENT end of AREMOTE\n")));
    WRITEF((VBuff,TEXT("   ---------------------------------\n")));
    WRITEF((VBuff,TEXT("   Syntax : AREMOTE /C <ServerLU> [/T <TPName>] [/P <TPName>] [/L <LocalLU>]\n")));
    WRITEF((VBuff,TEXT("                                  [/N <# lines>] [/M <Modename>]\n")));
WRITEF((VBuff,TEXT("                                  [/F <Color>] [/B <Color>]\n")));
    WRITEF((VBuff,TEXT("\n")));

    WRITEF((VBuff,TEXT("   <ServerLU>      SNA LU for connecting to Server\n")));

WRITEF((VBuff,TEXT("   [/T <TPName>]   TP name that server is using (default is \"%s\")\n"), TPName));
WRITEF((VBuff,TEXT("   [/P <TPName>]   TP name that client is using (default is \"%s\")\n"), locTPName));
WRITEF((VBuff,TEXT("   [/M <Modename>] The mode by which the Local LU and Server LU are partnered\n")));
WRITEF((VBuff,TEXT("                   (defaults to \"%s\")\n"), mode_name));
WRITEF((VBuff,TEXT("   [/L <LocalLU>]  LU name for local TP to use (default is \"%s\")\n"), LocalLU));
    WRITEF((VBuff,TEXT("   [/N <# lines>]  Number of Lines to Get.\n")));
    WRITEF((VBuff,TEXT("   [/F <color>]    Foreground color eg blue, red, etc.\n")));
    WRITEF((VBuff,TEXT("   [/B <color>]    Background color eg blue, lwhite,etc.\n")));
    WRITEF((VBuff,TEXT("\n")));
    WRITEF((VBuff,TEXT("   Example: aremote /C serverlu\n")));
    WRITEF((VBuff,TEXT("\n")));
    WRITEF((VBuff,TEXT("   To Exit: %cQ (Leaves the Remote Server Running)\n"), COMMANDCHAR));
    WRITEF((VBuff,TEXT("\n")));
}
/*************************************************************/

VOID DisplayServerHlp() {
    WRITEF((VBuff,TEXT("\n   To Start the SERVER end of AREMOTE\n")));
    WRITEF((VBuff,TEXT("   ---------------------------------\n")));
    WRITEF((VBuff,TEXT("   Syntax : AREMOTE /S \"<Cmd>\" [/T <TPName>] [/F <color>] [/B <color>]\n")));
    WRITEF((VBuff,TEXT("\n")));

    WRITEF((VBuff,TEXT("   \"<Cmd>\"         A Text-Mode program that you want to control\n")));
    WRITEF((VBuff,TEXT("                   from another computer.\n")));

WRITEF((VBuff,TEXT("   [/T <TPName>]   TP name that server is using (default is \"%s\")\n"), TPName));
    WRITEF((VBuff,TEXT("   [/F <color>]    Foreground color eg blue, red, etc.\n")));
    WRITEF((VBuff,TEXT("   [/B <color>]    Background color eg blue, lwhite, etc.\n")));

    WRITEF((VBuff,TEXT("\n")));
    WRITEF((VBuff,TEXT("   Example: aremote /S \"cmd\"\n")));
    WRITEF((VBuff,TEXT("\n")));
    WRITEF((VBuff,TEXT("   To Exit: %cK from Client\n"), COMMANDCHAR));
    WRITEF((VBuff,TEXT("\n")));

}

WORD GetColorNum(TCHAR *color) {
    int i;

    _wcslwr(color);
    for (i=0;i<16;i++)
    {
        if (lstrcmp(ColorList[i],color)==0)
        {
            return(i);
        }
    }
    return 0xffff;
}

VOID
SetColor(
    WORD attr
    )
{
COORD  origin={0,0};
    DWORD  dwrite;
    FillConsoleOutputAttribute
    (
    MyOutHandle,attr,csbiOriginal.dwSize.
    X*csbiOriginal.dwSize.Y,origin,&dwrite
    );
    SetConsoleTextAttribute(MyOutHandle,attr);
}

/*************************************************************/
VOID
Errormsg(
    TCHAR* str
    )
{
    WRITEF((VBuff,TEXT("Error (%d) - %s\n"),GetLastError(),str));
}

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

/*
 * this main procedure gets called by the appropriate main depending on
 * if AREMOTE is setup as a service or not.
 */
int remote_main(int argc, TCHAR **argv) {
    WORD  RunType;              // Server or Client end of Remote
    DWORD len=HOSTNAMELEN-1;
    int   i;

    TCHAR  sTitle[100];          // New Title
    TCHAR  orgTitle[100];        // Old Title
    BOOL  bSetAttrib=FALSE;     // Change Console Attributes
    WORD  wAttrib;              // Console Attributes
char  abuff[1024];// temporary ansi buffer

    GetComputerName((LPTSTR)HostName,&len);

    MyOutHandle=GetStdHandle(STD_OUTPUT_HANDLE);

    GetConsoleScreenBufferInfo(MyOutHandle,&csbiOriginal);

    // Parameter Processing
    //
    // For Server:
    // Remote /S <Executable> [Optional Params]
//
// For AutoStarted TP:
// Remote /A <Executable> [Optional Params]
//   - this case is handled in main()
    //
    // For Client:
    // Remote /C <Server LU> [Optional Params]

    if ((argc < 2)||((argv[1][0] != TEXT('/'))&&(argv[1][0] != TEXT('-')))) {
        DisplayServerHlp();
        DisplayClientHlp();
WRITEF((VBuff, TEXT("\nFor server help only: aremote /s")));
WRITEF((VBuff, TEXT("\nFor client help only: aremote /c")));
        return(1);
    }

    switch(tolower(argv[1][1])) {
    case TEXT('c'):
        // Is Client End of Remote
        if ((argc < 3) || ((argv[1][0] != TEXT('/')) && (argv[1][0] != TEXT('-')))) {
            DisplayClientHlp();
            return 1;
        }

ServerLU = _wcsupr(argv[2]);

        RunType=REMOTE_CLIENT;
        break;

    case TEXT('s'):
        // Is Server End of Remote
        if ((argc < 3) || ((argv[1][0] != TEXT('/')) && (argv[1][0] != TEXT('-')))) {
            DisplayServerHlp();
            return 1;
        }

        ChildCmd = argv[2];

        RunType = REMOTE_SERVER;
        break;

    default:
        DisplayServerHlp();
        DisplayClientHlp();
WRITEF((VBuff, TEXT("\nFor server help only: aremote /s")));
WRITEF((VBuff, TEXT("\nFor client help only: aremote /c")));
 return(1);
    }

    //
    // Save Existing Values
    //

    //
    //Colors /f   <ForeGround> /b <BackGround>
    //

    wAttrib = csbiOriginal.wAttributes;

    GetConsoleTitle(orgTitle,sizeof(orgTitle));

    if (RunType == REMOTE_SERVER) {
    //
    // Base Name of Executable
    // For setting the title
    //

        TCHAR *tcmd=ChildCmd;

        while ((*tcmd!=TEXT(' '))    &&(*tcmd!=0))   tcmd++;
        while ((tcmd!=ChildCmd)&&(*tcmd!=TEXT('\\')))tcmd--;

        wsprintf(sTitle,TEXT("%8.8s [Remote /C %s %s]"),tcmd,HostName,TPName);
    }

    //
    //Process Common (Optional) Parameters
    //

    for (i = 3; i < argc; i++) {

        if ((argv[i][0] != TEXT('/')) && (argv[i][0] != TEXT('-'))) {
            WRITEF((VBuff,TEXT("Invalid parameter %s:Ignoring\n"),argv[i]));
            continue;
        }

        switch(tolower(argv[i][1])) {
        case TEXT('n'):   // Only Valid for client End, max number of lines to get from server
            i++;
            if (i >= argc) {
                WRITEF((VBuff,TEXT("Incomplete Param %s..Ignoring\n"),argv[i-1]));
                break;
            }
wcstombs(abuff, argv[i], 1024);
            LinesToSend=(DWORD)atoi(abuff)+1;
            break;

case TEXT('l'):// name of LU for client to use
i++;
if (i >= argc) {
                WRITEF((VBuff,TEXT("Incomplete Param %s..Ignoring\n"),argv[i-1]));
                break;
            }
LocalLU = _wcsupr(argv[i]);
break;

case TEXT('m'):// name of LU-LU connection mode
i++;
if (i >= argc) {
                WRITEF((VBuff,TEXT("Incomplete Param %s..Ignoring\n"),argv[i-1]));
                break;
            }
mode_name = _wcsupr(argv[i]);
break;

case TEXT('t'):// name of tp
i++;
if (i >= argc) {
                WRITEF((VBuff,TEXT("Incomplete Param %s..Ignoring\n"),argv[i-1]));
                break;
            }
TPName = _wcsupr(argv[i]);
break;

   case TEXT('p'):// name of tp
i++;
if (i >= argc) {
                WRITEF((VBuff,TEXT("Incomplete Param %s..Ignoring\n"),argv[i-1]));
                break;
            }
locTPName = _wcsupr(argv[i]);
break;

        case TEXT('b'):   // Background color
            i++;
            if (i >= argc) {
                WRITEF((VBuff,TEXT("Incomplete Param %s..Ignoring\n"),argv[i-1]));
                break;
            }
            {
                WORD col = GetColorNum(argv[i]);
                if (col != 0xffff) {
                    bSetAttrib = TRUE;
                    wAttrib = col << 4 | (wAttrib & 0x000f);
                }
                break;
            }

        case TEXT('f'):   // Foreground color
            i++;
            if (i>=argc) {
                WRITEF((VBuff,TEXT("Incomplete Param %s..Ignoring\n"),argv[i-1]));
                break;
            }
            {
                WORD col = GetColorNum(argv[i]);
                if (col != 0xffff) {
                    bSetAttrib = TRUE;
                    wAttrib = col | (wAttrib & 0x00f0);
                }
                break;
            }

        case TEXT('q'):
            IsAdvertise = FALSE;
            ClientToServerFlag |= 0x80000000;
            break;

        default:
            WRITEF((VBuff,TEXT("Unknown Parameter=%s %s\n"),argv[i-1],argv[i]));
            break;

        }
    }

    //
    //Now Set various Parameters
    //

    //
    //Colors
    //

    SetColor(wAttrib);

    if (RunType==REMOTE_CLIENT) {
        // Start Client (Client.C)
        Client(LocalLU, ServerLU, TPName, locTPName, mode_name);
    }

    if (RunType==REMOTE_SERVER) {
        // Start Server (Server.C)
        Server(ChildCmd, TPName);
    }

    //
    //Reset Colors
    //
    SetColor(csbiOriginal.wAttributes);

    ExitProcess(0);

return0;// satisfy the ppc compiler; statement never reached
}

/*****************************************************************************/
/* The following code makes this TP invokable as an NT service. There are 3  */
/* routines.                                                                 */
/*                                                                           */
/* 1. main. This is the entry point for the process, it sets up a service    */
/*          table entry and then calls StartServiceCtrlDispatcher. This call */
/*          doesn't return, but uses the thread which called it as a         */
/*          control dispatcher for all the services implemented by this      */
/*          process (in this case, just the TP itself).                      */
/*                                                                           */
/* 2. ServiceMain. This is the main entry point for the service itself, the  */
/*          service control dispatcher creates a thread to start at this     */
/*          routine. It must register a service control handler for the      */
/*          service which will be called by the control dispatcher when it   */
/*          has control instructions for the service. It then informs the    */
/*          service control manager that the service is running and finally  */
/*          calls the start of the TP itself. This routine should not return */
/*          until the service is ready to die.                               */
/*                                                                           */
/* 3. ControlHandler. This routine is called by the control dispatcher when  */
/*          it has instructions for the service. We do not respond to any    */
/*          of the instructions as this service should be transitory and not */
/*          actually run for more than a few seconds so we don't need to do  */
/*          anything with the STOP or SHUTDOWN requests.                     */
/*          Note that we MUST call SetServiceStatus, even if the status      */
/*          hasn't changed.                                                  */
/*****************************************************************************/

VOID WINAPI ServiceMain(DWORD dwNumServiceArgs, LPTSTR * lpServiceArgs);
VOID WINAPI ControlHandler(DWORD dwControl);
SERVICE_STATUS_HANDLE stat_hand;
SERVICE_STATUS servstat;

int __cdecl main(void) {
SERVICE_TABLE_ENTRY stab[2];
int argc, i;
TCHAR *argv[50];
TCHARcmdline[1024];

// read the command line and break it out into argc/argv.  We have to do
// this because the standard command line passed into main isn't in Unicode
lstrcpy(cmdline, GetCommandLine());
i = 0; argc = 0;
argv[argc] = &(cmdline[i]);
while (cmdline[i] != 0) {
if (cmdline[i] == TEXT(' ')) {
cmdline[i++] = 0;
argv[++argc] = &(cmdline[i]);
}
i++;
}
argc++;

if ((argc >= 3) &&
    (tolower(argv[1][1]) == TEXT('a')) &&
    ((argv[1][0] == TEXT('/')) || (argv[1][0] == TEXT('-')))) {

AutoStarted = TRUE;

//
// hack to fix NET START AREMOTE bug. Since we're not autostarted
// by an SNA Server component we have no control over the service
// cmd line.  Therefore we copy out the 3rd parameter passed to the
// service process and use it if and only there are less than 3 params
// passed to StartService
//
lstrcpy( ChildCmdBuffer, argv[2] );

//
//
// This is being run as a service
//
//
// Start the control dispatcher. This call gives the SCManager this
// thread for the entire period that this service is running, so that
// it can call us back with service controls. It will spawn a new
// thread to run the service itself, starting at entrypoint ServiceMain
//
stab[0].lpServiceName = TEXT("AREMOTE\0");
stab[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION) ServiceMain;

stab[1].lpServiceName = NULL;
stab[1].lpServiceProc = NULL;

if (!StartServiceCtrlDispatcher(stab)) {
WRITEF((VBuff, TEXT("Remote: This was run as an service\n\n")));
WRITEF((VBuff, TEXT("For command line usage type \"Remote\"\n")));
}
} else {
//
// This is being run as a user-invoked program
//
AutoStarted = FALSE;
remote_main(argc, argv);
}

return 0;
}

/*****************************************************************************/
/* This routine is the entry-point for the service itself the service        */
/* control dispatcher creates a thread to start here when we issue           */
/* StartServiceControlDispatcher.                                            */
/*                                                                           */
/* Inputs:  number of arguments to services, array of strings.               */
/*                                                                           */
/* Outputs: none                                                             */
/*                                                                           */
/*****************************************************************************/
VOID WINAPI ServiceMain(DWORD argc, LPTSTR *argv) {
DWORD rc;

stat_hand = RegisterServiceCtrlHandler(TEXT("AREMOTE\0"),
(LPHANDLER_FUNCTION) ControlHandler);

if (stat_hand == (SERVICE_STATUS_HANDLE)NULL) {
rc = GetLastError();
DebugBreak();
}

/*************************************************************************/
/* Let the SCManager know that we are running.                           */
/*************************************************************************/
servstat.dwServiceType              = SERVICE_WIN32;
servstat.dwCurrentState             = SERVICE_RUNNING;
servstat.dwControlsAccepted= SERVICE_ACCEPT_STOP |
SERVICE_ACCEPT_SHUTDOWN;
servstat.dwWin32ExitCode            = NO_ERROR;
servstat.dwServiceSpecificExitCode  = NO_ERROR;
servstat.dwCheckPoint               = 0;
servstat.dwWaitHint                 = 0;

rc = SetServiceStatus(stat_hand, &servstat);

if (!rc) {
rc = GetLastError();
DebugBreak();
}

{
int new_argc = 3;
TCHAR *new_argv[3];

new_argv[0] = argv[0];
new_argv[1] = (TCHAR *) LocalAlloc(0, 16);
lstrcpy(new_argv[1], TEXT("/s"));

if ( argc < 3 )
{
//
// hack to fix NET START AREMOTE bug. Since we're not autostarted
// by an SNA Server component we have no control over the service
// cmd line.  Therefore we copy out the 3rd parameter passed to the
// service process and use it if and only there are less than 3 params
// passed to StartService
//
new_argv[2] = ChildCmdBuffer;
}
else
{
new_argv[2] = argv[2];
}

remote_main(new_argc, (TCHAR **) new_argv);

LocalFree(new_argv[1]);
}
}

/***************************************************************************/
/* This routine is the callback from the SCManager to handle specific      */
/* service control requests. It MUST call SetServiceStatus before it       */
/* returns, regardless of whether the status has changed.                  */
/*                                                                         */
/* Inputs: service control requested                                       */
/*                                                                         */
/* Outputs: none                                                           */
/*                                                                         */
/***************************************************************************/
VOID WINAPI ControlHandler(DWORD dwControl) {
DWORD rc;

switch (dwControl) {
case SERVICE_CONTROL_STOP :
servstat.dwCurrentState = SERVICE_STOP_PENDING;
servstat.dwWaitHint     = 24000;
break;

case SERVICE_CONTROL_PAUSE :
case SERVICE_CONTROL_CONTINUE :
case SERVICE_CONTROL_INTERROGATE :
servstat.dwWaitHint     = 0;
break;

case SERVICE_CONTROL_SHUTDOWN:
servstat.dwCurrentState = SERVICE_STOP_PENDING;
servstat.dwWaitHint     = 10000;
break;
}

rc=SetServiceStatus(stat_hand, &servstat);
if (!rc) {
rc=GetLastError();
}
}


AREMOTE.H

#define REMOTE_SERVER       1
#define REMOTE_CLIENT       2
#define REMOTE_INIT_MSGTEXT("APPC Remote")

// commands for the remote server begin with this
#define COMMANDCHAR         TEXT('@')
#define CTRLC               3

#define CLIENT_ATTR         FOREGROUND_INTENSITY|FOREGROUND_GREEN|FOREGROUND_RED|BACKGROUND_BLUE
#define SERVER_ATTR         FOREGROUND_INTENSITY|FOREGROUND_GREEN|FOREGROUND_BLUE|BACKGROUND_RED

//
//Some General purpose Macros
//
#define MINIMUM(x,y)          ((x)>(y)?(y):(x))
#define MAXIMUM(x,y)          ((x)>(y)?(x):(y))

// All because printf does not work
// with NT IO redirection
#define WRITEF(wsprintfargs) \
{                                                  \
HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);       \
wchar_tVBuff[256];                                \
DWORD  tmp;                                       \
    wsprintf wsprintfargs;\
    WriteConsole(h, VBuff, lstrlen(VBuff), &tmp, NULL);\
}

#define HOSTNAMELEN         16

#define CHARS_PER_LINE      80

// magic number is version number is 00yyyymm (yyyy = year, mm = month)
#define MAGICNUMBER     0x00199406
#define BEGINMARK       '\xfe'
#define ENDMARK         '\xff'
#define LINESTOSEND     200

#define MAX_SESSION     10

typedef struct
{
    DWORD    MagicNumber;// version of client software
    TCHAR    ClientName[HOSTNAMELEN];// name of client machine
    DWORD    LinesToSend;// lines to send on startup
    DWORD    Flag;// optional flags
}   SESSION_STARTUPINFO;

typedef struct
{
    DWORD MagicNumber; // New Remote
    DWORD FileSize;         // Num bytes sent
}   SESSION_STARTREPLY;

typedef struct
{
    TCHAR    Name[HOSTNAMELEN];     //Name of client Machine;
    BOOL    Active;         //Client at the other end connected
    BOOL    CommandRcvd;    //True if a command recieved
    BOOL    SendOutput;     //True if Sendoutput output
tpconvid_t tpconv;//TP and conversation IDs for client's APPC connection
    HANDLE  rSaveFile;      //Sessions read handle to SaveFile
    HANDLE  hThread;        //Session Thread
    HANDLE  MoreData;       //Event handle set if data available to be read
HANDLEDoClose;//Event saying that this session should close
} SESSION_TYPE;

VOID Server(TCHAR *ChildCmd, TCHAR *PipeName);

VOID Client(TCHAR *local_lu, TCHAR *server_lu, TCHAR *tp_name, TCHAR *loc_tp_name, TCHAR *mode_name);

VOID ErrorExit(TCHAR* str);

VOID DisplayClientHlp();

VOID DisplayServerHlp();

ULONG DbgPrint(PCH Format, ...);

// Timeout is ignored for now
DWORD ReadFixBytes(HANDLE hRead, TCHAR *Buffer, DWORD ToRead, DWORD  TimeOut);

VOID Errormsg(TCHAR* str);

externTCHAR   HostName[];
externTCHAR   *ChildCmd;
externDWORD  ClientToServerFlag;
externDWORDLinesToSend;
externTCHAR*UserName;


ARSETUP.C

/*************************************************************************\
*
* APPC Remote Installer
*
* This program brings up a dialog box that prompts for TP configuration
* information.  The information is then placed in the registry under
* Windows NT, and in the WIN.INI file under Windows.  The WIN32 compiler
* flag specifies the NT version, while the WINDOWS flag specifies the
* Windows version.
*
* 6/93 Initial coding    ARK
* 6/94 Modified to work with APPC Remote    T-ALEXWE
*
\*************************************************************************/

#define STRICT
#include <windows.h>
#ifdef WIN32
#include <winsvc.h>
#endif

#ifdef        WIN32
        #include <windowsx.h>
#else
        #include <windowsx.h16>
#endif

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include "arsetup.h"

HANDLE  hInst;                       // This program's instance
HWND    hDialog;                     // Global handle to main dialog
HWND    hTimeout;                    // A handle to the "timeout" window
HWND    hList;                       // Handle to the "users" list box

WNDPROC lPrevWndProcInt = NULL,      // Holds original integer edit box window proc
        lPrevWndProcAppc = NULL,     // Holds original APPC edit box window proc
        lPrevWndProcInfinite = NULL, // Original window proc for "infinite"
                                     // radio button
        lPrevWndProcConvSec = NULL,  // Same for "conversation security" box
        lPrevWndProcService = NULL;  // Same for "service" box

FARPROC lpfnInt = NULL,              // Pointer to the ValidateFieldInt
                                     // procedure; becomes non-NULL upon
                                     // initialization in ValidateField().
        lpfnAppc = NULL;         // Pointer to ValidateFieldAppc procedure

/*************************************************************************\
*
*  FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int)
*
*  PURPOSE: Creates the dialog box.
*
*  COMMENTS:
*
\*************************************************************************/

int PASCAL WinMain (HINSTANCE hInstance,
                      HINSTANCE hPrevInstance,
                      LPSTR     lpCmdLine,
                      int       nCmdShow)
{
  DWORD retCode;
  FARPROC lpfn;

  hInst   = hInstance;

  lpfn = (FARPROC) MakeProcInstance( (FARPROC)MainDlgProc, hInst);
  retCode = DialogBoxParam ((HANDLE)hInst,
                            (LPCSTR)"MainDlg",
                            NULL,
                            (DLGPROC) lpfn,
                            0);
  FreeProcInstance(lpfn);

  return  (retCode);
}
/************************************************************************/
/*
 *  MainDlgProc: Handle messages to the main dialog.
 *  Note:  Under Windows NT, installation consists of creating a service
 *         and creating some keys in the registry, while under Windows
 *         we instead add slightly different information to the WIN.INI file.
 *         So two different dialogs are necessary for the different operating
 *         systems.  Appropriate sections of the procedure below are
 *         #ifdef'ed to handle parts of the installation that are unique
 *         to a particular operating system.
 */
/************************************************************************/
BOOL CALLBACK MainDlgProc (HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam)
  {
  static   HWND hInfinite;
  char     lpPathName[MAXBINPATHLEN], *lpTPName, lpParameters[MAXBINPATHLEN];
  char     *lpTimeout, *lpLocalLUName, *lpCommand, *divider;
  char     lpFileTitle[MAXBINPATHLEN], lpSysDir[MAXBINPATHLEN];
  INT      cmd;
  char     lpszName[MAX_COMPUTERNAME_LENGTH + 1];
  int      lpcchName = MAX_COMPUTERNAME_LENGTH + 1;
  HANDLE   hTemp;
  WIN32_FIND_DATA  fdFile;

#ifdef WIN32
  UNREFERENCED_PARAMETER(divider);
  UNREFERENCED_PARAMETER(lpParameters);
#endif

  switch (wMsg)
    {
      case WM_INITDIALOG:
        hDialog = hDlg;

        // Set maximum TP name to 128 characters
        SendMessage(GetDlgItem(hDlg, IDE_TPNAME),
                    EM_LIMITTEXT, MAXTPLEN, 0);

        // set the default TPName
        SetDlgItemText(hDlg, IDE_TPNAME, "AREMOTE");

        // Set maximum pathname to 512 characters
        SendMessage(GetDlgItem(hDlg, IDE_CMDPATH),
                    EM_LIMITTEXT, MAXBINPATHLEN, 0);

        // Have it use cmd.exe by default for the command
        GetSystemDirectory(lpSysDir, sizeof(lpSysDir));
        strcat(lpSysDir, "\\cmd.exe");
        SetDlgItemText(hDlg, IDE_CMDPATH, lpSysDir);

        // Set maximum pathname to 512 characters
        SendMessage(GetDlgItem(hDlg, IDE_REMOTEPATH),
                    EM_LIMITTEXT, MAXBINPATHLEN, 0);

        // Have it use aremote.exe by default for remote
        GetCurrentDirectory(sizeof(lpSysDir), lpSysDir);
        strcat(lpSysDir, "\\aremote.exe");
        SetDlgItemText(hDlg, IDE_REMOTEPATH, lpSysDir);

        // Allow only legal characters in "Local LU Alias" box:
        ValidateField(GetDlgItem(hDlg, IDE_LOCALLU), VALIDATE_APPC);

        // Set maximum length of LU name to be 8 characters
        SendMessage(GetDlgItem(hDlg, IDE_LOCALLU), EM_LIMITTEXT, MAXLULEN, 0);

        // set the LU name to this computers name by default
        GetComputerName(lpszName, &lpcchName);
        MakeValidLUName(lpszName);
        SetDlgItemText(hDlg, IDE_LOCALLU, lpszName);

        return TRUE;

      case WM_COMMAND:
        switch (cmd = GET_WM_COMMAND_ID(wParam, lParam))
          {
            case IDOK:
              // User hits OK; we get relevant info & try to install.

              // If TP name field is blank, error out
              if (ReadString(hDlg, IDE_TPNAME, &lpTPName, MAXTPLEN) == 0)
                {
                  DisplayError(hDlg, IDS_BADTPNAME);
                  free(lpTPName);
                  return TRUE;
                }

              // read path name for remote
              ReadString(hDlg, IDE_REMOTEPATH, &lpCommand, MAXBINPATHLEN);

              // open the file to make sure it exists
              hTemp = FindFirstFile(lpCommand, &fdFile);
              if (hTemp == INVALID_HANDLE_VALUE) {
                  DisplayError(hDlg, IDS_BADREMOTEPATH);
                  return TRUE;
              } else CloseHandle(hTemp);

              strcpy(lpPathName, lpCommand);
              strcat(lpPathName, " /a ");

              // read path name for command
              ReadString(hDlg, IDE_CMDPATH, &lpCommand, MAXBINPATHLEN);

              // open the file to make sure it exists
              hTemp = FindFirstFile(lpCommand, &fdFile);
              if (hTemp == INVALID_HANDLE_VALUE) {
                  DisplayError(hDlg, IDS_BADCMDPATH);
                  return TRUE;
              } else CloseHandle(hTemp);

              strcat(lpPathName, lpCommand);

              strcpy(lpParameters, "/a ");
              strcat(lpParameters, lpCommand);

              // read Local LU Alias
              ReadString(hDlg, IDE_LOCALLU, &lpLocalLUName, MAXLULEN);
              if (strlen(lpLocalLUName) < 1) {
                  DisplayError(hDlg, IDS_BADLUNAME);
                  return TRUE;
              }

              lpTimeout = INFINITE_TIMEOUT;

#ifdef WIN32
              // Install the new service; if successful, set subkeys
              if (InstallServiceNT(hDlg, lpTPName, lpPathName) != 0)
                {
                  free(lpPathName);
                  free(lpTPName);
                  return TRUE;
                }

              if (CreateKeys(  lpTPName,
                               TRUE,
                               lpLocalLUName,
                               FALSE,
                               FALSE,
                               FALSE,
                               lpTimeout,
                               lpParameters,
                               lpPathName
                               )
                               != 0)
                {
                  free(lpPathName);
                  free(lpTPName);
                  return TRUE;
                }
              DisplayInfo(hDlg, IDS_SUCCESS);

#else

              // Add entries to WIN.INI
              if (InstallWindows(hDlg, lpTPName, lpPathName,
                     lpLocalLUName, lpParameters,
                                 IsDlgButtonChecked(hDlg, IDC_CONVSEC) == 1,
                                 IsDlgButtonChecked(hDlg, IDC_ALREADYVER) == 1,
                                 IsDlgButtonChecked(hDlg, IDC_QUEUED) == 1,
                                 lpTimeout
                                 ) != 0)
                {
                  free(lpPathName);
                  free(lpTPName);
                  return TRUE;
                }
              DisplayInfo(hDlg, IDS_SUCCESS);

#endif //ifdef WIN32

              EndDialog(hDlg, TRUE);
              free(lpPathName);
              free(lpTPName);
              return TRUE;

            case IDC_CMDPATH_BROWSE: {
                char filters[256];
                char lpDirPath[MAXBINPATHLEN], lpFileName[MAXBINPATHLEN];
                char *lpTemp;
                OPENFILENAME ofn;

                GetDlgItemText(hDlg, IDE_CMDPATH, lpFileName,
                    sizeof(lpFileName));
                GetFullPathName(lpFileName, sizeof(lpDirPath),
                    lpDirPath, &lpTemp);
                strcpy(lpFileName, lpTemp);
                lpTemp[0] = 0;

                memcpy(filters,
                    "Executable Programs\0*.exe;*.bat;*.com\0\0\0", 256);

                memset(&ofn, 0, sizeof(ofn));
                ofn.lStructSize = sizeof(OPENFILENAME);
                ofn.hwndOwner = hDlg;
                ofn.lpstrFilter = filters;
                ofn.lpstrCustomFilter = NULL;
                ofn.nFilterIndex = 0;
                ofn.lpstrFile = lpFileName;
                ofn.nMaxFile = sizeof(lpFileName);
                ofn.lpstrFileTitle = lpFileTitle;
                ofn.nMaxFileTitle = sizeof(lpFileTitle);
                ofn.lpstrTitle = "Path to Command";
                ofn.lpstrInitialDir = lpDirPath;
                ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST |
                            OFN_HIDEREADONLY;

                if (GetOpenFileName(&ofn)) {
                    SetDlgItemText(hDlg, IDE_CMDPATH, lpFileName);
                }

                return TRUE;
              }

            case IDC_REMOTEPATH_BROWSE: {
                char filters[256];
                char lpDirPath[MAXBINPATHLEN], lpFileName[MAXBINPATHLEN];
                char *lpTemp;
                OPENFILENAME ofn;

                GetDlgItemText(hDlg, IDE_REMOTEPATH, lpFileName,
                    sizeof(lpFileName));
                GetFullPathName(lpFileName, sizeof(lpDirPath),
                    lpDirPath, &lpTemp);
                strcpy(lpFileName, lpTemp);
                lpTemp[0] = 0;

                memcpy(filters,
                    "APPC Remote\0aremote.exe\0Executable Programs\0*.exe;*.bat;*.com\0\0\0",
                    256);

                memset(&ofn, 0, sizeof(ofn));
                ofn.lStructSize = sizeof(OPENFILENAME);
                ofn.hwndOwner = hDlg;
                ofn.lpstrFilter = filters;
                ofn.lpstrCustomFilter = NULL;
                ofn.nFilterIndex = 0;
                ofn.lpstrFile = lpFileName;
                ofn.nMaxFile = sizeof(lpFileName);
                ofn.lpstrFileTitle = lpFileTitle;
                ofn.nMaxFileTitle = sizeof(lpFileTitle);
                ofn.lpstrTitle = "Path to APPC Remote";
                ofn.lpstrInitialDir = lpDirPath;
                ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST |
                            OFN_HIDEREADONLY;

                if (GetOpenFileName(&ofn)) {
                    SetDlgItemText(hDlg, IDE_REMOTEPATH, lpFileName);
                }

                return TRUE;
              }

            case IDCANCEL:
              EndDialog(hDlg, FALSE);
              return TRUE;
          }

        break;

      case WM_DESTROY:
        PostQuitMessage(0);
        return TRUE;
    }
    return FALSE;
}

#ifdef WIN32

/*****************************************************************************/
/*
 * InstallServiceNT( HWND hDlg, LPSTR lpServiceName, LPSTR lpPath )
 *
 * Windows NT version of installation--register a new service.
 *
 * Parameters
 * ----------
 *
 * hDlg: Handle to top level dialog; used for error messages.
 * lpServiceName: A string giving the name of the TP.
 * lpPath: A string giving the full pathname of the TP's executable.
 *
 * Returns
 * -------
 *
 *  Zero upon successful completion, nonzero otherwise.
 *
 * Comments
 * --------
 *  Installs new service if possible.  Puts up appropriate message boxes if
 *  installation fails.
 */
/*****************************************************************************/
int InstallServiceNT(HWND hDlg, LPSTR lpServiceName, LPSTR lpBinaryPath)
{

  SC_HANDLE hSCManager = NULL;
  SC_HANDLE hService   = NULL;
  SC_LOCK   lSCLock    = NULL;

  hSCManager = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );

  if( hSCManager != NULL )
  {
    /*************************************************************************/
    /* Lock the service database                                             */
    /*************************************************************************/
    lSCLock = LockServiceDatabase( hSCManager );
    if ( lSCLock != NULL )
    {
      /***********************************************************************/
      /* Create the service                                                  */
      /***********************************************************************/
      hService = CreateService( hSCManager,
                                lpServiceName,     // Service's name
                                lpServiceName,     // Display name (new for NT)
                                SERVICE_ALL_ACCESS,// Access (allow all)
                                0x10,              // Service type
                                0x3,               // Startup behavior
                                0x1,               // Error control
                                lpBinaryPath,      // Full pathname of binary
                                NULL,              // Load order group
                                NULL,              // Tag ID
                                NULL,              // Dependencies (none)
                                NULL,              // Account name
                                NULL               // Password
                                );
      if ( hService != NULL )
        {
          /*********************************************************************/
          /* Close our handle to the new service                               */
          /*********************************************************************/
          CloseServiceHandle( hService );
        }
      else
        {
          // Try to display the reason for the create failure
          ParseCreateError(hDlg, GetLastError());

          // Must release lock in case we are called again
          UnlockServiceDatabase( lSCLock );

          return 1;
        }

      /***********************************************************************/
      /* Unlock the database                                                 */
      /***********************************************************************/
      UnlockServiceDatabase( lSCLock );
    }
    else
      {
        DisplayError(hDlg, IDS_LOCKFAILED);
        return 1;
      }
    /*************************************************************************/
    /* Free our handle to the service control manager                        */
    /*************************************************************************/
    CloseServiceHandle( hSCManager );
    return 0;
  }

  DisplayError(hDlg, IDS_OPENSCMFAILED);
  return 1;
}

/*****************************************************************************/
/*
 * CreateKeys: Set the required key values for the newly created service.
 *  The keys are:
 *                 Linkage:
 *                        OtherDependencies: REG_MULTI_SZ: SnaBase
 *                Parameters:
 *                        SNAServiceType: REG_DWORD: 0x5
 *                        ConversationSecurity: REG_SZ:  "yes" or "no"
 *                           (depending on setting of radio button in dialog box)
 *                AlreadyVerified: REG_SZ: "yes" or "no"
 * If the timeout isn't infinite, there appears the key:
 *                        Timeout: REG_DWORD: <timeout in seconds>
 *                        Parameters: REG_SZ: cmd line parameters
 * If ConversationSecurity is yes, there are also these keys:
 *                        <User1>: REG_SZ: <Password1>
 *                                ...
 *                        <Usern>: REG_SZ: <Passwordn>
 *
 *  Note that the users and passwords are read from the list box.
 *
 *  Arguments:
 *    lpServiceName is the name of the TP.
 *    lpLUName is the Local LU Alias.
 *    The two integer arguments give the state of the radio buttons on the
 *      dialog box.  They should be zero to indicate that "No" is selected,
 *      nonzero otherwise.
 *    lpTimeout is a string containing the timeout value; if the timeout is
 *      infinite, it holds the value of INFINITE_TIMEOUT.
 *
 *  Returns: 0 on success, nonzero otherwise.
 */
/*****************************************************************************/
INT CreateKeys(LPSTR lpServiceName, BOOL bService, LPSTR lpLUName, int iConvSec,
               int iAlreadyVer, int iQueued, LPSTR lpTimeout, LPSTR lpParameters,
               LPSTR lpExeName)
{
  LPSTR  lpServiceFullName;
  int    bufsize, i, nCount;
  LOCALHANDLE hLocalMem;

  // If any of the buttons says "no", change the key values so that they
  // will be inserted into the registry below.  This is kind of gross, but
  // it keeps the loop below simple.
  if (iConvSec == 0)
    {
      keyinfo[CONVSEC].lpData = "no";
      keyinfo[CONVSEC].iDataSize = 3;
    }
  if (iAlreadyVer == 0)
    {
      keyinfo[ALREADYVER].lpData = "no";
      keyinfo[ALREADYVER].iDataSize = 3;
    }
  if (bService == FALSE && iQueued == 0)
    {
      //
      // the default of 5 is OK for queued support.
      // needs to be redefined to 6 for nonqueued.
      //
      keyinfo[SNASRVTYPE].lpData = "6";
      keyinfo[SNASRVTYPE].iDataSize = 4;
    }

  // Put timeout in structure (it won't be written out if infinite)
  keyinfo[TIMEOUT].lpData = lpTimeout;

  // Put Parameters in structure
  keyinfo[PARAMETERS].lpData = lpParameters;
  keyinfo[PARAMETERS].iDataSize = strlen(lpParameters) + 1;   // Count null

  // Put Local LU Alias in structure; it won't be written out if blank
  keyinfo[LUNAME].lpData = lpLUName;
  keyinfo[LUNAME].iDataSize = strlen(lpLUName) + 1;   // Count null

  // Create full path of TP by concatenating the registry path with the
  // TP's name
  bufsize = MAXREGPATHLEN + 1;
  lpServiceFullName = (LPSTR) malloc(bufsize);

  i = bService ? IDS_REGISTRYPATH : IDS_REGISTRYAPPLPATH ;
  LoadString( hInst, i, lpServiceFullName, bufsize );
  lstrcat(lpServiceFullName, lpServiceName);

  // Write out the basic keys; first key is skipped for non-service tps
  for ( i = bService ? 0 : 1; i < NUMKEYS; i++ )
    {
      if ( WriteKeyNT(lpServiceFullName, keyinfo[i], TRUE ) )
          return 1;
    }

  // if not a service then write out the path name
  if ( bService == FALSE )
    {
      keyinfo[EXENAME].lpData = lpExeName;
      keyinfo[EXENAME].iDataSize = strlen(lpExeName) + 1;   // Count null
      if (WriteKeyNT(lpServiceFullName, keyinfo[EXENAME], TRUE ) )
          return 1;
    }

  // If timeout isn't infinite, write out the "timeout" key
  if (lstrcmp(lpTimeout, INFINITE_TIMEOUT))
    {
      if (WriteKeyNT(lpServiceFullName, keyinfo[TIMEOUT], TRUE ) )
          return 1;
    }

  // If Local LU Alias isn't blank, write out its key
  if (strlen(lpLUName) > 0)
     {
      if (WriteKeyNT(lpServiceFullName, keyinfo[LUNAME], TRUE ) )
          return 1;
    }

  // Only write out user stuff if conversation security is "yes"
  if (iConvSec)
    {
      keyinfo[USER].lpName = (LPSTR) malloc(MAXUSERNAMELEN + 1);
      nCount = SendMessage(hList, LB_GETCOUNT, 0, 0);
      for (i = 0; i < nCount; i++)
        {
          // Get username & make it the name of this key
          SendMessage(hList, LB_GETTEXT, (WPARAM) i,
                                  (LPARAM) keyinfo[USER].lpName);

          // Get pointer to password and then get password
          hLocalMem = (LOCALHANDLE) SendMessage(hList, LB_GETITEMDATA, i, 0);
          keyinfo[USER].lpData = (LPSTR) LocalLock(hLocalMem);

          // Password shouldn't be blank, but just in case...
          if (keyinfo[USER].lpData == NULL)
            keyinfo[USER].iDataSize = 0;
          else
            keyinfo[USER].iDataSize = strlen(keyinfo[USER].lpData);

          if (WriteKeyNT(lpServiceFullName, keyinfo[USER], TRUE ) )
            {
              LocalUnlock(hLocalMem);
              return 1;
            }
          LocalUnlock(hLocalMem);
        }
    }

  free(lpServiceFullName);
  return 0;
}
/*****************************************************************************/
/*
 * WriteKeyNT: Write out the given key to the registry.
 * Return 0 on success, nonzero otherwise.
 */
/*****************************************************************************/
INT WriteKeyNT(LPSTR lpServiceFullName, KEYENTRY keyinfo, BOOL bAppendParent )
{
static BOOL bAskToReplace = TRUE;
  DWORD     dwResult;
  HKEY      hKey;
  LPSTR     lpKeyName;
  DWORD     dwTemp;

  lpKeyName = (LPSTR) malloc(MAXREGPATHLEN + 1);

  // Build full pathname of key by appending a backslash and the name of the
  // key to the full pathname of the TP.
  lstrcpy(lpKeyName, lpServiceFullName);
  lstrcat(lpKeyName, TEXT("\\"));
  lstrcat(lpKeyName, keyinfo.lpParent);

  // Now create the subkeys and set their values
  if (RegCreateKeyEx(HKEY_LOCAL_MACHINE,      // Predefined key
      lpKeyName,                      // Our subkey's name
      0,                       // Reserved
      NULL,                    // Class string
      REG_OPTION_NON_VOLATILE, // Option type (value is saved)
      KEY_ALL_ACCESS,          // Access mask
      NULL,                    // Security attributes
      &hKey,                   // Handle to key is put here
      &dwResult)               // Was key opened or created?
      == ERROR_SUCCESS)
        {
          if ( bAskToReplace && dwResult == REG_OPENED_EXISTING_KEY )
            {
            char szCaption[64];
            char szText[256];
              LoadString( hInst, IDS_REPLACECAPTION, szCaption, sizeof(szCaption) );
              LoadString( hInst, IDS_REPLACETEXT, szText, sizeof(szText) );
              switch( MessageBox( hDialog, szText, szCaption, MB_OKCANCEL|MB_ICONQUESTION ) )
                {
              case IDCANCEL:
                  return 1;

                }
            }

          //
          // after first successful pass no need to query
          //
          bAskToReplace = FALSE;

          // We stored all the values as strings, but some are actually numbers.
          // So we have to make a temporary buffer to pass RegSetValueEx the
          // pointer to a DWORD that it wants for DWORD data.
          if ( keyinfo.iDataType == REG_DWORD )
            {
              dwTemp = atol(keyinfo.lpData);
              keyinfo.lpData = (LPSTR) &dwTemp;
              keyinfo.iDataSize = sizeof(DWORD);
            }

          if (RegSetValueEx(hKey,
                        keyinfo.lpName,    // Key name
                        0,                    // Reserved
                        keyinfo.iDataType, // Type of key data
                        keyinfo.lpData,    // Key's value
                        keyinfo.iDataSize  // Length of value (incl. nulls)
                        )
                        != ERROR_SUCCESS)
            {
              DisplayError(hDialog, IDS_SETKEYFAILED);
              return 1;
            }
        }
      else
        {
          DisplayError(hDialog, IDS_OPENKEYFAILED);
          return 1;
        }

  free(lpKeyName);
  return 0;
}
/*****************************************************************************/
/*
 * ParseCreateError: Given an error code from a registry operation, attempt
 *   to give an appropriate error message.
 */
/*****************************************************************************/
void ParseCreateError(HWND hDlg, UINT uError)
{
  UINT code;
  switch (uError)
    {
      case ERROR_INVALID_PARAMETER:
        // "Invalid parameter" is a rather generic error name.  Since a blank
        // service name is the most probable way the error arose, say so:
        code = IDS_BADPATHNAME;
        break;

      case ERROR_INVALID_NAME:
        code = IDS_BADTPNAME;
        break;

      case ERROR_SERVICE_EXISTS:
        code = IDS_SERVICEEXISTS;
        break;

      default:
        // Since we don't have a code for this error, the following will bring
        // up the "unknown" error message
        code = uError;
    }
  DisplayError(hDlg, code);
}

#else

/*****************************************************************************/
/*
 * InstallWindows: Windows version of installation; add lines to WIN.INI
 *
 * First we must add an entry under the heading SNAServerAutoTPs.  This entry
 * has the name of the TP and points to another heading that contains TP-
 * specific data.  Sample WIN.INI:
 *
 * [SNAServerAutoTPs]
 * BounceTP = BounceTPParams
 *
 * [BounceTPParams]
 * PathName = c:\sna\bounce.exe
 * Parameters = /t
 * etc....
 *
 * For a list of entries, see the comment at CreateKeys().  The only difference
 * is that WIN.INI also contains an entry for the "queued" toggle.
 *
 *
 * Parameters:
 *    szTPName: The name of the TP
 *    szBinaryPath: The full pathname of the executable
 *    szLUName: Local LU Alias
 *    szParameters: The list of command line parameters
 *    iConvSec: "Conversation security" toggle (1 = yes, 0 = no)
 *    iAlreadyVer: "Already verified" toggle
 *    iQueued: "Queued" toggle
 *    lpTimeout: A string specifying the timeout in seconds, e.g. "2"
 *
 * Returns: zero on success, nonzero otherwise
 */
/*****************************************************************************/
INT InstallWindows(HWND hDlg, char *szTPName, char *szBinaryPath, char *szLUName,
                   char *szParameters,
                   int iConvSec, int iAlreadyVer, int iQueued, char *lpTimeout
                   )
{
  char szNewKeyName[MAXTPLEN + 6];
  int  i, nCount;
  LOCALHANDLE hLocalMem;

  // Make new heading by concatenating TP's name and the string "Params"
  strcpy(szNewKeyName, szTPName);
  strcat(szNewKeyName, "Params");

  // Now add the key to the SNAServerAutoTPs heading
  if (WriteKeyWindows("SNAServerAutoTPs", szTPName, szNewKeyName))
    return 1;

  // Set the data fields of the keys, one at a time
  keyinfo[PATHNAME].data    = szBinaryPath;
  keyinfo[PARAMETERS].data  = szParameters;
  keyinfo[LUNAME].data      = szLUName;
  keyinfo[QUEUED].data      = iQueued ? "yes" : "no";
  keyinfo[TIMEOUT].data     = lpTimeout;
  keyinfo[CONVSEC].data     = iConvSec ? "yes" : "no";
  keyinfo[ALREADYVER].data  = iAlreadyVer ? "yes" : "no";

  // Loop through all the keys and add them under the new "Params" heading
  for (i=0; i < NUMKEYS; i++)
    if (WriteKeyWindows(szNewKeyName, keyinfo[i].name, keyinfo[i].data))
      return 1;

  // Only write out Local LU Alias if it's not blank
  if (strlen(szLUName) > 0)
    if (WriteKeyWindows(szNewKeyName, keyinfo[LUNAME].name, keyinfo[LUNAME].data))
      return 1;

  // If conversation security is on, write out users & passwords
  if (iConvSec)
    {
      keyinfo[USER].name = (char *) malloc(MAXUSERNAMELEN + 1);
      nCount = SendMessage(hList, LB_GETCOUNT, 0, 0);

      // Read usernames and passwords from list box & create keys
      for (i = 0; i < nCount; i++)
        {
          // Get username & make it the name of this key
          SendMessage(hList, LB_GETTEXT, (WPARAM) i,
                                  (LPARAM) keyinfo[USER].name);

          // Get pointer to password and then get password
          hLocalMem = (LOCALHANDLE) SendMessage(hList, LB_GETITEMDATA, i, 0);
          keyinfo[USER].data = (char *) LocalLock(hLocalMem);

if (WriteKeyWindows(szNewKeyName, keyinfo[USER].name,
                                keyinfo[USER].data))
            {
              LocalUnlock(hLocalMem);
              return 1;
            }
          LocalUnlock(hLocalMem);
        }
      free(keyinfo[USER].name);

    }

  return 0;
}
/*****************************************************************************/
/*
 * WriteKeyWindows:  Write the given key out to WIN.INI
 * Return 0 on success, nonzero otherwise.
 */
/*****************************************************************************/
INT WriteKeyWindows(char *heading, char *keyname, char *value)
{
  if (WriteProfileString(heading, keyname, value) == FALSE)
    {
      DisplayError(hDialog, IDS_INIWRITEFAILED);
      return 1;
    }
  return 0;
}

#endif //ifdef WIN32

/*****************************************************************************/
/*
 * ValidateField: Install a window procedure that handles messages to the
 *   given edit window.
 *
 * Parameters: hwnd is the edit window, usType an identifier for the window
 *   procuedure to install.  Currently the only allowed type is VALIDATE_INT,
 *   which allows only digits to be typed into the edit box.
 *
 * Returns:  TRUE if window procedure is successfully installed,
 *   FALSE otherwise.
/*****************************************************************************/
BOOL ValidateField( HWND hwnd, USHORT usType)
{
        FARPROC lpfn = NULL;
        LONG        lPrevWndProc;
        LONG FAR *lplNewWndProc;

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

        lPrevWndProc = GetWindowLong( hwnd, GWL_WNDPROC );

        if (lPrevWndProc == 0) {
                return(FALSE);
        }

        // check that the old wndproc is the same as the one we have stored
        // also, get the new wndproc, based on contents of edit field

        // We only need two validation procedures, but if you want others
        // (such as hexadecimal, filenames, etc.) you can add extra cases
        // and procedures below.

        switch (usType) {
          case VALIDATE_INT:
            if (lpfnInt == NULL) {
              lpfnInt = MakeProcInstance( (FARPROC)ValidateFieldInt, hInst );
            }
            lpfn = lpfnInt;

            lplNewWndProc = (LONG FAR *) &lPrevWndProcInt;
            break;

          case VALIDATE_APPC:
            if (lpfnAppc == NULL) {
              lpfnAppc = MakeProcInstance( (FARPROC)ValidateFieldAppc, hInst );
            }
            lpfn = lpfnAppc;

            lplNewWndProc = (LONG FAR *) &lPrevWndProcAppc;
            break;

          default:
            return(FALSE);
          }

        if (*lplNewWndProc == (LONG) NULL) {

                *lplNewWndProc = lPrevWndProc;

        } else {

                if (*lplNewWndProc != lPrevWndProc) {
                        return(FALSE);
                }

                *lplNewWndProc = lPrevWndProc;
        }

        if (lpfn == NULL ||
                SetWindowLong(hwnd, GWL_WNDPROC, (LONG) lpfn) == (LONG) NULL) {

                return(FALSE);
        }
        return(TRUE);
}

/*****************************************************************************/
/*
 * ValidateFieldInt: A window proc that allows only 0 through 9 to be typed to
 *   an edit box.
 */
/*****************************************************************************/
LRESULT CALLBACK ValidateFieldInt( HWND hwnd, WORD msg, WPARAM wParam, LPARAM lParam )
{
        if (msg == WM_CHAR) {

                char ch = LOBYTE(LOWORD(wParam));

                if ((ch < '0' || ch > '9') && ch != VK_BACK )
                  return(0);
        }
        // If character was legal or message wasn't WM_CHAR,
        // pass message on to default window proc.
        return CallWindowProc( lPrevWndProcInt, hwnd, msg, wParam, lParam);
}
/*****************************************************************************/
/*
 * ValidateFieldAppc: A window proc that allows only legal APPC name characters
 *    to be typed to an edit box.
 */
/*****************************************************************************/
LRESULT CALLBACK ValidateFieldAppc( HWND hwnd, WORD msg, WPARAM wParam, LPARAM lParam )
{
    if (msg == WM_CHAR) {

        char ch = LOBYTE(LOWORD(wParam));

        if (!(ch >= '0' && ch <= '9' ||
            ch >= 'A' && ch <= 'Z' ||
            ch >= 'a' && ch <= 'z' ||
            ch == '@'   ||
            ch == '#'   ||
            ch == '$'   ||
            ch == '%'   ||
            ch == VK_BACK ))
            return(0);
    }
    return CallWindowProc( lPrevWndProcAppc, hwnd, msg, wParam, lParam);
}
/*****************************************************************************/
/*
 * InstallCallback: Install a callback window proc.
 *   Arguments:
 *        hDlg: Handle of the parent dialog
 *      uId: ID of window for which callback will be installed
 *      OldWndProc: Handle for previous window proc
 *      NewWndProc: Pointer to new window proc
 */
/*****************************************************************************/
void InstallCallback(HWND hDlg, UINT uId, WNDPROC *OldWndProc, FARPROC NewWndProc)
{
  HANDLE hTemp;

  hTemp = GetDlgItem(hDlg, uId);
  *OldWndProc = (WNDPROC) GetWindowLong(hTemp, GWL_WNDPROC);
  SetWindowLong(hTemp, GWL_WNDPROC,
               (LONG) MakeProcInstance( (FARPROC)NewWndProc, hInst));
}
/*****************************************************************************/
/*
 * InfiniteWndProc: Disable "timeout" box when the "infinite"
 *   button is pressed, and enable it when "finite" is pressed.
 */
/*****************************************************************************/
LRESULT CALLBACK InfiniteWndProc( HWND hwnd, WORD msg, WPARAM wParam, LPARAM lParam )
{
  if (msg == BM_SETCHECK)
    // wParam is 0 if button is being turned off, 1 if being turned on
    EnableWindow(hTimeout, (wParam == 0));
  return CallWindowProc(lPrevWndProcInfinite, hwnd, msg, wParam, lParam);
}

/*****************************************************************************/
/*
 * ServiceWndProc: Disable "queued" box when the "service" button is pressed
 */
/*****************************************************************************/
LRESULT CALLBACK ServiceWndProc( HWND hwnd, WORD msg, WPARAM wParam, LPARAM lParam )
{
  HWND hwndCntl;

  if (msg == BM_SETCHECK) {
    //
    // wParam is 0 if button is being turned off, 1 if being turned on
    //
    hwndCntl = GetDlgItem( hDialog, IDC_QUEUED );
    EnableWindow( hwndCntl, wParam == 1 ? FALSE : TRUE );

    if ( wParam == 1 ) {
        SendMessage( hwndCntl, BM_SETCHECK, 1, 0 );
    }
  }

  return CallWindowProc(lPrevWndProcService, hwnd, msg, wParam, lParam);
}

/*****************************************************************************/
/*
 * ConvSecWndProc: Window proc for "conversation security" checkbox.  Grey
 *   out "users" group box when checkbox is off
 */
/*****************************************************************************/
LRESULT CALLBACK ConvSecWndProc( HWND hwnd, WORD msg, WPARAM wParam, LPARAM lParam )
{
  int nItems = SendMessage(hList, LB_GETCOUNT, 0, 0L);
  if (msg == BM_SETCHECK)
    {
      EnableWindow(GetDlgItem(hDialog, IDC_USERBOX), wParam == 1 ? TRUE : FALSE);
      EnableWindow(GetDlgItem(hDialog, IDC_ADD), wParam == 1 ? TRUE : FALSE);
      EnableWindow(hList, wParam == 1 ? TRUE : FALSE);
      // Only turn on Delete & Edit if there's something in the list box
      EnableWindow(GetDlgItem(hDialog, IDC_DELETE), wParam == 1 && nItems);
      EnableWindow(GetDlgItem(hDialog, IDC_EDIT), wParam == 1 && nItems);
    }
  return CallWindowProc(lPrevWndProcConvSec, hwnd, msg, wParam, lParam);
}

/*****************************************************************************/
/*
 * DeleteListItem: Handle all aspects of deleting a user from the list box,
 *   given his index.
 */
/*****************************************************************************/
void DeleteListItem(INT nIndex)
{
  LOCALHANDLE hLocalMem;
  INT nUsers;

  // Free associated password memory, if necessary
  if (hLocalMem = (LOCALHANDLE) SendMessage(hList, LB_GETITEMDATA,
                                            nIndex, 0))
    LocalFree(hLocalMem);

  // Simply remove currently selected user
  SendMessage(hList, LB_DELETESTRING, (WPARAM) nIndex, 0);

  nUsers = SendMessage(hList, LB_GETCOUNT, 0, 0);

  // Disable Delete & Edit buttons if no one is left
  if (0 == nUsers)
    {
      EnableWindow(GetDlgItem(hDialog, IDC_DELETE), FALSE);
      EnableWindow(GetDlgItem(hDialog, IDC_EDIT), FALSE);
      return;
    }

  // Move the highlight:
  // 1) If the deleted user was the last one in the list, go to previous user.
  // 2) Otherwise go to the next user.
  SendMessage(hList, LB_SETCURSEL,
              (nUsers == nIndex) ? (nIndex - 1) : nIndex, 0);
}
/*****************************************************************************/
/*
 * ReadString: Get the text from an edit box and turn it into a C string.
 *
 * Parameters:
 *
 * hDlg:  The dialog box.
 * id: The dialog item id.
 * lpString:  A double pointer to a character.  ReadString reserves space
 *            for the incoming string.
 * maxlen: The maximum number of characters to read from the edit box.
 *
 * Returns:
 *   The number of characters read from the edit box.
 */
/*****************************************************************************/
INT ReadString(HWND hDlg, INT id, char **lpString, INT maxlen)
{
  // Leave space for null character
  *lpString = malloc((maxlen + 1) * sizeof(char));

  return GetDlgItemText(hDlg, id, *lpString, maxlen+1);
}
/*****************************************************************************/
/*
 * DisplayError: Bring up MessageBox with given message code's string.
 */
/*****************************************************************************/
void DisplayError( HWND hwnd, UINT uError)
{
        char        sz[256], szFormat[256];

        if ( LoadString( hInst, uError, sz, sizeof(sz)) == 0 ) {
                LoadString( hInst, IDS_UNKNOWN, szFormat, sizeof(szFormat) );
                sprintf( sz, szFormat, uError );
        }

        LoadString( hInst, IDS_ERRORTITLE, szFormat, sizeof(szFormat) );
        MessageBox( hwnd, sz, szFormat, MB_ICONEXCLAMATION | MB_OK);
}
/*****************************************************************************/
/*
 * DisplayInfo:  Put up an information box with given message string.
 */
/*****************************************************************************/
void DisplayInfo( HWND hwnd, UINT uInfo)
{
        char        sz[256], szFormat[256];

        if ( LoadString( hInst, uInfo, sz, sizeof(sz) ) == 0 ) {
                LoadString( hInst, IDS_NOMESSAGE, szFormat, sizeof(szFormat) );
                sprintf( sz, szFormat, uInfo );
        }

        LoadString( hInst, IDS_INFOTITLE, szFormat, sizeof(szFormat) );
        MessageBox( hwnd, sz, szFormat, MB_ICONINFORMATION | MB_OK);
}

void MakeValidLUName(LPTSTR name) {
    int o, n;
    char oldname[MAX_COMPUTERNAME_LENGTH + 1], c;
    int l;

    strcpy(oldname, name);

    l = strlen(oldname);
    for (o = 0, n = 0; o < l; o++) {
        c = toupper(oldname[o]);

        // make sure this is a valid character
        if (((c >= '0') && (c <= '9')) ||
            ((c >= 'A') && (c <= 'Z')) ||
            (c == '@') ||
            (c == '#') ||
            (c == '$') ||
            (c == '%') ||
            (c == ' ')) name[n++] = c;

        // only allow 8 characters in a LU name
        if (n == 8) break;
    }
    name[n] = 0;
}


ARSETUP.H

#include "setupdlg.h"
#define MAXREGPATHLEN1024     // Max length of registry path
#define MAXTPLEN128             // Max length of TP name
#define MAXBINPATHLEN   256             // Max length of file path
#define MAXPARAMLEN  128        // Max length of command-line parameters
#define MAXUSERNAMELEN  32// Max length of username
#define MAXPASSWORDLEN  32// Max length of password
#define MAXLULEN8// Max length of LU name

#define VALIDATE_INT 1
#define VALIDATE_APPC 2

#define INFINITE_TIMEOUT  "-1"     // Value used for infinite timeout
#define INIT_TIMEOUT    5     // Initial value displayed in timeout box
#define MAXTIMEOUTLEN5     // Max digits in timeout

#ifdef WIN32

typedef struct _KeyEntry {
  LPSTR lpParent;                       // Parent key
  LPSTR lpName;                         // Data field name
  int   iDataType;                      // Type of data
  LPSTR lpData;// Data itself
  int   iDataSize;// Length of data, including nulls
} KEYENTRY;

// The following variable defines the subkeys that are placed in the registry.
// The first field gives the key under which to create the new subkey.  The
// second field is the subkey name.  The third field is the data type.
// The fourth field gives the data itself.  This should always be a string,
// HOWEVER, if the third field isn't REG_SZ, some interpretation will go on,
// as follows:
//   If the third field is REG_MULTI_SZ, you should add an extra null character
//   to the end of the string to simulate an array of strings.
//   If the third field is REG_DWORD, you should enter the numeric value as
//   a string in the fourth field, as in "345" if you want the data to be the
//   number 345.
// The fifth field is the length of the fourth field in bytes, INCLUDING nulls.
// This field is set automatically for REG_DWORD data.
KEYENTRY keyinfo[] = {
  { "Linkage",    "OtherDependencies",    REG_MULTI_SZ, "SnaBase\0", 9 },
  { "Parameters", "SNAServiceType",       REG_DWORD,    "5",         4 },
  { "Parameters", "ConversationSecurity", REG_SZ,       "yes",       4 },
  { "Parameters", "AlreadyVerified",      REG_SZ,       "yes",       4 },
  { "Parameters", "Timeout",              REG_DWORD,    "",     4 },
  { "Parameters", "Parameters",           REG_SZ,       "",       4 },
  { "Parameters", "LocalLU",              REG_SZ,       "",     4 },
  { "Parameters", "",                     REG_SZ,       "",          4 },
  { "Parameters", "PathName",             REG_SZ,       "",          4 },
};

// These numbers must correspond to the indices in keyinfo above!
#define SNASRVTYPE 1
#define CONVSEC    2
#define ALREADYVER 3
#define TIMEOUT    4
#define PARAMETERS 5
#define LUNAME     6
#define USER       7  // Blank field for writing out users & passwords
#define EXENAME    8
#define NUMKEYS    6      // Last three keys only written out if needed

int  InstallServiceNT(HWND, LPSTR, LPSTR);
int  CreateKeys(LPSTR, BOOL, LPSTR, int, int, int, LPSTR, LPSTR, LPSTR);
INT  WriteKeyNT(LPSTR, KEYENTRY, BOOL);

#else

#define USHORT unsigned short
#define INT    int
#define SHORT  short

typedef struct _KeyEntry {
  char *name;                        // Data field name
  char *data;     // Data itself
} KEYENTRY;

// If you change the fields below, change the #defines too; they are used
// as indices into the keyinfo structure to set the data fields, which are
// initially all NULL.

KEYENTRY keyinfo[] = {
  { "PathName",     NULL },
  { "Parameters",     NULL },
  { "Queued",     NULL },
  { "Timeout",     NULL },
  { "ConversationSecurity", NULL },
  { "AlreadyVerified",      NULL },
  { "LocalLU",    NULL },
  { "",                     NULL },
};
#define PATHNAME    0
#define PARAMETERS  1
#define QUEUED      2
#define TIMEOUT     3
#define CONVSEC     4
#define ALREADYVER  5
#define LUNAME      6
#define USER        7
#define NUMKEYS     6// Last keys only written out if necessary

int  InstallWindows(HWND, char *, char *, char *, char *, int, int, int, char *);
INT  WriteKeyWindows(char *, char *, char *);

#endif //ifdef WIN32

extern BOOL CALLBACK MainDlgProc (HWND, UINT, WPARAM, LPARAM);
extern BOOL CALLBACK UserDlgProc (HWND, UINT, WPARAM, LPARAM);
BOOL ValidateField(HWND, USHORT);
void InstallCallback(HWND, UINT, WNDPROC *, FARPROC);
extern LRESULT CALLBACK ValidateFieldInt(HWND, WORD, WPARAM, LPARAM);
extern LRESULT CALLBACK ValidateFieldAppc(HWND, WORD, WPARAM, LPARAM);
extern LRESULT CALLBACK InfiniteWndProc(HWND, WORD, WPARAM, LPARAM);
extern LRESULT CALLBACK FiniteWndProc(HWND, WORD, WPARAM, LPARAM);
extern LRESULT CALLBACK ConvSecWndProc(HWND, WORD, WPARAM, LPARAM);
extern LRESULT CALLBACK ServiceWndProc(HWND, WORD, WPARAM, LPARAM);
void ParseCreateError(HWND, UINT);
void DeleteListItem(INT);
INT  ReadString(HWND, INT, char **, INT);
void DisplayError(HWND, UINT);
void DisplayInfo(HWND, UINT);
void MakeValidLUName(LPTSTR name);


CLIENT.C

/*
Copyright (c) 1994, 1993 Microsoft Corporation

Module Name:
    Client.c

Abstract:
    The Client component of Remote. Connects to the remote
    server using named pipes. It sends its stdin to
    the server and output everything from server to
    its stdout.

Author:
    Rajivendra Nath (rajnath) 2-Jan-1993

Environment:
    Console App. User mode.

Revision History:
Alex Wetmore (t-alexwe) 6-Jun-1994
- converted remote to use APPC with Windows SNA Server instead of named
  pipes
*/

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <io.h>
#include <string.h>
#include <winappc.h>
#include "appclib.h"
#include "aremote.h"

tpconvid_t EstablishSession(TCHAR *locallu, TCHAR *serverlu, TCHAR *tpname,
TCHAR *loctpname, TCHAR *mode_name);
DWORD WINAPI GetServerOut(PVOID *Noarg);
DWORD WINAPI SendServerInp(PVOID *Noarg);
BOOL FilterClientInp(TCHAR *buff, int count);
BOOL Mych(DWORD ctrlT);
VOID SendMyInfo(tpconvid_t tpconv);

HANDLE iothreads[2];
HANDLE MyStdInp;
HANDLE MyStdOut;
tpconvid_t tpconv;

CONSOLE_SCREEN_BUFFER_INFO csbi;

TCHAR   MyEchoStr[30];
BOOL   CmdSent;
DWORD  LinesToSend=LINESTOSEND;

VOID Client(TCHAR *local_lu, TCHAR *server_lu, TCHAR *tp_name,
        TCHAR *loc_tp_name, TCHAR *mode_name) {
    DWORD  tid;

    MyStdInp=GetStdHandle(STD_INPUT_HANDLE);
    MyStdOut=GetStdHandle(STD_OUTPUT_HANDLE);

    WRITEF((VBuff,TEXT("**************************************\n")));
    WRITEF((VBuff,TEXT("***********     REMOTE    ************\n")));
    WRITEF((VBuff,TEXT("***********     CLIENT    ************\n")));
    WRITEF((VBuff,TEXT("**************************************\n")));

tpconv = EstablishSession(local_lu, server_lu, tp_name, loc_tp_name,
mode_name);
if (tpconv == NULL) {
appcdestroy();
return;
}

    SetConsoleCtrlHandler((PHANDLER_ROUTINE)Mych,TRUE);

    // Start Thread For Server --> Client Flow
    if ((iothreads[0]=CreateThread((LPSECURITY_ATTRIBUTES)NULL,     // No security attributes.
            (DWORD)0,                           // Use same stack size.
            (LPTHREAD_START_ROUTINE)GetServerOut, // Thread procedure.
            (LPVOID)NULL,              // Parameter to pass.
            (DWORD)0,                           // Run immediately.
            (LPDWORD)&tid))==NULL)              // Thread identifier.
    {

        Errormsg(TEXT("Could Not Create rwSrv2Cl Thread"));
        return;
    }

    //
    // Start Thread for Client --> Server Flow
    //

    if ((iothreads[1]=CreateThread((LPSECURITY_ATTRIBUTES)NULL,           // No security attributes.
                    (DWORD)0,                           // Use same stack size.
                    (LPTHREAD_START_ROUTINE)SendServerInp, // Thread procedure.
                    (LPVOID)NULL,          // Parameter to pass.
                    (DWORD)0,                           // Run immediately.
                    (LPDWORD)&tid))==NULL)              // Thread identifier.
    {

        Errormsg(TEXT("Could Not Create rwSrv2Cl Thread"));
        return;
    }

    WaitForSingleObject(iothreads[0], INFINITE);

    TerminateThread(iothreads[1],1);

appcclose(tpconv);
appcdestroy();

WRITEF((VBuff,TEXT("*** SESSION OVER ***\n")));
}

DWORD WINAPI GetServerOut(PVOID *Noarg) {
    TCHAR buffin[1024];
    DWORD dread=0,tmp;

while (appcvalid(tpconv)) {
dread = appcread(tpconv, (void *) buffin, 1000);
if (dread > 0) {
if (!WriteConsole(MyStdOut, buffin, dread/2, &tmp, NULL))
break;
}
}
    return 1;
}

DWORD WINAPI SendServerInp(PVOID *Noarg) {
    TCHAR buff[1000];
    DWORD dread;
    SetLastError(0);

    while(ReadConsole(MyStdInp,buff,999,&dread,NULL)) {
        if (FilterClientInp(buff, dread)) continue;
appcwrite(tpconv, (void *) buff, dread * sizeof(TCHAR));
    }
    return 0;
}

BOOL FilterClientInp(TCHAR *buff, int count) {
    if (count==0)
        return(TRUE);

    if (buff[0]==2)  //Adhoc screening of ^B so that i386kd/mipskd
        return(TRUE);//do not terminate.

    if (buff[0]==COMMANDCHAR) {
        switch (tolower(buff[1])) {
        case TEXT('k'):
        case TEXT('q'):
            return(FALSE);

        case TEXT('h'):
            WRITEF((VBuff,TEXT("%cM : Send Message\n"),COMMANDCHAR));
            WRITEF((VBuff,TEXT("%cP : Show Popup on Server\n"),COMMANDCHAR));
            WRITEF((VBuff,TEXT("%cS : Status of Server\n"),COMMANDCHAR));
            WRITEF((VBuff,TEXT("%cQ : Quit client\n"),COMMANDCHAR));
            WRITEF((VBuff,TEXT("%cH : This Help\n"),COMMANDCHAR));
            return(TRUE);

        default:
            return(FALSE);
        }

    }
    return(FALSE);
}

BOOL Mych(DWORD ctrlT) {
    TCHAR  c[3];
    DWORD send;

    if (ctrlT==CTRL_C_EVENT) {
c[0] = 3;
send = 1;
appcwrite(tpconv, (void *) c, send);
    }
    if ((ctrlT==CTRL_BREAK_EVENT) ||
        (ctrlT==CTRL_CLOSE_EVENT) ||
        (ctrlT==CTRL_LOGOFF_EVENT) ||
        (ctrlT==CTRL_SHUTDOWN_EVENT)) {
c[0] = COMMANDCHAR;
c[1] = TEXT('q');
send = 2;
appcwrite(tpconv, (void *) c, send);
    }
    return(TRUE);
}

tpconvid_t EstablishSession(TCHAR *local_lu, TCHAR *server_lu, TCHAR *tp_name,
                            TCHAR *loc_tp_name, TCHAR *mode_name) {
TCHAR buf[255];
tpconvid_t tpconv;

appcinit();
WRITEF((VBuff, TEXT("\nClient Connection Information:\n")));
WRITEF((VBuff, TEXT("  Local LU       = %ws\n"), local_lu));
WRITEF((VBuff, TEXT("  Server LU      = %ws\n"), server_lu));
WRITEF((VBuff, TEXT("  Server TP Name = %ws\n"), tp_name));
WRITEF((VBuff, TEXT("  Client TP Name = %ws\n"), loc_tp_name));
WRITEF((VBuff, TEXT("  Mode Name      = %ws\n"), mode_name));
WRITEF((VBuff, TEXT("\n")));
WRITEF((VBuff, TEXT("Connecting . . . ")));

// connect to the server
tpconv = appcconnect(local_lu, server_lu, tp_name, loc_tp_name, mode_name);

// the server will either send us zero bytes (signifying a valid
// connection), or close our connection.  If it is closed then
// let the client know
appcread(tpconv, buf, 255);
if (!appcvalid(tpconv)) {
WRITEF((VBuff,
TEXT("Remote: server doesn't have any available sessions\n")));
return NULL;
}
if (lstrcmp(buf, REMOTE_INIT_MSG) != 0) {
WRITEF((VBuff,
TEXT("Remote: this doesn't seem to be a remote server\n")));
return NULL;
}

WRITEF((VBuff, TEXT("\rConnected        \n\n")));
SendMyInfo(tpconv);

return tpconv;
}

VOID SendMyInfo(tpconvid_t tpconv) {
    DWORD  hostlen=HOSTNAMELEN-1;
    DWORD  tmp, BytesToRead;
    SESSION_STARTUPINFO ssi;
    SESSION_STARTREPLY  ssr;
    TCHAR   *buff;

// fill out the SESSION_STARTUPINFO structure
    ssi.MagicNumber = MAGICNUMBER;
    GetComputerName((TCHAR *) ssi.ClientName, &hostlen);
    ssi.LinesToSend = LinesToSend;
    ssi.Flag = ClientToServerFlag;
appcwrite(tpconv, (void *) &ssi, sizeof(ssi));

// receive a SESSION_STARTUPREPLY structure
appcread(tpconv, (void *) &ssr, sizeof(ssr));

if (ssr.MagicNumber < MAGICNUMBER) {
WRITEF((VBuff, TEXT("Warning: this APPC Remote server is older than your client\n")));
} else if (ssr.MagicNumber > MAGICNUMBER) {
WRITEF((VBuff, TEXT("Warning: your APPC Remote client is older than the server \n")));
}

    BytesToRead=MINIMUM(ssr.FileSize,ssi.LinesToSend*CHARS_PER_LINE);
    buff=calloc(BytesToRead+1,sizeof(TCHAR));
    if (buff!=NULL)
    {
        DWORD  bytesread=0;

        bytesread = appcread(tpconv, (void *) buff, BytesToRead*sizeof(TCHAR));

        WriteConsole(MyStdOut,buff,bytesread/sizeof(TCHAR),&tmp,NULL);
        LocalFree(buff);
    }

}


QUEUE.C

#include <windows.h>
#include "queue.h"

qnode *addtoq(queue *q, void *p) {
qnode *t;

t = (qnode *) LocalAlloc(0, sizeof(qnode));
if (t == NULL) return NULL;
t->ptr = p;
t->next = NULL;
if (q->head == NULL) {
t->prev = NULL;
q->head = t;
q->tail = t;
} else {
t->prev = q->tail;
q->tail->next = t;
q->tail = t;
}

return t;
}

void *removeheadfromq(queue *q) {
qnode *t;
void *p;

if (q->head == NULL) return NULL;

t = q->head;
q->head = t->next;
if (q->head == NULL) q->tail = NULL;
else q->head->prev = NULL;
p = t->ptr;
LocalFree(t);

return p;
}

void *removeqnodefromq(queue *q, qnode *t) {
void *p;

if (q->head == t) q->head = t->next;
if (q->tail == t) q->tail = t->prev;
if (t->next != NULL) t->next->prev = t->prev;
if (t->prev != NULL) t->prev->next = t->next;
p = t->ptr;
LocalFree(t);

return p;
}

queue *newq(void) {
queue *q;

q = (queue *) LocalAlloc(0, sizeof(queue));
if (q == NULL) return NULL;
q->head = NULL;
q->tail = NULL;

return q;
}


QUEUE.H

#ifndef __QUEUE_H__
#define __QUEUE_H__
typedef struct qnode_st {
struct qnode_st*next;
struct qnode_st*prev;
void *ptr;
intqnodeid;/* do not change*/
} qnode;

typedef struct queue_st {
qnode*head;
qnode*tail;
} queue;

qnode *addtoq(queue *q, void *p);
void *removeheadfromq(queue *q);
void *removeqnodefromq(queue *q, qnode *t);
queue *newq(void);
#endif /* __QUEUE_H__ */


SERVER.C

/*
Copyright (c) 1994, 1993 Microsoft Corporation

Module Name:
    Server.c

Abstract:
    The server component of Remote. It spawns a child process
    and redirects the stdin/stdout/stderr of child to itself.
    Waits for connections from clients - passing the
    output of child process to client and the input from clients
    to child process.

Author:
    Rajivendra Nath (rajnath) 2-Jan-1993

Environment:
    Console App. User mode.

Revision History:
Alex Wetmore (t-alexwe) 6-Jun-1994
- converted remote to use APPC with Windows SNA Server instead of named
  pipes
- converted to use Unicode
*/

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <io.h>
#include <string.h>
#include <winappc.h>
#include "appclib.h"
#include "aremote.h"

#define COMMANDFORMAT       TEXT("%c%-15s    [%-15s %d:%d]\n%c")
#define RemoteInfo(prt,flg) {if (!(flg&0x80000000)) prt;}

#define CMDSTRING(OutBuff,InpBuff,Client,sTime) \
        {                                \
            /* int xxlen; */             \
            wsprintf                      \
            (                            \
               &OutBuff[0],COMMANDFORMAT,\
               BEGINMARK,InpBuff,        \
               Client->Name,sTime.wHour, \
               sTime.wMinute,ENDMARK     \
            );                           \
         }

#define BUFFSIZE      1024

SESSION_TYPE ClientList[MAX_SESSION];

HANDLE  ChildStdInp;     //Server Writes to  it
HANDLE  ChildStdOut;     //Server Reads from it
HANDLE  ChildStdErr;     //Server Reads from it

HANDLE  SaveFile;       //File containing all that was
                        //output by child process.
                        //Each connection opens a handle to this file
                        //and is sent through PipeWriteH.

TCHAR   SaveFileName[64]; //Name of above file - all new sessions need
HANDLE  ChldProc;         //Handle to the Child Process
HANDLE  ListenThreadH;    //Handle to the thread listening for connections
                          //from Remote Clients.
extern BOOL AutoStarted;// true if this is an autostarted service

CRITICAL_SECTIONclose_crit;// critical section for closing

HANDLE
ForkChildProcess(          // Creates a new process
    TCHAR *cmd,             // Redirects its stdin,stdout
    PHANDLE in,            // and stderr - returns the
    PHANDLE out,           // corresponding pipe ends. Using currently
    PHANDLE err
    );

DWORD WINAPI
ListenForSession(          //THREAD:Listens for new connections and
    TCHAR* pipe             //spawns of new seesions - Updates the
    );                     //Status in Client DataStructure. Seperate Thread.

//
// threads for dealing with local session (only when non-autostarted)
DWORD WINAPI LocalSession(SESSION_TYPE *cli);
DWORD WINAPI StdoutThread(SESSION_TYPE *cli);
DWORD WINAPI StdinThread(SESSION_TYPE *cli);

BOOL
CreateMySecurityDescriptor(                    //
    PSECURITY_DESCRIPTOR pSecurityDescriptor,  // Creates a security descriptor
    TCHAR *Owner                                // with discretionary access for
    );                                         // access for Owner.

DWORD
NewSession(                //Manages the session with a client.
    SESSION_TYPE* Client
    );

DWORD WINAPI               //2 THREAD:Each reads either
GetChldOutput(             //StdOut or StdErr of child and
    HANDLE rhandle         //writes to SaveFile. Seperate Thread.
    );

DWORD WINAPI
TransferFileToClient(      //X THREADS:Reads the save
    SESSION_TYPE* Client   //file and sendsoutput to a client. Seperate Thread
    );

DWORD WINAPI
GetClientInput(            //Times X THREADS:Gets input from Child pipe
    SESSION_TYPE* Client   //and sends to childs StdIn. Seperate Thread.
    );

BOOL
FilterCommand(             //Filters input from client
    SESSION_TYPE *cl,      //for commands intended for REMOTE
    TCHAR *buff,
    int dread
    );

DWORD WINAPI               // Manages the IO with Remote Client.
RemoteSession(
    SESSION_TYPE* Client
    );

BOOL                       // Ctrl-C handler
SrvCtrlHand(
    DWORD event
    );

VOID                       // @s command to remote
SendStatus(
    tpconvid_t tpconv
    );

DWORD WINAPI               // @p command to remote
ShowPopup(
    TCHAR *mssg
    );

VOID                       // Removes the command begin and end markers
RemoveInpMark(             // from the save file.
    char* Buff,
    DWORD Size
    );

VOID                       // Cleans up the session
CloseClient(               // once it ends.
    SESSION_TYPE *Client
    );
                           // Initialises the Client datastructs
VOID
InitClientList(
    );

/*************************************************************/
/* The main entry point for the Server End of Remote         */
/*************************************************************/
VOID Server(TCHAR* ChildCmd, TCHAR *tp_name) {
    DWORD  ThreadID ;
    HANDLE WaitH[3], objs[MAX_SESSION];
    DWORD  WaitObj;
    TCHAR  tmpdir[32];
int    i, active;

    WRITEF((VBuff, TEXT("**************************************\n")));
    WRITEF((VBuff, TEXT("***********     REMOTE    ************\n")));
    WRITEF((VBuff, TEXT("***********     SERVER    ************\n")));
    WRITEF((VBuff, TEXT("**************************************\n")));

    InitClientList();

appcinit();

//
// This critical section is used around close so that only one appcclose
// is happening at a time.
//
InitializeCriticalSection(&close_crit);

    //
    //Start the command as a child process
    //

    ChldProc=ForkChildProcess(ChildCmd,&ChildStdInp,&ChildStdOut,&ChildStdErr);

    //
    //Create a tempfile for storing Child process output.
    //
    {
         DWORD size=sizeof(tmpdir);
         if (
             (GetEnvironmentVariable(TEXT("TMP"),tmpdir,size)==0)&&
             (GetEnvironmentVariable(TEXT("TEMP"),tmpdir,size)==0)
            )
         {
            wsprintf(tmpdir,TEXT("%ws"),TEXT("."));
         }
         if (!GetTempFileName(tmpdir,TEXT("REMOTE"),0,SaveFileName))
            GetTempFileName(TEXT("."),TEXT("REMOTE"),0,SaveFileName);
    }

    if ((SaveFile=CreateFile
                  (
                     (LPCTSTR)SaveFileName,           /* address of name of the file  */
                     GENERIC_READ|GENERIC_WRITE,      /* access (read/write) mode */
                     FILE_SHARE_READ|FILE_SHARE_WRITE,/* share mode   */
                     (LPSECURITY_ATTRIBUTES)NULL,     /* security descriptor  */
                     CREATE_ALWAYS,                   /* how to create    */
                     FILE_ATTRIBUTE_NORMAL,           /* File Attribute */
                     (HANDLE)NULL)
                  )==NULL)
    {
        TerminateProcess(ChldProc,0);
        ErrorExit(TEXT("Could not Create Output File"));
    }

    //
    //Start 2 threads to save the output from stdout and stderr of cmd to savefile.
    //

    if ((WaitH[0]=CreateThread
                  (
                     (LPSECURITY_ATTRIBUTES)NULL,           // No security attributes.
                     (DWORD)0,                              // Use same stack size.
                     (LPTHREAD_START_ROUTINE)GetChldOutput, // Thread procedure.
                     (LPVOID)ChildStdErr,                   // Parameter to pass.
                     (DWORD)0,                              // Run immediately.
                     (LPDWORD)&ThreadID)
                   )==NULL)
    {

        TerminateProcess(ChldProc,0);
        ErrorExit(TEXT("Failed to Create GetGhldOutput#1 Thread"));
    }

    if ((WaitH[1]=CreateThread
                  (
                     (LPSECURITY_ATTRIBUTES)NULL,           // No security attributes.
                     (DWORD)0,                              // Use same stack size.
                     (LPTHREAD_START_ROUTINE)GetChldOutput, // Thread procedure.
                     (LPVOID)ChildStdOut,                   // Parameter to pass.
                     (DWORD)0,                              // Run immediately.
                     (LPDWORD)&ThreadID)
                  )==NULL)
    {

        TerminateProcess(ChldProc,0);
        ErrorExit(TEXT("Failed to Create GetGhldOutput#2 Thread"));
    }

    //
    // Start Thread for local session
// only run if this is not an autostarted TP
    //
if (!AutoStarted) {
ClientList[0].Active = TRUE;
   if ((ClientList[0].hThread = CreateThread(
      (LPSECURITY_ATTRIBUTES) NULL,   // No security attributes.
      (DWORD) 0,                      // Use same stack size.
      (LPTHREAD_START_ROUTINE) LocalSession, // Thread procedure.
      (LPVOID) &ClientList[0],            // Parameter to pass.
      (DWORD) 0,                          // Run immediately.
      (LPDWORD) &ThreadID))==NULL) {
       TerminateProcess(ChldProc,0);
       ErrorExit(TEXT("Failed To Create LocalSession Thread"));
   }
}

    //
    //Start Thread to listen for new Connections
    //

    if ((ListenThreadH=CreateThread
                       (
                         (LPSECURITY_ATTRIBUTES)NULL,        // No security attributes.
                         (DWORD)0,                           // Use same stack size.
                         (LPTHREAD_START_ROUTINE)ListenForSession, // Thread procedure.
                         (LPVOID)tp_name,                    // Parameter to pass.
                         (DWORD)0,                           // Run immediately.
                         (LPDWORD)&ThreadID)
                       )==NULL)
    {

        TerminateProcess(ChldProc,0);
        ErrorExit(TEXT("Failed To Create ListenForSession Thread"));

    }

    SetConsoleCtrlHandler((PHANDLER_ROUTINE)SrvCtrlHand,TRUE);

    WaitH[2]=ChldProc;

    //
    // Wait until the child process terminates
    // or local IO thread terminates
    // or IO with child process ends
    //

    WaitObj=WaitForMultipleObjects(3,WaitH,FALSE,INFINITE);

    switch (WaitObj-WAIT_OBJECT_0)
    {
        case 0:      // Error Writing to savefile
        case 1:
            TerminateProcess(ChldProc,0);
            break;
        case 2:      // Child Proc Terminated
            break;

        default:     // Out of Some Resource
            WRITEF((VBuff,
TEXT("Out of Resource Error %d..Terminating\n"),
GetLastError()));
            break;

    }

    TerminateThread(ListenThreadH,0);

    CloseHandle(ChildStdInp);
    CloseHandle(ChildStdOut);
    CloseHandle(ChildStdErr);

    WRITEF((VBuff,
TEXT("\nServer: Child process \"%ws\" died, parent exiting...\n"),
ChildCmd));

    CloseHandle(SaveFile);

// signal all of the client threads to tell them that they should die
for (i = 0, active = 0; i < MAX_SESSION; i++) {
if (ClientList[i].Active) {
SetEvent(ClientList[i].DoClose);
objs[active++] = ClientList[i].hThread;
}
}

// Wait for all of the client to die gracefully
WaitForMultipleObjects(active, objs, TRUE, INFINITE);

    for (i = 0; i < MAX_SESSION; i++) {
    if (ClientList[i].rSaveFile != INVALID_HANDLE_VALUE)
        CloseHandle(ClientList[i].rSaveFile);

    if (ClientList[i].MoreData != NULL)
        CloseHandle(ClientList[i].MoreData);
}

    if (!DeleteFile(SaveFileName))
          WRITEF((VBuff,TEXT("Temp File %ws not deleted..\n"),SaveFileName));

appcdestroy();

    return;
}

/*************************************************************/
/* Creates the child process and redirects its std.IO handles*/
/*************************************************************/
// these handles should be treated as if they send and receive ANSI, not Unicode
HANDLE
ForkChildProcess(          // Creates a new process
    TCHAR *cmd,             // Redirects its stdin,stdout
    PHANDLE inH,           // and stderr - returns the
    PHANDLE outH,          // corresponding pipe ends.
    PHANDLE errH
    )
{
    SECURITY_ATTRIBUTES lsa;
    STARTUPINFO         si;
    PROCESS_INFORMATION pi;

    HANDLE ChildIn;
    HANDLE ChildOut;
    HANDLE ChildErr;

    lsa.nLength=sizeof(SECURITY_ATTRIBUTES);
    lsa.lpSecurityDescriptor=NULL;
    lsa.bInheritHandle=TRUE;

    //
    //Create Parent_Write to ChildStdIn Pipe
    //

    if (!CreatePipe(&ChildIn,inH,&lsa,0))
        ErrorExit(TEXT("Could Not Create Parent-->Child Pipe"));

    //
    //Create ChildStdOut to Parent_Read pipe
    //

    if (!CreatePipe(outH,&ChildOut,&lsa,0))
        ErrorExit(TEXT("Could Not Create Child-->Parent Pipe"));

    //
    //Create ChildStdOut to Parent_Read pipe
    //

    if (!CreatePipe(errH,&ChildErr,&lsa,0))
        ErrorExit(TEXT("Could Not Create Child-->Parent Pipe"));

    //
    // Lets Redirect Console StdHandles - easy enough
    //

    si.cb=sizeof(STARTUPINFO);
    si.lpReserved=NULL;
    si.lpTitle=NULL;
    si.lpDesktop=NULL;
    si.dwX=si.dwY=si.dwYSize=si.dwXSize=0;
    si.dwFlags=STARTF_USESTDHANDLES;
    si.hStdInput =ChildIn;
    si.hStdOutput=ChildOut;
    si.hStdError =ChildErr;
    si.wShowWindow=SW_SHOW;
    si.lpReserved2=NULL;
    si.cbReserved2=0;

    //
    //Create Child Process
    //

    if (!CreateProcess
         (
            NULL,
            cmd,
            NULL,
            NULL,
            TRUE,
            NORMAL_PRIORITY_CLASS,
            NULL,
            NULL,
            &si,
            &pi)
         )
    {
        if (GetLastError()==2)
            WRITEF((VBuff,TEXT("Executable %ws not found\n"),cmd));
        ErrorExit(TEXT("Could Not Create Child Process"));
    }

    //
    //Close unneccesary Handles and Restore the crt handles
    //

    CloseHandle(ChildIn);
    CloseHandle(ChildOut);
    CloseHandle(ChildErr);

    return(pi.hProcess);
}

/*************************************************************/
/* Listens for sessions from Clients and creates a new thread*/
/* for each client                                           */
/*************************************************************/

DWORD WINAPI ListenForSession(TCHAR* tp_name) {
    int    i;
    DWORD  ThreadID;
tpconvid_t tpconv;
    HANDLE TokenHandle;
    TOKEN_DEFAULT_DACL DefaultDacl;

WRITEF((VBuff, TEXT("\nServer: Waiting for clients on TP: %ws\n\n"), tp_name));

    DefaultDacl.DefaultDacl = NULL;

    if (OpenProcessToken
        (
            GetCurrentProcess(),
            TOKEN_ADJUST_DEFAULT,
            &TokenHandle
        ))
    {

        //
        // Remove the default DACL on the token
        //

        SetTokenInformation
        (
            TokenHandle,
            TokenDefaultDacl,
            &DefaultDacl,
            sizeof( TOKEN_DEFAULT_DACL )
        );

    }

    while(TRUE) {
tpconv = appclisten(tp_name);

        //
        // Look For a Free Slot
// There is guaranteed to be one or appclisten wouldn't have run.
        //

        for (i = 0; i < MAX_SESSION; i++) {
            //
            // Locate a Free Client block
            //
            if (!ClientList[i].Active) break;
        }

// check to see if we couldn't find a free session
if (i == MAX_SESSION) {
WRITEF((VBuff, TEXT("\nServer: Client tried to connect with all sessions in use\n")));
appcclose(tpconv);
continue;
} else {
appcwrite(tpconv, REMOTE_INIT_MSG,
lstrlen(REMOTE_INIT_MSG) * sizeof(TCHAR));
}

        //
        // Initialize the Client
        //
        ClientList[i].tpconv = tpconv;
        ClientList[i].Active = TRUE;
        ClientList[i].SendOutput = TRUE;
        ClientList[i].CommandRcvd = FALSE;

        //
        //start new thread for this connection
        //

        if((ClientList[i].hThread=CreateThread
            (
                 (LPSECURITY_ATTRIBUTES)NULL,        // No security attributes.
                 (DWORD)0,                           // Use same stack size.
                 (LPTHREAD_START_ROUTINE)RemoteSession, // Thread procedure.
                 (LPVOID)&ClientList[i],             // Parameter to pass.
                 (DWORD)0,                           // Run immediately.
                 (LPDWORD)&ThreadID)
            )==NULL)
        {
            CloseClient(&ClientList[i]);
            continue;
        }

    }
    return(0);
}

/*************************************************************/
/* Creates a security descriptor with the discrtionry access */
/* for the account specified in the /U switch  if any        */
/*************************************************************/

BOOL
CreateMySecurityDescriptor(
    PSECURITY_DESCRIPTOR pSecurityDescriptor,
    TCHAR *Owner
    )
{
    PSID pOwnerSid;
    PACL pAcl;
    BOOL Ret=FALSE;

    //
    // Initialize the Security Descriptor struct.
    //

    InitializeSecurityDescriptor
    (
        pSecurityDescriptor,
        SECURITY_DESCRIPTOR_REVISION
    );

    if (Owner==NULL)
    {
        //
        // No security required.
        //

        SetSecurityDescriptorDacl
        (
               pSecurityDescriptor,
               TRUE,
               NULL,
               FALSE
        );

        return TRUE;
    }

    {
        //
        // Get the SID for the account/Group
        //

        DWORD len1=1024,len2=1024;
        TCHAR RefDomain[1024];
        SID_NAME_USE snu=0;        //don't care

        if ((pOwnerSid=(PSID)LocalAlloc(LMEM_FIXED, len1))==NULL)
            return FALSE;

        Ret=
        LookupAccountName
        (
            NULL,
            Owner,
            pOwnerSid,
            &len1,
            RefDomain,
            &len2,
            &snu
        );

        if (!Ret)
        {
            LocalFree(pOwnerSid);
            return FALSE;
        }

    }

    {

        //
        // Create the access control list with access for
        // the SID obtained above.
        //

        DWORD aclsize=sizeof(ACL)+
                      sizeof(ACCESS_ALLOWED_ACE)+
                      GetLengthSid(pOwnerSid)-
                      sizeof(DWORD);

        if ((pAcl=(PACL)LocalAlloc(LMEM_FIXED,aclsize))==NULL)
        {
            LocalFree(pOwnerSid);
            return FALSE;
        }

        //
        // Initialize the acl buffer
        //

        Ret=
        InitializeAcl
        (
            pAcl,
            aclsize,
            ACL_REVISION
        );

        if (!Ret)
        {
            LocalFree(pOwnerSid);
            LocalFree(pAcl);
            return FALSE;
        }

        //
        // Add the sid to the access allowed part in ACL
        //

        Ret=
        AddAccessAllowedAce
        (
            pAcl,
            ACL_REVISION,
            GENERIC_ALL,
            pOwnerSid
        );

        if (!Ret)
        {
            LocalFree(pOwnerSid);
            LocalFree(pAcl);
            return FALSE;
        }
    }

    //
    // Add the created ACL to the discreationary control list
    //

    Ret=
    SetSecurityDescriptorDacl
    (
        pSecurityDescriptor,
        TRUE,
        pAcl,
        FALSE
    );

    if (!Ret)
    {
        LocalFree(pOwnerSid);
        LocalFree(pAcl);
        return FALSE;
    }
    return TRUE;
}

/*************************************************************/
/* Manages the Session with a Client - Creates a thread for  */
/* Inputs from the client and a thread for sending outputs to*/
/* the client. Could have just as easily done with 1 thread  */
/* using Asyn IO.                                            */
/*************************************************************/
DWORD WINAPI RemoteSession(SESSION_TYPE         *MyClient) {
    SESSION_STARTUPINFO  ssi;
    DWORD                tmp;
    SESSION_STARTREPLY   ssr;
    SYSTEMTIME           st;

    GetLocalTime(&st);
    memset((TCHAR *)&ssi,0,sizeof(ssi));

    //
    // Open a new handle to the save file ...
    // contains the saved output from the child process
    // and the commands already given to it.
    //

    if ((MyClient->rSaveFile=CreateFile
        (
            SaveFileName,
            GENERIC_READ|GENERIC_WRITE,
            FILE_SHARE_READ|FILE_SHARE_WRITE,
            NULL,OPEN_EXISTING,
            FILE_ATTRIBUTE_NORMAL,NULL)
        )==NULL)

    {
        CloseClient(MyClient);
        return(1);
    }

    //
    // Exchange Remote Information with Client.
    //
appcread(MyClient->tpconv, (void *) &ssi, sizeof(ssi));
wcscpy(MyClient->Name, ssi.ClientName);
MyClient->Name[15] = 0;

ssr.FileSize = GetFileSize(MyClient->rSaveFile, &tmp);
ssr.MagicNumber = MAGICNUMBER;

appcwrite(MyClient->tpconv, (void *) &ssr, sizeof(ssr));

    /* Lines  */
    if (ssi.LinesToSend!=-1)
    {
        long  PosFromEnd=ssi.LinesToSend*CHARS_PER_LINE;
        DWORD BytesToSend=MINIMUM((DWORD)PosFromEnd,ssr.FileSize);
        DWORD BytesRead, usize;
        char *abuff=(char *)LocalAlloc(LMEM_FIXED,(BytesToSend+1)*sizeof(char));
        TCHAR *ubuff=(TCHAR *)LocalAlloc(0, (BytesToSend+1)*sizeof(TCHAR));

        if (ssr.FileSize > (DWORD)PosFromEnd)
        {
            SetFilePointer(
                            MyClient->rSaveFile,
                            -PosFromEnd,
                            (PLONG)NULL,
                            FILE_END
                          );
        }

        if (abuff!=NULL && ubuff!=NULL)
        {
            if (!ReadFile(MyClient->rSaveFile,abuff,BytesToSend,&BytesRead,NULL))
            {
                CloseClient(MyClient);
                return(1);
            }

            // Don't want the markers to be part of the output display
            // at the client end.
            RemoveInpMark(abuff, BytesRead);

// convert to wbs
usize = mbstowcs(ubuff, abuff, BytesRead);

appcwrite(MyClient->tpconv, (void *) ubuff, usize * sizeof(TCHAR));
        }
        LocalFree(abuff);
LocalFree(ubuff);

    }

    RemoteInfo(WRITEF((VBuff, TEXT("\nServer: Connected To %ws [%02d:%02d]\n"),MyClient->Name,st.wHour,st.wMinute)),ssi.Flag);

    //
    // Start off the new session.
    //
    NewSession(MyClient);

    RemoteInfo(WRITEF((VBuff, TEXT("\nServer: Disconnected From %ws [%02d:%02d]\n"),MyClient->Name,st.wHour,st.wMinute)),ssi.Flag);

LocalFree(MyClient->tpconv);

    return(0);
}

//
// LocalSession: creates a session for use by the local terminal
//
// argument is ignored
//
DWORD WINAPI LocalSession(SESSION_TYPE *cli) {
BOOLdone = FALSE;
HANDLErthread, wthread;
DWORDtid;
    SYSTEMTIME  st;

    GetLocalTime(&st);

cli->Active = TRUE;
cli->tpconv = NULL;
cli->SendOutput = TRUE;
lstrcpy(cli->Name, TEXT("Local"));

    if ((cli->rSaveFile=CreateFile(SaveFileName,
            GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
            NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL))==NULL) {
cli->Active = FALSE;
        return 0;
    }

    cli->MoreData = CreateEvent(
        (LPSECURITY_ATTRIBUTES) NULL, /* address of security attributes*/
        FALSE,                      /* flag for manual-reset event*/
        TRUE,                      /* flag for initial state*/
        NULL                      /* address of event-object name*/
    );

cli->DoClose = CreateEvent(
(LPSECURITY_ATTRIBUTES) NULL,// no security on event
FALSE,// auto reset event mode
FALSE,// initially false
NULL);// unnamed object

    WRITEF((VBuff, TEXT("\nServer: Connected To %ws [%02d:%02d]\n"),cli->Name,st.wHour,st.wMinute));

if ((wthread = CreateThread(NULL,// no security attributes
0,// use same stack size
(LPTHREAD_START_ROUTINE) StdinThread,// function to run
cli,// pass client info
0,// run now
&tid)) == NULL) {// return thread id
ExitThread(0);
}

if ((rthread = CreateThread(NULL,// no security attributes
0,// use same stack size
(LPTHREAD_START_ROUTINE) StdoutThread,// function to run
cli,// pass client info
0,// run now
&tid)) == NULL) {// return thread id
ExitThread(0);
}

WaitForSingleObject(cli->DoClose, INFINITE);

TerminateThread(rthread, 0);
TerminateThread(wthread, 0);

cli->Active = FALSE;
CloseHandle(cli->DoClose);
CloseHandle(cli->MoreData);
CloseHandle(cli->rSaveFile);
CloseHandle(stdin);
CloseHandle(stdout);

    WRITEF((VBuff, TEXT("\nServer: Disconnected From %ws [%02d:%02d]\n"),cli->Name,st.wHour,st.wMinute));

return 0;
}

DWORD WINAPI StdinThread(SESSION_TYPE *cli) {
HANDLEStdin;
TCHARubuf[BUFFSIZE];
charabuf[BUFFSIZE];
DWORDtmp, dread, asize;

Stdin = GetStdHandle(STD_INPUT_HANDLE);
// read a command from the user into a buffer (it will be in Unicode)
while (ReadConsole(Stdin, ubuf, BUFFSIZE - 1, &dread, NULL)) {
cli->CommandRcvd = TRUE;
ubuf[dread] = 0;
// see if it is a valid command
if (!FilterCommand(cli, ubuf, dread)) {
// convert the buffer from Unicode to ANSI
asize = wcstombs(abuf, ubuf, BUFFSIZE);

// write the buffer to the child's pipe
if (!WriteFile(ChildStdInp, abuf, asize, &tmp, NULL))
ExitThread(0);
}
}
return 0;
}

DWORD WINAPI StdoutThread(SESSION_TYPE *cli) {
HANDLEStdout;
charabuffin[BUFFSIZE];
    TCHAR   buffin[BUFFSIZE], buffout[BUFFSIZE], cmdbuff[BUFFSIZE];
    DWORD  aread, dread=0, dwrite=0, tmp, cmdP = 0, i;
    BOOL   incmd=FALSE;
    TCHAR   MyEchoStr[30];

    wsprintf(MyEchoStr,TEXT("[%-15s"),cli->Name);

    Stdout = GetStdHandle(STD_OUTPUT_HANDLE);

// read a buffer from an ansi file (the buffer will be ANSI)
    while (ReadFile(cli->rSaveFile, abuffin, BUFFSIZE-1, &aread, NULL)) {
        if (aread == 0) {
// wait for more data to arrive and loop again
WaitForSingleObject(cli->MoreData, INFINITE);
continue;
}

abuffin[aread] = 0;
dread = mbstowcs(buffin, abuffin, BUFFSIZE);

        dwrite=0;

        //
        // This is all to insure that the commands entered
        // by clients are not echoed back to them.
        // A Beginmark and an Endmark is placed around commands
        // sent to the child process from some client.
        //

        for (i = 0; i < dread; i++) {
            if (incmd) {
                if ((buffin[i] == ENDMARK) || (cmdP == BUFFSIZE - 1)) {
                    incmd = FALSE;
                    cmdbuff[cmdP] = 0;
                    if ((wcsstr(cmdbuff, MyEchoStr) == NULL) ||
                        (!cli->CommandRcvd)) {
WriteConsole(Stdout, cmdbuff, dread, &tmp, NULL);
                    }
                    cmdP=0;
                } else {
                    cmdbuff[cmdP++] = buffin[i];
                }
            } else {
                if (buffin[i] == BEGINMARK) {
                    if (dwrite != 0) {
WriteConsole(Stdout, buffout, dread, &tmp, NULL);
                        dwrite = 0;
                    }
                    incmd = TRUE;
                    continue;
                } else {
                    buffout[dwrite++] = buffin[i];
                }
            }
        }

        if (dwrite != 0) {
WriteConsole(Stdout, buffout, dread, &tmp, NULL);
        }
    }

return 0;
}

DWORD NewSession(SESSION_TYPE* MyClient) {
    DWORD ThreadId;
    HANDLE  rThread, wThread;

    MyClient->MoreData = CreateEvent(
        (LPSECURITY_ATTRIBUTES) NULL, /* address of security attributes*/
        FALSE,                      /* flag for manual-reset event*/
        TRUE,                      /* flag for initial state*/
        NULL                      /* address of event-object name*/
    );

MyClient->DoClose = CreateEvent(
(LPSECURITY_ATTRIBUTES) NULL,// no security on event
FALSE,// flag for manual-reset event
FALSE,// initially false
NULL);// unnamed object

    if ((rThread = CreateThread(
            (LPSECURITY_ATTRIBUTES) NULL,         // No security attributes.
            (DWORD) 0,                            // Use same stack size.

(LPTHREAD_START_ROUTINE) GetClientInput,
            (LPVOID) MyClient,                    // Parameter to pass.
            (DWORD) 0,                            // Run immediately.
            (LPDWORD) &ThreadId))==NULL) {
        return(GetLastError());
    }

    if ((wThread=CreateThread(
            (LPSECURITY_ATTRIBUTES) NULL,        // No security attributes.
            (DWORD) 0,                           // Use same stack size.
            (LPTHREAD_START_ROUTINE) TransferFileToClient,
            (LPVOID) MyClient,                   // Parameter to pass.
            (DWORD) 0,                           // Run immediately.
            (LPDWORD) &ThreadId))==NULL) {
        CloseHandle(rThread);
        return(GetLastError());
    }

// Wait for the input thread to terminate.  when it does the session is
// over.
    ThreadId = WaitForSingleObject(wThread, INFINITE);

CloseClient(MyClient);

ThreadId = WaitForSingleObject(rThread, INFINITE);

// close the thread handles
    CloseHandle(rThread);
    CloseHandle(wThread);

    return 0;
}

/*************************************************************/
/* Saves the output from the child process into the savefile */
/* All the remote client thread and local client thread      */
/* open a seperate handle to this and output its content     */
/* sequentially.                                             */
/*************************************************************/
DWORD WINAPI GetChldOutput(HANDLE readH) {
    TCHAR  buff[BUFFSIZE];
    DWORD dread;
    DWORD tmp;
inti;

    while (ReadFile(readH,buff,BUFFSIZE-1,&dread,NULL)) {
        buff[dread]=TEXT('\0');

        if (!WriteFile(SaveFile,buff,dread,&tmp,NULL))
            return(1);

        //
        // Signal Reader Thread that more data is available
        //
        for (i=0;i<MAX_SESSION;i++)
if (ClientList[i].Active) SetEvent(ClientList[i].MoreData);
    }
    return 1;
}

/*************************************************************/
/* A thread for each client connection and one for local IO  */
/* Reads the contents of Save file and sends it to client for*/
/* display.                                                  */
/*************************************************************/
DWORD WINAPI TransferFileToClient(SESSION_TYPE *MyClient) {
    TCHAR   buffin[BUFFSIZE], buffout[BUFFSIZE], cmdbuff[BUFFSIZE];
charabuffin[BUFFSIZE];
    DWORD  dread=0,dwrite=0,aread;
    BOOL   incmd=FALSE;
    DWORD  cmdP=0;
    DWORD  i;
    TCHAR   MyEchoStr[30];
HANDLEobjs[2];
DWORD which;

    wsprintf(MyEchoStr,TEXT("[%-15s"),MyClient->Name);

    while (ReadFile(MyClient->rSaveFile, abuffin, BUFFSIZE-1, &aread, NULL)) {
        if (aread == 0) {
            //
            // Event is set by GetChldOutput() func. to signal
            // More data is available in save file.
            //
objs[0] = MyClient->MoreData;
objs[1] = MyClient->DoClose;

// wait for moredata or a close request
which = WaitForMultipleObjects(2, objs, FALSE, INFINITE);
which = which - WAIT_OBJECT_0;

            if (which == 0) {
// there is more data waiting
continue;
} else {
// they want us to close
return 0;
}
        }

dread = mbstowcs(buffin, abuffin, aread);

        dwrite=0;

        //
        // This is all to insure that the commands entered
        // by clients are not echoed back to them.
        // A Beginmark and an Endmark is placed around commands
        // sent to the child process from some client.
        //

        for (i = 0; i < dread; i++) {
            if (incmd) {
                if ((buffin[i] == ENDMARK) || (cmdP == BUFFSIZE - 1)) {
                    incmd = FALSE;
                    cmdbuff[cmdP] = 0;
                    if ((wcsstr(cmdbuff, MyEchoStr) == NULL) ||
                        (!MyClient->CommandRcvd)) {
appcwrite(MyClient->tpconv, (void *) cmdbuff,
cmdP * sizeof(TCHAR));
                    }
                    cmdP=0;
                } else {
                    cmdbuff[cmdP++] = buffin[i];
                }
            } else {
                if (buffin[i] == BEGINMARK) {
                    if (dwrite != 0) {
appcwrite(MyClient->tpconv, (void *) buffout,
dwrite * sizeof(TCHAR));
                        dwrite = 0;
                    }
                    incmd = TRUE;
                    continue;
                } else {
                    buffout[dwrite++] = buffin[i];
                }
            }
        }

        if (dwrite != 0) {
appcwrite(MyClient->tpconv, (void *) buffout,
dwrite * sizeof(TCHAR));
        }
    }
    return 0;
}

/*************************************************************/
/* Commands from the clients are sent to the child process   */
/* and also saved in the SaveFile with Begin and End markers */
/* around them to seperate them from the output from child   */
/* process.                                                  */
/*************************************************************/
DWORD WINAPI GetClientInput(SESSION_TYPE *MyClient) {
    TCHAR ubuff[BUFFSIZE];
char  abuff[BUFFSIZE];
    DWORD tmp,uread,aread;

while (TRUE) {
    uread = appcread(MyClient->tpconv, (void *) ubuff, BUFFSIZE) /
sizeof(TCHAR);
if (!appcvalid(MyClient->tpconv)) break;

        MyClient->CommandRcvd = TRUE;

        if (FilterCommand(MyClient, ubuff, uread)) continue;

aread = wcstombs(abuff, ubuff, uread);

        if (!WriteFile(ChildStdInp, abuff, aread, &tmp, NULL)) ExitThread(0);
    }

    return(1);
}

/*************************************************************/
/* If a client command is intended for the Remote server -   */
/* those beginning with COMMANDCHAR (are not intended        */
/* for the child process) - they are executed here           */
/* and the output sent to the client.                        */
/*************************************************************/
BOOL FilterCommand(SESSION_TYPE *cl, TCHAR *buff, int dread) {
    SYSTEMTIME st;
    TCHAR       inp_buff[4096];
    TCHAR       tmpchar;
    TCHAR       ch[3];
    DWORD      tmp;
    int        len;
    DWORD      ThreadID; //Useless

    if (dread==0)
        return(FALSE);

    buff[dread]=0;

    GetLocalTime(&st);

    if (buff[0]==COMMANDCHAR) {
        switch(tolower(buff[1])) {
            case TEXT('o'):
                cl->SendOutput=!cl->SendOutput;
                break;

            case TEXT('k'):
                TerminateProcess(ChldProc,1);
                break;

case TEXT('q'):
SetEvent(cl->DoClose);
break;

            case TEXT('s'):
                SendStatus(cl->tpconv);
                break;

            case TEXT('p'):
if (cl->tpconv == NULL) break;
{
 //Free it in called Proc
TCHAR *mssg=(TCHAR *)LocalAlloc(0, 4096*sizeof(TCHAR));
                TCHAR  *ack=TEXT("Server: Popup Shown..\n");

                    if (mssg==NULL)
                        break;

                    wsprintf(mssg,TEXT("From %ws [%d:%d]\n\n%ws\n"),cl->Name,st.wHour,st.wMinute,&buff[2]);
                    CreateThread(
                          (LPSECURITY_ATTRIBUTES)NULL,         // No security attributes.
                          (DWORD)0,              // Use same stack size.
                          (LPTHREAD_START_ROUTINE)ShowPopup, // Thread procedure.
                          (LPVOID)mssg,          // Parameter to pass.
                          (DWORD)0,              // Run immediately.
                          (LPDWORD)&ThreadID
                         );
appcwrite(cl->tpconv, (void *) ack, lstrlen(ack) *
sizeof(TCHAR));
                    break;
                 }

            case TEXT('m'):
                buff[dread-2]=0;
                CMDSTRING(inp_buff,buff,cl,st);
                len=lstrlen(inp_buff);
                WriteFile(SaveFile,inp_buff,len,&tmp,NULL);
                break;

            case TEXT('@'):
                buff[dread-2]=0;
                CMDSTRING(inp_buff,&buff[1],cl,st);
                len=lstrlen(inp_buff);
                WriteFile(SaveFile,inp_buff,len,&tmp,NULL);
                //
                // Remove the first @ sign
                //
                MoveMemory(buff,&buff[1],dread-1);
                buff[dread-1]=TEXT(' ');
                return(FALSE); //Send it it to the chile process
                break;

            default :
if (cl->tpconv == NULL) break;
                lstrcpy(inp_buff, TEXT("** Unknown Command **\n"));
appcwrite(cl->tpconv, (void *) inp_buff,
lstrlen(inp_buff) * sizeof(TCHAR));

            case TEXT('h'):
if (cl->tpconv != NULL) {
                wsprintf(inp_buff,TEXT("%cM: To Send Message\n"),COMMANDCHAR);
appcwrite(cl->tpconv, (void *) inp_buff, lstrlen(inp_buff) * sizeof(TCHAR));
               wsprintf(inp_buff,TEXT("%cP: To Generate popup\n"),COMMANDCHAR);
appcwrite(cl->tpconv, (void *) inp_buff, lstrlen(inp_buff) * sizeof(TCHAR));
               wsprintf(inp_buff,TEXT("%cK: To kill the server\n"),COMMANDCHAR);
appcwrite(cl->tpconv, (void *) inp_buff, lstrlen(inp_buff) * sizeof(TCHAR));
               wsprintf(inp_buff,TEXT("%cH: This Help\n"),COMMANDCHAR);
appcwrite(cl->tpconv, (void *) inp_buff, lstrlen(inp_buff) * sizeof(TCHAR));
}
                break;
        }
        return(TRUE);
    }

    if ((buff[0]<26)) {
        BOOL ret=FALSE;

        wsprintf(ch,TEXT("^%c"),buff[0]+64);
//        CMDSTRING(inp_buff,ch,cl,st);
        len=lstrlen(inp_buff);

        if (buff[0]==CTRLC)
        {
            cl->CommandRcvd=FALSE;
            GenerateConsoleCtrlEvent(CTRL_C_EVENT,0);
            ret=TRUE; //Already sent to child
        }

        WriteFile(SaveFile,inp_buff,len,&tmp,NULL);
        return(ret); //FALSE:send it to child StdIn
    }

    tmpchar=buff[dread-2]; //must be 13;but just incase
    buff[dread-2]=0;
//    CMDSTRING(inp_buff,buff,cl,st);
    buff[dread-2]=tmpchar;
    len=lstrlen(inp_buff);
    WriteFile(SaveFile,inp_buff,len,&tmp,NULL);
    return(FALSE);
}

/*************************************************************/
VOID SendStatus(tpconvid_t tpconv) {
    TCHAR  buff[1024];
    int   i;
    TCHAR  *env=(TCHAR *)GetEnvironmentStrings();
    DWORD ver=GetVersion();

if (tpconv == NULL) return;

    wsprintf(buff,TEXT("Command = %ws\n"), ChildCmd);
appcwrite(tpconv, (void *) buff, lstrlen(buff) * sizeof(TCHAR));

    wsprintf(buff,TEXT("Build = %d \n"),((WORD *)&ver)[1]);
appcwrite(tpconv, (void *) buff, lstrlen(buff) * sizeof(TCHAR));

    for (i=1;i<MAX_SESSION;i++)
    {
        if (ClientList[i].Active)
        {
            wsprintf(buff,TEXT("ACTIVE SESSION=%ws\n"),ClientList[i].Name);
appcwrite(tpconv, (void *) buff, lstrlen(buff) * sizeof(TCHAR));
        }
    }

    wsprintf(buff,TEXT("====================\n"));
appcwrite(tpconv, (void *) buff, lstrlen(buff) * sizeof(TCHAR));

    wsprintf(buff,TEXT("ENVIRONMENT VARIABLES\n"));
appcwrite(tpconv, (void *) buff, lstrlen(buff) * sizeof(TCHAR));

    wsprintf(buff,TEXT("====================\n"));
appcwrite(tpconv, (void *) buff, lstrlen(buff) * sizeof(TCHAR));

    __try {
        while (*env!=0)
        {
            wsprintf(buff,TEXT("%ws\n"),env);
appcwrite(tpconv, (void *) buff, lstrlen(buff) * sizeof(TCHAR));

            while(*(env++)!=0);
        }
    } __except(EXCEPTION_EXECUTE_HANDLER) {
        wsprintf(buff,TEXT("Exception Generated Getting Environment Block\n"),env);
appcwrite(tpconv, (void *) buff, lstrlen(buff) * sizeof(TCHAR));
    }

    wsprintf(buff,TEXT("====================\n"));
appcwrite(tpconv, (void *) buff, lstrlen(buff) * sizeof(TCHAR));
    return;
}

/*************************************************************/
DWORD WINAPI
ShowPopup(
    TCHAR *mssg
    )
{
    MessageBox(GetActiveWindow(),mssg,TEXT("***REMOTE***"),MB_OK|MB_SETFOREGROUND);
    LocalFree(mssg);
    return(0);
}
/*************************************************************/
BOOL SrvCtrlHand(
    DWORD event
    )
{
    if (event==CTRL_BREAK_EVENT)
    {
        TerminateProcess(ChldProc,1);
    }
    return(TRUE);
}
/*************************************************************/

VOID CloseClient(SESSION_TYPE *Client) {
EnterCriticalSection(&close_crit);

appcclose(Client->tpconv);

    if (Client->rSaveFile != INVALID_HANDLE_VALUE) {
        CloseHandle(Client->rSaveFile);
        Client->rSaveFile = INVALID_HANDLE_VALUE;
    }

    if (Client->MoreData != NULL) {
        CloseHandle(Client->MoreData);
        Client->MoreData = NULL;
    }

Client->Active=FALSE; //Keep it last else synch problem.

LeaveCriticalSection(&close_crit);

    return;
}

VOID InitClientList(VOID) {
    int i;
    for (i=0;i<MAX_SESSION;i++)
    {
        ZeroMemory(ClientList[i].Name,HOSTNAMELEN);
ClientList[i].tpconv = NULL;
        ClientList[i].rSaveFile = INVALID_HANDLE_VALUE;
        ClientList[i].MoreData = NULL;
        ClientList[i].Active = FALSE;
        ClientList[i].CommandRcvd = FALSE;
        ClientList[i].SendOutput = FALSE;
        ClientList[i].hThread = NULL;
    }
    return;
}

VOID RemoveInpMark(char* Buff, DWORD Size) {
    DWORD i;
    for (i=0;i<Size;i++)
    {
        switch (Buff[i])
        {
        case BEGINMARK:
            Buff[i]=' ';
            break;

        case ENDMARK:
            if (i<2)
            {
                Buff[i]= ' ';
            }
            else
            {
                Buff[i]  =Buff[i-1];
                Buff[i-1]=Buff[i-2];
                Buff[i-2]=' ';
            }
            break;

        default:
           break;
       }
    }
}


SETUPDLG.H

//{{NO_DEPENDENCIES}}
// App Studio generated include file.
// Used by ARSETUP.RC
//
#define IDS_ERRORTITLE                  16
#define IDS_UNKNOWN                     17
#define IDS_OPENSCMFAILED               18
#define IDS_CREATEFAILED                19
#define IDS_LOCKFAILED                  20
#define IDS_SETKEYFAILED                21
#define IDS_OPENKEYFAILED               22
#define IDS_INIWRITEFAILED              23
#define IDS_BADPATHNAME                 24
#define IDS_BADTPNAME                   25
#define IDS_SERVICEEXISTS               26
#define IDS_BADTIMEOUT                  27
#define IDS_INFOTITLE                   28
#define IDS_NOMESSAGE                   29
#define IDS_SUCCESS                     30
#define IDS_REGISTRYPATH                31
#define IDS_NOUSERNAME                  32
#define IDS_NOPASSWORD                  33
#define IDS_OUTOFMEMORY                 34
#define IDS_REGISTRYAPPLPATH            35
#define IDS_REPLACECAPTION              36
#define IDS_REPLACETEXT                 37
#define IDS_BADCMDPATH                  38
#define IDS_BADREMOTEPATH               39
#define IDS_BADLUNAME                   40
#define IDR_ACCELERATOR2                102
#define IDR_ACCELERATOR1                103
#define IDC_PROGRAMTYPE                 110
#define IDC_SERVICE                     111
#define IDC_APPLICATION                 112
#define IDC_CONVSEC                     505
#define IDC_ALREADYVER                  506
#define IDE_PATHNAME                    507
#define IDE_TPNAME                      508
#define IDC_QUEUED                      509
#define IDE_REMOTEPATH                  509
#define IDD_FINITE                      512
#define IDD_INFINITE                    513
#define IDE_TIMEOUT                     514
#define IDC_ADD                         516
#define IDC_EDIT                        517
#define IDC_DELETE                      518
#define IDC_LIST                        519
#define IDC_USERBOX                     520
#define IDE_USERNAME                    521
#define IDE_PASSWORD                    522
#define IDT_PASSWDTEXT                  523
#define IDE_LOCALLU                     524
#define IDC_REMOTEPATH_BROWSE           1000
#define IDC_CMDPATH_BROWSE              1001
#define IDE_CMDPATH                     1002

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS

#define _APS_NEXT_RESOURCE_VALUE        104
#define _APS_NEXT_COMMAND_VALUE         101
#define _APS_NEXT_CONTROL_VALUE         1004
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif


AREXEC.C

/*****************************************************************************
 *
 *  MODULE NAME : AREXEC.C
 *
 *  COPYRIGHTS:
 *             This module contains code made available by IBM
 *             Corporation on an AS IS basis.  Any one receiving the
 *             module is considered to be licensed under IBM copyrights
 *             to use the IBM-provided source code in any way he or she
 *             deems fit, including copying it, compiling it, modifying
 *             it, and redistributing it, with or without
 *             modifications.  No license under any IBM patents or
 *             patent applications is to be implied from this copyright
 *             license.
 *
 *             A user of the module should understand that IBM cannot
 *             provide technical support for the module and will not be
 *             responsible for any consequences of use of the program.
 *
 *             Any notices, including this one, are not to be removed
 *             from the module without the prior written consent of
 *             IBM.
 *
 *  AUTHOR:    Peter J. Schwaller
 *             VNET:     PJS at RALVM6           Tie Line: 444-4376
 *             Internet: pjs@ralvm6.vnet.ibm.com     (919) 254-4376
 *
 *  FUNCTION:  Execute a command on a remote workstation and display the
 *             output from the command (both stdout and stderr) on the
 *             requesting workstation.
 *
 *  Usage:
 *      AREXEC destination command
 *
 *    Destination may be either a partner LU alias (up to eight
 *    characters) or a fully qualified LU name (3-17 characters separated
 *    by a period)
 *
 *  AVAILABILITY:
 *             These sample programs and source are also available on
 *             CompuServe through the APPC Information Exchange.  To get
 *             to the APPC forum just type 'GO APPC' from any CompuServe
 *             prompt.  The samples are available in the Sample Programs
 *             library section.  Just search on the keyword CPICPGMS to
 *             find all the samples in this series.
 *
 *             Updates for the sample programs and support for many more
 *             CPI-C platforms will also be made available on CompuServe.
 *
 *  RELATED FILES:
 *             See AREXEC.DOC for usage instructions.
 *
 *  CHANGE HISTORY:
 *  Date       Description
 *  06/15/92   NS/DOS accepts version 2.02 into system test.
 *  08/05/92   Version 2.31 released to CompuServe
 *             This version was also distributed at the APPC/APPN Platform
 *             Developer's Conference held in Raleigh, NC.
 *  08/13/92   Changed all printf and fprintf calls to use a write_*() call.
 *  08/17/92   Made security handling code conditionally compiled.
 *  08/23/92   Removed call to cpicinit_default_destination().
 *  08/24/92   Version 2.32 released to CompuServe.
 *  09/22/92   Version 2.33 released to CompuServe.
 *  11/17/92   Added support for printing remote operating system name.
 *             Version 2.34 released to CompuServe
 *  01/07/93   Version 2.35
 *             Fixed a number of problems when compiling with IBM C Set/2
 *                password input was displayed
 *                timer resolution was 1 second
 *
 *****************************************************************************/

/*****************************************************************************
 * OVERVIEW of AREXEC CPI-C Flows
 *
 *   Client (AREXEC)                     Server (AREXECD)
 *   --------------                      ---------------
 * Set up conversation
 *   Allocate       -------------------> Accept Conversation
 * Exchange Version Numbers
 *   Send Data      -------------------> Receive
 *   Receive        <------------------- Send Data
 * Send the Command
 *   Send Data      -------------------> Receive
 * Receive the Output
 * -->                                           <--
 * | Receive        <------------------- Send Data |     LOOP
 * ---                                           ---
 * Command is finished
 * Receive          <------------------- Deallocate(FLUSH)
 *****************************************************************************/

#if defined(WINDOWS)||defined(WIN32)
#include <windows.h>
#endif
#include "wincpic.h"

/* standard C include files */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

/* Set up constant declarations */
#include "cpicdefs.h"

/* Collection of routines with special ported version for each platform */
#include "cpicport.h"

/* CPI-C error handling routines */
/* This file is supplied with AREXEC */
#include "cpicerrs.h"

/* Argument processing procedure */
/* This file is supplied with AREXEC */
#include "getopt.h"

/* CPI-C initialization routines */
/* This file is supplied with AREXEC */
#include "cpicinit.h"

#define  MAX_COMMAND_LENGTH   500

/* CPI-C error handling info */
CPICERR * cpicerr;

/* These are the defaults to be used if the user does not provide arguments  */
/* to override these values.                                                 */
#define  DEFAULT_TP_NAME   "AREXECD"
#define  DEFAULT_MODE_NAME "#INTER"
#define  DEFAULT_SYM_DEST  "AREXECD"

/* Define these here so we can make changes throughout the code. */
/*
 * The PROGRAM_INFO string should be kept in sync with the
 * MAJOR_VERSION and MINOR_VERSION constants.  Although the
 * cpicerr_exchange_version() call will support values up to 255,
 * values for MINOR_VERSION should be from 00-99 to maintain the
 * two character format in the version string.
 */
#define  PROGRAM_NAME      "AREXEC"
#define  PROGRAM_INFO      "version 2.35"
#define  MAJOR_VERSION     (2)
#define  MINOR_VERSION     (35)
#define  LOG_FILE_NAME     "arexec.err"
#define  LOG_FILE_PATH     "$LOGPATH"

/* local structure definition */
typedef struct command_info {
    char        buffer[MAX_COMMAND_LENGTH];
    CM_INT32    buffer_length;
} COMMAND_INFO;

/* local function prototypes */
void process_arguments(int argc,
                       char *argv[],
                       CPICINIT * cpicinit,
                       COMMAND_INFO * command_info);

/*
 * Message displayed with show_info() when APING is started.
 */
char * intro[] = {
    PROGRAM_NAME " " PROGRAM_INFO " - Execute a remote command.",
    "  by Peter J. Schwaller (pjs@ralvm6.vnet.ibm.com)",
    NULL
    };

/*
 * Message displayed with show_info() when AREXEC is started.
 */
char * usage[] = {
    "",
    "Usage:",
    "AREXEC [flags] destination command",
    "Flags:",
    "  destination",
    "\tmay be either a symbolic destination name or a partner LU name",
    "  command",
    "\tcommand string to be executed on the remote machine",
    "  -m mode_name",
    "\tMode name (default: " DEFAULT_MODE_NAME ")",
    "  -t tp_name",
#if defined(SUPPORTS_SETTING_SECURITY)
    "\tthe TP to use on the allocate (default: " DEFAULT_TP_NAME ")",
    "  -u userid",
    "  -p password",
    "\tSecurity parameters.  If a userid is specified without a password,",
    "\tyou will be prompted for the password.",
    "  -n",
    "\tDo not use any security (SECURITY=NONE).",
#endif
    NULL
    };

void _cdecl
main( int argc, char *argv[])
{
    /* Variables used for CPI-C calls */
    unsigned char cm_conv_id[8];            /* CPI-C conversation ID         */
    CM_INT32     cm_rc;                     /* CPI-C return code             */
    CM_INT32     rts_received;              /* request to send received      */
    CM_INT32     max_receive_len;           /* Max receive length on CMRCV   */
    CM_INT32     what_received;             /* What received parm from CMRCV */
    CM_INT32     received_len;              /* Amount of data rcvd on CMRCV  */
    CM_INT32     status_received;           /* Status from CMRCV             */

    /* Destination information */
    CPICINIT *   cpicinit;

    char         partner_major_version;
    char         partner_minor_version;
    char         opsys_string[64];

#if defined(WINDOWS) || defined(WIN32)
    unsigned short WinCPICVersion = 0x0001;
    WCPICDATA CPICData;
#endif

    COMMAND_INFO command_info;

    /*
     * Make sure all output is seen as soon as possible.
     */
    setbuf(stdout, NULL);

    show_info(intro);                       /* Show program information      */

#if (defined(WIN32) || defined(WINDOWS))                              /*WIN32*/
       /****************************************************************WIN32*/
       /* Initialisation for WinCPIC                                   *WIN32*/
       /****************************************************************WIN32*/
    if (WinCPICStartup(WinCPICVersion,&CPICData))                     /*WIN32*/
    {                                                                 /*WIN32*/
       return;                                                        /*WIN32*/
    }                                                                 /*WIN32*/
#endif                                                                /*WIN32*/

    /*
     * Create a new CPICINIT structure and initialize values.
     * The procedures are in CPICINIT.C
     */
    cpicinit = cpicinit_new();
    cpicinit_default_tp_name(cpicinit, DEFAULT_TP_NAME);
    cpicinit_default_mode_name(cpicinit, DEFAULT_MODE_NAME);
    cpicinit_default_sym_dest_name(cpicinit, DEFAULT_SYM_DEST);

    /*
     * Process all of the command line arguments.  All of the conversation
     * setup arguments are stored in the cpicinit object.  The command
     * to be sent is returned in the buffer variable within command_info.
     */
    process_arguments(argc, argv, cpicinit, &command_info);

    if (cpicinit_query_password_needed(cpicinit)) {
        /* get a password from the user */
        cpicinit_get_password(cpicinit);
    }

    /*
     * Initialize the CPICERR structure.  This is done before the CMINIT
     * call so that we can use CPICERR for help with errors on CMINIT.
     * The procedure is in CPICERR.C
     */
    cpicerr = cpicerr_new();
    cpicerr_set_program_name(cpicerr, PROGRAM_NAME);
    cpicerr_set_program_info(cpicerr, PROGRAM_INFO);
    cpicerr_set_major_version(cpicerr, MAJOR_VERSION);
    cpicerr_set_minor_version(cpicerr, MINOR_VERSION);
    cpicerr_set_log_file_name(cpicerr, LOG_FILE_NAME);
    cpicerr_set_log_file_path(cpicerr, LOG_FILE_PATH);

    cpicinit_setup_conversation(cpicinit, cm_conv_id, cpicerr);

    {
    CM_SYNC_LEVEL sync_level = CM_CONFIRM;
    cmssl(cm_conv_id,                        /* Set sync level               */
          &sync_level,
          &cm_rc);
    }

    cmallc(cm_conv_id,                       /* Allocate the conversation    */
           &cm_rc);
    if (cm_rc != CM_OK) cpicerr_handle_rc(cpicerr, MSG_CMALLC, cm_rc);

    cpicerr_exchange_version_plus(cpicerr,
                                  cm_conv_id,
                                  CM_SEND_STATE,
                                  &partner_major_version,
                                  &partner_minor_version,
                                  opsys_string,
                                  sizeof(opsys_string));
    /*
     * Display the operating system string returned in the exchange.
     * The string length will be 0 if the operating system string
     * wasn't available.
     */

    if (strlen(opsys_string) != 0) {
        write_output("\nConnected to a partner running on: %s\n",
                     opsys_string);
    }

    convert_to_ascii(command_info.buffer, command_info.buffer_length);
    cmsend(cm_conv_id,                     /* Send Data                      */
           (unsigned char *)command_info.buffer, /* data pointer             */
           &command_info.buffer_length,    /* length of data sent            */
           &rts_received,                  /* request to send indicator      */
           &cm_rc);
    if (cm_rc != CM_OK) cpicerr_handle_rc(cpicerr, MSG_CMSEND, cm_rc);

    max_receive_len = sizeof(command_info.buffer);
    do {
        cmrcv(cm_conv_id,                   /* Receive Data                  */
              (unsigned char *)command_info.buffer, /* Data Pointer          */
              &max_receive_len,             /* Size of Data Buffer           */
              &what_received,               /* returned - what received      */
              &received_len,                /* returned - length of data     */
              &status_received,             /* returned - status received    */
              &rts_received,                /* returned - request to send    */
              &cm_rc);

       if (what_received != CM_NO_DATA_RECEIVED &&
            (cm_rc == CM_OK || cm_rc == CM_DEALLOCATED_NORMAL)) {
                                            /* Write the received data       */
            convert_from_ascii(command_info.buffer, received_len);
            fwrite(command_info.buffer, 1, (unsigned int)received_len, stdout);
       }

    } while ( !cm_rc );

    if (cm_rc != CM_DEALLOCATED_NORMAL) {
        cpicerr_handle_rc(cpicerr, MSG_CMRCV, cm_rc);
    }

    /* destroy the object we created with cpicinit_new() */
    cpicinit_destroy(cpicinit);

    /* destroy the object we created with cpicerr_new() */
    cpicerr_destroy(cpicerr);

    exit(EXIT_SUCCESS);
}

void
process_arguments(int argc,
                  char *argv[],
                  CPICINIT * cpicinit,
                  COMMAND_INFO * command_info)
{
    int         set_destination = 0;
    int         c;                          /* flag specifed, used w/getopt  */

    command_info -> buffer_length = 0;
    command_info -> buffer[0] = '\0';

    while (optind != argc) {
        c = getopt(argc, argv, "?t:m:u:p:T:M:U:P:Nn");
        switch (c) {
        case EOF:
            if (set_destination == 0) {
                set_destination = 1;
                optarg = argv[optind];
                if (optarg[0] == '?') {
                   show_info(usage);
                   exit(EXIT_FAILURE);
                }
                optind++;
                cpicinit_set_destination(cpicinit, optarg);
            } else {
                command_info -> buffer[0] = '\0';
                for ( ; optind<argc ; optind++ ) {
                    command_info -> buffer_length += strlen(argv[optind])+1;
                    if (command_info->buffer_length < MAX_COMMAND_LENGTH) {
                        strcat(command_info->buffer, argv[optind]);
                        strcat(command_info->buffer, " ");
                    } else {
                        write_error(
                                "Command length exceeds max allowed (%d).\n",
                                MAX_COMMAND_LENGTH-1);
                        write_error(
                                "No command will be sent.\n");
                        exit(EXIT_FAILURE);
                    }
                }
            }
            break;
        case 'M':
        case 'm':
            cpicinit_set_mode_name(cpicinit, optarg);
            break;
        case 'T':
        case 't':
            cpicinit_set_tp_name(cpicinit,optarg);
            break;
#if defined(SUPPORTS_SETTING_SECURITY)
        case 'U':
        case 'u':
            cpicinit_set_userid(cpicinit, optarg);
            break;
        case 'P':
        case 'p':
            cpicinit_set_password(cpicinit, optarg);
            break;
        case 'N':
        case 'n':
            cpicinit_set_security_type(cpicinit, CM_SECURITY_NONE);
            break;
#endif
        case '?':
            show_info(usage);
            exit(EXIT_FAILURE);
            break;
        default:
            write_output("Invalid flag.  Use AREXEC -? for usage\n");
            exit(EXIT_FAILURE);
        }
    }
    if (!(set_destination && command_info->buffer_length)) {
        write_error(
                "You must specify at least a destination and a command.\n");
        show_info(usage);
        exit(EXIT_FAILURE);
    }
}


AREXECD.C

/*****************************************************************************
 *
 *  MODULE NAME : AREXECD.C
 *
 *  COPYRIGHTS:
 *             This module contains code made available by IBM
 *             Corporation on an AS IS basis.  Any one receiving the
 *             module is considered to be licensed under IBM copyrights
 *             to use the IBM-provided source code in any way he or she
 *             deems fit, including copying it, compiling it, modifying
 *             it, and redistributing it, with or without
 *             modifications.  No license under any IBM patents or
 *             patent applications is to be implied from this copyright
 *             license.
 *
 *             A user of the module should understand that IBM cannot
 *             provide technical support for the module and will not be
 *             responsible for any consequences of use of the program.
 *
 *             Any notices, including this one, are not to be removed
 *             from the module without the prior written consent of
 *             IBM.
 *
 *  AUTHOR:    Peter J. Schwaller
 *             VNET:     PJS at RALVM6           Tie Line: 444-4376
 *             Internet: pjs@ralvm6.vnet.ibm.com     (919) 254-4376
 *
 *  FUNCTION:  This module is the server side of AREXEC.  This module
 *             will execute a specified command and route the output
 *             back to the AREXEC transaction program.
 *
 *  AVAILABILITY:
 *             These sample programs and source are also available on
 *             CompuServe through the APPC Information Exchange.  To get
 *             to the APPC forum just type 'GO APPC' from any CompuServe
 *             prompt.  The samples are available in the Sample Programs
 *             library section.  Just search on the keyword CPICPGMS to
 *             find all the samples in this series.
 *
 *             Updates for the sample programs and support for many more
 *             CPI-C platforms will also be made available on CompuServe.
 *
 *  RELATED FILES:
 *             See AREXEC.DOC for usage instructions.
 *
 *  CHANGE HISTORY:
 *  Date       Description
 *  06/15/92   NS/DOS accepts version 2.02 into system test.
 *  08/05/92   Version 2.31 released to CompuServe
 *             This version was also distributed at the APPC/APPN Platform
 *             Developer's Conference held in Raleigh, NC.
 *  08/13/92   Changed all printf and fprintf calls to use a write_*() call.
 *  08/24/92   Version 2.32 released to CompuServe.
 *  09/22/92   Version 2.33 released to CompuServe.
 *  11/17/92   Supports sending operating system string - see CPICERR.C
 *             Version 2.34 released to CompuServe
 *  01/07/93   Version 2.35
 *             Fixed a number of problems when compiling with IBM C Set/2
 *                password input was displayed
 *                timer resolution was 1 second
 *                arexecd server did not function properly
 *
 *****************************************************************************/

#ifdef WIN32                                                           /*WIN32*/
#include <windows.h>                                                   /*WIN32*/
SERVICE_STATUS_HANDLE stat_hand;                                       /*WIN32*/
SERVICE_STATUS servstat;                                               /*WIN32*/
#endif                                                                 /*WIN32*/

#include "wincpic.h"

/* standard C include files */
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>

/* Set up constant declarations */
#include "cpicdefs.h"

/* Collection of routines with special ported version for each platform */
#include "cpicport.h"

#define  MAX_COMMAND_LENGTH   500

/*--------------------------------------------------------------------------*/
/*      CPI-C Error Handling Global Variables                               */
/*--------------------------------------------------------------------------*/
#include "cpicerrs.h"                        /* CPI-C error handling vars.   */
CPICERR * cpicerr;

/* Define these here so we can make changes throughout the code. */
/*
 * The PROGRAM_INFO string should be kept in sync with the
 * MAJOR_VERSION and MINOR_VERSION constants.  Although the
 * cpicerr_exchange_version() call will support values up to 255,
 * values for MINOR_VERSION should be from 00-99 to maintain the
 * two character format in the version string.
 */
#define  PROGRAM_NAME      "AREXECD"
#define  PROGRAM_INFO      "version 2.35"
#define  MAJOR_VERSION     (2)
#define  MINOR_VERSION     (35)
#define  LOG_FILE_NAME     "arexecd.err"
#define  LOG_FILE_PATH     "$LOGPATH"

char * intro[] = {
    PROGRAM_NAME " " PROGRAM_INFO " - Remote execution of a command. (Server)",
    NULL
    };

void
TPStart(void)
{
    /* Variables used for CPI-C calls */
    unsigned char cm_conv_id[8];            /* CPI-C conversation ID         */
    CM_INT32    temp;                                                 /*WIN32*/
    CM_INT32    cm_rc;                      /* CPI-C return code             */
    CM_INT32    length;                     /* generic length variable       */
    CM_INT32    rts_received;               /* request to send received      */
    CM_INT32    max_receive_len;            /* Max receive length on CMRCV   */
    CM_INT32    what_received;              /* What received parm from CMRCV */
    CM_INT32    received_len;               /* Amount of data rcvd on CMRCV  */
    CM_INT32    status_received;            /* Status from CMRCV             */

    /* Data buffer for send and receive */
    char        buffer[MAX_COMMAND_LENGTH]; /* CPIC data buffer              */

    char partner_major_version;
    char partner_minor_version;

    unsigned char destination[MAX_FQPLU_NAME];

    show_info(intro);

#ifdef WIN32
    {
       /**********************************************************************/
       /* Initialisation for WinCPIC                                         */
       /**********************************************************************/
       unsigned short WinCPICVERSION = 0x0001;
       WCPICDATA CPICData;
       if (WinCPICStartup(WinCPICVERSION,&CPICData))
       {
         return;
       }
       /**********************************************************************/
       /* Set our local TP Name                                              */
       /**********************************************************************/
       temp=7;
       cmsltp("AREXECD",&temp,&cm_rc);
    }
#endif

    /*
     * Initialize the CPICERR structure.  This is done before the CMINIT
     * call so that we can use CPICERR for help with errors on CMINIT.
     * The procedure is in CPICERR.C
     */
    cpicerr = cpicerr_new();
    cpicerr_set_program_name(cpicerr, PROGRAM_NAME);
    cpicerr_set_program_info(cpicerr, PROGRAM_INFO);
    cpicerr_set_major_version(cpicerr, MAJOR_VERSION);
    cpicerr_set_minor_version(cpicerr, MINOR_VERSION);
    cpicerr_set_log_file_name(cpicerr, LOG_FILE_NAME);
    cpicerr_set_log_file_path(cpicerr, LOG_FILE_PATH);

    cmaccp(cm_conv_id,
           &cm_rc);
    /*
     * Note that as we have used cmsltp to specify our local TP name,
     * cmaccp may return asynchronously, so we must do a cmwait
     */
    if (cm_rc == CM_OPERATION_INCOMPLETE)                             /*WIN32*/
    {                                                                 /*WIN32*/
      cmwait(cm_conv_id, &cm_rc, &temp);                              /*WIN32*/
    }                                                                 /*WIN32*/

   /*------------------------------------------------------------------------*
    * Fill in conversation information for CPI-C error reporting.
    *------------------------------------------------------------------------*/
    cpicerr_set_conv_id(cpicerr, cm_conv_id);

    if (cm_rc != CM_OK) {
        cpicerr_handle_rc(cpicerr, MSG_CMACCP, cm_rc);
    } else {
        length = 17;
        cmepln(cm_conv_id,
               destination,
               &length,
               &cm_rc );
        destination[(unsigned int)length] = '\0';
        write_output("\nContacted by partner: ");
        write_output("%s", destination);
        write_output("\n");
    }

    cpicerr_exchange_version(cpicerr,
                             cm_conv_id,
                             CM_RECEIVE_STATE,
                             &partner_major_version,
                             &partner_minor_version);

    {
    CM_SEND_TYPE send_type = CM_SEND_AND_FLUSH;
    cmsst(cm_conv_id,                       /* Set send type                 */
          &send_type,
          &cm_rc);
    }

    max_receive_len = MAX_COMMAND_LENGTH-1;

    cmrcv(cm_conv_id,                       /* Receive Data                  */
          (unsigned char *) buffer,         /* Data Pointer                  */
          &max_receive_len,                 /* Size of Data Buffer           */
          &what_received,                   /* returned - what received      */
          &received_len,                    /* returned - length of data     */
          &status_received,                 /* returned - status received    */
          &rts_received,                    /* returned - request to send    */
          &cm_rc);

    if (cm_rc != CM_OK) cpicerr_handle_rc(cpicerr, MSG_CMRCV, cm_rc);

    buffer[(unsigned int)received_len] = '\0';

    convert_from_ascii(buffer, strlen(buffer));

    write_output("The command is:\n%s\n\n", buffer);

    execute_and_send_output(buffer,
                            cm_conv_id,
                            cpicerr);

    {
    CM_DEALLOCATE_TYPE deallocate_type = CM_DEALLOCATE_FLUSH;

    cmsdt(cm_conv_id,
          &deallocate_type,
          &cm_rc);
    }

    cmdeal(cm_conv_id,
           &cm_rc);
    if (cm_rc) cpicerr_handle_rc(cpicerr, MSG_CMDEAL, cm_rc);

    /* destroy the object we created with cpicerr_new() */
    cpicerr_destroy(cpicerr);

    exit(EXIT_SUCCESS);

}

#ifdef WIN32
/*****************************************************************************/
/* The following code makes this TP invokable as an NT service. There are 3  */
/* routines.                                                                 */
/*                                                                           */
/* 1. main. This is the entry point for the process, it sets up a service    */
/*          table entry and then calls StartServiceCtrlDispatcher. This call */
/*          doesn't return, but uses the thread which called it as a         */
/*          control dispatcher for all the services implemented by this      */
/*          process (in this case, just the TP itself).                      */
/*                                                                           */
/* 2. ServiceMain. This is the main entry point for the service itself, the  */
/*          service control dispatcher creates a thread to start at this     */
/*          routine. It must register a service control handler for the      */
/*          service which will be called by the control dispatcher when it   */
/*          has control instructions for the service. It then informs the    */
/*          service control manager that the service is running and finally  */
/*          calls the start of the TP itself. This routine should not return */
/*          until the service is ready to die.                               */
/*                                                                           */
/* 3. ControlHandler. This routine is called by the control dispatcher when  */
/*          it has instructions for the service. We do not respond to any    */
/*          of the instructions as this service should be transitory and not */
/*          actually run for more than a few seconds so we don't need to do  */
/*          anything with the STOP or SHUTDOWN requests.                     */
/*          Note that we MUST call SetServiceStatus, even if the status      */
/*          hasn't changed.                                                  */
/*****************************************************************************/

void __cdecl main( DWORD argc, LPSTR * argv);
void WINAPI ServiceMain(DWORD dwNumServiceArgs, LPTSTR * lpServiceArgs);
VOID WINAPI ControlHandler(DWORD dwControl);
SERVICE_STATUS_HANDLE stat_hand;
SERVICE_STATUS servstat;

void __cdecl main( DWORD argc, LPSTR * argv)
{
  SERVICE_TABLE_ENTRY  stab[2];

  /***************************************************************************/
  /* Start the control dispatcher. This call gives the SCManager this        */
  /* thread for the entire period that this service is running, so that it   */
  /* can call us back with service controls. It will spawn a new thread to   */
  /* run the service itself, starting at entrypoint ServiceMain.             */
  /***************************************************************************/
  stab[0].lpServiceName = "AREXECD\0";
  stab[0].lpServiceProc = ServiceMain;

  stab[1].lpServiceName = NULL;
  stab[1].lpServiceProc = NULL;

  StartServiceCtrlDispatcher(stab);

}

/*****************************************************************************/
/* This routine is the entry-point for the service itself the service        */
/* control dispatcher creates a thread to start here when we issue           */
/* StartServiceControlDispatcher.                                            */
/*                                                                           */
/* Inputs:  number of arguments to services, array of strings.               */
/*                                                                           */
/* Outputs: none                                                             */
/*                                                                           */
/*****************************************************************************/
void WINAPI ServiceMain(DWORD dwNumServiceArgs, LPTSTR * lpServiceArgs)
{

  DWORD rc;

  stat_hand = RegisterServiceCtrlHandler("AREXECD\0", ControlHandler);
  if (stat_hand == (SERVICE_STATUS_HANDLE)NULL)
  {
    rc = GetLastError();
    DebugBreak();
  }

  /***************************************************************************/
  /* Let the SCManager know that we are running.                             */
  /***************************************************************************/
  servstat.dwServiceType              = SERVICE_WIN32;
  servstat.dwCurrentState             = SERVICE_RUNNING;
  servstat.dwControlsAccepted         = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
  servstat.dwWin32ExitCode            = NO_ERROR;
  servstat.dwServiceSpecificExitCode  = NO_ERROR;
  servstat.dwCheckPoint               = 0;
  servstat.dwWaitHint                 = 0;

  rc = SetServiceStatus(stat_hand, &servstat);
  if (!rc)
  {
     rc = GetLastError();
     DebugBreak();
  }

  TPStart();

}

/*****************************************************************************/
/* This routine is the callback from the SCManager to handle specific        */
/* service control requests. It MUST call SetServiceStatus before it         */
/* returns, regardless of whether the status has changed.                    */
/*                                                                           */
/* Inputs: service control requested                                         */
/*                                                                           */
/* Outputs: none                                                             */
/*                                                                           */
/*****************************************************************************/
VOID WINAPI ControlHandler(DWORD dwControl)
{
  DWORD rc;

  switch (dwControl)
  {
    case SERVICE_CONTROL_STOP :
      servstat.dwCurrentState = SERVICE_STOP_PENDING;
      servstat.dwWaitHint     = 24000;
      break;

    case SERVICE_CONTROL_PAUSE :
    case SERVICE_CONTROL_CONTINUE :
    case SERVICE_CONTROL_INTERROGATE :
      servstat.dwWaitHint     = 0;
      break;

 case SERVICE_CONTROL_SHUTDOWN:
      servstat.dwCurrentState = SERVICE_STOP_PENDING;
      servstat.dwWaitHint     = 10000;
break;
  }

  rc=SetServiceStatus(stat_hand, &servstat);
  if (!rc)
  {
     rc=GetLastError();
  }

}

#endif


CPICSR.C

/* CPICSR.C */
/* (C) COPYRIGHT DATA CONNECTION LIMITED 1993 */

/*****************************************************************************/
/* Change History                                                            */
/*                                                                           */
/*       30/06/93 NGR Created.                                               */
/*****************************************************************************/

/*****************************************************************************/
/*                                                                           */
/* ROUTINE : CPICSR                                                          */
/*                                                                           */
/* FUNCTION: This file contains the main routines for CPI-C versions of the  */
/*           send and receive TPs.                                           */
/*           sending and receiving TPs SENDTP and RECVTP                     */
/*                                                                           */
/* INPUTS  : C:\CPICSEND.CFG (file) (documented below)                       */
/*           C:\CPICRECV.CFG (file) (documented below)                       */
/*           Note files are in executable's directory in WIN32               */
/*                                                                           */
/* OUTPUTS : CPICSEND.OUT                                                    */
/*           CPICRECV.OUT                                                    */
/*                                                                           */
/*****************************************************************************/

/*****************************************************************************/
/* Configuration files:                                                      */
/*                                                                           */
/* Configuration file is C:\CPICSEND.CFG or CPICRECV.CFG which contains      */
/* the following, one per line in any order. If not present then the given   */
/* default is assumed.                                                       */
/*                                                                           */
/* ResultFile = <Name of file for results, default C:\CPIC(SEND/RECV).OUT>   */
/* NumConversations = <Number of conversations to be done, default = 1>      */
/* LocalTPName = <Name to be used for TP started, default SENDTP/RECVTP>     */
/* WaitMode = Yes/No/Block (default = No)                                    */
/*            Yes      - Verbs are non-blocking but completed via CMWAIT     */
/*            No       - Verbs are completed via posted Windows messages     */
/*            Block    - All verbs are blocking                              */
/*                                                                           */
/* The following only apply to CPICSEND:                                     */
/* SymDestName = <symbolic destination name, default = CPICRECV>             */
/* NumSends = <number of CMSEND verbs per conversation, default = 2>         */
/* ConfirmEvery = <number of CMSEND verbs between CMCFMs, default = 1>       */
/* SendSize = <number of bytes per CMSEND, default = 1024>                   */
/*                                                                           */
/* The following only applies to CPICRECV:                                   */
/* LocalTPName = <Name to be used for CMSLTP verb, default CPICRECV          */
/*                                                                           */
/* If NumConversations is zero, then the TPs will do an infinite number of   */
/* conversations.                                                            */
/* If NumSends is zero, then CPICSEND will never CMDEAL the first            */
/* conversation.                                                             */
/* If ConfirmEvery is zero, then SENDTP will not issue CONFIRM verbs.        */
/*****************************************************************************/

#if (defined(WINDOWS)||defined(WIN32))
#include <windows.h>
HINSTANCE hInst;
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wincpic.h>
#include "cpicsr.h"

/*****************************************************************************/
/* WinMain - reads initialisation info and controls message loop             */
/*           NT and Win16 version                                            */
/*****************************************************************************/
int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine, int nCmdShow)
{

  MSG msg;

  hInst = hInstance;

  ReadConfig();

  if (!InitialiseWinMain(hInstance))
  {
     return (FALSE);
  }

  InitialiseMain();

  while(GetMessage(&msg,NULL,0,0))
  {
     TranslateMessage(&msg);
     DispatchMessage(&msg);
  }

  return msg.wParam;         /* save exit parameter for return               */

}

/*****************************************************************************/
/* InitialiseWinMain - does the windows bits of initialisation               */
/*****************************************************************************/
BOOL InitialiseWinMain(HINSTANCE hInstance)
{
   WCPICDATA CPICData;
   WNDCLASS class;
   #define WinCPICVERSION  0x0001

   /**************************************************************************/
   /* Startup WinAPPC                                                        */
   /**************************************************************************/
   if (WinCPICStartup(WinCPICVERSION,&CPICData))
   {
      return (FALSE);
   }

   if ( (ASYNC_COMPLETE =
         RegisterWindowMessage(WIN_CPIC_ASYNC_COMPLETE_MESSAGE)) == 0 )
   {
      return (0);
   }

   /**************************************************************************/
   /* Register Window Class for our icon                                     */
   /**************************************************************************/

   class.style = 0;
   class.lpfnWndProc   = (WNDPROC)TPWndProc;
   class.cbClsExtra    = (DWORD)0;
   class.cbWndExtra    = (DWORD)0;
   class.hInstance     = hInstance;
   class.hIcon         = LoadIcon(hInstance,"MainIcon");
   class.hCursor       = LoadCursor(NULL, IDC_ARROW);
   class.hbrBackground = GetStockObject(WHITE_BRUSH);
   class.lpszMenuName  = (LPSTR) NULL;
   class.lpszClassName = (LPSTR) "CPICSR\0";

   if (!RegisterClass(&class))
   {
     return (FALSE);
   }

   /**************************************************************************/
   /* Create the window                                                      */
   /**************************************************************************/
#ifdef CPICSEND
   sprintf(title,"CPI-C Send TP\0");
#else
   sprintf(title,"CPI-C Receive TP\0");
#endif

   if ((hWndMain = CreateWindow("CPICSR\0",        /* window class           */
       title,                                      /* window name            */
       WS_MINIMIZE|WS_OVERLAPPEDWINDOW,            /* window style           */
       0,                                          /* x position             */
       0,                                          /* y position             */
       10,                                         /* width                  */
       10,                                         /* height                 */
       NULL,                                       /* parent handle          */
       NULL,                                       /* menu or child ID       */
       hInstance,                                  /* instance               */
       NULL))                                      /* additional info        */
       == NULL)
   {
      return (FALSE);
   }

   ShowWindow(hWndMain, SW_MINIMIZE);

   /**************************************************************************/
   /* Specify the window handle to be used, if this is NULL then we must use */
   /* Wait_For_Conversation to wait for the verb to complete.                */
   /**************************************************************************/
   xchwnd((wait_mode || blocking) ? NULL : hWndMain, &return_code);
#ifndef CPICSEND
   /**************************************************************************/
   /* Specify our local TP name. Note that because we have done this, the    */
   /* cmaccp will return asynchronously using the current notification type  */
   /**************************************************************************/
   {
      CM_INT32 temp_length;
      CM_INT32 temp_return_code;
      temp_length = strlen(LocalTPName);
      cmsltp(LocalTPName,&temp_length,&temp_return_code);
   }
#endif

   return(TRUE);

}

/*****************************************************************************/
/* Window proc for the iconised window                                       */
/*****************************************************************************/
LONG FAR PASCAL TPWndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
{
   if (message == ASYNC_COMPLETE)
   {
      return_code = (CM_RETURN_CODE) wParam;
      IssueNextVerb();
   }
   else
   {
      switch (message)
      {
         case WM_CREATE:
            /*****************************************************************/
            /* Post a message to ourselves to kick off the first verb        */
            /*****************************************************************/
            PostMessage(hWnd, ASYNC_COMPLETE, 0, 0);
            break;

         case WM_QUERYOPEN:
            /*****************************************************************/
            /* Prevent the window being opened                               */
            /*****************************************************************/
            break;

         case WM_CLOSE:
            TPDead = TRUE;
            WinCPICCleanup();
            OutputResults();
            return DefWindowProc(hWnd, message, wParam, lParam);
            break;

         case WM_DESTROY:
            PostQuitMessage(0);
            break;

         default:
            return DefWindowProc(hWnd, message, wParam, lParam);
            break;
      }
   }
   return 0l;
}

/*****************************************************************************/
/* InitialiseMain - blanks out variables and allocates buffers.              */
/*****************************************************************************/
void InitialiseMain()
{
   TPDead = FALSE;
   FirstConv = TRUE;
   last_verb = 0;
   ConvCount = 0;

   DataPtr = malloc((size_t) SendSize);
#ifdef CPICSEND
   GenerateData();
   SendCount = 0;
   ConfirmCount = 0;
#else
   Deallocated = FALSE;
#endif

   ResultBuf = (NumConversations == 0) ?
               NULL : malloc(NumConversations * sizeof(RESULT));
   ResultPtr = ResultBuf;

}

/*****************************************************************************/
/* IssueNextVerb - looks at the verb which has just completed and does the   */
/*                 next one                                                  */
/*****************************************************************************/
void IssueNextVerb()
{
   unsigned char wait_conv_ID[8];
   CM_INT32 wait_return_code;
   /**************************************************************************/
   /* last_verb = 0 is special case, before any verb has been issued.        */
   /**************************************************************************/
   if (last_verb != 0)
   {
      ProcessReturns();
   }
   if (!TPDead)
   {
      switch (last_verb)
      {
         case 0x0000:
#ifdef CPICSEND
            NewConversation();
            Do_cminit();
#else
            Do_cmaccp();
#endif
            break;

#ifdef CPICSEND
         case C_INIT:
            /*****************************************************************/
            /* Set the processing mode for this conversation to non-blocking */
            /* we have already used xchwnd to set the window handle (or to   */
            /* NULL it for WAIT mode)                                        */
            /*****************************************************************/
            {
               CM_INT32 receive_type = (blocking) ? CM_BLOCKING :
                                                    CM_NON_BLOCKING ;
               CM_INT32 temp_return_code;
               cmspm(conversation_ID, &receive_type, &temp_return_code);
            }
            /*****************************************************************/
            /* Set the sync level for this conversation                      */
            /*****************************************************************/
            {
               CM_INT32 sync_level;
               CM_INT32 temp_return_code;
               sync_level = (ConfirmEvery == 0) ? CM_NONE : CM_CONFIRM;
               cmssl(conversation_ID, &sync_level, &temp_return_code);
            }
            Do_cmallc();
            break;

         case C_ALLC:
            Do_cmsend();
            break;

         case C_SEND:
            SendCount++;
            ConfirmCount++;
            if ((NumSends != 0) && (SendCount == NumSends))
            {
               Do_cmdeal();
            }
            else if ((ConfirmEvery != 0) && (ConfirmCount == ConfirmEvery))
            {
               Do_cmcfm();
            }
            else
            {
               Do_cmsend();
            }
            break;

         case C_CFM:
            ConfirmCount=0;
            Do_cmsend();
            break;

         case C_DEAL:
            if ((NumConversations !=0) && (ConvCount == NumConversations))
            {
               NewConversation();
               TPDead = TRUE;
            }
            else
            {
#if 0
            /*****************************************************************/
            /* Enable this to check Startup/Cleanup in loop                  */
            /*****************************************************************/
     WCPICDATA CPICData;
  WinCPICCleanup();
  WinCPICStartup(WinCPICVERSION,&CPICData);
#endif
               NewConversation();
               Do_cminit();
            }
            break;

#else
         case C_ACCP:
            NewConversation();
            Do_cmrcv();
            break;

         case C_RCV:
            if (return_code == CM_DEALLOCATED_NORMAL)
            {
               if ((NumConversations != 0) && (ConvCount < NumConversations))
               {
                  Do_cmaccp();
               }
               else
               {
                  NewConversation();
                  TPDead = TRUE;
               }
            }
            else if (status_received == CM_CONFIRM_RECEIVED)
            {
               Do_cmcfmd();
               Deallocated = FALSE;
            }
            else if (status_received == CM_CONFIRM_DEALLOC_RECEIVED)
            {
               Do_cmcfmd();
               Deallocated = TRUE;
            }
            else
            {
               Do_cmrcv();
            }
            break;

         case C_CFMD:
            if (Deallocated)
            {
               if ((NumConversations != 0) && (ConvCount < NumConversations))
               {
                  Do_cmaccp();
               }
               else
               {
                  NewConversation();
                  TPDead = TRUE;
               }
            }
            else
            {
               Do_cmrcv();
            }
            break;

#endif

         default:
            /*****************************************************************/
            /* What is this verb then ??                                     */
            /*****************************************************************/
            TPDead = TRUE;
#ifdef WIN32
            DebugBreak();
#endif
            break;

      } /* last_verb switch */

   } /* TPDead after previous verb */
   if (!TPDead)
   {
      /***********************************************************************/
      /* If the verb just issued has already completed, then post ourselves  */
      /* a message to kick off the next verb.                                */
      /* Otherwise, if the async mode being used is WAIT, then issue a       */
      /* Wait_For_Conversation verb and then post ourselves a message        */
      /* If the async mode is POST then WinCPIC will post a message for us   */
      /***********************************************************************/
      if (return_code != CM_OPERATION_INCOMPLETE)
      {
         PostMessage(hWndMain, ASYNC_COMPLETE, (WPARAM)return_code,
                                      (LPARAM)((void FAR *)&conversation_ID));
      }
      else if (wait_mode)
      {
         cmwait(wait_conv_ID, &return_code, &wait_return_code);
         /********************************************************************/
         /* We don't bother checking the wait_conv_ID since we only have one */
         /* conversation in progress.                                        */
         /********************************************************************/
         PostMessage(hWndMain, ASYNC_COMPLETE, (WPARAM)return_code,
                                      (LPARAM)((void FAR *)&conversation_ID));
      }
   }
   else
   {
      /***********************************************************************/
      /* If we have finished then post ourselves a close message. Note that  */
      /* WinCPICCleanup will be done in the WM_CLOSE processing, so that the */
      /* case where the user closes the application prematurely is caught    */
      /***********************************************************************/
      PostMessage(hWndMain, WM_CLOSE, 0, 0);
   }
} /* Issue next verb */

#ifdef CPICSEND
void Do_cminit()
{
   cminit(conversation_ID,SymDestName,&return_code);
   last_verb = C_INIT;
}

void Do_cmallc()
{
   cmallc(conversation_ID,&return_code);
   last_verb = C_ALLC;
}

void Do_cmdeal()
{
   cmdeal(conversation_ID,&return_code);
   last_verb = C_DEAL;
}

void Do_cmsend()
{
   cmsend(conversation_ID,DataPtr,&SendSize,&request_to_send_received,
                                                                 &return_code);
   last_verb = C_SEND;
}

void Do_cmcfm()
{
   cmcfm(conversation_ID,&request_to_send_received,&return_code);
   last_verb = C_CFM;
}

#else

void Do_cmaccp()
{
   cmaccp(conversation_ID,&return_code);
   last_verb = C_ACCP;
}

void Do_cmrcv()
{
   cmrcv(conversation_ID,DataPtr,&SendSize,&data_received,&received_length,
                      &status_received,&request_to_send_received,&return_code);
   last_verb = C_RCV;
}

void Do_cmcfmd()
{
   cmcfmd(conversation_ID,&return_code);
   last_verb = C_CFMD;
}
#endif

/*****************************************************************************/
/* ProcessReturns - Checks return codes from the last verb to complete and   */
/*                  saves off any useful information. If the return code is  */
/*                  bad then we just die.                                    */
/*****************************************************************************/
void ProcessReturns()
{
   if ( (return_code != CM_OK) &&
       !( (last_verb == C_RCV) &&
          (return_code == CM_DEALLOCATED_NORMAL) ) )
   {
      TPDead = TRUE;
   }
}

/*****************************************************************************/
/* ReadConfig - Reads config info from SENDTP.CFG also allocates buffer for  */
/*              sending                                                      */
/*****************************************************************************/
void ReadConfig()
{
   char buffer[200];

#ifdef CPICSEND

   if (!ReadString("ResultFile",FileName,20))
   {
      strcpy(FileName,"C:\\CPICSEND.OUT");
   }
   if (!ReadString("SymDestName",SymDestName,8))
   {
      strcpy(SymDestName,"CPICRECV");
   }
   NumSends=2;
   if (ReadString("NumSends",buffer,200))
   {
      NumSends=atoi(buffer);
   }
   ConfirmEvery=1;
   if (ReadString("ConfirmEvery",buffer,200))
   {
      ConfirmEvery=atoi(buffer);
   }
   SendSize=1024;
   if (ReadString("SendSize",buffer,200))
   {
      SendSize=atoi(buffer);
   }

#else

   SendSize = 0x7FFF;
   if (!ReadString("ResultFile",FileName,20))
   {
      strcpy(FileName,"C:\\CPICRECV.OUT");
   }
   if (!ReadString("LocalTPName",LocalTPName,64))
   {
      strcpy(LocalTPName,"CPICRECV");
   }

#endif

   wait_mode = FALSE;                                                  /*BLOK*/
   blocking  = FALSE;                                                  /*BLOK*/
   if (ReadString("WaitMode",buffer,200))                              /*BLOK*/
   {                                                                   /*BLOK*/
      wait_mode = (*(strupr(buffer)) != 'N');  /* Yes or Blocking */   /*BLOK*/
#ifdef CPICSEND                                                        /*BLOK*/
      blocking  = (*(strupr(buffer)) == 'B');  /* Blocking  */         /*BLOK*/
#endif                                                                 /*BLOK*/
   }                                                                   /*BLOK*/

   NumConversations=1;
   if (ReadString("NumConversations",buffer,200))
   {
      NumConversations=atoi(buffer);
   }

}

/*****************************************************************************/
/* NewConversation - Reset and record timers for this conversation.          */
/*****************************************************************************/
void NewConversation()
{
   RESULT NewTime;

#ifdef CPICSEND
   SendCount = 0;
   ConfirmCount =0;
#endif

   if (FirstConv)
   {
      FirstConv = FALSE;
      ConvStarted = GetTickCount();
   }
   else if (ResultPtr != NULL)
   {
      *ResultPtr++ = ((NewTime = GetTickCount()) - ConvStarted);
      ConvStarted = NewTime;
   }
   ConvCount++;
   OUTPUTNUMBER
}

#ifdef CPICSEND
/*****************************************************************************/
/* GenerateData    - Fill in data buffer                                     */
/*****************************************************************************/
void GenerateData()
{
   int i;
   int div;
   int rem;
   char * dptr;

   dptr = DataPtr;
   div = SendSize / 5;
   rem = SendSize % 5;

   for (; div--;)
   {
      for (i=4; i--; *dptr++ = datach);
      *dptr++ = '.';
   }
   for (; rem--; *dptr++ = datach);

   datach = (datach=='Z' ? 'A' : datach + 1);
}
#endif

/*****************************************************************************/
/* ReadString - Get a line of text from the config file.                     */
/*****************************************************************************/
int ReadString(char * lpValueName,char * lpData, int maxlen)
{
   char       buffer[200];
   char       value[200];
   char      *p = NULL;
   FILE      *h = NULL;
   BOOL       match = FALSE;
   BOOL       eof   = FALSE;
   int        rc = 0;
   int        ch = 0;
   int        i = 0;
   BOOL       gotdata = FALSE;
   char       separators[] = " =\t\n";

#if (defined(WINDOWS)||defined(WIN32))                                 /*PATH*/
                                                                       /*PATH*/
GetModuleFileName( hInst, buffer, sizeof(buffer) );                /*PATH*/
lstrcpy( buffer+lstrlen(buffer) - 4, ".CFG" );                     /*PATH*/
h = fopen( buffer, "r" );                                          /*PATH*/
buffer[0] = '\0';                                                  /*PATH*/
                                                                       /*PATH*/
#else                                                                  /*PATH*/
#ifdef CPICSEND
   h = fopen("C:\\cpicsend.cfg", "r");
#else
   h = fopen("C:\\cpicrecv.cfg", "r");
#endif
#endif                                                                 /*PATH*/

   strupr(strcpy(value,lpValueName));

   if (h != NULL)
   {
      while ((!match) && (!eof))
      {
         /********************************************************************/
         /* Use fgetc to read a line of text from the file.                  */
         /********************************************************************/
         for (i=0; (i<sizeof(buffer))     &&
                   ((ch=getc(h)) != EOF)  &&
                   ((char)ch != '\n');
                                      i++)
         {
            buffer[i] = (char)ch;
         }
         if ((char)ch == '\n')
         {
            buffer[i++] = (char)ch;
         }
         if (ch == EOF)
         {
            eof = TRUE;
         }
         else
         {
            /*****************************************************************/
            /* Compare the 1st token in the line read with the requested     */
            /* param.                                                        */
            /*****************************************************************/
            if (!strcmpi(strupr(strtok(buffer, separators)), lpValueName))
            {
               match = TRUE;
               /**************************************************************/
               /* Get a pointer to the 2nd token (the value we want)         */
               /**************************************************************/
               p = strtok(NULL, separators);

               /**************************************************************/
               /* Copy the data IF there is some.                            */
               /**************************************************************/
               if (p != NULL)
               {
                  /***********************************************************/
                  /* Force a NULL after the 2nn token                        */
                  /***********************************************************/
                  strtok(NULL, separators);

                  /***********************************************************/
                  /* Copy the data                                           */
                  /***********************************************************/
                  strncpy(lpData, p, maxlen);
                  gotdata = TRUE;
               }
               else
               {
                  gotdata = FALSE;
               }
            }
         }
      }

      if (gotdata)
      {
         rc = 1;
      }

      fclose(h);
   }

return(rc);
}

/*****************************************************************************/
/* OutputResults - dump the times of conversations to file                   */
/*****************************************************************************/
void OutputResults()
{
   FILE *h = NULL;
   RESULT * ptr = NULL;
   unsigned short i = 0;

   h = fopen(FileName,"w");
   if (h != NULL)
   {
#ifdef CPICSEND
     fprintf(h,"CPICSEND Results\n----------------\n\n");
     fprintf(h,"Symbolic destination name = %s\n",SymDestName);
     fprintf(h,"Number of conversations = %d\n",NumConversations);
     fprintf(h,"Sends per conversation  = %d\n",NumSends);
     fprintf(h,"Sends between confirms  = %d\n",ConfirmEvery);
     fprintf(h,"Bytes per send          = %d\n",SendSize);
     fprintf(h,"Wait mode               = %s\n",(wait_mode ? "Yes" : "No"));
     fprintf(h,"Blocking                = %s\n",(blocking  ? "Yes" : "No"));
     fprintf(h,"\n");
#else
     fprintf(h,"CPICRECV Results\n----------------\n\n");
     fprintf(h,"Local TP Name           = %s\n",LocalTPName);
     fprintf(h,"Number of conversations = %d\n",NumConversations);
     fprintf(h,"Wait mode               = %s\n",(wait_mode ? "Yes" : "No"));
     fprintf(h,"\n");
#endif

     ptr = ResultBuf;
     while (ptr < ResultPtr)
     {
       fprintf(h,"Conversation number %d, time = %.3f seconds\n",i++,
                                              (((float) *ptr++) / 1000.0 ));
     }
     fclose(h);
   }
}


CPICSR.H

/* cpicsr.h */
/* (C) COPYRIGHT DATA CONNECTION LIMITED 1993 */

/*****************************************************************************/
/* Change History                                                            */
/*                                                                           */
/*       30/06/93 NGR Created.                                               */
/*****************************************************************************/

/* Global variables */

BOOL TPDead;
BOOL FirstConv;
CM_INT32 return_code;
CM_INT32 request_to_send_received;
int last_verb;
char * DataPtr;
unsigned short ConvCount;
unsigned char conversation_ID[8];
HWND hWndMain;
unsigned short ASYNC_COMPLETE;
char title[30]={'\0'};
typedef DWORD RESULT;
RESULT ConvStarted;
RESULT * ResultPtr;
RESULT * ResultBuf;

#ifdef CPICSEND
char datach;
unsigned short SendCount;
unsigned short ConfirmCount;
#else
CM_INT32 status_received;
CM_INT32 data_received;
CM_INT32 received_length;
BOOL Deallocated;
#endif

/* Config parameters */

CM_INT32 SendSize;
unsigned short NumConversations;
BOOL wait_mode;
BOOL blocking;
char FileName[60];
#ifdef CPICSEND
char SymDestName[9];
unsigned short NumSends;
unsigned short ConfirmEvery;
#else
char LocalTPName[64];
#endif

/* Function prototypes */

int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine, int nCmdShow);
BOOL InitialiseWinMain(HINSTANCE hInstance);
LONG FAR PASCAL TPWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
void InitialiseMain(void);
void IssueNextVerb(void);
void ProcessReturns(void);
void ReadConfig(void);
void NewConversation(void);
int ReadString(char * lpValueName, char * lpData, int maxlen);
void PadString(char * string, int length);
void OutputResults(void);
void Build_TP_ENDED(void);
void GenerateData(void);
void Do_cminit(void);
void Do_cmallc(void);
void Do_cmsend(void);
void Do_cmrcv(void);
void Do_cmcfm(void);
void Do_cmcfmd(void);
void Do_cmdeal(void);
void Do_cmaccp(void);

/* Macros */
#define C_INIT 1
#define C_ALLC 2
#define C_SEND 3
#define C_CFM  4
#define C_RCV  5
#define C_ACCP 6
#define C_CFMD 7
#define C_DEAL 8

#ifdef CPICSEND
#define OUTPUTNUMBER sprintf(title,"CPI-C Send TP %d",ConvCount); \
                     SetWindowText(hWndMain,title);
#else
#define OUTPUTNUMBER sprintf(title,"CPI-C Receive TP %d",ConvCount); \
                     SetWindowText(hWndMain,title);
#endif


MPINGD.C

/*****************************************************************************
 *
 *  MODULE NAME: APINGD.C
 *
 *  COPYRIGHTS:
 *             This module contains code made available by IBM
 *             Corporation on an AS IS basis.  Any one receiving the
 *             module is considered to be licensed under IBM copyrights
 *             to use the IBM-provided source code in any way he or she
 *             deems fit, including copying it, compiling it, modifying
 *             it, and redistributing it, with or without
 *             modifications.  No license under any IBM patents or
 *             patent applications is to be implied from this copyright
 *             license.
 *
 *             A user of the module should understand that IBM cannot
 *             provide technical support for the module and will not be
 *             responsible for any consequences of use of the program.
 *
 *             Any notices, including this one, are not to be removed
 *             from the module without the prior written consent of
 *             IBM.
 *
 *  AUTHOR:    Peter J. Schwaller
 *             VNET:     PJS at RALVM6           Tie Line: 444-4376
 *             Internet: pjs@ralvm6.vnet.ibm.com     (919) 254-4376
 *
 *  FUNCTION:  Perform an echo test to a specified LU.
 *             APING can be used when you are first installing APPC on
 *             your computer to make sure you can connect to another
 *             computer in the network.  APING can also be used to
 *             get an estimate of the delay time or throughput to another
 *             computer in the network.
 *
 *             APINGD echos whatever is sent by APING.
 *                Keep receiving until you get permission to send
 *                Send the same number of same size records
 *
 *  AVAILABILITY:
 *             These sample programs and source are also available on
 *             CompuServe through the APPC Information Exchange.  To get
 *             to the APPC forum just type 'GO APPC' from any CompuServe
 *             prompt.  The samples are available in the Sample Programs
 *             library section.  Just search on the keyword CPICPGMS to
 *             find all the samples in this series.
 *
 *             Updates for the sample programs and support for many more
 *             CPI-C platforms will also be made available on CompuServe.
 *
 *  RELATED FILES:
 *             See APING.DOC for usage instructions.
 *
 *  PORTABILIITY NOTES:
 *             The APINGD server program is completely portable.  In fact,
 *             all of the source modules can be compiled without #define-ing
 *             any platform constant value.
 *
 *             To take advantage of a performance optimization on the
 *             OS/2 platform, the alloc_cpic_buffer() is used.  If the
 *             OS/2 platform is specified (#define of OS2, FAPI, or OS2_20)
 *             alloc_cpic_buffer() will return a shared memory buffer.
 *             If not, a buffer allocated with malloc() will be returned.
 *
 *             If you are porting to a platform that can take advantage
 *             of a specially allocated memory buffer, you should
 *             add this support to the alloc_cpic_buffer() call in the
 *             CPICPORT.C file.
 *
 *  CHANGE HISTORY:
 *  Date       Description
 *  06/15/92   NS/DOS accepts version 2.02 into system test.
 *  08/05/92   Version 2.31 released to CompuServe
 *             This version was also distributed at the APPC/APPN Platform
 *             Developer's Conference held in Raleigh, NC.
 *  08/13/92   Changed all printf and fprintf calls to use a write_*() call.
 *  08/24/92   Version 2.32 released to CompuServe.
 *  09/22/92   Version 2.33 released to CompuServe.
 *  11/17/92   Supports sending operating system string - see CPICERR.C
 *             Version 2.34 released to CompuServe
 *  01/07/93   Version 2.35
 *             Fixed a number of problems when compiling with IBM C Set/2
 *                password input was displayed
 *                timer resolution was 1 second
 *  24/02/94   Close thread handles
 *
 *****************************************************************************/

#ifdef WIN32                                                           /*WIN32*/
#include <windows.h>                                                   /*WIN32*/
HANDLE hWaitEvent;                                                     /*WIN32*/
void __cdecl main( DWORD argc, LPSTR * argv);
void WINAPI ServiceMain(DWORD dwNumServiceArgs, LPTSTR * lpServiceArgs);
void TPStart( DWORD argc, LPSTR *argv);
DWORD WINAPI ThreadStart( int threadarg );
VOID WINAPI ControlHandler(DWORD dwControl);
SERVICE_STATUS_HANDLE stat_hand = {0};
SERVICE_STATUS servstat = {0};
#endif                                                                 /*WIN32*/

#include "wincpic.h"

/* standard C include files */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* Set up constant declarations */
#include "cpicdefs.h"

/* Collection of routines with special ported version for each platform */
#include "cpicport.h"

/* CPI-C error handling routines */
/* This file is supplied with APINGD */
#include "cpicerrs.h"

/* CPI-C initialization routines */
/* This file is supplied with APINGD */
#include "cpicinit.h"

/* CPI-C error handling info */
CPICERR * cpicerr = {0};

/*
 * Max size of a data buffer.  This is the largest size buffer that can
 * be specified on a call to CPI-C.
 */
#define  MAX_SIZE (0x7FFF)

/* Define these here so we can make changes throughout the code. */
/*
 * The PROGRAM_INFO string should be kept in sync with the
 * MAJOR_VERSION and MINOR_VERSION constants.  Although the
 * cpicerr_exchange_version() call will support values up to 255,
 * values for MINOR_VERSION should be from 00-99 to maintain the
 * two character format in the version string.
 */
#define  PROGRAM_NAME      "APINGD"
#define  PROGRAM_INFO      "version 2.35"
#define  MAJOR_VERSION     (2)
#define  MINOR_VERSION     (35)
#define  LOG_FILE_NAME     "apingd.err"
#define  LOG_FILE_PATH     "$LOGPATH"

void TPStart( DWORD argc, LPSTR *argv)
{
    DWORD Tid;
    HANDLE      ThreadHandle;               /* thread handle for closing     */
    CM_INT32    temp;                                                 /*WIN32*/
    CM_INT32    cm_rc;                      /* CPI-C return code             */

    {
       /**********************************************************************/
       /* Initialisation for WinCPIC                                         */
       /**********************************************************************/
       unsigned short WinCPICVERSION = 0x0001;
       WCPICDATA CPICData;
       if (WinCPICStartup(WinCPICVERSION,&CPICData))
       {
         return;
       }
       /**********************************************************************/
       /* Set our local TP Name                                              */
       /**********************************************************************/
       temp=6;
       cmsltp("APINGD",&temp,&cm_rc);
    }

    /*
     * Initialize the CPICERR structure.  This is done before the CMACCP
     * call so that we can use CPICERR for help with errors on CMACCP.
     * The procedure is in CPICERR.C
     */
    cpicerr = cpicerr_new();
    cpicerr_set_program_name(cpicerr, PROGRAM_NAME);
    cpicerr_set_program_info(cpicerr, PROGRAM_INFO);
    cpicerr_set_major_version(cpicerr, MAJOR_VERSION);
    cpicerr_set_minor_version(cpicerr, MINOR_VERSION);
    cpicerr_set_log_file_name(cpicerr, LOG_FILE_NAME);
    cpicerr_set_log_file_path(cpicerr, LOG_FILE_PATH);

    /*
     * Create the first receive thread.
     */
    ThreadHandle = CreateThread(NULL,
                                16000,
                                (LPTHREAD_START_ROUTINE)ThreadStart,
                                NULL,
                                0,
                                &Tid);
    if (ThreadHandle == NULL)
    {
       GetLastError();
       DebugBreak();
    }
    CloseHandle(ThreadHandle);

    /*
     * As this is effectively the ServiceMain routine, we should not return
     * here until we are ready to die. We wait for an event to signal.
     */

    WaitForSingleObject( hWaitEvent, INFINITE );

    /*
     * Cleanup WinCPIC now.
     */

    WinCPICCleanup();

}

DWORD WINAPI ThreadStart( int threadarg )                              /*WIN32*/
{
    /* Variables used for CPI-C calls */
    unsigned char cm_conv_id[8];            /* CPI-C conversation ID         */
    CM_INT32    temp;                                                 /*WIN32*/
    DWORD       Tid;                                                  /*WIN32*/
    HANDLE      ThreadHandle;               /* thread handle for closing     */
    CM_INT32    cm_rc;                      /* CPI-C return code             */
    CM_INT32    length;                     /* generic length variable       */
    CM_INT32    rts_received;               /* request to send received      */
    CM_INT32    max_receive_len;            /* Max receive length on CMRCV   */
    CM_INT32    data_received;              /* Data received parm from CMRCV */
    CM_INT32    received_len;               /* Amount of data rcvd on CMRCV  */
    CM_INT32    status_received;            /* Status from CMRCV             */

    /* Data buffer for send and receive */
    unsigned char CM_PTR buffer = NULL;    /* CPIC data buffer              */

    char        destination[MAX_DESTINATION];/* Partner destination          */
    unsigned int max_size;                  /* size to receive               */

    char partner_major_version;
    char partner_minor_version;

    cmaccp(cm_conv_id,                      /* Accept Conversation           */
           &cm_rc);
    /*
     * Note that as we have used cmsltp to specify our local TP name,
     * cmaccp may return asynchronously, so we must do a cmwait
     */
    if (cm_rc == CM_OPERATION_INCOMPLETE)                             /*WIN32*/
    {                                                                 /*WIN32*/
      cmwait(cm_conv_id, &cm_rc, &temp);                              /*WIN32*/
    }                                                                 /*WIN32*/

    /*
     * Create the next receive thread. Note if this fails, we will die
     * horribly by kicking the main thread's wait event.
     */
    ThreadHandle = CreateThread(NULL,
                                16000,
                                (LPTHREAD_START_ROUTINE)ThreadStart,
                                NULL,
                                0,
                                &Tid);
    if (ThreadHandle == NULL)
    {
      SetEvent(hWaitEvent);
    }
    else
    {
      CloseHandle(ThreadHandle);
    }

    /*
     * Fill in conversation information for CPI-C error reporting.
     */
    cpicerr_set_conv_id(cpicerr, cm_conv_id);

    if (cm_rc != CM_OK) {
        cpicerr_handle_rc(cpicerr, MSG_CMACCP, cm_rc);
    } else {
        CM_INT32 pln_length;
        /*
         * Extract the partner LU name and display it.
         */
        cmepln(cm_conv_id,
               (unsigned char *)destination,
               &pln_length,
               &cm_rc );

        destination[(int)pln_length] = '\0';
        write_output("\nContacted by partner: %s\n", destination);

    }

    {
    CM_PREPARE_TO_RECEIVE_TYPE prep_to_receive = CM_PREP_TO_RECEIVE_FLUSH;
    cmsptr(cm_conv_id,                       /* Set prepare to receive type  */
           &prep_to_receive,
           &cm_rc);
    if (cm_rc != CM_OK) cpicerr_handle_rc(cpicerr, MSG_CMSPTR, cm_rc);
    }

    max_receive_len = max_size = MAX_SIZE;

    buffer = (unsigned char CM_PTR)alloc_cpic_buffer(max_size);
                                            /* allocate a buffer             */

    cpicerr_exchange_version(cpicerr,
                             cm_conv_id,
                             CM_RECEIVE_STATE,
                             &partner_major_version,
                             &partner_minor_version);

    do {
        unsigned long count;                /* number of consecutive         */
                                            /* sends or receives             */
        count = 0;                          /* initialize count of recvs     */
        do {
           cmrcv (cm_conv_id,               /* Receive Data                  */
                  buffer,                   /* Data Pointer                  */
                  &max_receive_len,         /* Size of Data Buffer           */
                  &data_received,           /* returned - data received      */
                  &received_len,            /* returned - length of data     */
                  &status_received,         /* returned - status received    */
                  &rts_received,            /* returned - request to send    */
                  &cm_rc);

            if (data_received != CM_NO_DATA_RECEIVED) {
                count++;                    /* keep track of receives        */
            }
        } while ( (status_received != CM_SEND_RECEIVED) &&
                  (status_received != CM_CONFIRM_RECEIVED) &&
                   !cm_rc);
        /*
         * loop until we get permission to send data or until error
         */

        if (cm_rc != CM_OK) {
            if (cm_rc == CM_DEALLOCATED_NORMAL) {
free_cpic_buffer( buffer );
buffer = NULL;
                do_exit(EXIT_SUCCESS);
            } else {
                cpicerr_handle_rc(cpicerr, MSG_CMRCV, cm_rc);
            }
        }

        if (status_received != CM_CONFIRM_RECEIVED) {
            /*
             * count is now equal to the number of data blocks we received
             * now we will send back the same number of data blocks of equal
             * size
             */
            {
            CM_SEND_TYPE send_type = CM_BUFFER_DATA;
            cmsst(cm_conv_id,
                  &send_type,
                  &cm_rc);
            if (cm_rc != CM_OK) cpicerr_handle_rc(cpicerr, MSG_CMSST, cm_rc);
            }

                                 /* send back the same number except for one */
            for ( count--; count && !cm_rc; count-- ) {
                length = received_len;
                cmsend(cm_conv_id,
                       buffer,
                       &length,
                       &rts_received,
                       &cm_rc);
                if (cm_rc != CM_OK) {
                    cpicerr_handle_rc(cpicerr, MSG_CMSEND, cm_rc);
                }
            }

            /*
             * Set the send type to do a send and a prepare to receive.
             * This will send both the data and the send permission indicator
             * to our partner all at once.
             */
            {
            CM_SEND_TYPE send_type = CM_SEND_AND_PREP_TO_RECEIVE;
            cmsst(cm_conv_id,
                  &send_type,
                  &cm_rc);
            if (cm_rc != CM_OK) cpicerr_handle_rc(cpicerr, MSG_CMSST, cm_rc);
            }

            length = received_len;
            cmsend(cm_conv_id,
                   buffer,
                   &length,
                   &rts_received,
                   &cm_rc);
            if (cm_rc != CM_OK) cpicerr_handle_rc(cpicerr, MSG_CMSEND, cm_rc);
        } else {
            /*
             * The partner has requested one way data transfer only.
             * We'll just issue Confirmed, then go back up to receive
             * more data.
             */
            cmcfmd(cm_conv_id,
                   &cm_rc);
            if (cm_rc != CM_OK) cpicerr_handle_rc(cpicerr, MSG_CMCFMD, cm_rc);
        }
    } while (cm_rc == CM_OK);

if( buffer != NULL )
{
free_cpic_buffer( buffer );
}
    /* destroy the object we created with cpicerr_new() */
    cpicerr_destroy(cpicerr);

    do_exit(EXIT_SUCCESS);

/* never executed, just to keep compiler happy */
return(0);
}

/*****************************************************************************/
/* The following code makes this TP invokable as an NT service. There are 3  */
/* routines.                                                                 */
/*                                                                           */
/* 1. main. This is the entry point for the process, it sets up a service    */
/*          table entry and then calls StartServiceCtrlDispatcher. This call */
/*          doesn't return, but uses the thread which called it as a         */
/*          control dispatcher for all the services implemented by this      */
/*          process (in this case, just the TP itself).                      */
/*                                                                           */
/* 2. ServiceMain. This is the main entry point for the service itself, the  */
/*          service control dispatcher creates a thread to start at this     */
/*          routine. It must register a service control handler for the      */
/*          service which will be called by the control dispatcher when it   */
/*          has control instructions for the service. It then informs the    */
/*          service control manager that the service is running and finally  */
/*          calls the start of the TP itself. This routine should not return */
/*          until the service is ready to die.                               */
/*                                                                           */
/* 3. ControlHandler. This routine is called by the control dispatcher when  */
/*          it has instructions for the service. We do not respond to any    */
/*          of the instructions as this service should be transitory and not */
/*          actually run for more than a few seconds so we don't need to do  */
/*          anything with the STOP or SHUTDOWN requests.                     */
/*          Note that we MUST call SetServiceStatus, even if the status      */
/*          hasn't changed.                                                  */
/*****************************************************************************/

void __cdecl main( DWORD argc, LPSTR * argv)
{
  SERVICE_TABLE_ENTRY  stab[2];

  /***************************************************************************/
  /* Start the control dispatcher. This call gives the SCManager this        */
  /* thread for the entire period that this service is running, so that it   */
  /* can call us back with service controls. It will spawn a new thread to   */
  /* run the service itself, starting at entrypoint ServiceMain.             */
  /***************************************************************************/
  stab[0].lpServiceName = "APINGD\0";
  stab[0].lpServiceProc = ServiceMain;

  stab[1].lpServiceName = NULL;
  stab[1].lpServiceProc = NULL;

  StartServiceCtrlDispatcher(stab);

}

/*****************************************************************************/
/* This routine is the entry-point for the service itself the service        */
/* control dispatcher creates a thread to start here when we issue           */
/* StartServiceControlDispatcher.                                            */
/*                                                                           */
/* Inputs:  number of arguments to services, array of strings.               */
/*                                                                           */
/* Outputs: none                                                             */
/*                                                                           */
/*****************************************************************************/
void WINAPI ServiceMain(DWORD dwNumServiceArgs, LPTSTR * lpServiceArgs)
{

  DWORD rc;

  stat_hand = RegisterServiceCtrlHandler("APINGD\0", ControlHandler);
  if (stat_hand == (SERVICE_STATUS_HANDLE)NULL)
  {
    rc = GetLastError();
    DebugBreak();
  }

  if ((hWaitEvent = CreateEvent( NULL, FALSE, FALSE, NULL )) == NULL)
  {
    rc = GetLastError();
    DebugBreak();
  }

  /***************************************************************************/
  /* Let the SCManager know that we are running.                             */
  /***************************************************************************/
  servstat.dwServiceType              = SERVICE_WIN32;
  servstat.dwCurrentState             = SERVICE_RUNNING;
  servstat.dwControlsAccepted         = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
  servstat.dwWin32ExitCode            = NO_ERROR;
  servstat.dwServiceSpecificExitCode  = NO_ERROR;
  servstat.dwCheckPoint               = 0;
  servstat.dwWaitHint                 = 0;

  rc = SetServiceStatus(stat_hand, &servstat);
  if (!rc)
  {
     rc = GetLastError();
     DebugBreak();
  }

  TPStart(dwNumServiceArgs, lpServiceArgs);

  servstat.dwCurrentState = SERVICE_STOPPED;
  rc = SetServiceStatus(stat_hand, &servstat);
  if (!rc)
  {
     rc = GetLastError();
     DebugBreak();
  }

}

/*****************************************************************************/
/* This routine is the callback from the SCManager to handle specific        */
/* service control requests. It MUST call SetServiceStatus before it         */
/* returns, regardless of whether the status has changed.                    */
/*                                                                           */
/* Inputs: service control requested                                         */
/*                                                                           */
/* Outputs: none                                                             */
/*                                                                           */
/*****************************************************************************/
VOID WINAPI ControlHandler(DWORD dwControl)
{
  DWORD rc;

  switch (dwControl)
  {
    case SERVICE_CONTROL_PAUSE :
    case SERVICE_CONTROL_CONTINUE :
    case SERVICE_CONTROL_INTERROGATE :
      servstat.dwWaitHint     = 0;
      break;

 case SERVICE_CONTROL_SHUTDOWN:
    case SERVICE_CONTROL_STOP :
      servstat.dwCurrentState = SERVICE_STOP_PENDING;
      servstat.dwWaitHint     = 10000;
break;
  }

  rc=SetServiceStatus(stat_hand, &servstat);
  if (!rc)
  {
     rc=GetLastError();
     DebugBreak();
  }

  if ((dwControl == SERVICE_CONTROL_STOP) ||
      (dwControl == SERVICE_CONTROL_SHUTDOWN))
  {
     if (!SetEvent(hWaitEvent))
     {
        rc=GetLastError();
        DebugBreak();
     }
  }
}


MRCV.C

/* mrcv.c */
/* (C) COPYRIGHT DATA CONNECTION LIMITED 1994 */

/*****************************************************************************/
/* Change History                                                            */
/*                                                                           */
/*       21/02/94 DK  Created                                                */
/*       15/03/94 DK  Thread priorities added and signalled event goes to    */
/*                    back of the list (for WaitForMultipleObjects)          */
/*       14/04/94 DK  OutputResults added                                    */
/*****************************************************************************/

/*****************************************************************************/
/* If you want internal tracing, #define SRTRC here                          */
/*****************************************************************************/
//#define SRTRC

/*****************************************************************************/
/*                                                                           */
/* ROUTINE : RECEIVE using event completion                                  */
/*                                                                           */
/* FUNCTION: This file contains the routines for a multi-threaded routine    */
/*           which uses asynchronous APPC calls with event completion        */
/*           to receive data.                                                */
/*                                                                           */
/*           It runs with either the single-threaded or the multi-threaded   */
/*           version of send (msend or sendtp).                              */
/*                                                                           */
/* INPUTS  : MRCV.CFG (file) (documented below)                              */
/*                                                                           */
/* OUTPUTS : MRCV.TRC                                                        */
/*                                                                           */
/*****************************************************************************/

/*****************************************************************************/
/* Operation:                                                                */
/*                                                                           */
/* This is a Windows NT application which runs in a minimized window.        */
/*                                                                           */
/* Thread structure:                                                         */
/*                                                                           */
/*   A receive_allocate thread                                               */
/*     This thread issues a receive_allocate and, when it completes, hands   */
/*     it to a receive thread.  It then waits for the receive thread to      */
/*     accept the conversation.  This thread uses event completion, but      */
/*     its operation is essentially synchronous.                             */
/*                                                                           */
/*   A variable number of receive threads                                    */
/*     Each receive thread processes a variable number of conversations and  */
/*     issues a WaitForMultipleObjects call to wait for completion of any    */
/*     one of its receive operations or for a request for a new conversation */
/*     from the receive_allocate thread.  It sets a second event to tell the */
/*     receive_allocate thread that it has accepted the conversation.        */
/*     Each conversation issues MC_RECEIVE_AND_WAIT verbs to receive data.   */
/*     If confirmation is requested, an MC_CONFIRM verb is issued.           */
/*                                                                           */
/*   Note:  this program is compatible with the single-threaded versions of  */
/*   send, which can be run for example on WIN16 clients.                    */
/*                                                                           */
/*****************************************************************************/

/*****************************************************************************/
/* Configuration file:                                                       */
/*                                                                           */
/* The configuration file is called MRCV.CFG and must reside in the          */
/* same directory as the program.  It contains the following, in any order.  */
/* If any parameter is omitted, the default is assumed.                      */
/*                                                                           */
/* ResultFile = <Name of file for results, default MRCV.OUT>                 */
/* TraceFile = <Name of file for tracing, default MRCV.TRC>                  */
/* LocalTPName = <Name used for receive_allocate, default MRCVTP>            */
/*                                                                           */
/* NumRcvConvs = <Number of conversations to be received, default = 4>       */
/* NumRcvThreads = <Number of receive threads, default = 2>                  */
/*                                                                           */
/* The name used for TP_STARTED is fixed at MRCV.                            */
/*                                                                           */
/* If NumRcvConvs is zero, the TP will keep issuing receive_allocates.       */
/* If NumRcvConvs is non-zero and NumRcvThreads is greater than NumRcvConvs, */
/*    NumRcvThreads is reduced to NumRcvConvs.                               */
/* If NumRcvConvs is non-zero and NumRcvThreads*63 < NumRcvConvs,            */
/*    NumRcvThreads is increased to NumRcvConvs / 63 + 1.                    */
/*                                                                           */
/* Configuration constants (in mrcv.h)                                       */
/*                                                                           */
/* #define MAX_RECEIVE_THREADS  Max number of receive threads          (64)  */
/* #define MAX_RCV_PER_THREAD   Max conversations per receive thread   (63)  */
/*                                                                           */
/* MAX_RCV_PER_THREAD is constrained by the limit of 64 objects that can be  */
/* waited for in WaitForMultipleObjects.                                     */
/*                                                                           */
/*****************************************************************************/

#include <windows.h>
HINSTANCE hInst;
BOOL verbs_started = FALSE;
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winappc.h>
#include <wincsv.h>
#include "mrcv.h"

/*****************************************************************************/
/* Trace macros                                                              */
/*****************************************************************************/
#ifdef SRTRC
#define SRTROPEN() t = fopen(TraceFile,"w");
#define SRTRFLUSH() fflush(t);
#define SRTRCLOSE() fclose(t);
#define SRTRACE fprintf
#else
#define SRTROPEN()
#define SRTRFLUSH()
#define SRTRCLOSE()
#define SRTRACE 1 ? (void) 0 : fprintf
#endif

/*****************************************************************************/
/* WinMain - reads initialization info and controls message loop             */
/*           NT version                                                      */
/*****************************************************************************/
int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine, int nCmdShow)
{

  MSG msg;
  DWORD Tid;
  int i;
  HANDLE ThreadHandle;

  hInst = hInstance;

  InitializeMain();

  if (!InitializeWinMain(hInstance))
  {
    return (FALSE);
  }

  ReadConfig();
  SRTROPEN()

  /***************************************************************************/
  /* Create two events for each receive thread                               */
  /***************************************************************************/
  for (i = 0; i < NumRcvThreads; i++)
  {
    /*************************************************************************/
    /* Create an event for kicking the receive thread                        */
    /*************************************************************************/
    RcvThreadArray[i].convptr = NULL;
    RcvThreadArray[i].event1 = CreateEvent(NULL,FALSE,FALSE,NULL);
    RcvThreadArray[i].event2 = CreateEvent(NULL,FALSE,FALSE,NULL);
  }

  /***************************************************************************/
  /* Create receive threads                                                  */
  /***************************************************************************/
  for (i = 0; i < NumRcvThreads; i++)
  {
    SRTRACE(t,"Created event handles %p %p for receive thread %d\n",
            RcvThreadArray[i].event1, RcvThreadArray[i].event2,i);

    /*************************************************************************/
    /* Create the thread                                                     */
    /*************************************************************************/
    ThreadHandle = CreateThread(NULL,
                                16000,
                                (LPTHREAD_START_ROUTINE)ReceiveThread,
                                (void *)i,
                                0,
                                &Tid);
    if (ThreadHandle == NULL)
    {
      GetLastError();
      DebugBreak();
    }
    SetThreadPriority(ThreadHandle,THREAD_PRIORITY_LOWEST);
    SRTRACE(t,"Created receive thread %d with priority %d\n",
            i,GetThreadPriority(ThreadHandle));
    CloseHandle(ThreadHandle);
  }

  /***************************************************************************/
  /* Create receive_allocate thread                                          */
  /***************************************************************************/
  if (NumRcvThreads > 0)
  {
    ThreadHandle = CreateThread(NULL,
                                16000,
                                (LPTHREAD_START_ROUTINE)RcvAllocThread,
                                NULL,
                                0,
                                &Tid);
    if (ThreadHandle == NULL)
    {
      GetLastError();
      DebugBreak();
    }
    SetThreadPriority(ThreadHandle,THREAD_PRIORITY_NORMAL);
    SRTRACE(t,"Created receive allocate thread with priority %d\n",
            GetThreadPriority(ThreadHandle));
    CloseHandle(ThreadHandle);
  }

  /***************************************************************************/
  /* Windows processing loop                                                 */
  /***************************************************************************/
  while(GetMessage(&msg,NULL,0,0))
  {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }

  WinAPPCCleanup();
  OutputResults();
  SRTRFLUSH()
  SRTRCLOSE()
  DeleteCriticalSection(&runsem);
  return msg.wParam;         /* save exit parameter for return               */

}

/*****************************************************************************/
/* RcvAllocThread - separate thread for receive_allocates                    */
/*****************************************************************************/
DWORD WINAPI RcvAllocThread()
{
  /***************************************************************************/
  /* Local variables                                                         */
  /***************************************************************************/
  CONVCB * convptr;
  struct appc_hdr * vcbptr;
  unsigned short ThreadNo;
  unsigned short NextReceive = 0;
  BOOL   RcvAllocEnded = FALSE;
  DWORD rc;
  SYSTEMTIME st;

  /***************************************************************************/
  /* Count threads within critical section                                   */
  /***************************************************************************/
  EnterCriticalSection(&runsem);
  ThreadCount++;
  ThreadNo = ThreadCount;
  LeaveCriticalSection(&runsem);

  SRTRACE(t,"Thread %d (ralc) Started\n",ThreadNo);

  /***************************************************************************/
  /* Loop round until the required number of conversations have been         */
  /* allocated                                                               */
  /***************************************************************************/
  while ((NumRcvConvs == 0) || (NumRalcs < NumRcvConvs))
  {
    /*************************************************************************/
    /* Make a conversation control block                                     */
    /*************************************************************************/
    convptr = malloc (sizeof(CONVCB));
    convptr->thread       = ThreadNo;
    convptr->conv         = 0;
    convptr->TPid[0]      = '\0';
    convptr->Convid       = 0;
    convptr->async_corr   = 0;
    convptr->RcvCount     = 0;
    convptr->TPEnded      = FALSE;
    convptr->Deallocated  = FALSE;
    convptr->DataPtr      = malloc(RcvSize);
    vcbptr = (struct appc_hdr *) &convptr->vcb;
    memset(vcbptr,0,sizeof(VCB));
    strcpy (convptr->type,"ralc");

    /*************************************************************************/
    /* Create an event                                                       */
    /*************************************************************************/
    convptr->hEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
    SRTRACE(t,"Thread %d (ralc) convptr %p\n",ThreadNo,convptr);
    SRTRACE(t,"Thread %d (ralc) event handle %p\n",ThreadNo,convptr->hEvent);

    /*************************************************************************/
    /* Build a receive_allocate                                              */
    /*************************************************************************/
    Build_RECEIVE_ALLOCATE (convptr);

    /*************************************************************************/
    /* Issue receive_allocate using event notification                       */
    /*************************************************************************/
    convptr->async_corr = WinAsyncAPPCEx(convptr->hEvent,
                                         (long)(char *)(vcbptr));
//  SRTRACE(t,"Thread %d (ralc) Receive_Allocate issued corr %p\n",
//          ThreadNo,convptr->async_corr);
    SRTRFLUSH()
    if (convptr->async_corr == 0)
    {
      SRTRACE(t,"Thread %d (ralc) WinAsync call %x failed zero corr %p\n",
              ThreadNo,vcbptr->opcode,convptr->async_corr);
      break;
    }
    else
    {
      /***********************************************************************/
      /* Wait for event completion                                           */
      /***********************************************************************/
      rc = WaitForSingleObject(convptr->hEvent,INFINITE);
      if (rc == WAIT_FAILED)
      {
        rc = GetLastError();
        SRTRACE(t,"Thread %d (ralc) wait for event_1 %p failed with rc %d\n",
                ThreadNo,convptr->hEvent,rc);
        break;
      }
      if (vcbptr->primary_rc != AP_OK)
      {
        SRTRACE(t,"Thread %d (ralc) WinAsync error: %s prc %4.4x src %8.8x\n",
                ThreadNo,VerbName[vcbptr->opcode],
                APPC_FLIPI(vcbptr->primary_rc),
                APPC_FLIPL(vcbptr->secondary_rc));
        break;
      }
      else
      {
        NumRalcs++;
        SRTRACE(t,"Thread %d (ralc) Receive_Allocate %d completed\n",
                ThreadNo,NumRalcs);

        /*********************************************************************/
        /* give it to a receive thread by kicking event 1                    */
        /*********************************************************************/
        RcvThreadArray[NextReceive].convptr = convptr;
//      SRTRACE(t,"Thread %d (ralc) convptr %p\n",ThreadNo,convptr);
//      SRTRACE(t,"Thread %d (ralc) kicking receive thread %d\n",
//              ThreadNo,NextReceive);
//      SRTRACE(t,"Thread %d (ralc) setting event_1 %p\n",ThreadNo,
//              RcvThreadArray[NextReceive].event1);
        rc = SetEvent (RcvThreadArray[NextReceive].event1);
        if (rc == FALSE)
        {
          rc = GetLastError();
          SRTRACE(t,"Thread %d (ralc) setting event_1 %p failed with rc %d\n",
                  ThreadNo,RcvThreadArray[NextReceive].event1,rc);
          break;
        }

        /*********************************************************************/
        /* wait for receive thread to release event 2                        */
        /*********************************************************************/
//      SRTRACE(t,"Thread %d (ralc) waiting for event_2 %d\n",ThreadNo,
//              RcvThreadArray[NextReceive].event2);
        rc = WaitForSingleObject(RcvThreadArray[NextReceive].event2,INFINITE);
        if (rc == WAIT_FAILED)
        {
          rc = GetLastError();
          SRTRACE(t,"Thread %d (ralc) wait for event_2 %p failed with rc %d\n",
                  ThreadNo,RcvThreadArray[NextReceive].event2,rc);
          break;
        }

        /*********************************************************************/
        /* ready for next receive allocate                                   */
        /*********************************************************************/
        NextReceive++;
        if (NextReceive == NumRcvThreads)
        {
          NextReceive = 0;
        }
      }
    }
  }

  /***************************************************************************/
  /* Kick receive threads for last time in case they are idle                */
  /* convptr is set to NULL to indicate this final kick                      */
  /***************************************************************************/
  SRTRACE(t,"Thread %d (ralc) last kicks\n",ThreadNo);
  for (NextReceive = 0; NextReceive < NumRcvThreads; NextReceive++)
  {
    RcvThreadArray[NextReceive].convptr = NULL;
    rc = SetEvent (RcvThreadArray[NextReceive].event1);
  }

  /***************************************************************************/
  /* Count threads within critical section                                   */
  /***************************************************************************/
  EnterCriticalSection(&runsem);
  ThreadCount--;
  GetLocalTime(&st);
  SRTRACE(t,"Thread %d (ralc) Exit at %d:%d:%d: ThreadCount %d\n",
          ThreadNo,st.wHour,st.wMinute,st.wSecond,ThreadCount);
  if (ThreadCount == 0)
  {
    PostMessage(hWndMain, WM_CLOSE, 0, 0);
  }
  LeaveCriticalSection(&runsem);
  return(0);
}

/*****************************************************************************/
/* ReceiveThread - service thread which processes multiple conversations     */
/*****************************************************************************/
DWORD WINAPI ReceiveThread(DWORD k)
{
  /***************************************************************************/
  /* Separate variables for each instance of this thread                     */
  /***************************************************************************/
  DWORD ObjIndex;
  DWORD i;
  DWORD j;
  DWORD rc;
  unsigned short NumConvs = 0;
  unsigned short RcvMax = 0;
  unsigned short ThreadNo;
  unsigned short RcvThreadNo;
  CONVCB * ConvptrArray [MAX_RCV_PER_THREAD+2];
  HANDLE EventArray [MAX_RCV_PER_THREAD+2];
  SYSTEMTIME st;

  EventArray[0] = RcvThreadArray[k].event1;
  ConvptrArray[0] = NULL;
  for (i = 1; i < MAX_RCV_PER_THREAD+2; i++)
  {
    EventArray[i] = NULL;
    ConvptrArray[i] = NULL;
  }

  /***************************************************************************/
  /* Count threads                                                           */
  /***************************************************************************/
  EnterCriticalSection(&runsem);
  ThreadCount++;
  RcvThreads++;
  ThreadNo = ThreadCount;
  RcvThreadNo = RcvThreads;
  OUTPUTNUMBER
  LeaveCriticalSection(&runsem);

  SRTRACE(t,"Thread %d (recv) Started\n",k);
  SRTRACE(t,"Thread %d (recv) has event handles %p %p\n",k,
          RcvThreadArray[k].event1,RcvThreadArray[k].event2);

  /***************************************************************************/
  /* Loop round until finished                                               */
  /***************************************************************************/
  while (TRUE)
  {
    /*************************************************************************/
    /* Wait for event completion                                             */
    /*************************************************************************/
//  SRTRACE(t,"Thread %d (recv) waiting for %d events to complete\n",
//          k,NumConvs+1);
    ObjIndex = WaitForMultipleObjects(NumConvs+1,EventArray,FALSE,INFINITE);
    if (ObjIndex == WAIT_FAILED)
    {
      rc = GetLastError();
      SRTRACE(t,"Thread %d (recv) wait for %d events failed with rc %d\n",
              k,NumConvs+1,rc);
      for (j = 0; j <= NumConvs; j++)
      {
        SRTRACE(t,"Thread %d (recv) event %d has handle %p\n",
                k,j,EventArray[j]);
      }
      break;
    }
    ObjIndex -= WAIT_OBJECT_0;
//  SRTRACE(t,"Thread %d (recv) event %d has completed\n",k,ObjIndex);
    if (ObjIndex == 0)
    {
      /***********************************************************************/
      /* final kick from receive_allocate thread - terminate if idle         */
      /***********************************************************************/
      if (RcvThreadArray[k].convptr == NULL)
      {
        SRTRACE(t,"Thread %d (recv) ralc thread terminating\n",k);
        SRTRACE(t,"Thread %d (recv) NumRalcs %d NumRcvConvs %d NumConvs %d\n",
                k,NumRalcs,NumRcvConvs,NumConvs);
        if (NumConvs == 0)
        {
          break;
        }
        else
        {
          continue;
        }
      }
      else
      {
        /*********************************************************************/
        /* new conversation - save convptr and release rcv_alloc thread      */
        /*********************************************************************/
        SRTRACE(t,"Thread %d (recv) new conversation\n",k);

        /*********************************************************************/
        /* select next entry in the convptr array as conversation index      */
        /*********************************************************************/
        NumConvs++;
        i = NumConvs;

        /*********************************************************************/
        /* set up convptr array                                              */
        /* save service thread number and slot number in convcb for tracing  */
        /* add event to event array                                          */
        /*********************************************************************/
        ConvptrArray[i] = RcvThreadArray[k].convptr;
//      SRTRACE(t,"Thread %d:%d (recv) using convptr %p\n",k,i,ConvptrArray[i]);
        ConvptrArray[i]->thread = (unsigned short)k;
        ConvptrArray[i]->conv   = (unsigned short)i;
        strcpy (ConvptrArray[i]->type,"recv");
        EventArray[i] = ConvptrArray[i]->hEvent;
        EnterCriticalSection(&runsem);
        SimRcvConvs++;
        OUTPUTNUMBER
        SRTRACE(t,"Thread %d:%d (recv) NumConvs %d SimRcvConvs %d\n",
                k,i,NumConvs,SimRcvConvs);
        LeaveCriticalSection(&runsem);

        /*********************************************************************/
        /* release receive_allocate thread                                   */
        /*********************************************************************/
//      SRTRACE(t,"Thread %d:%d (recv) setting event_2 %p\n",
//              k,i,RcvThreadArray[k].event2);
        rc = SetEvent (RcvThreadArray[k].event2);
        if (rc == FALSE)
        {
          rc = GetLastError();
          SRTRACE(t,"Thread %d:%d (recv) setting event_2 %p failed with rc %d\n",
                  k,i,RcvThreadArray[k].event2,rc);
          break;
        }
      }
    }
    else
    {
      /***********************************************************************/
      /* Object index locates existing conversation                          */
      /***********************************************************************/
      i = ObjIndex;
    }

    /*************************************************************************/
    /* Issue the next verb                                                   */
    /*************************************************************************/
//  SRTRACE(t,"Thread %d:%d (recv) issuing next verb\n",
//          k,ConvptrArray[i]->conv);
    ConvptrArray[i]->TPEnded = IssueRcvVerb(ConvptrArray[i]);
    if (ConvptrArray[i]->TPEnded)
    {
      /***********************************************************************/
      /* end of conversation                                                 */
      /***********************************************************************/
      SRTRACE(t,"Thread %d:%d (recv) conversation completed\n",
              k,ConvptrArray[i]->conv);
      NumConvs--;
      EnterCriticalSection(&runsem);
      SimRcvConvs--;
      RcvConvs++;
      OUTPUTNUMBER
      SRTRACE(t,"Thread %d:%d (recv) NumConvs %d SimRcvConvs %d RcvConvs %d\n",
              k,ConvptrArray[i]->conv,NumConvs,SimRcvConvs,RcvConvs);
      SRTRACE(t,"Thread %d:%d (recv) NumRalcs %d NumRcvConvs %d NumConvs %d\n",
              k,ConvptrArray[i]->conv,NumRalcs,NumRcvConvs,NumConvs);
      LeaveCriticalSection(&runsem);

      /***********************************************************************/
      /* free resources                                                      */
      /***********************************************************************/
      SRTRACE(t,"Thread %d:%d (recv) clearing entry %d\n",
              k,ConvptrArray[i]->conv,i);
      CloseHandle(ConvptrArray[i]->hEvent);
      free (ConvptrArray[i]->DataPtr);
      free (ConvptrArray[i]);
      ConvptrArray[i] = NULL;
      EventArray[i]   = NULL;

      /***********************************************************************/
      /* if the required number of conversations have been allocated and     */
      /* this thread has just finished its last conversation, end loop       */
      /***********************************************************************/
      if ((NumRcvConvs > 0) && (NumRalcs == NumRcvConvs) && (NumConvs == 0))
      {
        break;
      }

      /***********************************************************************/
      /* the event array cannot have holes in it, so shuffle up the          */
      /* pointers and events                                                 */
      /* note that this means convptr->conv no longer matches i              */
      /* which is why tracing uses ConvptrArray[i]->conv                     */
      /***********************************************************************/
      for (j = i; j <= NumConvs; j++)
      {
        ConvptrArray [j] = ConvptrArray [j+1];
        EventArray [j] = EventArray [j+1];
      }
      ConvptrArray [NumConvs+1] = NULL;
      EventArray [NumConvs+1] = NULL;
    }
    else
    {
      /***********************************************************************/
      /* to stop one conversation getting an unfair share of the time,       */
      /* move this conversation to the end and shuffle up the pointers.      */
      /* note that this means convptr->conv no longer matches i              */
      /* which is why tracing uses ConvptrArray[i]->conv                     */
      /***********************************************************************/
      ConvptrArray [NumConvs+1] = ConvptrArray [i];
      EventArray [NumConvs+1] = EventArray [i];
      for (j = i; j <= NumConvs; j++)
      {
        ConvptrArray [j] = ConvptrArray [j+1];
        EventArray [j] = EventArray [j+1];
      }
      ConvptrArray [NumConvs+1] = NULL;
      EventArray [NumConvs+1] = NULL;
    }
  }

  /***************************************************************************/
  /* Count threads within critical section                                   */
  /***************************************************************************/
  EnterCriticalSection(&runsem);
  ThreadCount--;
  RcvThreads--;
  GetLocalTime(&st);
  SRTRACE(t,"Thread %d (recv) Exit at %d:%d:%d: ThreadCount %d\n",
          k,st.wHour,st.wMinute,st.wSecond,ThreadCount);
  OUTPUTNUMBER
  if (ThreadCount == 0)
  {
    PostMessage(hWndMain, WM_CLOSE, 0, 0);
  }
  LeaveCriticalSection(&runsem);

  return(0);
}

/*****************************************************************************/
/* InitializeWinMain - does the windows bits of initialisation               */
/*****************************************************************************/

BOOL InitializeWinMain(HINSTANCE hInstance)
{
  WAPPCDATA APPCData;
  WNDCLASS class;
  #define WinAPPCVERSION  0x0001

  /***************************************************************************/
  /* Startup WinAPPC                                                         */
  /***************************************************************************/
  if (WinAPPCStartup(WinAPPCVERSION,&APPCData))
  {
    return (FALSE);
  }

  /***************************************************************************/
  /* Register Window Class for our icon                                      */
  /***************************************************************************/

  class.style = 0;
  class.lpfnWndProc   = (WNDPROC)TPWndProc;
  class.cbClsExtra    = (DWORD)0;
  class.cbWndExtra    = (DWORD)0;
  class.hInstance     = hInstance;
  class.hIcon         = LoadIcon(hInstance,"MainIcon");
  class.hCursor       = LoadCursor(NULL, IDC_ARROW);
  class.hbrBackground = GetStockObject(WHITE_BRUSH);
  class.lpszMenuName  = (LPSTR) NULL;
  class.lpszClassName = (LPSTR) "MRCV\0";

  if (!RegisterClass(&class))
  {
    return (FALSE);
  }

  /***************************************************************************/
  /* Create the window                                                       */
  /***************************************************************************/
  sprintf(title,"APPC Receive TP\0");

  if ((hWndMain = CreateWindow("MRCV\0",          /* window class            */
      title,                                      /* window name             */
      WS_MINIMIZE|WS_OVERLAPPEDWINDOW,            /* window style            */
      0,                                          /* x position              */
      0,                                          /* y position              */
      10,                                         /* width                   */
      10,                                         /* height                  */
      NULL,                                       /* parent handle           */
      NULL,                                       /* menu or child ID        */
      hInstance,                                  /* instance                */
      NULL))                                      /* additional info         */
      == NULL)
  {
    return (FALSE);
  }

  ShowWindow(hWndMain, SW_MINIMIZE);

  return(TRUE);

}

/*****************************************************************************/
/* Window proc for the iconised window                                       */
/*****************************************************************************/
LONG PASCAL TPWndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
{
  switch (message)
  {
    case WM_CREATE:
      break;

    case WM_QUERYOPEN:
      /***********************************************************************/
      /* Prevent the window being opened                                     */
      /***********************************************************************/
      break;

    case WM_CLOSE:
      return DefWindowProc(hWnd, message, wParam, lParam);
      break;

    case WM_DESTROY:
      PostQuitMessage(0);
      break;

    default:
      return DefWindowProc(hWnd, message, wParam, lParam);
      break;
  }
  return 0l;
}

/*****************************************************************************/
/* InitializeMain - blanks out variables not set in ReadConfig               */
/*****************************************************************************/
void InitializeMain()
{
  RcvThreads  = 0;
  ThreadCount = 0;
  NumResults  = 0;
  cnvtptr     = (char *)&cnvt;

  InitializeCriticalSection(&runsem);
}

/*****************************************************************************/
/* IssueRcvVerb - looks at the verb which has just completed and does the    */
/*                 next one                                                  */
/*****************************************************************************/
BOOL IssueRcvVerb(CONVCB * convptr)
{
  BOOL TPEnded;
  struct appc_hdr * vcbptr;

//SRTRACE(t,"Thread %d:%d (recv) IssueRcvVerb\n",
//        convptr->thread,convptr->conv);
  TPEnded = FALSE;
  vcbptr = (struct appc_hdr *) &convptr->vcb;
  if (vcbptr->opcode != 0x0000)
  {
    TPEnded = ProcessReturns(convptr);
  }
  if (!TPEnded)
  {
    switch (vcbptr->opcode)
    {
      case 0x0000:
        Build_RECEIVE_ALLOCATE(convptr);
        break;

      case AP_RECEIVE_ALLOCATE:
        StartConversation(convptr);
        Build_MC_RECEIVE_AND_WAIT(convptr);
        break;

      case AP_M_RECEIVE_AND_WAIT:
        if ((P_M_RAW(vcbptr)->what_rcvd == AP_DATA_COMPLETE) ||
            (P_M_RAW(vcbptr)->what_rcvd == AP_DATA_COMPLETE_CONFIRM) ||
            (P_M_RAW(vcbptr)->what_rcvd == AP_DATA_COMPLETE_CONFIRM_DEALL))
        {
          convptr->RcvCount++;
        }
        if ((P_M_RAW(vcbptr)->primary_rc == AP_DEALLOC_NORMAL))
        {
          /*******************************************************************/
          /* Issue TP_ENDED every time conversation deallocated              */
          /*******************************************************************/
          EndConversation(convptr);
          Build_TP_ENDED(convptr);
        }
        else if ((P_M_RAW(vcbptr)->what_rcvd == AP_CONFIRM_WHAT_RECEIVED) ||
                 (P_M_RAW(vcbptr)->what_rcvd == AP_DATA_COMPLETE_CONFIRM))
        {
          Build_MC_CONFIRMED(convptr);
          convptr->Deallocated = FALSE;
        }
        else if ((P_M_RAW(vcbptr)->what_rcvd == AP_CONFIRM_DEALLOCATE) ||
                 (P_M_RAW(vcbptr)->what_rcvd == AP_DATA_COMPLETE_CONFIRM_DEALL))
        {
          Build_MC_CONFIRMED(convptr);
          convptr->Deallocated = TRUE;
        }
        else
        {
          Build_MC_RECEIVE_AND_WAIT(convptr);
        }
        break;

      case AP_M_CONFIRMED:
        if (convptr->Deallocated)
        {
          /*******************************************************************/
          /* Issue TP_ENDED every time conversation deallocated              */
          /*******************************************************************/
          EndConversation(convptr);
          Build_TP_ENDED(convptr);
        }
        else
        {
          Build_MC_RECEIVE_AND_WAIT(convptr);
        }
        break;

      case AP_TP_ENDED:
        /*********************************************************************/
        /* set TPEnded                                                       */
        /*********************************************************************/
        TPEnded = TRUE;
        break;

      default:
        /*********************************************************************/
        /* What is this verb then ??                                         */
        /*********************************************************************/
        TPEnded = TRUE;
        DebugBreak();
        break;

    } /* Op-code switch */
    SRTRFLUSH()

  }

  /***************************************************************************/
  /* Now go ahead and issue the verb, if we're not finished                  */
  /***************************************************************************/
  if (!TPEnded)
  {
    convptr->async_corr = WinAsyncAPPCEx(convptr->hEvent,
                          (long)(char *)(vcbptr));
    if (convptr->async_corr == 0)
    {
      SRTRACE(t,"Thread %d:%d (recv) WinAsync call %x failed corr %p\n",
              convptr->thread,convptr->conv,
              vcbptr->opcode,convptr->async_corr);
      convptr->TPEnded = TRUE;
    }
  }

  SRTRFLUSH()
  return(TPEnded);

} /* Issue receive verb */

/*****************************************************************************/
/* Build routines to build all required verbs                                */
/*****************************************************************************/

void Build_TP_ENDED(CONVCB * convptr)
{
  TP_ENDED * vcbptr;
  SRTRACE(t,"Thread %d:%d (%s) Build_TP_Ended\n",
          convptr->thread,convptr->conv,convptr->type);
  vcbptr = (TP_ENDED *) &(convptr->vcb);

  CLEARVCB

  vcbptr->opcode = AP_TP_ENDED;
  memcpy(&(vcbptr->tp_id), convptr->TPid, 8);
  vcbptr->type = AP_SOFT;
}

void Build_RECEIVE_ALLOCATE(CONVCB * convptr)
{
  RECEIVE_ALLOCATE * vcbptr;
  SRTRACE(t,"Thread %d:%d (%s) Build_Receive_Allocate\n",
          convptr->thread,convptr->conv,convptr->type);
  vcbptr = (RECEIVE_ALLOCATE *) &(convptr->vcb);

  CLEARVCB

  vcbptr->opcode = AP_RECEIVE_ALLOCATE;
  memcpy(vcbptr->tp_name, LocalTPName, 64);
}

void Build_MC_CONFIRMED(CONVCB * convptr)
{
  MC_CONFIRMED * vcbptr;
//SRTRACE(t,"Thread %d:%d (%s) Build_MC_Confirmed\n",
//        convptr->thread,convptr->conv,convptr->type);
  vcbptr = (MC_CONFIRMED *) &(convptr->vcb);

  CLEARVCB

  vcbptr->opcode = AP_M_CONFIRMED;
  vcbptr->opext = AP_MAPPED_CONVERSATION;
  memcpy(&(vcbptr->tp_id),convptr->TPid, 8);
  vcbptr->conv_id = convptr->Convid;
}

void Build_MC_RECEIVE_AND_WAIT(CONVCB * convptr)
{
  MC_RECEIVE_AND_WAIT * vcbptr;
//SRTRACE(t,"Thread %d:%d (%s) Build_MC_Receive_And_Wait\n",
//        convptr->thread,convptr->conv,convptr->type);
  vcbptr = (MC_RECEIVE_AND_WAIT *) &(convptr->vcb);

  CLEARVCB

  vcbptr->opcode = AP_M_RECEIVE_AND_WAIT;
  vcbptr->opext = AP_MAPPED_CONVERSATION;
  memcpy(&(vcbptr->tp_id),convptr->TPid, 8);
  vcbptr->conv_id = convptr->Convid;
  vcbptr->rtn_status = AP_YES;
  vcbptr->max_len = RcvSize;
  vcbptr->dptr = convptr->DataPtr;
}

/*****************************************************************************/
/* ProcessReturns - Checks return codes from the last verb to complete and   */
/*                  saves conversation id and tp id in the conversation cb   */
/*****************************************************************************/
BOOL ProcessReturns(CONVCB * convptr)
{
  BOOL TPEnded = FALSE;
  struct appc_hdr * vcbptr;
  SYSTEMTIME st;

//SRTRACE(t,"Thread %d:%d (%s) ProcessReturns\n",
//        convptr->thread,convptr->conv,convptr->type);
  vcbptr = (struct appc_hdr *) &(convptr->vcb);

  GetLocalTime(&st);
  if (vcbptr->primary_rc != AP_OK)
  {
    SRTRACE(t,"Thread %d:%d (%s) error: %s prc %4.4x src %8.8x at %d:%d:%d\n",
            convptr->thread,convptr->conv,convptr->type,
            VerbName[vcbptr->opcode],
            APPC_FLIPI(vcbptr->primary_rc),APPC_FLIPL(vcbptr->secondary_rc),
            st.wHour,st.wMinute,st.wSecond);
    if ((vcbptr->opcode == AP_M_RECEIVE_AND_WAIT) &&
        (vcbptr->primary_rc == AP_DEALLOC_NORMAL))
    {
      SRTRACE(t,"Thread %d:%d (%s) MC_RECEIVE_AND_WAIT completed with DEALLOC_NORMAL\n",\
              convptr->thread,convptr->conv,convptr->type);
    }
    else if ((vcbptr->opcode == AP_M_CONFIRM) &&
             (vcbptr->primary_rc == AP_ALLOCATION_ERROR))
    {
      SRTRACE(t,"Thread %d:%d (%s) MC_CONFIRM completed with ALLOCATION_ERROR\n",
              convptr->thread,convptr->conv,convptr->type);
    }
    else
    {
      TPEnded = TRUE;
      SRTRACE(t,"Thread %d:%d (%s) unexpected error on %s - set TPEnded\n",
              convptr->thread,convptr->conv,convptr->type,
              VerbName[vcbptr->opcode]);
    }
  }
  else
  {
    switch (vcbptr->opcode)
    {
      case AP_TP_ENDED:
        SRTRACE(t,"Thread %d:%d (%s) TP_Ended completed at %d:%d:%d\n",
                convptr->thread,convptr->conv,convptr->type,
                st.wHour,st.wMinute,st.wSecond);
        break;

      case AP_RECEIVE_ALLOCATE:
        SRTRACE(t,"Thread %d:%d (%s) Receive_Allocate completed at %d:%d:%d\n",
                convptr->thread,convptr->conv,convptr->type,
                st.wHour,st.wMinute,st.wSecond);
        memcpy(convptr->TPid,&(P_RAL(vcbptr)->tp_id),8);
        convptr->Convid = P_RAL(vcbptr)->conv_id;
        break;

      case AP_M_RECEIVE_AND_WAIT:
//      SRTRACE(t,"Thread %d:%d (%s) MC_Receive_And_Wait completed at %d:%d:%d\n",
//              convptr->thread,convptr->conv,convptr->type,
//              st.wHour,st.wMinute,st.wSecond);
        break;

      case AP_M_CONFIRMED:
SRTRACE(t,"Thread %d:%d (%s) MC_Confirmed completed at %d:%d:%d: receive count %d\n",
                convptr->thread,convptr->conv,convptr->type,
                st.wHour,st.wMinute,st.wSecond,
                convptr->RcvCount);
        break;

      default:
        SRTRACE(t,"Thread %d:%d (%s) UNKNOWN opcode - set TPEnded\n",
                convptr->thread,convptr->conv,convptr->type);
        TPEnded = TRUE;
        DebugBreak();
        break;
    }
  }
  SRTRFLUSH()
  return(TPEnded);
}

/*****************************************************************************/
/* ReadConfig - Reads config info from MRCV.CFG and allocates buffer for     */
/*              receiving                                                    */
/*****************************************************************************/
void ReadConfig()
{
  char buffer[200];

  if (!ReadString("ResultFile",FileName,60))
  {
    strcpy(FileName,"MRCV.OUT");
  }

  if (!ReadString("TraceFile",TraceFile,60))
  {
    strcpy(TraceFile,"MRCV.TRC");
  }

  strcpy(TPName,"MRCV");
  PadString(TPName,64);
  CONV_A_TO_E(TPName,64);

  if (!ReadString("LocalTPName",LocalTPName,64))
  {
    strcpy(LocalTPName,"MRCVTP");
  }
  PadString(LocalTPName,64);
  CONV_A_TO_E(LocalTPName,64);

  RcvSize=4096;
  if (ReadString("RcvSize",buffer,200))
  {
    RcvSize=atoi(buffer);
  }

  NumRcvThreads = 2;
  if (ReadString("NumRcvThreads",buffer,200))
  {
    NumRcvThreads=atoi(buffer);
  }

  NumRcvConvs = 4;
  if (ReadString("NumRcvConvs",buffer,200))
  {
    NumRcvConvs=atoi(buffer);
  }
  NumRalcs = 0;
  RcvConvs = 0;

  if (NumRcvConvs > 0 && NumRcvThreads * 63 < NumRcvConvs)
  {
    NumRcvThreads = NumRcvConvs / 63 + 1;
  }

  if (NumRcvConvs > 0 && NumRcvThreads > NumRcvConvs)
  {
    NumRcvThreads = NumRcvConvs;
  }
}

/*****************************************************************************/
/* CONV_A_TO_E - ASCII to EBCDIC conversion routine                          */
/*****************************************************************************/
void CONV_A_TO_E(char * string,int length)
{
  memset(cnvtptr,0,sizeof(cnvt));

  cnvt.opcode       = SV_CONVERT;
  cnvt.direction    = SV_ASCII_TO_EBCDIC;
  cnvt.char_set     = SV_AE;

  cnvt.len          = length;
  cnvt.source       = string;
  cnvt.target       = string;

  ACSSVC_C((long)(char *) (cnvtptr));
}

/*****************************************************************************/
/* CONV_E_TO_A - EBCDIC to ASCII conversion routine                          */
/*****************************************************************************/
void CONV_E_TO_A(char * string,int length)
{
  memset(cnvtptr,0,sizeof(cnvt));

  cnvt.opcode       = SV_CONVERT;
  cnvt.direction    = SV_EBCDIC_TO_ASCII;
  cnvt.char_set     = SV_AE;
  cnvt.len          = length;
  cnvt.source       = string;
  cnvt.target       = string;

  ACSSVC_C((long)(char *) (cnvtptr));
}

/*****************************************************************************/
/* StartConversation - Record start time for this conversation               */
/*****************************************************************************/
void StartConversation(CONVCB * convptr)
{
  convptr->ConvStarted = GetTickCount();
}

/*****************************************************************************/
/* EndConversation - Calculate elapsed time for this conversation            */
/*****************************************************************************/
void EndConversation(CONVCB * convptr)
{
  if (ResultPtr != NULL)
  {
    /*************************************************************************/
    /* Make sure we don't go over the end of the allocated area              */
    /*************************************************************************/
    if (NumResults < NumRcvConvs)
    {
      *ResultPtr++ = (GetTickCount() - convptr->ConvStarted);
      NumResults++;
    }
    else
    {
      SRTRACE(t,"Thread %d:%d Too many calls to EndConversation\n",
              convptr->thread,convptr->conv);
    }
  }
}

/*****************************************************************************/
/* OutputResults - dump the times of conversations to file                   */
/*****************************************************************************/
void OutputResults()
{
  FILE *h = NULL;
  RESULT * ptr = NULL;
  unsigned short i = 0;

  h = fopen(FileName,"w");
  if (h != NULL)
  {
    fprintf(h,"\nMRCV Results\n------------\n\n");
    CONV_E_TO_A(LocalTPName,64);
    fprintf(h,"Local TP Name              = %.64s\n",LocalTPName);
    CONV_E_TO_A(RemoteTPName,64);
    fprintf(h,"Remote TP Name             = %.64s\n",RemoteTPName);
    fprintf(h,"Local LU Alias             = %.8s\n",LocalLUAlias);
    fprintf(h,"Remote LU Alias            = %.8s\n",RemoteLUAlias);
    CONV_E_TO_A(ModeName,8);
    fprintf(h,"ModeName                   = %.8s\n",ModeName);
    fprintf(h,"No. of receive threads     = %d\n",NumRcvThreads);
    fprintf(h,"No. of rcv convs started   = %d\n",NumRalcs);
    fprintf(h,"No. of rcv convs completed = %d\n",RcvConvs);
    fprintf(h,"\n");

    ptr = ResultBuf;
    while (ptr < ResultPtr)
    {
      fprintf(h,"Conversation number %d, time = %.3f seconds\n",i++,
                                                (((float) *ptr++) / 1000.0 ));
    }
    fclose(h);
  }
}

/*****************************************************************************/
/* ReadString - Get a line of text from the config file                      */
/*****************************************************************************/
int ReadString(char * lpValueName,char * lpData, int maxlen)
{
  char       buffer[200];
  char      *p = NULL;
  FILE      *h = NULL;
  BOOL       match = FALSE;
  BOOL       eof   = FALSE;
  int        rc = 0;
  int        ch = 0;
  int        i = 0;
  BOOL       gotdata = FALSE;
  char       separators[] = " =\t\n";

  GetModuleFileName( hInst, buffer, sizeof(buffer) );
  lstrcpy( buffer+lstrlen(buffer) - 4, ".CFG" );
  h = fopen( buffer, "r" );
  buffer[0] = '\0';

  if (h != NULL)
  {
    while ((!match) && (!eof))
    {
      /***********************************************************************/
      /* Use fgetc to read a line of text from the file                      */
      /***********************************************************************/
      for (i=0; (i<sizeof(buffer))     &&
                ((ch=getc(h)) != EOF)  &&
                ((char)ch != '\n');
                                   i++)
      {
        buffer[i] = (char)ch;
      }
      if ((char)ch == '\n')
      {
        buffer[i++] = (char)ch;
      }
      if (ch == EOF)
      {
        eof = TRUE;
      }
      else
      {
        /*********************************************************************/
        /* Compare the first token in the line read with the requested parm  */
        /*********************************************************************/
        if (!strcmpi(strupr(strtok(buffer, separators)), lpValueName))
        {
          match = TRUE;
          /*******************************************************************/
          /* Get a pointer to the second token (the value we want)           */
          /*******************************************************************/
          p = strtok(NULL, separators);

          /*******************************************************************/
          /* Copy the data IF there is some                                  */
          /*******************************************************************/
          if (p != NULL)
          {
            /*****************************************************************/
            /* Force a NULL after the second token                           */
            /*****************************************************************/
            strtok(NULL, separators);

            /*****************************************************************/
            /* Copy the data                                                 */
            /*****************************************************************/
            strncpy(lpData, p, maxlen);
            gotdata = TRUE;
          }
          else
          {
            gotdata = FALSE;
          }
        }
      }
    }

    if (gotdata)
    {
      rc = 1;
    }

    fclose(h);

  }

  return(rc);
}

/*****************************************************************************/
/* PadString - Remove terminating NULL  and pad on the right with spaces     */
/*****************************************************************************/
void PadString(char * string,int length)
{
  char * p;
  if ((p=memchr(string,'\0',length)) != NULL)
  {
    while (p < string+length)
    {
      *p++=' ';
    }
  }
}


MRCV.H

/* mrcv.h */
/* (C) COPYRIGHT DATA CONNECTION LIMITED 1994 */

/*****************************************************************************/
/* Change History                                                            */
/*                                                                           */
/*       11/01/94 DK  Created                                                */
/*       14/04/94 DK  Name table added for tracing                           */
/*****************************************************************************/

/*****************************************************************************/
/* static configuration constants                                            */
/* Note that MAX_RCV_PER_SERVICE cannot exceed 63, because we cannot wait    */
/*      on more than 64 events (and one extra event is needed for receive)   */
/*****************************************************************************/
#define MAX_RCV_PER_THREAD  63
#define MAX_RECEIVE_THREADS 64

/*****************************************************************************/
/* global critical section                                                   */
/*****************************************************************************/
CRITICAL_SECTION runsem;

/*****************************************************************************/
/* APPC verb control block and macros for manipulation of vcb                */
/*****************************************************************************/
#define CLEARVCB memset(vcbptr,0,sizeof(VCB));
#define P_TPE(X)    ((struct tp_ended *)X)
#define P_RAL(X)    ((struct receive_allocate *)X)

#define P_M_CFD(X)  ((struct mc_confirmed *)X)
#define P_M_DAL(X)  ((struct mc_deallocate *)X)
#define P_M_RAW(X)  ((struct mc_receive_and_wait *)X)

typedef struct vcb
{
  union
  {
    struct appc_hdr             hdr;
    struct receive_allocate     rcvalloc;
    struct tp_ended             tpend;
    struct mc_confirmed         confirmed;
    struct mc_deallocate        deallocate;
    struct mc_receive_and_wait  rcvwait;
  } U_VCB;
} VCB;

/*****************************************************************************/
/* structures for convert verb                                               */
/*****************************************************************************/
struct convert cnvt;
char * cnvtptr;

/*****************************************************************************/
/* for outputting results                                                    */
/*****************************************************************************/
typedef unsigned long RESULT;
RESULT * ResultPtr;
RESULT * ResultBuf;
short    NumResults;

/*****************************************************************************/
/* conversation control block - one per conversation                         */
/*****************************************************************************/
typedef struct convcb
{
  char           TPid[8];
  char           type[8];
  unsigned long  Convid;
  HANDLE         async_corr;
  char *         DataPtr;
  unsigned short thread;
  unsigned short conv;
  unsigned short RcvCount;
  BOOL           TPEnded;
  BOOL           Deallocated;
  RESULT         ConvStarted;
  HANDLE         hEvent;
  VCB            vcb;
} CONVCB;

/*****************************************************************************/
/* structures for receive threads                                            */
/*****************************************************************************/
typedef struct rcv_thread
{
  HANDLE event1;
  HANDLE event2;
  CONVCB * convptr;
} RCV_THREAD;

RCV_THREAD RcvThreadArray [MAX_RECEIVE_THREADS];

/*****************************************************************************/
/* File Names and handle for trace file                                      */
/*****************************************************************************/
char FileName [60];
char TraceFile[60];
FILE *t = NULL;

/*****************************************************************************/
/* APPC Names from configuration file                                        */
/*****************************************************************************/
char TPName[64];
char LocalTPName[64];
char LocalLUAlias[8];
char RemoteLUAlias[8];
char ModeName[8];
char RemoteTPName[64];

/*****************************************************************************/
/* Global Variables from configuration file                                  */
/*****************************************************************************/
unsigned short NumRcvThreads;
unsigned short NumRcvConvs;
unsigned short RcvSize;

/*****************************************************************************/
/* Global Variables                                                          */
/*****************************************************************************/
unsigned short NumRalcs;                /* number of receive_allocs issued   */
unsigned short RcvConvs;                /* number of conversations completed */
unsigned short ThreadCount;             /* total thread count                */
unsigned short RcvThreads;              /* count of receive threads          */
unsigned short SimRcvConvs;             /* number of simultaneous rcv convs  */

/*****************************************************************************/
/* Names of verbs for tracing                                                */
/*****************************************************************************/
char *VerbName[] =
{
  "UNKNOWN_VERB",                       /* 0x0000                            */
  "MC_ALLOCATE",                        /* 0x0001                            */
  "UNKNOWN_VERB",                       /* 0x0002                            */
  "MC_CONFIRM",                         /* 0x0003                            */
  "MC_CONFIRMED",                       /* 0x0004                            */
  "MC_DEALLOCATE",                      /* 0x0005                            */
  "MC_FLUSH",                           /* 0x0006                            */
  "MC_GET_ATTRIBUTES",                  /* 0x0007                            */
  "UNKNOWN_VERB",                       /* 0x0008                            */
  "UNKNOWN_VERB",                       /* 0x0009                            */
  "MC_PREPARE_TO_RECEIVE",              /* 0x000a                            */
  "MC_RECEIVE_AND_WAIT",                /* 0x000b                            */
  "MC_RECEIVE_IMMEDIATE",               /* 0x000c                            */
  "MC_RECEIVE_AND_POST",                /* 0x000d                            */
  "MC_REQUEST_TO_SEND",                 /* 0x000e                            */
  "MC_SEND_DATA",                       /* 0x000f                            */
  "MC_SEND_ERROR",                      /* 0x0010                            */
  "UNKNOWN_VERB",                       /* 0x0011                            */
  "MC_TEST_RTS",                        /* 0x0012                            */
  "TP_ENDED",                           /* 0x0013                            */
  "TP_STARTED",                         /* 0x0014                            */
  "UNKNOWN_VERB",                       /* 0x0015                            */
  "RECEIVE_ALLOCATE",                   /* 0x0016                            */
  "UNKNOWN_VERB",                       /* 0x0017                            */
  "MC_SEND_CONVERSATION"                /* 0x0018                            */
};

/*****************************************************************************/
/* Windows stuff                                                             */
/*****************************************************************************/
HWND hWndMain;
char title[30]={'\0'};
#define OUTPUTNUMBER sprintf(title,"thrd simR done  %d  %d  %d", \
                     ThreadCount,SimRcvConvs,RcvConvs);  \
                     SetWindowText(hWndMain,title);

/*****************************************************************************/
/* Function Prototypes                                                       */
/*****************************************************************************/
int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine, int nCmdShow);
BOOL InitializeWinMain(HINSTANCE hInstance);
LONG PASCAL TPWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
void InitializeMain(void);
BOOL IssueRcvVerb(CONVCB * convptr);
BOOL ProcessReturns(CONVCB * convptr);
void ReadConfig(void);
void CONV_A_TO_E(char * string,int length);
void CONV_E_TO_A(char * string,int length);
void StartConversation(CONVCB * convptr);
void EndConversation(CONVCB * convptr);
int  ReadString(char * lpValueName, char FAR * lpData, int maxlen);
void PadString(char * string, int length);
void OutputResults(void);
void Build_RECEIVE_ALLOCATE(CONVCB * convptr);
void Build_TP_ENDED(CONVCB * convptr);
void Build_MC_CONFIRMED(CONVCB * convptr);
void Build_MC_DEALLOCATE(CONVCB * convptr);
void Build_MC_RECEIVE_AND_WAIT(CONVCB * convptr);
DWORD WINAPI RcvAllocThread();
DWORD WINAPI ReceiveThread(DWORD k);


MSEND.C

/* msend.c */
/* (C) COPYRIGHT DATA CONNECTION LIMITED 1994 */

/*****************************************************************************/
/* Change History                                                            */
/*                                                                           */
/*       21/02/94 DK  Created                                                */
/*       15/03/94 DK  Thread priorities added and signalled event goes to    */
/*                    back of the list (for WaitForMultipleObjects)          */
/*       14/04/94 DK  OutputResults removed                                  */
/*****************************************************************************/

/*****************************************************************************/
/* If you want internal tracing, #define SRTRC here                          */
/*****************************************************************************/
//#define SRTRC

/*****************************************************************************/
/*                                                                           */
/* ROUTINE : SEND using event completion                                     */
/*                                                                           */
/* FUNCTION: This file contains the routines for a multi-threaded routine    */
/*           which uses asynchronous APPC calls with event completion        */
/*           to send data.                                                   */
/*                                                                           */
/*           It runs with either the single-threaded or the multi-threaded   */
/*           version of receive (mrcv or recvtp).                            */
/*                                                                           */
/* INPUTS  : MSEND.CFG (file) (documented below)                             */
/*                                                                           */
/* OUTPUTS : MSEND.OUT                                                       */
/*           MSEND.TRC                                                       */
/*                                                                           */
/*****************************************************************************/

/*****************************************************************************/
/* Operation:                                                                */
/*                                                                           */
/* This is a Windows NT application which runs in a minimized window.        */
/*                                                                           */
/* Thread structure:                                                         */
/*                                                                           */
/*   A variable number of send threads                                       */
/*     Each send thread processes a variable number of conversations and     */
/*     issues a WaitForMultipleObjects to wait for completion of any of its  */
/*     send operations.  Each conversation starts with TP_STARTED, followed  */
/*     by MC_ALLOCATE and MC_CONFIRM.  Then it issues MC_SEND_DATA verbs     */
/*     to send data.  MC_CONFIRMs are issued at configurable intervals.      */
/*     If a confirm fails, an attempt is made to restart the conversation    */
/*     after five seconds.                                                   */
/*                                                                           */
/*   Note:  this program is compatible with the single-threaded version of   */
/*   receive, which can be run for example on WIN16 clients.                 */
/*                                                                           */
/*****************************************************************************/

/*****************************************************************************/
/* Configuration file:                                                       */
/*                                                                           */
/* The configuration file is called MSEND.CFG and must reside in the         */
/* same directory as the program.  It contains the following, in any order.  */
/* If any parameter is omitted, the default is assumed.                      */
/*                                                                           */
/* TraceFile = <Name of file for tracing, default MSEND.TRC>                 */
/* RemoteTPName = <Name used for allocate, default MRCVTP>                   */
/* LocalLUAlias = <Alias for local LU, default SENDLU>                       */
/* RemoteLUAlias = <Alias for remote LU, default RECVLU>                     */
/* ModeName = <Mode Name, default #INTER>                                    */
/*                                                                           */
/* NumSendConvs = <Number of conversations to be sent, default = 4>          */
/* NumSends = <number of SEND_DATA verbs per conversation, default = 8>      */
/* ConfirmEvery = <number of SEND_DATA verbs between CONFIRMs, default = 2>  */
/* SendSize = <number of bytes per SEND_DATA, default = 256>                 */
/*                                                                           */
/* The name used for TP_STARTED is fixed at MSEND.                           */
/*                                                                           */
/* If NumSends is zero, the TP will never DEALLOCATE a conversation.         */
/* If ConfirmEvery is zero, the TP will not issue CONFIRM verbs except       */
/*    after the MC_ALLOCATE.                                                 */
/* If ConfirmEvery is non-zero, the TP issues a CONFIRM verbs ConfirmEvery   */
/*    sends.                                                                 */
/*                                                                           */
/* Configuration constants (in msend.h)                                      */
/*                                                                           */
/* #define MAX_SEND_PER_THREAD  Max conversations per send thread      (64)  */
/*                                                                           */
/* MAX_SEND_PER_THREAD is constrained by the limit of 64 objects that can be */
/* waited for in WaitForMultipleObjects.                                     */
/*                                                                           */
/*****************************************************************************/

#include <windows.h>
HINSTANCE hInst;
BOOL verbs_started = FALSE;
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winappc.h>
#include <wincsv.h>
#include "msend.h"

/*****************************************************************************/
/* Trace macros                                                              */
/*****************************************************************************/
#ifdef SRTRC
#define SRTROPEN() t = fopen(TraceFile,"w");
#define SRTRFLUSH() fflush(t);
#define SRTRCLOSE() fclose(t);
#define SRTRACE fprintf
#else
#define SRTROPEN()
#define SRTRFLUSH()
#define SRTRCLOSE()
#define SRTRACE 1 ? (void) 0 : fprintf
#endif

/*****************************************************************************/
/* WinMain - reads initialization info and controls message loop             */
/*           NT version                                                      */
/*****************************************************************************/
int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine, int nCmdShow)
{

  MSG msg;
  DWORD Tid;
  int i;
  DWORD NumConvs;
  HANDLE ThreadHandle;

  hInst = hInstance;

  InitializeMain();

  if (!InitializeWinMain(hInstance))
  {
    return (FALSE);
  }

  ReadConfig();
  SRTROPEN()

  /***************************************************************************/
  /* Create enough send threads to process conversations                     */
  /***************************************************************************/
  i = NumSendConvs;
  while (i > 0)
  {
    NumConvs = (i > 64) ? 64 : i;
    ThreadHandle = CreateThread(NULL,
                                16000,
                                (LPTHREAD_START_ROUTINE)SendThread,
                                (void *)NumConvs,
                                0,
                                &Tid);
    if (ThreadHandle == NULL)
    {
      GetLastError();
      DebugBreak();
    }
    SetThreadPriority(ThreadHandle,THREAD_PRIORITY_BELOW_NORMAL);
    SRTRACE(t,"Created send thread with %d conversations and priority %d\n",
            NumConvs,GetThreadPriority(ThreadHandle));
    CloseHandle(ThreadHandle);
    i -= NumConvs;
  }

  /***************************************************************************/
  /* Windows processing loop                                                 */
  /***************************************************************************/
  while(GetMessage(&msg,NULL,0,0))
  {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }

  WinAPPCCleanup();
  SRTRFLUSH()
  SRTRCLOSE()
  DeleteCriticalSection(&runsem);
  return msg.wParam;         /* save exit parameter for return               */

}

/*****************************************************************************/
/* SendThread - thread which processes multiple conversations                */
/*****************************************************************************/
DWORD WINAPI SendThread(DWORD NumConvs)
{
  /***************************************************************************/
  /* Separate variables for each instance of this thread                     */
  /***************************************************************************/
  CONVCB * convptr;
  struct appc_hdr * vcbptr;
  unsigned short ThreadNo;
  unsigned short SendThreadNo;
  DWORD ObjIndex;
  DWORD i;
  DWORD j;
  DWORD rc;
  CONVCB * ConvptrArray [MAX_SEND_PER_THREAD+1];
  HANDLE EventArray [MAX_SEND_PER_THREAD+1];
  SYSTEMTIME st;

  for (i = 0; i < MAX_SEND_PER_THREAD+1; i++)
  {
    ConvptrArray[i] = NULL;
    EventArray[i] = NULL;
  }

  /***************************************************************************/
  /* Count threads within critical section                                   */
  /***************************************************************************/
  EnterCriticalSection(&runsem);
  ThreadCount++;
  SendThreads++;
  ThreadNo = ThreadCount;
  SendThreadNo = SendThreads;
  OUTPUTNUMBER
  LeaveCriticalSection(&runsem);

  for (i = 0; i < NumConvs; i++)
  {
    /*************************************************************************/
    /* Make a conversation control block                                     */
    /*************************************************************************/
    convptr = malloc (sizeof(CONVCB));
    convptr->thread       = SendThreadNo;
    convptr->conv         = (unsigned short)i;
    convptr->async_corr   = 0;
    convptr->TPid[0]      = '\0';
    convptr->Convid       = 0;
    convptr->SendCount    = 0;
    convptr->ConfirmCount = 0;
    convptr->TPEnded      = FALSE;
    convptr->Deallocated  = FALSE;
    convptr->Counted      = FALSE;
    convptr->SendSize     = SendSize;
    convptr->DataPtr      = malloc(convptr->SendSize);
    vcbptr = (struct appc_hdr *) &convptr->vcb;
    memset(vcbptr,0,sizeof(VCB));
    strcpy (convptr->type,"send");
//  SRTRACE(t,"Thread %d:%d (send) has convptr %p\n",
//          SendThreadNo,i,convptr);

    /*************************************************************************/
    /* Create an event                                                       */
    /*************************************************************************/
    convptr->hEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
    SRTRACE(t,"Thread %d:%d (send) has event handle %p\n",
            SendThreadNo,i,convptr->hEvent);

    /*************************************************************************/
    /* Add to arrays                                                         */
    /*************************************************************************/
    ConvptrArray[i] = convptr;
    EventArray[i]   = convptr->hEvent;

    /*************************************************************************/
    /* Build a tp_started                                                    */
    /*************************************************************************/
    Build_TP_STARTED(convptr);
    SRTRFLUSH()

    /*************************************************************************/
    /*PERF* Don't re-generate data each time we issue a send!                */
    /*************************************************************************/
    GenerateData(convptr);

    /*************************************************************************/
    /* Issue tp_started                                                      */
    /*************************************************************************/
    convptr->async_corr = WinAsyncAPPCEx(convptr->hEvent,
                                         (long)(char *)(vcbptr));
//  SRTRACE(t,"Thread %d:%d (send) tp_started issued corr %p\n",
//          SendThreadNo,i,convptr->async_corr);
    SRTRFLUSH()
    if (convptr->async_corr == 0)
    {
      SRTRACE(t,"Thread %d:%d (send) WinAsync call %x failed corr %p\n",
              SendThreadNo,i,vcbptr->opcode,convptr->async_corr);
      convptr->TPEnded = TRUE;
    }
  }

  /***************************************************************************/
  /* Loop round until finished                                               */
  /***************************************************************************/
  while (TRUE)
  {
    /*************************************************************************/
    /* Wait for event completion                                             */
    /*************************************************************************/
//  SRTRACE(t,"Thread %d (send) waiting for %d events to complete\n",
//          SendThreadNo,NumConvs);
    ObjIndex = WaitForMultipleObjects(NumConvs,EventArray,FALSE,INFINITE);
    if (ObjIndex == WAIT_FAILED)
    {
      rc = GetLastError();
      SRTRACE(t,"Thread %d (send) wait for %d events failed with rc %d\n",
              SendThreadNo,NumConvs,rc);
      for (j = 0; j < NumConvs; j++)
      {
        SRTRACE(t,"Thread %d (send) event %d has handle %p\n",
                SendThreadNo,j,EventArray[j]);
      }
      break;
    }

    /*************************************************************************/
    /* Get index to conversation array                                       */
    /*************************************************************************/
    i = ObjIndex - WAIT_OBJECT_0;
//  SRTRACE(t,"Thread %d:%d (send) event %d has completed\n",
//          SendThreadNo,ConvptrArray[i]->conv,i);

    /*************************************************************************/
    /* Issue the next verb                                                   */
    /*************************************************************************/
//  SRTRACE(t,"Thread %d:%d (send) issuing next send verb\n",
//          SendThreadNo,ConvptrArray[i]->conv);
    ConvptrArray[i]->TPEnded = IssueSendVerb(ConvptrArray[i]);

    if (ConvptrArray[i]->TPEnded)
    {
      /***********************************************************************/
      /* end of conversation                                                 */
      /***********************************************************************/
      NumConvs--;
      SRTRACE(t,"Thread %d:%d (send) conversation completed: NumConvs %d\n",
              SendThreadNo,ConvptrArray[i]->conv,NumConvs);
      EnterCriticalSection(&runsem);
      SendConvs++;
      OUTPUTNUMBER
      SRTRACE(t,"Thread %d:%d (send) NumConvs %d SendConvs %d\n",
              SendThreadNo,ConvptrArray[i]->conv,NumConvs,SendConvs);
      LeaveCriticalSection(&runsem);

      /***********************************************************************/
      /* free resources                                                      */
      /***********************************************************************/
      CloseHandle(ConvptrArray[i]->hEvent);
      free (ConvptrArray[i]->DataPtr);
      free (ConvptrArray[i]);
      ConvptrArray[i] = NULL;
      EventArray[i]   = NULL;

      /***********************************************************************/
      /* if all conversations have completed, end loop                       */
      /***********************************************************************/
      if (NumConvs == 0)
      {
        break;
      }
      /***********************************************************************/
      /* the event array cannot have holes in it, so shuffle up the          */
      /* pointers and events                                                 */
      /* note that this means convptr->conv no longer matches i              */
      /* which is why tracing uses ConvptrArray[i]->conv                     */
      /***********************************************************************/
      for (j = i; j < MAX_SEND_PER_THREAD - 1; j++)
      {
        ConvptrArray [j] = ConvptrArray [j+1];
        EventArray [j] = EventArray [j+1];
      }
      ConvptrArray [MAX_SEND_PER_THREAD-1] = NULL;
      EventArray [MAX_SEND_PER_THREAD-1] = NULL;
    }
    else
    {
      /***********************************************************************/
      /* to stop one conversation getting an unfair share of the time,       */
      /* move this conversation to the end and shuffle up the pointers.      */
      /* note that this means convptr->conv no longer matches i              */
      /* which is why tracing uses ConvptrArray[i]->conv                     */
      /***********************************************************************/
      ConvptrArray [NumConvs] = ConvptrArray [i];
      EventArray [NumConvs] = EventArray [i];
      for (j = i; j < NumConvs; j++)
      {
        ConvptrArray [j] = ConvptrArray [j+1];
        EventArray [j] = EventArray [j+1];
      }
      ConvptrArray [NumConvs] = NULL;
      EventArray [NumConvs] = NULL;
    }
  }

  /***************************************************************************/
  /* Count threads within critical section                                   */
  /***************************************************************************/
  EnterCriticalSection(&runsem);
  ThreadCount--;
  SendThreads--;
  GetLocalTime(&st);
  SRTRACE(t,"Thread %d (send) Exit at %d:%d:%d: ThreadCount %d\n",
          SendThreadNo,st.wHour,st.wMinute,st.wSecond,ThreadCount);
  OUTPUTNUMBER
  if (ThreadCount == 0)
  {
    PostMessage(hWndMain, WM_CLOSE, 0, 0);
  }
  LeaveCriticalSection(&runsem);
  return(0);
}

/*****************************************************************************/
/* InitializeWinMain - does the windows bits of initialisation               */
/*****************************************************************************/
BOOL InitializeWinMain(HINSTANCE hInstance)
{
  WAPPCDATA APPCData;
  WNDCLASS class;
  #define WinAPPCVERSION  0x0001

  /***************************************************************************/
  /* Startup WinAPPC                                                         */
  /***************************************************************************/
  if (WinAPPCStartup(WinAPPCVERSION,&APPCData))
  {
    return (FALSE);
  }

  /***************************************************************************/
  /* Register Window Class for our icon                                      */
  /***************************************************************************/

  class.style = 0;
  class.lpfnWndProc   = (WNDPROC)TPWndProc;
  class.cbClsExtra    = (DWORD)0;
  class.cbWndExtra    = (DWORD)0;
  class.hInstance     = hInstance;
  class.hIcon         = LoadIcon(hInstance,"MainIcon");
  class.hCursor       = LoadCursor(NULL, IDC_ARROW);
  class.hbrBackground = GetStockObject(WHITE_BRUSH);
  class.lpszMenuName  = (LPSTR) NULL;
  class.lpszClassName = (LPSTR) "MSEND\0";

  if (!RegisterClass(&class))
  {
    return (FALSE);
  }

  /***************************************************************************/
  /* Create the window                                                       */
  /***************************************************************************/
  sprintf(title,"APPC Send TP\0");

  if ((hWndMain = CreateWindow("MSEND\0",         /* window class            */
      title,                                      /* window name             */
      WS_MINIMIZE|WS_OVERLAPPEDWINDOW,            /* window style            */
      0,                                          /* x position              */
      0,                                          /* y position              */
      10,                                         /* width                   */
      10,                                         /* height                  */
      NULL,                                       /* parent handle           */
      NULL,                                       /* menu or child ID        */
      hInstance,                                  /* instance                */
      NULL))                                      /* additional info         */
      == NULL)
  {
    return (FALSE);
  }

  ShowWindow(hWndMain, SW_MINIMIZE);

  return(TRUE);

}

/*****************************************************************************/
/* Window proc for the iconised window                                       */
/*****************************************************************************/
LONG PASCAL TPWndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
{
  switch (message)
  {
    case WM_CREATE:
      break;

    case WM_QUERYOPEN:
      /***********************************************************************/
      /* Prevent the window being opened                                     */
      /***********************************************************************/
      break;

    case WM_CLOSE:
      return DefWindowProc(hWnd, message, wParam, lParam);
      break;

    case WM_DESTROY:
      PostQuitMessage(0);
      break;

    default:
      return DefWindowProc(hWnd, message, wParam, lParam);
      break;
  }
  return 0l;
}

/*****************************************************************************/
/* InitializeMain - blanks out variables not set in ReadConfig               */
/*****************************************************************************/
void InitializeMain()
{
  SendThreads = 0;
  ThreadCount = 0;
  cnvtptr     = (char *)&cnvt;

  datach      = 'A';

  InitializeCriticalSection(&runsem);
}

/*****************************************************************************/
/* IssueSendVerb - looks at the verb which has just completed and does the   */
/*                 next one                                                  */
/*****************************************************************************/
BOOL IssueSendVerb(CONVCB * convptr)
{
  BOOL TPEnded;
  struct appc_hdr * vcbptr;

//SRTRACE(t,"Thread %d (send) IssueSendVerb\n",convptr->thread);
  TPEnded = FALSE;
  vcbptr = (struct appc_hdr *) &convptr->vcb;
  if (vcbptr->opcode != 0x0000)
  {
    TPEnded = ProcessReturns(convptr);
  }
  if (!TPEnded)
  {
    switch (vcbptr->opcode)
    {
      case 0x0000:
        Build_TP_STARTED(convptr);

        /*********************************************************************/
        /*PERF* Don't re-generate data each time we issue a send!            */
        /*********************************************************************/
        GenerateData(convptr);
        break;

      case AP_TP_STARTED:
        Build_MC_ALLOCATE(convptr);
        break;

      case AP_M_ALLOCATE:
        if (vcbptr->primary_rc == AP_OK)
        {
          /*******************************************************************/
          /* confirm before the first send to find out if anyone there       */
          /*******************************************************************/
          Build_MC_CONFIRM(convptr);
        }
        else
        {
          /*******************************************************************/
          /* allocate timed out or connection is for incoming call           */
          /* if there are no conversations active, try again in 5 seconds    */
          /* otherwise retry immediately - someone must be out there         */
          /*******************************************************************/
          if (SimSendConvs == 0)
          {
            SRTRACE(t,"Thread %d:%d (send) wait 5 seconds before retry\n",
                    convptr->thread,convptr->conv);
            Sleep (5000);
          }
          SRTRACE(t,"Thread %d:%d (send) retrying\n",
                  convptr->thread,convptr->conv);
          Build_MC_ALLOCATE(convptr);
        }
        break;

      case AP_M_SEND_DATA:
        convptr->SendCount++;
        convptr->ConfirmCount++;
        if ((NumSends != 0) && (convptr->SendCount == NumSends))
        {
          /*******************************************************************/
          /* all sends done - deallocate                                     */
          /*******************************************************************/
          Build_MC_DEALLOCATE(convptr);
        }
        else if ((ConfirmEvery != 0) &&
                 (convptr->ConfirmCount == ConfirmEvery))
        {
          /*******************************************************************/
          /* time to confirm                                                 */
          /*******************************************************************/
          Build_MC_CONFIRM(convptr);
        }
        else
        {
          /*******************************************************************/
          /* just send more data                                             */
          /*******************************************************************/
          Build_MC_SEND_DATA(convptr);
        }
        break;

      case AP_M_CONFIRM:
        if (vcbptr->primary_rc == AP_OK)
        {
          if (convptr->SendCount == 0)
          {
            convptr->Counted = TRUE;
            EnterCriticalSection(&runsem);
            SimSendConvs++;
            OUTPUTNUMBER
            SRTRACE(t,"Thread %d (send) SimSendConvs %d\n",
                    convptr->thread,SimSendConvs);
            LeaveCriticalSection(&runsem);
          }
          convptr->ConfirmCount=0;
          Build_MC_SEND_DATA(convptr);
        }
        else
        {
          /*******************************************************************/
          /* if there are no conversations active, try again in 5 seconds    */
          /* otherwise retry immediately - someone must be out there         */
          /*******************************************************************/
          if (SimSendConvs == 0)
          {
            SRTRACE(t,"Thread %d:%d (send) wait 5 seconds before retry\n",
                    convptr->thread,convptr->conv);
            Sleep (5000);
          }
          SRTRACE(t,"Thread %d:%d (send) retrying\n",
                  convptr->thread,convptr->conv);
          Build_MC_ALLOCATE(convptr);
        }
        break;

      case AP_M_DEALLOCATE:
        Build_TP_ENDED(convptr);
        break;

      case AP_TP_ENDED:
        /*********************************************************************/
        /* quit                                                              */
        /*********************************************************************/
        TPEnded = TRUE;
        break;

      default:
        /*********************************************************************/
        /* What is this verb then ??                                         */
        /*********************************************************************/
        TPEnded = TRUE;
        DebugBreak();
        break;

    } /* Op-code switch */

  }

  /***************************************************************************/
  /* If send conversation has finished and was counted, count it down        */
  /***************************************************************************/
  if (TPEnded && convptr->Counted)
  {
    EnterCriticalSection(&runsem);
    SimSendConvs--;
    OUTPUTNUMBER
    SRTRACE(t,"Thread %d (send) SimSendConvs %d\n",
            convptr->thread,SimSendConvs);
    LeaveCriticalSection(&runsem);
  }

  /***************************************************************************/
  /* Now go ahead and issue the verb, if we're not finished                  */
  /***************************************************************************/
  if (!TPEnded)
  {
    convptr->async_corr = WinAsyncAPPCEx(convptr->hEvent,

(long)(char *)(vcbptr));
    if (convptr->async_corr == 0)
      {
        SRTRACE(t,"Thread %d (send) WinAsync call %x failed corr %p\n",
                convptr->thread,vcbptr->opcode,
                convptr->async_corr);
        convptr->TPEnded = TRUE;
      }
  }

  SRTRFLUSH()
  return(TPEnded);

} /* Issue send verb */

/*****************************************************************************/
/* Build routines to build all required verbs                                */
/*****************************************************************************/

void Build_TP_STARTED(CONVCB * convptr)
{
  TP_STARTED * vcbptr;
  SRTRACE(t,"Thread %d:%d (%s) Build_TP_Started\n",
          convptr->thread,convptr->conv,convptr->type);
  vcbptr = (TP_STARTED *) &(convptr->vcb);

  CLEARVCB

  vcbptr->opcode = AP_TP_STARTED;
  memcpy(&(vcbptr->lu_alias), LocalLUAlias, 8);
  memcpy(&(vcbptr->tp_name), TPName, 64);
}

void Build_TP_ENDED(CONVCB * convptr)
{
  TP_ENDED * vcbptr;
  SRTRACE(t,"Thread %d:%d (%s) Build_TP_Ended\n",
          convptr->thread,convptr->conv,convptr->type);
  vcbptr = (TP_ENDED *) &(convptr->vcb);

  CLEARVCB

  vcbptr->opcode = AP_TP_ENDED;
  memcpy(&(vcbptr->tp_id), convptr->TPid, 8);
  vcbptr->type = AP_SOFT;
}

void Build_MC_ALLOCATE(CONVCB * convptr)
{
  MC_ALLOCATE * vcbptr;
  SRTRACE(t,"Thread %d:%d (%s) Build_MC_Allocate\n",
          convptr->thread,convptr->conv,convptr->type);
  vcbptr = (MC_ALLOCATE *) &(convptr->vcb);

  CLEARVCB

  vcbptr->opcode = AP_M_ALLOCATE;
  vcbptr->opext = AP_MAPPED_CONVERSATION;
  memcpy(vcbptr->tp_id,convptr->TPid, 8);
  vcbptr->sync_level = AP_CONFIRM_SYNC_LEVEL;
  vcbptr->rtn_ctl = AP_WHEN_SESSION_ALLOCATED;
  memcpy(vcbptr->plu_alias, RemoteLUAlias, 8);
  memcpy(vcbptr->mode_name, ModeName, 8);
  memcpy(vcbptr->tp_name, RemoteTPName, 64);
  vcbptr->security = AP_NONE;
}

void Build_MC_CONFIRM(CONVCB * convptr)
{
  MC_CONFIRM * vcbptr;
//SRTRACE(t,"Thread %d:%d (%s) Build_MC_Confirm\n",
//        convptr->thread,convptr->conv,convptr->type);
  vcbptr = (MC_CONFIRM *) &(convptr->vcb);

  CLEARVCB

  vcbptr->opcode = AP_M_CONFIRM;
  vcbptr->opext = AP_MAPPED_CONVERSATION;
  memcpy(vcbptr->tp_id,convptr->TPid, 8);
  vcbptr->conv_id = convptr->Convid;
}

void Build_MC_DEALLOCATE(CONVCB * convptr)
{
  MC_DEALLOCATE * vcbptr;
  SRTRACE(t,"Thread %d:%d (%s) Build_MC_Deallocate\n",
          convptr->thread,convptr->conv,convptr->type);
  vcbptr = (MC_DEALLOCATE *) &(convptr->vcb);

  CLEARVCB

  vcbptr->opcode = AP_M_DEALLOCATE;
  vcbptr->opext = AP_MAPPED_CONVERSATION;
  memcpy(&(vcbptr->tp_id),convptr->TPid, 8);
  vcbptr->conv_id = convptr->Convid;
  if (ConfirmEvery == 0)
  {
     vcbptr->dealloc_type = AP_FLUSH;
  }
  else
  {
     vcbptr->dealloc_type = AP_SYNC_LEVEL;
  }
}

void Build_MC_SEND_DATA(CONVCB * convptr)
{
  MC_SEND_DATA * vcbptr;
//SRTRACE(t,"Thread %d:%d (%s) Build_MC_Send_Data\n",
//        convptr->thread,convptr->conv,convptr->type);
  vcbptr = (MC_SEND_DATA *) &(convptr->vcb);

  CLEARVCB

  //  PERF - GenerateData();

  vcbptr->opcode = AP_M_SEND_DATA;
  vcbptr->opext = AP_MAPPED_CONVERSATION;
  memcpy(&(vcbptr->tp_id),convptr->TPid, 8);
  vcbptr->conv_id = convptr->Convid;
  vcbptr->dlen = convptr->SendSize;
  vcbptr->dptr = convptr->DataPtr;
  vcbptr->type = AP_NONE;
}

/*****************************************************************************/
/* ProcessReturns - Checks return codes from the last verb to complete and   */
/*                  saves conversation id and tp id in the conversation cb   */
/*****************************************************************************/
BOOL ProcessReturns(CONVCB * convptr)
{
  BOOL TPEnded = FALSE;
  struct appc_hdr * vcbptr;
  SYSTEMTIME st;

//SRTRACE(t,"Thread %d:%d (%s) ProcessReturns\n",
//        convptr->thread,convptr->conv,convptr->type);
  vcbptr = (struct appc_hdr *) &(convptr->vcb);

  GetLocalTime(&st);
  if (vcbptr->primary_rc != AP_OK)
  {
    SRTRACE(t,"Thread %d:%d (%s) error: %s prc %4.4x src %8.8x at %d:%d:%d\n",
            convptr->thread,convptr->conv,convptr->type,
            VerbName[vcbptr->opcode],
            APPC_FLIPI(vcbptr->primary_rc),APPC_FLIPL(vcbptr->secondary_rc),
            st.wHour,st.wMinute,st.wSecond);
    if ((vcbptr->opcode == AP_M_CONFIRM) &&
             (vcbptr->primary_rc == AP_ALLOCATION_ERROR))
    {
      SRTRACE(t,"Thread %d:%d (%s) MC_CONFIRM completed with ALLOCATION_ERROR\n",
              convptr->thread,convptr->conv,convptr->type);
    }
    else
    {
      TPEnded = TRUE;
      SRTRACE(t,"Thread %d:%d (%s) unexpected error on %s - set TPEnded\n",
              convptr->thread,convptr->conv,convptr->type,
              VerbName[vcbptr->opcode]);
    }
  }
  else
  {
    switch (vcbptr->opcode)
    {
      case AP_TP_STARTED:
        SRTRACE(t,"Thread %d:%d (%s) TP_Started completed at %d:%d:%d\n",
                convptr->thread,convptr->conv,convptr->type,
                st.wHour,st.wMinute,st.wSecond);
        memcpy(convptr->TPid,&(P_TPS(vcbptr)->tp_id),8);
        break;

      case AP_TP_ENDED:
        SRTRACE(t,"Thread %d:%d (%s) TP_Ended completed at %d:%d:%d\n",
                convptr->thread,convptr->conv,convptr->type,
                st.wHour,st.wMinute,st.wSecond);
        break;

      case AP_M_ALLOCATE:
        SRTRACE(t,"Thread %d:%d (%s) MC_Allocate completed at %d:%d:%d\n",
                convptr->thread,convptr->conv,convptr->type,
                st.wHour,st.wMinute,st.wSecond);
        convptr->Convid = P_M_ALC(vcbptr)->conv_id;
        break;

      case AP_M_SEND_DATA:
//      SRTRACE(t,"Thread %d:%d (%s) MC_Send_Data completed at %d:%d:%d\n",
//              convptr->thread,convptr->conv,convptr->type,
//              st.wHour,st.wMinute,st.wSecond);
        break;

      case AP_M_DEALLOCATE:
SRTRACE(t,"Thread %d:%d (%s) MC_Deallocate completed at %d:%d:%d: send count %d\n",
                convptr->thread,convptr->conv,convptr->type,
                st.wHour,st.wMinute,st.wSecond,
                convptr->SendCount);
        convptr->Convid = 0;
        break;

      case AP_M_CONFIRM:
SRTRACE(t,"Thread %d:%d (%s) MC_Confirm completed at %d:%d:%d: send count %d\n",
                convptr->thread,convptr->conv,convptr->type,
                st.wHour,st.wMinute,st.wSecond,
                convptr->SendCount);
        break;

      default:
        SRTRACE(t,"Thread %d:%d (%s) UNKNOWN opcode - set TPEnded\n",
                convptr->thread,convptr->conv,convptr->type);
        TPEnded = TRUE;
        DebugBreak();
        break;
    }
  }
  SRTRFLUSH()
  return(TPEnded);
}

/*****************************************************************************/
/* ReadConfig - Reads config info from MSEND.CFG and allocates buffer for    */
/*              sending                                                      */
/*****************************************************************************/
void ReadConfig()
{
  char buffer[200];

  if (!ReadString("TraceFile",TraceFile,60))
  {
    strcpy(TraceFile,"MSEND.TRC");
  }

  strcpy(TPName,"MSEND");
  PadString(TPName,64);
  CONV_A_TO_E(TPName,64);

  if (!ReadString("RemoteTPName",RemoteTPName,64))
  {
    strcpy(RemoteTPName,"MRCVTP");
  }
  PadString(RemoteTPName,64);
  CONV_A_TO_E(RemoteTPName,64);

  if (!ReadString("LocalLUAlias",LocalLUAlias,8))
  {
    strcpy(LocalLUAlias,"SENDLU");
  }
  PadString(LocalLUAlias,8);

  if (!ReadString("RemoteLUAlias",RemoteLUAlias,8))
  {
    strcpy(RemoteLUAlias,"RECVLU");
  }
  PadString(RemoteLUAlias,8);

  if (!ReadString("ModeName",ModeName,8))
  {
    strcpy(ModeName,"#INTER");
  }
  PadString(ModeName,8);
  CONV_A_TO_E(ModeName,8);

  NumSends=8;
  if (ReadString("NumSends",buffer,200))
  {
    NumSends=atoi(buffer);
  }

  ConfirmEvery=2;
  if (ReadString("ConfirmEvery",buffer,200))
  {
    ConfirmEvery=atoi(buffer);
  }

  SendSize=256;
  if (ReadString("SendSize",buffer,200))
  {
    SendSize=atoi(buffer);
  }

  NumSendConvs = 4;
  if (ReadString("NumSendConvs",buffer,200))
  {
    NumSendConvs=atoi(buffer);
  }

}

/*****************************************************************************/
/* CONV_A_TO_E - ASCII to EBCDIC conversion routine                          */
/*****************************************************************************/
void CONV_A_TO_E(char * string,int length)
{
  memset(cnvtptr,0,sizeof(cnvt));

  cnvt.opcode       = SV_CONVERT;
  cnvt.direction    = SV_ASCII_TO_EBCDIC;
  cnvt.char_set     = SV_AE;

  cnvt.len          = length;
  cnvt.source       = string;
  cnvt.target       = string;

  ACSSVC_C((long)(char *) (cnvtptr));
}

/*****************************************************************************/
/* CONV_E_TO_A - EBCDIC to ASCII conversion routine                          */
/*****************************************************************************/
void CONV_E_TO_A(char * string,int length)
{
  memset(cnvtptr,0,sizeof(cnvt));

  cnvt.opcode       = SV_CONVERT;
  cnvt.direction    = SV_EBCDIC_TO_ASCII;
  cnvt.char_set     = SV_AE;
  cnvt.len          = length;
  cnvt.source       = string;
  cnvt.target       = string;

  ACSSVC_C((long)(char *) (cnvtptr));
}

/*****************************************************************************/
/* GenerateData    - Fill in data buffer                                     */
/*****************************************************************************/
void GenerateData(CONVCB * convptr)
{
  int i;
  int div;
  int rem;
  char * dptr;

  dptr = convptr->DataPtr;
  div = convptr->SendSize / 5;
  rem = convptr->SendSize % 5;

  for (; div--;)
  {
    for (i=4; i--; *dptr++ = datach);
    *dptr++ = '.';
  }
  for (; rem--; *dptr++ = datach);

  EnterCriticalSection(&runsem);
  datach = (datach=='Z' ? 'A' : datach + 1);
  LeaveCriticalSection(&runsem);
}

/*****************************************************************************/
/* ReadString - Get a line of text from the config file                      */
/*****************************************************************************/
int ReadString(char * lpValueName,char * lpData, int maxlen)
{
  char       buffer[200];
  char      *p = NULL;
  FILE      *h = NULL;
  BOOL       match = FALSE;
  BOOL       eof   = FALSE;
  int        rc = 0;
  int        ch = 0;
  int        i = 0;
  BOOL       gotdata = FALSE;
  char       separators[] = " =\t\n";

  GetModuleFileName( hInst, buffer, sizeof(buffer) );
  lstrcpy( buffer+lstrlen(buffer) - 4, ".CFG" );
  h = fopen( buffer, "r" );
  buffer[0] = '\0';

  if (h != NULL)
  {
    while ((!match) && (!eof))
    {
      /***********************************************************************/
      /* Use fgetc to read a line of text from the file                      */
      /***********************************************************************/
      for (i=0; (i<sizeof(buffer))     &&
                ((ch=getc(h)) != EOF)  &&
                ((char)ch != '\n');
                                   i++)
      {
        buffer[i] = (char)ch;
      }
      if ((char)ch == '\n')
      {
        buffer[i++] = (char)ch;
      }
      if (ch == EOF)
      {
        eof = TRUE;
      }
      else
      {
        /*********************************************************************/
        /* Compare the first token in the line read with the requested parm  */
        /*********************************************************************/
        if (!strcmpi(strupr(strtok(buffer, separators)), lpValueName))
        {
          match = TRUE;
          /*******************************************************************/
          /* Get a pointer to the second token (the value we want)           */
          /*******************************************************************/
          p = strtok(NULL, separators);

          /*******************************************************************/
          /* Copy the data IF there is some                                  */
          /*******************************************************************/
          if (p != NULL)
          {
            /*****************************************************************/
            /* Force a NULL after the second token                           */
            /*****************************************************************/
            strtok(NULL, separators);

            /*****************************************************************/
            /* Copy the data                                                 */
            /*****************************************************************/
            strncpy(lpData, p, maxlen);
            gotdata = TRUE;
          }
          else
          {
            gotdata = FALSE;
          }
        }
      }
    }

    if (gotdata)
    {
      rc = 1;
    }

    fclose(h);

  }

  return(rc);
}

/*****************************************************************************/
/* PadString - Remove terminating NULL  and pad on the right with spaces     */
/*****************************************************************************/
void PadString(char * string,int length)
{
  char * p;
  if ((p=memchr(string,'\0',length)) != NULL)
  {
    while (p < string+length)
    {
      *p++=' ';
    }
  }
}


MSEND.H

/* msend.h */
/* (C) COPYRIGHT DATA CONNECTION LIMITED 1994 */

/*****************************************************************************/
/* Change History                                                            */
/*                                                                           */
/*       11/01/94 DK  Created                                                */
/*       14/04/94 DK  Name table added for tracing                           */
/*****************************************************************************/

/*****************************************************************************/
/* static configuration constants                                            */
/*****************************************************************************/
#define MAX_SEND_PER_THREAD 64

/*****************************************************************************/
/* global critical section                                                   */
/*****************************************************************************/
CRITICAL_SECTION runsem;

/*****************************************************************************/
/* APPC verb control block and macros for manipulation of vcb                */
/*****************************************************************************/
#define CLEARVCB memset(vcbptr,0,sizeof(VCB));
#define P_TPS(X)    ((struct tp_started *)X)
#define P_TPE(X)    ((struct tp_ended *)X)

#define P_M_ALC(X)  ((struct mc_allocate *)X)
#define P_M_CNF(X)  ((struct mc_confirm *)X)
#define P_M_DAL(X)  ((struct mc_deallocate *)X)
#define P_M_SDD(X)  ((struct mc_send_data *)X)

typedef struct vcb
{
  union
  {
    struct appc_hdr             hdr;
    struct tp_started           tpstart;
    struct tp_ended             tpend;
    struct mc_allocate          allocate;
    struct mc_confirm           confirm;
    struct mc_deallocate        deallocate;
    struct mc_send_data         snddata;
  } U_VCB;
} VCB;

/*****************************************************************************/
/* structures for convert verb                                               */
/*****************************************************************************/
struct convert cnvt;
char * cnvtptr;

/*****************************************************************************/
/* conversation control block - one per conversation                         */
/*****************************************************************************/
typedef struct convcb
{
  char           TPid[8];
  char           type[8];
  unsigned long  Convid;
  HANDLE         async_corr;
  char *         DataPtr;
  unsigned short thread;
  unsigned short conv;
  unsigned short SendSize;
  unsigned short SendCount;
  unsigned short ConfirmCount;
  BOOL           TPEnded;
  BOOL           Deallocated;
  BOOL           Counted;
  HANDLE         hEvent;
  VCB            vcb;
} CONVCB;

/*****************************************************************************/
/* File Names and handle for trace file                                      */
/*****************************************************************************/
char FileName [60];
char TraceFile[60];
FILE *t = NULL;

/*****************************************************************************/
/* APPC Names from configuration file                                        */
/*****************************************************************************/
char TPName[64];
char LocalTPName[64];
char LocalLUAlias[8];
char RemoteLUAlias[8];
char ModeName[8];
char RemoteTPName[64];

/*****************************************************************************/
/* Global Variables from configuration file                                  */
/*****************************************************************************/
unsigned short NumSends;
unsigned short SendSize;
unsigned short NumSendConvs;
unsigned short ConfirmEvery;

/*****************************************************************************/
/* Global Variables                                                          */
/*****************************************************************************/
unsigned short NumRalcs;                /* number of receive_allocs issued   */
unsigned short ThreadCount;             /* total thread count                */
unsigned short SendThreads;             /* count of send threads             */
unsigned short SimSendConvs;            /* number of simultaneous send convs */
unsigned short SendConvs;               /* number of send convs completed    */

/*****************************************************************************/
/* Names of verbs for tracing                                                */
/*****************************************************************************/
char *VerbName[] =
{
  "UNKNOWN_VERB",                       /* 0x0000                            */
  "MC_ALLOCATE",                        /* 0x0001                            */
  "UNKNOWN_VERB",                       /* 0x0002                            */
  "MC_CONFIRM",                         /* 0x0003                            */
  "MC_CONFIRMED",                       /* 0x0004                            */
  "MC_DEALLOCATE",                      /* 0x0005                            */
  "MC_FLUSH",                           /* 0x0006                            */
  "MC_GET_ATTRIBUTES",                  /* 0x0007                            */
  "UNKNOWN_VERB",                       /* 0x0008                            */
  "UNKNOWN_VERB",                       /* 0x0009                            */
  "MC_PREPARE_TO_RECEIVE",              /* 0x000a                            */
  "MC_RECEIVE_AND_WAIT",                /* 0x000b                            */
  "MC_RECEIVE_IMMEDIATE",               /* 0x000c                            */
  "MC_RECEIVE_AND_POST",                /* 0x000d                            */
  "MC_REQUEST_TO_SEND",                 /* 0x000e                            */
  "MC_SEND_DATA",                       /* 0x000f                            */
  "MC_SEND_ERROR",                      /* 0x0010                            */
  "UNKNOWN_VERB",                       /* 0x0011                            */
  "MC_TEST_RTS",                        /* 0x0012                            */
  "TP_ENDED",                           /* 0x0013                            */
  "TP_STARTED",                         /* 0x0014                            */
  "UNKNOWN_VERB",                       /* 0x0015                            */
  "RECEIVE_ALLOCATE",                   /* 0x0016                            */
  "UNKNOWN_VERB",                       /* 0x0017                            */
  "MC_SEND_CONVERSATION"                /* 0x0018                            */
};

/*****************************************************************************/
/* Character for filling buffer                                              */
/*****************************************************************************/
char datach;

/*****************************************************************************/
/* Windows stuff                                                             */
/*****************************************************************************/
HWND hWndMain;
char title[40]={'\0'};
#define OUTPUTNUMBER sprintf(title,"thrd simS done  %d  %d  %d", \
                     ThreadCount,SimSendConvs,SendConvs);  \
                     SetWindowText(hWndMain,title);

/*****************************************************************************/
/* Function Prototypes                                                       */
/*****************************************************************************/
int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine, int nCmdShow);
BOOL InitializeWinMain(HINSTANCE hInstance);
LONG PASCAL TPWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
void InitializeMain(void);
BOOL IssueSendVerb(CONVCB * convptr);
BOOL ProcessReturns(CONVCB * convptr);
void ReadConfig(void);
void CONV_A_TO_E(char * string,int length);
void CONV_E_TO_A(char * string,int length);
int  ReadString(char * lpValueName, char FAR * lpData, int maxlen);
void PadString(char * string, int length);
void Build_TP_STARTED(CONVCB * convptr);
void Build_TP_ENDED(CONVCB * convptr);
void Build_MC_ALLOCATE(CONVCB * convptr);
void Build_MC_CONFIRM(CONVCB * convptr);
void Build_MC_DEALLOCATE(CONVCB * convptr);
void Build_MC_SEND_DATA(CONVCB * convptr);
void GenerateData(CONVCB * convptr);
DWORD WINAPI SendThread(DWORD k);


MSENDRCV.C

/* msendrcv.c */
/* (C) COPYRIGHT DATA CONNECTION LIMITED 1994 */

/*****************************************************************************/
/* Change History                                                            */
/*                                                                           */
/*       11/01/94 DK  Created                                                */
/*       15/03/94 DK  Thread priorities added and signalled event goes to    */
/*                    back of the list (for WaitForMultipleObjects)          */
/*       14/04/94 DK  OutputResults fixed to not use global variable and     */
/*                    so that output is done for received conversations only */
/*****************************************************************************/

/*****************************************************************************/
/* If you want internal tracing, #define SRTRC here                          */
/*****************************************************************************/
//#define SRTRC

/*****************************************************************************/
/*                                                                           */
/* ROUTINE : SEND and RECEIVE using event completion                         */
/*                                                                           */
/* FUNCTION: This file contains the routines for a multi-threaded routine    */
/*           which uses asynchronous APPC calls with event completion        */
/*           to send and receive data.                                       */
/*                                                                           */
/*           It requires another copy of itself on another machine to run    */
/*           or it can send and receive to itself.                           */
/*                                                                           */
/* INPUTS  : MSENDRCV.CFG (file) (documented below)                          */
/*                                                                           */
/* OUTPUTS : MSENDRCV.OUT                                                    */
/*           MSENDRCV.TRC                                                    */
/*                                                                           */
/*****************************************************************************/

/*****************************************************************************/
/* Operation:                                                                */
/*                                                                           */
/* This is a Windows NT application which runs in a minimized window.        */
/*                                                                           */
/* Thread structure:                                                         */
/*                                                                           */
/*   A receive_allocate thread                                               */
/*     This thread issues a receive_allocate and, when it completes, hands   */
/*     it to a receive thread.  It then waits for the receive thread to      */
/*     accept the conversation.  This thread uses event completion, but      */
/*     its operation is essentially synchronous.                             */
/*                                                                           */
/*   A variable number of send threads                                       */
/*     Each send thread processes a variable number of conversations and     */
/*     issues a WaitForMultipleObjects to wait for completion of any of its  */
/*     send operations.  Each conversation starts with TP_STARTED, followed  */
/*     by MC_ALLOCATE and MC_CONFIRM.  Then it issues MC_SEND_DATA verbs     */
/*     to send data.  MC_CONFIRMs are issued at configurable intervals.      */
/*     If a confirm fails, an attempt is made to restart the conversation    */
/*     after five seconds.                                                   */
/*                                                                           */
/*   A variable number of receive threads                                    */
/*     Each receive thread processes a variable number of conversations and  */
/*     issues a WaitForMultipleObjects call to wait for completion of any    */
/*     one of its receive operations or for a request for a new conversation */
/*     from the receive_allocate thread.  It sets a second event to tell the */
/*     receive_allocate thread that it has accepted the conversation.        */
/*     Each conversation issues MC_RECEIVE_AND_WAIT verbs to receive data.   */
/*     If confirmation is requested, an MC_CONFIRM verb is issued.           */
/*                                                                           */
/*   Note:  this program is compatible with the single-threaded versions of  */
/*   send and receive, which can be run for example on WIN16 clients.        */
/*                                                                           */
/*****************************************************************************/

/*****************************************************************************/
/* Configuration file:                                                       */
/*                                                                           */
/* The configuration file is called MSENDRCV.CFG and must reside in the      */
/* same directory as the program.  It contains the following, in any order.  */
/* If any parameter is omitted, the default is assumed.                      */
/*                                                                           */
/* ResultFile = <Name of file for results, default MSENDRCV.OUT>             */
/* TraceFile = <Name of file for tracing, default MSENDRCV.TRC>              */
/* LocalTPName = <Name used for receive_allocate, default MSENDRCVTP>        */
/* RemoteTPName = <Name used for allocate, default MSENDRCVTP>               */
/* LocalLUAlias = <Alias for local LU, default SENDLU>                       */
/* RemoteLUAlias = <Alias for remote LU, default RECVLU>                     */
/* ModeName = <Mode Name, default #INTER>                                    */
/*                                                                           */
/* NumRcvConvs = <Number of conversations to be received, default = 4>       */
/* NumRcvThreads = <Number of receive threads, default = 2>                  */
/* NumSendConvs = <Number of conversations to be received, default = 4>      */
/* NumSends = <number of SEND_DATA verbs per conversation, default = 8>      */
/* ConfirmEvery = <number of SEND_DATA verbs between CONFIRMs, default = 2>  */
/* SendSize = <number of bytes per SEND_DATA, default = 256>                 */
/*                                                                           */
/* The name used for TP_STARTED is fixed at MSENDRCV.                        */
/*                                                                           */
/* If NumRcvConvs is zero, the TP will keep issuing receive_allocates.       */
/* If NumSends is zero, the TP will never DEALLOCATE a conversation.         */
/* If ConfirmEvery is zero, the TP will not issue CONFIRM verbs except       */
/*    after the MC_ALLOCATE.                                                 */
/* If ConfirmEvery is non-zero, the TP issues a CONFIRM verbs ConfirmEvery   */
/*    sends.                                                                 */
/* If NumRcvConvs is non-zero and NumRcvThreads is greater than NumRcvConvs, */
/*    NumRcvThreads is reduced to NumRcvConvs.                               */
/* If NumRcvConvs is non-zero and NumRcvThreads*63 < NumRcvConvs,            */
/*    NumRcvThreads is increased to NumRcvConvs / 63 + 1.                    */
/*                                                                           */
/* Configuration constants (in msendrcv.h)                                   */
/*                                                                           */
/* #define MAX_RECEIVE_THREADS  Max number of receive threads          (64)  */
/* #define MAX_RCV_PER_THREAD   Max conversations per receive thread   (63)  */
/* #define MAX_SEND_PER_THREAD  Max conversations per send thread      (64)  */
/*                                                                           */
/* MAX_RCV_PER_THREAD and MAX_SEND_PER_THREAD are constrained by the limit   */
/* of 64 objects that can be waited for in WaitForMultipleObjects.           */
/*                                                                           */
/*****************************************************************************/

#include <windows.h>
HINSTANCE hInst;
BOOL verbs_started = FALSE;
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winappc.h>
#include <wincsv.h>
#include "msendrcv.h"

/*****************************************************************************/
/* Trace macros                                                              */
/*****************************************************************************/
#ifdef SRTRC
#define SRTROPEN() t = fopen(TraceFile,"w");
#define SRTRFLUSH() fflush(t);
#define SRTRCLOSE() fclose(t);
#define SRTRACE fprintf
#else
#define SRTROPEN()
#define SRTRFLUSH()
#define SRTRCLOSE()
#define SRTRACE 1 ? (void) 0 : fprintf
#endif

/*****************************************************************************/
/* WinMain - reads initialization info and controls message loop             */
/*           NT version                                                      */
/*****************************************************************************/
int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine, int nCmdShow)
{

  MSG msg;
  DWORD Tid;
  int i;
  DWORD NumConvs;
  HANDLE ThreadHandle;

  hInst = hInstance;

  InitializeMain();

  if (!InitializeWinMain(hInstance))
  {
    return (FALSE);
  }

  ReadConfig();
  SRTROPEN()

  /***************************************************************************/
  /* Create two events for each receive thread                               */
  /***************************************************************************/
  for (i = 0; i < NumRcvThreads; i++)
  {
    /*************************************************************************/
    /* Create an event for kicking the receive thread                        */
    /*************************************************************************/
    RcvThreadArray[i].convptr = NULL;
    RcvThreadArray[i].event1 = CreateEvent(NULL,FALSE,FALSE,NULL);
    RcvThreadArray[i].event2 = CreateEvent(NULL,FALSE,FALSE,NULL);
  }

  /***************************************************************************/
  /* Create receive threads                                                  */
  /***************************************************************************/
  for (i = 0; i < NumRcvThreads; i++)
  {
    SRTRACE(t,"Created event handles %p %p for receive thread %d\n",
            RcvThreadArray[i].event1, RcvThreadArray[i].event2,i);

    /*************************************************************************/
    /* Create the thread                                                     */
    /*************************************************************************/
    ThreadHandle = CreateThread(NULL,
                                16000,
                                (LPTHREAD_START_ROUTINE)ReceiveThread,
                                (void *)i,
                                0,
                                &Tid);
    if (ThreadHandle == NULL)
    {
      GetLastError();
      DebugBreak();
    }
    SetThreadPriority(ThreadHandle,THREAD_PRIORITY_LOWEST);
    SRTRACE(t,"Created receive thread %d with priority %d\n",
            i,GetThreadPriority(ThreadHandle));
    CloseHandle(ThreadHandle);
  }

  /***************************************************************************/
  /* Create receive_allocate thread                                          */
  /***************************************************************************/
  if (NumRcvThreads > 0)
  {
    ThreadHandle = CreateThread(NULL,
                                16000,
                                (LPTHREAD_START_ROUTINE)RcvAllocThread,
                                NULL,
                                0,
                                &Tid);
    if (ThreadHandle == NULL)
    {
      GetLastError();
      DebugBreak();
    }
    SetThreadPriority(ThreadHandle,THREAD_PRIORITY_NORMAL);
    SRTRACE(t,"Created receive allocate thread with priority %d\n",
            GetThreadPriority(ThreadHandle));
    CloseHandle(ThreadHandle);
  }

  /***************************************************************************/
  /* Create enough send threads to process conversations                     */
  /***************************************************************************/
  i = NumSendConvs;
  while (i > 0)
  {
    NumConvs = (i > 64) ? 64 : i;
    ThreadHandle = CreateThread(NULL,
                                16000,
                                (LPTHREAD_START_ROUTINE)SendThread,
                                (void *)NumConvs,
                                0,
                                &Tid);
    if (ThreadHandle == NULL)
    {
      GetLastError();
      DebugBreak();
    }
    SetThreadPriority(ThreadHandle,THREAD_PRIORITY_BELOW_NORMAL);
    SRTRACE(t,"Created send thread with %d conversations and priority %d\n",
            NumConvs,GetThreadPriority(ThreadHandle));
    CloseHandle(ThreadHandle);
    i -= NumConvs;
  }

  /***************************************************************************/
  /* Windows processing loop                                                 */
  /***************************************************************************/
  while(GetMessage(&msg,NULL,0,0))
  {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }

  WinAPPCCleanup();
  OutputResults();
  SRTRFLUSH()
  SRTRCLOSE()
  DeleteCriticalSection(&runsem);
  return msg.wParam;         /* save exit parameter for return               */

}

/*****************************************************************************/
/* RcvAllocThread - separate thread for receive_allocates                    */
/*****************************************************************************/
DWORD WINAPI RcvAllocThread()
{
  /***************************************************************************/
  /* Local variables                                                         */
  /***************************************************************************/
  CONVCB * convptr;
  struct appc_hdr * vcbptr;
  unsigned short ThreadNo;
  unsigned short NextReceive = 0;
  BOOL   RcvAllocEnded = FALSE;
  DWORD rc;
  SYSTEMTIME st;

  /***************************************************************************/
  /* Count threads within critical section                                   */
  /***************************************************************************/
  EnterCriticalSection(&runsem);
  ThreadCount++;
  ThreadNo = ThreadCount;
  LeaveCriticalSection(&runsem);

  SRTRACE(t,"Thread %d (ralc) Started\n",ThreadNo);

  /***************************************************************************/
  /* Loop round until the required number of conversations have been         */
  /* allocated                                                               */
  /***************************************************************************/
  while ((NumRcvConvs == 0) || (NumRalcs < NumRcvConvs))
  {
    /*************************************************************************/
    /* Make a conversation control block                                     */
    /*************************************************************************/
    convptr = malloc (sizeof(CONVCB));
    convptr->thread       = ThreadNo;
    convptr->conv         = 0;
    convptr->TPid[0]      = '\0';
    convptr->Convid       = 0;
    convptr->async_corr   = 0;
    convptr->SendCount    = 0;
    convptr->RcvCount     = 0;
    convptr->ConfirmCount = 0;
    convptr->TPEnded      = FALSE;
    convptr->Deallocated  = FALSE;
    convptr->DataPtr      = malloc(RcvSize);
    vcbptr = (struct appc_hdr *) &convptr->vcb;
    memset(vcbptr,0,sizeof(VCB));
    strcpy (convptr->type,"ralc");

    /*************************************************************************/
    /* Create an event                                                       */
    /*************************************************************************/
    convptr->hEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
    SRTRACE(t,"Thread %d (ralc) convptr %p\n",ThreadNo,convptr);
    SRTRACE(t,"Thread %d (ralc) event handle %p\n",ThreadNo,convptr->hEvent);

    /*************************************************************************/
    /* Build a receive_allocate                                              */
    /*************************************************************************/
    Build_RECEIVE_ALLOCATE (convptr);

    /*************************************************************************/
    /* Issue receive_allocate using event notification                       */
    /*************************************************************************/
    convptr->async_corr = WinAsyncAPPCEx(convptr->hEvent,
                                         (long)(char *)(vcbptr));
//  SRTRACE(t,"Thread %d (ralc) Receive_Allocate issued corr %p\n",
//          ThreadNo,convptr->async_corr);
    SRTRFLUSH()
    if (convptr->async_corr == 0)
    {
      SRTRACE(t,"Thread %d (ralc) WinAsync call %x failed zero corr %p\n",
              ThreadNo,vcbptr->opcode,convptr->async_corr);
      break;
    }
    else
    {
      /***********************************************************************/
      /* Wait for event completion                                           */
      /***********************************************************************/
      rc = WaitForSingleObject(convptr->hEvent,INFINITE);
      if (rc == WAIT_FAILED)
      {
        rc = GetLastError();
        SRTRACE(t,"Thread %d (ralc) wait for event_1 %p failed with rc %d\n",
                ThreadNo,convptr->hEvent,rc);
        break;
      }
      if (vcbptr->primary_rc != AP_OK)
      {
        SRTRACE(t,"Thread %d (ralc) WinAsync error: %s prc %4.4x src %8.8x\n",
                ThreadNo,VerbName[vcbptr->opcode],
                APPC_FLIPI(vcbptr->primary_rc),
                APPC_FLIPL(vcbptr->secondary_rc));
        break;
      }
      else
      {
        NumRalcs++;
        SRTRACE(t,"Thread %d (ralc) Receive_Allocate %d completed\n",
                ThreadNo,NumRalcs);

        /*********************************************************************/
        /* give it to a receive thread by kicking event 1                    */
        /*********************************************************************/
        RcvThreadArray[NextReceive].convptr = convptr;
//      SRTRACE(t,"Thread %d (ralc) convptr %p\n",ThreadNo,convptr);
//      SRTRACE(t,"Thread %d (ralc) kicking receive thread %d\n",
//              ThreadNo,NextReceive);
//      SRTRACE(t,"Thread %d (ralc) setting event_1 %p\n",ThreadNo,
//              RcvThreadArray[NextReceive].event1);
        rc = SetEvent (RcvThreadArray[NextReceive].event1);
        if (rc == FALSE)
        {
          rc = GetLastError();
          SRTRACE(t,"Thread %d (ralc) setting event_1 %p failed with rc %d\n",
                  ThreadNo,RcvThreadArray[NextReceive].event1,rc);
          break;
        }

        /*********************************************************************/
        /* wait for receive thread to release event 2                        */
        /*********************************************************************/
//      SRTRACE(t,"Thread %d (ralc) waiting for event_2 %d\n",ThreadNo,
//              RcvThreadArray[NextReceive].event2);
        rc = WaitForSingleObject(RcvThreadArray[NextReceive].event2,INFINITE);
        if (rc == WAIT_FAILED)
        {
          rc = GetLastError();
          SRTRACE(t,"Thread %d (ralc) wait for event_2 %p failed with rc %d\n",
                  ThreadNo,RcvThreadArray[NextReceive].event2,rc);
          break;
        }

        /*********************************************************************/
        /* ready for next receive allocate                                   */
        /*********************************************************************/
        NextReceive++;
        if (NextReceive == NumRcvThreads)
        {
          NextReceive = 0;
        }
      }
    }
  }

  /***************************************************************************/
  /* Kick receive threads for last time in case they are idle                */
  /* convptr is set to NULL to indicate this final kick                      */
  /***************************************************************************/
  SRTRACE(t,"Thread %d (ralc) last kicks\n",ThreadNo);
  for (NextReceive = 0; NextReceive < NumRcvThreads; NextReceive++)
  {
    RcvThreadArray[NextReceive].convptr = NULL;
    rc = SetEvent (RcvThreadArray[NextReceive].event1);
  }

  /***************************************************************************/
  /* Count threads within critical section                                   */
  /***************************************************************************/
  EnterCriticalSection(&runsem);
  ThreadCount--;
  GetLocalTime(&st);
  SRTRACE(t,"Thread %d (ralc) Exit at %d:%d:%d: ThreadCount %d\n",
          ThreadNo,st.wHour,st.wMinute,st.wSecond,ThreadCount);
  if (ThreadCount == 0)
  {
    PostMessage(hWndMain, WM_CLOSE, 0, 0);
  }
  LeaveCriticalSection(&runsem);
  return(0);
}

/*****************************************************************************/
/* ReceiveThread - service thread which processes multiple conversations     */
/*****************************************************************************/
DWORD WINAPI ReceiveThread(DWORD k)
{
  /***************************************************************************/
  /* Separate variables for each instance of this thread                     */
  /***************************************************************************/
  DWORD ObjIndex;
  DWORD i;
  DWORD j;
  DWORD rc;
  unsigned short NumConvs = 0;
  unsigned short RcvMax = 0;
  unsigned short ThreadNo;
  unsigned short RcvThreadNo;
  CONVCB * ConvptrArray [MAX_RCV_PER_THREAD+2];
  HANDLE EventArray [MAX_RCV_PER_THREAD+2];
  SYSTEMTIME st;

  EventArray[0] = RcvThreadArray[k].event1;
  ConvptrArray[0] = NULL;
  for (i = 1; i < MAX_RCV_PER_THREAD+2; i++)
  {
    EventArray[i] = NULL;
    ConvptrArray[i] = NULL;
  }

  /***************************************************************************/
  /* Count threads                                                           */
  /***************************************************************************/
  EnterCriticalSection(&runsem);
  ThreadCount++;
  RcvThreads++;
  ThreadNo = ThreadCount;
  RcvThreadNo = RcvThreads;
  OUTPUTNUMBER
  LeaveCriticalSection(&runsem);

  SRTRACE(t,"Thread %d (recv) Started\n",k);
  SRTRACE(t,"Thread %d (recv) has event handles %p %p\n",k,
          RcvThreadArray[k].event1,RcvThreadArray[k].event2);

  /***************************************************************************/
  /* Loop round until finished                                               */
  /***************************************************************************/
  while (TRUE)
  {
    /*************************************************************************/
    /* Wait for event completion                                             */
    /*************************************************************************/
//  SRTRACE(t,"Thread %d (recv) waiting for %d events to complete\n",
//          k,NumConvs+1);
    ObjIndex = WaitForMultipleObjects(NumConvs+1,EventArray,FALSE,INFINITE);
    if (ObjIndex == WAIT_FAILED)
    {
      rc = GetLastError();
      SRTRACE(t,"Thread %d (recv) wait for %d events failed with rc %d\n",
              k,NumConvs+1,rc);
      for (j = 0; j <= NumConvs; j++)
      {
        SRTRACE(t,"Thread %d (recv) event %d has handle %p\n",
                k,j,EventArray[j]);
      }
      break;
    }
    ObjIndex -= WAIT_OBJECT_0;
//  SRTRACE(t,"Thread %d (recv) event %d has completed\n",k,ObjIndex);
    if (ObjIndex == 0)
    {
      /***********************************************************************/
      /* final kick from receive_allocate thread - terminate if idle         */
      /***********************************************************************/
      if (RcvThreadArray[k].convptr == NULL)
      {
        SRTRACE(t,"Thread %d (recv) ralc thread terminating\n",k);
        SRTRACE(t,"Thread %d (recv) NumRalcs %d NumRcvConvs %d NumConvs %d\n",
                k,NumRalcs,NumRcvConvs,NumConvs);
        if (NumConvs == 0)
        {
          break;
        }
        else
        {
          continue;
        }
      }
      else
      {
        /*********************************************************************/
        /* new conversation - save convptr and release rcv_alloc thread      */
        /*********************************************************************/
        SRTRACE(t,"Thread %d (recv) new conversation\n",k);

        /*********************************************************************/
        /* select next entry in the convptr array as conversation index      */
        /*********************************************************************/
        NumConvs++;
        i = NumConvs;

        /*********************************************************************/
        /* set up convptr array                                              */
        /* save service thread number and slot number in convcb for tracing  */
        /* add event to event array                                          */
        /*********************************************************************/
        ConvptrArray[i] = RcvThreadArray[k].convptr;
//      SRTRACE(t,"Thread %d:%d (recv) using convptr %p\n",k,i,ConvptrArray[i]);
        ConvptrArray[i]->thread = (unsigned short)k;
        ConvptrArray[i]->conv   = (unsigned short)i;
        strcpy (ConvptrArray[i]->type,"recv");
        EventArray[i] = ConvptrArray[i]->hEvent;
        EnterCriticalSection(&runsem);
        SimRcvConvs++;
        OUTPUTNUMBER
        SRTRACE(t,"Thread %d:%d (recv) NumConvs %d SimRcvConvs %d\n",
                k,i,NumConvs,SimRcvConvs);
        LeaveCriticalSection(&runsem);

        /*********************************************************************/
        /* release receive_allocate thread                                   */
        /*********************************************************************/
//      SRTRACE(t,"Thread %d:%d (recv) setting event_2 %p\n",
//              k,i,RcvThreadArray[k].event2);
        rc = SetEvent (RcvThreadArray[k].event2);
        if (rc == FALSE)
        {
          rc = GetLastError();
          SRTRACE(t,"Thread %d:%d (recv) setting event_2 %p failed with rc %d\n",
                  k,i,RcvThreadArray[k].event2,rc);
          break;
        }
      }
    }
    else
    {
      /***********************************************************************/
      /* Object index locates existing conversation                          */
      /***********************************************************************/
      i = ObjIndex;
    }

    /*************************************************************************/
    /* Issue the next verb                                                   */
    /*************************************************************************/
//  SRTRACE(t,"Thread %d:%d (recv) issuing next verb\n",
//          k,ConvptrArray[i]->conv);
    ConvptrArray[i]->TPEnded = IssueRcvVerb(ConvptrArray[i]);
    if (ConvptrArray[i]->TPEnded)
    {
      /***********************************************************************/
      /* end of conversation                                                 */
      /***********************************************************************/
      SRTRACE(t,"Thread %d:%d (recv) conversation completed\n",
              k,ConvptrArray[i]->conv);
      NumConvs--;
      EnterCriticalSection(&runsem);
      SimRcvConvs--;
      RcvConvs++;
      OUTPUTNUMBER
      SRTRACE(t,"Thread %d:%d (recv) NumConvs %d SimRcvConvs %d RcvConvs %d\n",
              k,ConvptrArray[i]->conv,NumConvs,SimRcvConvs,RcvConvs);
      SRTRACE(t,"Thread %d:%d (recv) NumRalcs %d NumRcvConvs %d NumConvs %d\n",
              k,ConvptrArray[i]->conv,NumRalcs,NumRcvConvs,NumConvs);
      LeaveCriticalSection(&runsem);

      /***********************************************************************/
      /* free resources                                                      */
      /***********************************************************************/
      SRTRACE(t,"Thread %d:%d (recv) clearing entry %d\n",
              k,ConvptrArray[i]->conv,i);
      CloseHandle(ConvptrArray[i]->hEvent);

free (ConvptrArray[i]->DataPtr);
      free (ConvptrArray[i]);
      ConvptrArray[i] = NULL;
      EventArray[i]   = NULL;

      /***********************************************************************/
      /* if the required number of conversations have been allocated and     */
      /* this thread has just finished its last conversation, end loop       */
      /***********************************************************************/
      if ((NumRcvConvs > 0) && (NumRalcs == NumRcvConvs) && (NumConvs == 0))
      {
        break;
      }

      /***********************************************************************/
      /* the event array cannot have holes in it, so shuffle up the          */
      /* pointers and events                                                 */
      /* note that this means convptr->conv no longer matches i              */
      /* which is why tracing uses ConvptrArray[i]->conv                     */
      /***********************************************************************/
      for (j = i; j <= NumConvs; j++)
      {
        ConvptrArray [j] = ConvptrArray [j+1];
        EventArray [j] = EventArray [j+1];
      }
      ConvptrArray [NumConvs+1] = NULL;
      EventArray [NumConvs+1] = NULL;
    }
    else
    {
      /***********************************************************************/
      /* to stop one conversation getting an unfair share of the time,       */
      /* move this conversation to the end and shuffle up the pointers.      */
      /* note that this means convptr->conv no longer matches i              */
      /* which is why tracing uses ConvptrArray[i]->conv                     */
      /***********************************************************************/
      ConvptrArray [NumConvs+1] = ConvptrArray [i];
      EventArray [NumConvs+1] = EventArray [i];
      for (j = i; j <= NumConvs; j++)
      {
        ConvptrArray [j] = ConvptrArray [j+1];
        EventArray [j] = EventArray [j+1];
      }
      ConvptrArray [NumConvs+1] = NULL;
      EventArray [NumConvs+1] = NULL;
    }
  }

  /***************************************************************************/
  /* Count threads within critical section                                   */
  /***************************************************************************/
  EnterCriticalSection(&runsem);
  ThreadCount--;
  RcvThreads--;
  GetLocalTime(&st);
  SRTRACE(t,"Thread %d (recv) Exit at %d:%d:%d: ThreadCount %d\n",
          k,st.wHour,st.wMinute,st.wSecond,ThreadCount);
  OUTPUTNUMBER
  if (ThreadCount == 0)
  {
    PostMessage(hWndMain, WM_CLOSE, 0, 0);
  }
  LeaveCriticalSection(&runsem);
  return(0);
}

/*****************************************************************************/
/* SendThread - thread which processes multiple conversations                */
/*****************************************************************************/
DWORD WINAPI SendThread(DWORD NumConvs)
{
  /***************************************************************************/
  /* Separate variables for each instance of this thread                     */
  /***************************************************************************/
  CONVCB * convptr;
  struct appc_hdr * vcbptr;
  unsigned short ThreadNo;
  unsigned short SendThreadNo;
  DWORD ObjIndex;
  DWORD i;
  DWORD j;
  DWORD rc;
  CONVCB * ConvptrArray [MAX_SEND_PER_THREAD+1];
  HANDLE EventArray [MAX_SEND_PER_THREAD+1];
  SYSTEMTIME st;

  for (i = 0; i < MAX_SEND_PER_THREAD+1; i++)
  {
    ConvptrArray[i] = NULL;
    EventArray[i] = NULL;
  }

  /***************************************************************************/
  /* Count threads within critical section                                   */
  /***************************************************************************/
  EnterCriticalSection(&runsem);
  ThreadCount++;
  SendThreads++;
  ThreadNo = ThreadCount;
  SendThreadNo = SendThreads;
  OUTPUTNUMBER
  LeaveCriticalSection(&runsem);

  for (i = 0; i < NumConvs; i++)
  {
    /*************************************************************************/
    /* Make a conversation control block                                     */
    /*************************************************************************/
    convptr = malloc (sizeof(CONVCB));
    convptr->thread       = SendThreadNo;
    convptr->conv         = (unsigned short)i;
    convptr->async_corr   = 0;
    convptr->TPid[0]      = '\0';
    convptr->Convid       = 0;
    convptr->SendCount    = 0;
    convptr->ConfirmCount = 0;
    convptr->TPEnded      = FALSE;
    convptr->Deallocated  = FALSE;
    convptr->Counted      = FALSE;
    convptr->SendSize     = SendSize;
    convptr->DataPtr      = malloc(convptr->SendSize);
    vcbptr = (struct appc_hdr *) &convptr->vcb;
    memset(vcbptr,0,sizeof(VCB));
    strcpy (convptr->type,"send");
//  SRTRACE(t,"Thread %d:%d (send) has convptr %p\n",
//          SendThreadNo,i,convptr);

    /*************************************************************************/
    /* Create an event                                                       */
    /*************************************************************************/
    convptr->hEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
    SRTRACE(t,"Thread %d:%d (send) has event handle %p\n",
            SendThreadNo,i,convptr->hEvent);

    /*************************************************************************/
    /* Add to arrays                                                         */
    /*************************************************************************/
    ConvptrArray[i] = convptr;
    EventArray[i]   = convptr->hEvent;

    /*************************************************************************/
    /* Build a tp_started                                                    */
    /*************************************************************************/
    Build_TP_STARTED(convptr);
    SRTRFLUSH()

    /*************************************************************************/
    /*PERF* Don't re-generate data each time we issue a send!                */
    /*************************************************************************/
    GenerateData(convptr);

    /*************************************************************************/
    /* Issue tp_started                                                      */
    /*************************************************************************/
    convptr->async_corr = WinAsyncAPPCEx(convptr->hEvent,
                                         (long)(char *)(vcbptr));
//  SRTRACE(t,"Thread %d:%d (send) tp_started issued corr %p\n",
//          SendThreadNo,i,convptr->async_corr);
    SRTRFLUSH()
    if (convptr->async_corr == 0)
    {
      SRTRACE(t,"Thread %d:%d (send) WinAsync call %x failed corr %p\n",
              SendThreadNo,i,vcbptr->opcode,convptr->async_corr);
      convptr->TPEnded = TRUE;
    }
  }

  /***************************************************************************/
  /* Loop round until finished                                               */
  /***************************************************************************/
  while (TRUE)
  {
    /*************************************************************************/
    /* Wait for event completion                                             */
    /*************************************************************************/
//  SRTRACE(t,"Thread %d (send) waiting for %d events to complete\n",
//          SendThreadNo,NumConvs);
    ObjIndex = WaitForMultipleObjects(NumConvs,EventArray,FALSE,INFINITE);
    if (ObjIndex == WAIT_FAILED)
    {
      rc = GetLastError();
      SRTRACE(t,"Thread %d (send) wait for %d events failed with rc %d\n",
              SendThreadNo,NumConvs,rc);
      for (j = 0; j < NumConvs; j++)
      {
        SRTRACE(t,"Thread %d (send) event %d has handle %p\n",
                SendThreadNo,j,EventArray[j]);
      }
      break;
    }

    /*************************************************************************/
    /* Get index to conversation array                                       */
    /*************************************************************************/
    i = ObjIndex - WAIT_OBJECT_0;
//  SRTRACE(t,"Thread %d:%d (send) event %d has completed\n",
//          SendThreadNo,ConvptrArray[i]->conv,i);

    /*************************************************************************/
    /* Issue the next verb                                                   */
    /*************************************************************************/
//  SRTRACE(t,"Thread %d:%d (send) issuing next send verb\n",
//          SendThreadNo,ConvptrArray[i]->conv);
    ConvptrArray[i]->TPEnded = IssueSendVerb(ConvptrArray[i]);

    if (ConvptrArray[i]->TPEnded)
    {
      /***********************************************************************/
      /* end of conversation                                                 */
      /***********************************************************************/
      NumConvs--;
      SRTRACE(t,"Thread %d:%d (send) conversation completed: NumConvs %d\n",
              SendThreadNo,ConvptrArray[i]->conv,NumConvs);

      /***********************************************************************/
      /* free resources                                                      */
      /***********************************************************************/
      CloseHandle(ConvptrArray[i]->hEvent);
      free (ConvptrArray[i]->DataPtr);
      free (ConvptrArray[i]);
      ConvptrArray[i] = NULL;
      EventArray[i]   = NULL;

      /***********************************************************************/
      /* if all conversations have completed, end loop                       */
      /***********************************************************************/
      if (NumConvs == 0)
      {
        break;
      }
      /***********************************************************************/
      /* the event array cannot have holes in it, so shuffle up the          */
      /* pointers and events                                                 */
      /* note that this means convptr->conv no longer matches i              */
      /* which is why tracing uses ConvptrArray[i]->conv                     */
      /***********************************************************************/
      for (j = i; j < MAX_SEND_PER_THREAD - 1; j++)
      {
        ConvptrArray [j] = ConvptrArray [j+1];
        EventArray [j] = EventArray [j+1];
      }
      ConvptrArray [MAX_SEND_PER_THREAD-1] = NULL;
      EventArray [MAX_SEND_PER_THREAD-1] = NULL;
    }
    else
    {
      /***********************************************************************/
      /* to stop one conversation getting an unfair share of the time,       */
      /* move this conversation to the end and shuffle up the pointers.      */
      /* note that this means convptr->conv no longer matches i              */
      /* which is why tracing uses ConvptrArray[i]->conv                     */
      /***********************************************************************/
      ConvptrArray [NumConvs] = ConvptrArray [i];
      EventArray [NumConvs] = EventArray [i];
      for (j = i; j < NumConvs; j++)
      {
        ConvptrArray [j] = ConvptrArray [j+1];
        EventArray [j] = EventArray [j+1];
      }
      ConvptrArray [NumConvs] = NULL;
      EventArray [NumConvs] = NULL;
    }
  }

  /***************************************************************************/
  /* Count threads within critical section                                   */
  /***************************************************************************/
  EnterCriticalSection(&runsem);
  ThreadCount--;
  SendThreads--;
  GetLocalTime(&st);
  SRTRACE(t,"Thread %d (send) Exit at %d:%d:%d: ThreadCount %d\n",
          SendThreadNo,st.wHour,st.wMinute,st.wSecond,ThreadCount);
  OUTPUTNUMBER
  if (ThreadCount == 0)
  {
    PostMessage(hWndMain, WM_CLOSE, 0, 0);
  }
  LeaveCriticalSection(&runsem);
  return(0);
}

/*****************************************************************************/
/* InitializeWinMain - does the windows bits of initialisation               */
/*****************************************************************************/
BOOL InitializeWinMain(HINSTANCE hInstance)
{
  WAPPCDATA APPCData;
  WNDCLASS class;
  #define WinAPPCVERSION  0x0001

  /***************************************************************************/
  /* Startup WinAPPC                                                         */
  /***************************************************************************/
  if (WinAPPCStartup(WinAPPCVERSION,&APPCData))
  {
    return (FALSE);
  }

  /***************************************************************************/
  /* Register Window Class for our icon                                      */
  /***************************************************************************/

  class.style = 0;
  class.lpfnWndProc   = (WNDPROC)TPWndProc;
  class.cbClsExtra    = (DWORD)0;
  class.cbWndExtra    = (DWORD)0;
  class.hInstance     = hInstance;
  class.hIcon         = LoadIcon(hInstance,"MainIcon");
  class.hCursor       = LoadCursor(NULL, IDC_ARROW);
  class.hbrBackground = GetStockObject(WHITE_BRUSH);
  class.lpszMenuName  = (LPSTR) NULL;
  class.lpszClassName = (LPSTR) "MSENDRCV\0";

  if (!RegisterClass(&class))
  {
    return (FALSE);
  }

  /***************************************************************************/
  /* Create the window                                                       */
  /***************************************************************************/
  sprintf(title,"APPC Send/Receive TP\0");

  if ((hWndMain = CreateWindow("MSENDRCV\0",      /* window class            */
      title,                                      /* window name             */
      WS_MINIMIZE|WS_OVERLAPPEDWINDOW,            /* window style            */
      0,                                          /* x position              */
      0,                                          /* y position              */
      10,                                         /* width                   */
      10,                                         /* height                  */
      NULL,                                       /* parent handle           */
      NULL,                                       /* menu or child ID        */
      hInstance,                                  /* instance                */
      NULL))                                      /* additional info         */
      == NULL)
  {
    return (FALSE);
  }

  ShowWindow(hWndMain, SW_MINIMIZE);

  return(TRUE);

}

/*****************************************************************************/
/* Window proc for the iconised window                                       */
/*****************************************************************************/
LONG PASCAL TPWndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
{
  switch (message)
  {
    case WM_CREATE:
      break;

    case WM_QUERYOPEN:
      /***********************************************************************/
      /* Prevent the window being opened                                     */
      /***********************************************************************/
      break;

    case WM_CLOSE:
      return DefWindowProc(hWnd, message, wParam, lParam);
      break;

    case WM_DESTROY:
      PostQuitMessage(0);
      break;

    default:
      return DefWindowProc(hWnd, message, wParam, lParam);
      break;
  }
  return 0l;
}

/*****************************************************************************/
/* InitializeMain - blanks out variables not set in ReadConfig               */
/*****************************************************************************/
void InitializeMain()
{
  RcvThreads  = 0;
  SendThreads = 0;
  ThreadCount = 0;
  NumResults  = 0;
  cnvtptr     = (char *)&cnvt;

  datach      = 'A';

  InitializeCriticalSection(&runsem);
}

/*****************************************************************************/
/* IssueSendVerb - looks at the verb which has just completed and does the   */
/*                 next one                                                  */
/*****************************************************************************/
BOOL IssueSendVerb(CONVCB * convptr)
{
  BOOL TPEnded;
  struct appc_hdr * vcbptr;

//SRTRACE(t,"Thread %d (send) IssueSendVerb\n",convptr->thread);
  TPEnded = FALSE;
  vcbptr = (struct appc_hdr *) &convptr->vcb;
  if (vcbptr->opcode != 0x0000)
  {
    TPEnded = ProcessReturns(convptr);
  }
  if (!TPEnded)
  {
    switch (vcbptr->opcode)
    {
      case 0x0000:
        Build_TP_STARTED(convptr);

        /*********************************************************************/
        /*PERF* Don't re-generate data each time we issue a send!            */
        /*********************************************************************/
        GenerateData(convptr);
        break;

      case AP_TP_STARTED:
        Build_MC_ALLOCATE(convptr);
        break;

      case AP_M_ALLOCATE:
        if (vcbptr->primary_rc == AP_OK)
        {
          /*******************************************************************/
          /* confirm before the first send to find out if anyone there       */
          /*******************************************************************/
          Build_MC_CONFIRM(convptr);
        }
        else
        {
          /*******************************************************************/
          /* allocate timed out or connection is for incoming call           */
          /* if there are no conversations active, try again in 5 seconds    */
          /* otherwise retry immediately - someone must be out there         */
          /*******************************************************************/
          if (SimSendConvs == 0)
          {
            SRTRACE(t,"Thread %d:%d (send) wait 5 seconds before retry\n",
                    convptr->thread,convptr->conv);
            Sleep (5000);
          }
          SRTRACE(t,"Thread %d:%d (send) retrying\n",
                  convptr->thread,convptr->conv);
          Build_MC_ALLOCATE(convptr);
        }
        break;

      case AP_M_SEND_DATA:
        convptr->SendCount++;
        convptr->ConfirmCount++;
        if ((NumSends != 0) && (convptr->SendCount == NumSends))
        {
          /*******************************************************************/
          /* all sends done - deallocate                                     */
          /*******************************************************************/
          Build_MC_DEALLOCATE(convptr);
        }
        else if ((ConfirmEvery != 0) &&
                 (convptr->ConfirmCount == ConfirmEvery))
        {
          /*******************************************************************/
          /* time to confirm                                                 */
          /*******************************************************************/
          Build_MC_CONFIRM(convptr);
        }
        else
        {
          /*******************************************************************/
          /* just send more data                                             */
          /*******************************************************************/
          Build_MC_SEND_DATA(convptr);
        }
        break;

      case AP_M_CONFIRM:
        if (vcbptr->primary_rc == AP_OK)
        {
          if (convptr->SendCount == 0)
          {
            convptr->Counted = TRUE;
            EnterCriticalSection(&runsem);
            SimSendConvs++;
            OUTPUTNUMBER
            SRTRACE(t,"Thread %d (send) SimSendConvs %d\n",
                    convptr->thread,SimSendConvs);
            LeaveCriticalSection(&runsem);
          }
          convptr->ConfirmCount=0;
          Build_MC_SEND_DATA(convptr);
        }
        else
        {
          /*******************************************************************/
          /* if there are no conversations active, try again in 5 seconds    */
          /* otherwise retry immediately - someone must be out there         */
          /*******************************************************************/
          if (SimSendConvs == 0)
          {
            SRTRACE(t,"Thread %d:%d (send) wait 5 seconds before retry\n",
                    convptr->thread,convptr->conv);
            Sleep (5000);
          }
          SRTRACE(t,"Thread %d:%d (send) retrying\n",
                  convptr->thread,convptr->conv);
          Build_MC_ALLOCATE(convptr);
        }
        break;

      case AP_M_DEALLOCATE:
        Build_TP_ENDED(convptr);
        break;

      case AP_TP_ENDED:
        /*********************************************************************/
        /* quit                                                              */
        /*********************************************************************/
        TPEnded = TRUE;
        break;

      default:
        /*********************************************************************/
        /* What is this verb then ??                                         */
        /*********************************************************************/
        TPEnded = TRUE;
        DebugBreak();
        break;

    } /* Op-code switch */

  }

  /***************************************************************************/
  /* If send conversation has finished and was counted, count it down        */
  /***************************************************************************/
  if (TPEnded && convptr->Counted)
  {
    EnterCriticalSection(&runsem);
    SimSendConvs--;
    OUTPUTNUMBER
    SRTRACE(t,"Thread %d (send) SimSendConvs %d\n",
            convptr->thread,SimSendConvs);
    LeaveCriticalSection(&runsem);
  }

  /***************************************************************************/
  /* Now go ahead and issue the verb, if we're not finished                  */
  /***************************************************************************/
  if (!TPEnded)
  {
    convptr->async_corr = WinAsyncAPPCEx(convptr->hEvent,
                                         (long)(char *)(vcbptr));
    if (convptr->async_corr == 0)
      {
        SRTRACE(t,"Thread %d (send) WinAsync call %x failed corr %p\n",
                convptr->thread,vcbptr->opcode,
                convptr->async_corr);
        convptr->TPEnded = TRUE;
      }
  }

  SRTRFLUSH()
  return(TPEnded);

} /* Issue send verb */

/*****************************************************************************/
/* IssueRcvVerb - looks at the verb which has just completed and does the    */
/*                 next one                                                  */
/*****************************************************************************/
BOOL IssueRcvVerb(CONVCB * convptr)
{
  BOOL TPEnded;
  struct appc_hdr * vcbptr;

//SRTRACE(t,"Thread %d:%d (recv) IssueRcvVerb\n",
//        convptr->thread,convptr->conv);
  TPEnded = FALSE;
  vcbptr = (struct appc_hdr *) &convptr->vcb;
  if (vcbptr->opcode != 0x0000)
  {
    TPEnded = ProcessReturns(convptr);
  }
  if (!TPEnded)
  {
    switch (vcbptr->opcode)
    {
      case 0x0000:
        Build_RECEIVE_ALLOCATE(convptr);
        break;

      case AP_RECEIVE_ALLOCATE:
        StartConversation(convptr);
        Build_MC_RECEIVE_AND_WAIT(convptr);
        break;

      case AP_M_RECEIVE_AND_WAIT:
        if ((P_M_RAW(vcbptr)->what_rcvd == AP_DATA_COMPLETE) ||
            (P_M_RAW(vcbptr)->what_rcvd == AP_DATA_COMPLETE_CONFIRM) ||
            (P_M_RAW(vcbptr)->what_rcvd == AP_DATA_COMPLETE_CONFIRM_DEALL))
        {
          convptr->RcvCount++;
        }
        if ((P_M_RAW(vcbptr)->primary_rc == AP_DEALLOC_NORMAL))
        {
          /*******************************************************************/
          /* Issue TP_ENDED every time conversation deallocated              */
          /*******************************************************************/
          EndConversation(convptr);
          Build_TP_ENDED(convptr);
        }
        else if ((P_M_RAW(vcbptr)->what_rcvd == AP_CONFIRM_WHAT_RECEIVED) ||
                 (P_M_RAW(vcbptr)->what_rcvd == AP_DATA_COMPLETE_CONFIRM))
        {
          Build_MC_CONFIRMED(convptr);
          convptr->Deallocated = FALSE;
        }
        else if ((P_M_RAW(vcbptr)->what_rcvd == AP_CONFIRM_DEALLOCATE) ||
                 (P_M_RAW(vcbptr)->what_rcvd == AP_DATA_COMPLETE_CONFIRM_DEALL))
        {
          Build_MC_CONFIRMED(convptr);
          convptr->Deallocated = TRUE;
        }
        else
        {
          Build_MC_RECEIVE_AND_WAIT(convptr);
        }
        break;

      case AP_M_CONFIRMED:
        if (convptr->Deallocated)
        {
          /*******************************************************************/
          /* Issue TP_ENDED every time conversation deallocated              */
          /*******************************************************************/
          EndConversation(convptr);
          Build_TP_ENDED(convptr);
        }
        else
        {
          Build_MC_RECEIVE_AND_WAIT(convptr);
        }
        break;

      case AP_TP_ENDED:
        /*********************************************************************/
        /* set TPEnded                                                       */
        /*********************************************************************/
        TPEnded = TRUE;
        break;

      default:
        /*********************************************************************/
        /* What is this verb then ??                                         */
        /*********************************************************************/
        TPEnded = TRUE;
        DebugBreak();
        break;

    } /* Op-code switch */
    SRTRFLUSH()

  }

  /***************************************************************************/
  /* Now go ahead and issue the verb, if we're not finished                  */
  /***************************************************************************/
  if (!TPEnded)
  {
    convptr->async_corr = WinAsyncAPPCEx(convptr->hEvent,
                          (long)(char *)(vcbptr));
    if (convptr->async_corr == 0)
    {
      SRTRACE(t,"Thread %d:%d (recv) WinAsync call %x failed corr %p\n",
              convptr->thread,convptr->conv,
              vcbptr->opcode,convptr->async_corr);
      convptr->TPEnded = TRUE;
    }
  }

  SRTRFLUSH()
  return(TPEnded);

} /* Issue receive verb */

/*****************************************************************************/
/* Build routines to build all required verbs                                */
/*****************************************************************************/

void Build_TP_STARTED(CONVCB * convptr)
{
  TP_STARTED * vcbptr;
  SRTRACE(t,"Thread %d:%d (%s) Build_TP_Started\n",
          convptr->thread,convptr->conv,convptr->type);
  vcbptr = (TP_STARTED *) &(convptr->vcb);

  CLEARVCB

  vcbptr->opcode = AP_TP_STARTED;
  memcpy(&(vcbptr->lu_alias), LocalLUAlias, 8);
  memcpy(&(vcbptr->tp_name), TPName, 64);
}

void Build_TP_ENDED(CONVCB * convptr)
{
  TP_ENDED * vcbptr;
  SRTRACE(t,"Thread %d:%d (%s) Build_TP_Ended\n",
          convptr->thread,convptr->conv,convptr->type);
  vcbptr = (TP_ENDED *) &(convptr->vcb);

  CLEARVCB

  vcbptr->opcode = AP_TP_ENDED;
  memcpy(&(vcbptr->tp_id), convptr->TPid, 8);
  vcbptr->type = AP_SOFT;
}

void Build_RECEIVE_ALLOCATE(CONVCB * convptr)
{
  RECEIVE_ALLOCATE * vcbptr;
  SRTRACE(t,"Thread %d:%d (%s) Build_Receive_Allocate\n",
          convptr->thread,convptr->conv,convptr->type);
  vcbptr = (RECEIVE_ALLOCATE *) &(convptr->vcb);

  CLEARVCB

  vcbptr->opcode = AP_RECEIVE_ALLOCATE;
  memcpy(vcbptr->tp_name, LocalTPName, 64);
}

void Build_MC_ALLOCATE(CONVCB * convptr)
{
  MC_ALLOCATE * vcbptr;
  SRTRACE(t,"Thread %d:%d (%s) Build_MC_Allocate\n",
          convptr->thread,convptr->conv,convptr->type);
  vcbptr = (MC_ALLOCATE *) &(convptr->vcb);

  CLEARVCB

  vcbptr->opcode = AP_M_ALLOCATE;
  vcbptr->opext = AP_MAPPED_CONVERSATION;
  memcpy(vcbptr->tp_id,convptr->TPid, 8);
  vcbptr->sync_level = AP_CONFIRM_SYNC_LEVEL;
  vcbptr->rtn_ctl = AP_WHEN_SESSION_ALLOCATED;
  memcpy(vcbptr->plu_alias, RemoteLUAlias, 8);
  memcpy(vcbptr->mode_name, ModeName, 8);
  memcpy(vcbptr->tp_name, RemoteTPName, 64);
  vcbptr->security = AP_NONE;
}

void Build_MC_CONFIRM(CONVCB * convptr)
{
  MC_CONFIRM * vcbptr;
//SRTRACE(t,"Thread %d:%d (%s) Build_MC_Confirm\n",
//        convptr->thread,convptr->conv,convptr->type);
  vcbptr = (MC_CONFIRM *) &(convptr->vcb);

  CLEARVCB

  vcbptr->opcode = AP_M_CONFIRM;
  vcbptr->opext = AP_MAPPED_CONVERSATION;
  memcpy(vcbptr->tp_id,convptr->TPid, 8);
  vcbptr->conv_id = convptr->Convid;
}

void Build_MC_CONFIRMED(CONVCB * convptr)
{
  MC_CONFIRMED * vcbptr;
//SRTRACE(t,"Thread %d:%d (%s) Build_MC_Confirmed\n",
//        convptr->thread,convptr->conv,convptr->type);
  vcbptr = (MC_CONFIRMED *) &(convptr->vcb);

  CLEARVCB

  vcbptr->opcode = AP_M_CONFIRMED;
  vcbptr->opext = AP_MAPPED_CONVERSATION;
  memcpy(&(vcbptr->tp_id),convptr->TPid, 8);
  vcbptr->conv_id = convptr->Convid;
}

void Build_MC_DEALLOCATE(CONVCB * convptr)
{
  MC_DEALLOCATE * vcbptr;
  SRTRACE(t,"Thread %d:%d (%s) Build_MC_Deallocate\n",
          convptr->thread,convptr->conv,convptr->type);
  vcbptr = (MC_DEALLOCATE *) &(convptr->vcb);

  CLEARVCB

  vcbptr->opcode = AP_M_DEALLOCATE;
  vcbptr->opext = AP_MAPPED_CONVERSATION;
  memcpy(&(vcbptr->tp_id),convptr->TPid, 8);
  vcbptr->conv_id = convptr->Convid;
  if (ConfirmEvery == 0)
  {
     vcbptr->dealloc_type = AP_FLUSH;
  }
  else
  {
     vcbptr->dealloc_type = AP_SYNC_LEVEL;
  }
}

void Build_MC_RECEIVE_AND_WAIT(CONVCB * convptr)
{
  MC_RECEIVE_AND_WAIT * vcbptr;
//SRTRACE(t,"Thread %d:%d (%s) Build_MC_Receive_And_Wait\n",
//        convptr->thread,convptr->conv,convptr->type);
  vcbptr = (MC_RECEIVE_AND_WAIT *) &(convptr->vcb);

  CLEARVCB

  vcbptr->opcode = AP_M_RECEIVE_AND_WAIT;
  vcbptr->opext = AP_MAPPED_CONVERSATION;
  memcpy(&(vcbptr->tp_id),convptr->TPid, 8);
  vcbptr->conv_id = convptr->Convid;
  vcbptr->rtn_status = AP_YES;
  vcbptr->max_len = RcvSize;
  vcbptr->dptr = convptr->DataPtr;
}

void Build_MC_SEND_DATA(CONVCB * convptr)
{
  MC_SEND_DATA * vcbptr;
//SRTRACE(t,"Thread %d:%d (%s) Build_MC_Send_Data\n",
//        convptr->thread,convptr->conv,convptr->type);
  vcbptr = (MC_SEND_DATA *) &(convptr->vcb);

  CLEARVCB

  //  PERF - GenerateData();

  vcbptr->opcode = AP_M_SEND_DATA;
  vcbptr->opext = AP_MAPPED_CONVERSATION;
  memcpy(&(vcbptr->tp_id),convptr->TPid, 8);
  vcbptr->conv_id = convptr->Convid;
  vcbptr->dlen = convptr->SendSize;
  vcbptr->dptr = convptr->DataPtr;
  vcbptr->type = AP_NONE;
}

/*****************************************************************************/
/* ProcessReturns - Checks return codes from the last verb to complete and   */
/*                  saves conversation id and tp id in the conversation cb   */
/*****************************************************************************/
BOOL ProcessReturns(CONVCB * convptr)
{
  BOOL TPEnded = FALSE;
  struct appc_hdr * vcbptr;
  SYSTEMTIME st;

//SRTRACE(t,"Thread %d:%d (%s) ProcessReturns\n",
//        convptr->thread,convptr->conv,convptr->type);
  vcbptr = (struct appc_hdr *) &(convptr->vcb);

  GetLocalTime(&st);
  if (vcbptr->primary_rc != AP_OK)
  {
    SRTRACE(t,"Thread %d:%d (%s) error: %s prc %4.4x src %8.8x at %d:%d:%d\n",
            convptr->thread,convptr->conv,convptr->type,
            VerbName[vcbptr->opcode],
            APPC_FLIPI(vcbptr->primary_rc),APPC_FLIPL(vcbptr->secondary_rc),
            st.wHour,st.wMinute,st.wSecond);
    if ((vcbptr->opcode == AP_M_RECEIVE_AND_WAIT) &&
        (vcbptr->primary_rc == AP_DEALLOC_NORMAL))
    {
      SRTRACE(t,"Thread %d:%d (%s) MC_RECEIVE_AND_WAIT completed with DEALLOC_NORMAL\n",\
              convptr->thread,convptr->conv,convptr->type);
    }
    else if ((vcbptr->opcode == AP_M_CONFIRM) &&
             (vcbptr->primary_rc == AP_ALLOCATION_ERROR))
    {
      SRTRACE(t,"Thread %d:%d (%s) MC_CONFIRM completed with ALLOCATION_ERROR\n",
              convptr->thread,convptr->conv,convptr->type);
    }
    else if ((vcbptr->opcode == AP_M_ALLOCATE) &&
             (vcbptr->primary_rc == AP_ALLOCATION_ERROR))
    {
      SRTRACE(t,"Thread %d:%d (%s) MC_ALLOCATE completed with ALLOCATION_ERROR\n",
              convptr->thread,convptr->conv,convptr->type);
    }
    else
    {
      TPEnded = TRUE;
      SRTRACE(t,"Thread %d:%d (%s) unexpected error on %s - set TPEnded\n",
              convptr->thread,convptr->conv,convptr->type,
              VerbName[vcbptr->opcode]);
    }
  }
  else
  {
    switch (vcbptr->opcode)
    {
      case AP_TP_STARTED:
        SRTRACE(t,"Thread %d:%d (%s) TP_Started completed at %d:%d:%d\n",
                convptr->thread,convptr->conv,convptr->type,
                st.wHour,st.wMinute,st.wSecond);
        memcpy(convptr->TPid,&(P_TPS(vcbptr)->tp_id),8);
        break;

      case AP_TP_ENDED:
        SRTRACE(t,"Thread %d:%d (%s) TP_Ended completed at %d:%d:%d\n",
                convptr->thread,convptr->conv,convptr->type,
                st.wHour,st.wMinute,st.wSecond);
        break;

      case AP_RECEIVE_ALLOCATE:
        SRTRACE(t,"Thread %d:%d (%s) Receive_Allocate completed at %d:%d:%d\n",
                convptr->thread,convptr->conv,convptr->type,
                st.wHour,st.wMinute,st.wSecond);
        memcpy(convptr->TPid,&(P_RAL(vcbptr)->tp_id),8);
        convptr->Convid = P_RAL(vcbptr)->conv_id;
        break;

      case AP_M_ALLOCATE:
        SRTRACE(t,"Thread %d:%d (%s) MC_Allocate completed at %d:%d:%d\n",
                convptr->thread,convptr->conv,convptr->type,
                st.wHour,st.wMinute,st.wSecond);
        convptr->Convid = P_M_ALC(vcbptr)->conv_id;
        break;

      case AP_M_SEND_DATA:
//      SRTRACE(t,"Thread %d:%d (%s) MC_Send_Data completed at %d:%d:%d\n",
//              convptr->thread,convptr->conv,convptr->type,
//              st.wHour,st.wMinute,st.wSecond);
        break;

      case AP_M_DEALLOCATE:
SRTRACE(t,"Thread %d:%d (%s) MC_Deallocate completed at %d:%d:%d: send count %d\n",
                convptr->thread,convptr->conv,convptr->type,
                st.wHour,st.wMinute,st.wSecond,
                convptr->SendCount);
        convptr->Convid = 0;
        break;

      case AP_M_CONFIRM:
SRTRACE(t,"Thread %d:%d (%s) MC_Confirm completed at %d:%d:%d: send count %d\n",
                convptr->thread,convptr->conv,convptr->type,
                st.wHour,st.wMinute,st.wSecond,
                convptr->SendCount);
        break;

      case AP_M_RECEIVE_AND_WAIT:
//      SRTRACE(t,"Thread %d:%d (%s) MC_Receive_And_Wait completed at %d:%d:%d\n",
//              convptr->thread,convptr->conv,convptr->type,
//              st.wHour,st.wMinute,st.wSecond);
        break;

      case AP_M_CONFIRMED:
SRTRACE(t,"Thread %d:%d (%s) MC_Confirmed completed at %d:%d:%d: receive count %d\n",
                convptr->thread,convptr->conv,convptr->type,
                st.wHour,st.wMinute,st.wSecond,
                convptr->RcvCount);
        break;

      default:
        SRTRACE(t,"Thread %d:%d (%s) UNKNOWN opcode - set TPEnded\n",
                convptr->thread,convptr->conv,convptr->type);
        TPEnded = TRUE;
        DebugBreak();
        break;
    }
  }
  SRTRFLUSH()
  return(TPEnded);
}

/*****************************************************************************/
/* ReadConfig - Reads config info from MSENDRCV.CFG and allocates buffer for */
/*              sending                                                      */
/*****************************************************************************/
void ReadConfig()
{
  char buffer[200];

  if (!ReadString("ResultFile",FileName,60))
  {
    strcpy(FileName,"MSENDRCV.OUT");
  }

  if (!ReadString("TraceFile",TraceFile,60))
  {
    strcpy(TraceFile,"MSENDRCV.TRC");
  }

  strcpy(TPName,"MSENDRCV");
  PadString(TPName,64);
  CONV_A_TO_E(TPName,64);

  if (!ReadString("LocalTPName",LocalTPName,64))
  {
    strcpy(LocalTPName,"MSENDRCVTP");
  }
  PadString(LocalTPName,64);
  CONV_A_TO_E(LocalTPName,64);

  if (!ReadString("RemoteTPName",RemoteTPName,64))
  {
    strcpy(RemoteTPName,"MSENDRCVTP");
  }
  PadString(RemoteTPName,64);
  CONV_A_TO_E(RemoteTPName,64);

  if (!ReadString("LocalLUAlias",LocalLUAlias,8))
  {
    strcpy(LocalLUAlias,"SENDLU");
  }
  PadString(LocalLUAlias,8);

  if (!ReadString("RemoteLUAlias",RemoteLUAlias,8))
  {
    strcpy(RemoteLUAlias,"RECVLU");
  }
  PadString(RemoteLUAlias,8);

  if (!ReadString("ModeName",ModeName,8))
  {
    strcpy(ModeName,"#INTER");
  }
  PadString(ModeName,8);
  CONV_A_TO_E(ModeName,8);

  NumSends=8;
  if (ReadString("NumSends",buffer,200))
  {
    NumSends=atoi(buffer);
  }

  ConfirmEvery=2;
  if (ReadString("ConfirmEvery",buffer,200))
  {
    ConfirmEvery=atoi(buffer);
  }

  SendSize=256;
  if (ReadString("SendSize",buffer,200))
  {
    SendSize=atoi(buffer);
  }

  RcvSize=4096;
  if (ReadString("RcvSize",buffer,200))
  {
    RcvSize=atoi(buffer);
  }

  NumRcvThreads = 2;
  if (ReadString("NumRcvThreads",buffer,200))
  {
    NumRcvThreads=atoi(buffer);
  }

  NumSendConvs = 4;
  if (ReadString("NumSendConvs",buffer,200))
  {
    NumSendConvs=atoi(buffer);
  }

  NumRcvConvs = 4;
  if (ReadString("NumRcvConvs",buffer,200))
  {
    NumRcvConvs=atoi(buffer);
  }
  NumRalcs = 0;
  RcvConvs = 0;

  ResultBuf = (NumRcvConvs == 0) ?
              NULL : malloc(NumRcvConvs * sizeof(RESULT));
  ResultPtr = ResultBuf;

  if (NumRcvConvs > 0 && NumRcvThreads * 63 < NumRcvConvs)
  {
    NumRcvThreads = NumRcvConvs / 63 + 1;
  }

  if (NumRcvConvs > 0 && NumRcvThreads > NumRcvConvs)
  {
    NumRcvThreads = NumRcvConvs;
  }
}

/*****************************************************************************/
/* CONV_A_TO_E - ASCII to EBCDIC conversion routine                          */
/*****************************************************************************/
void CONV_A_TO_E(char * string,int length)
{
  memset(cnvtptr,0,sizeof(cnvt));

  cnvt.opcode       = SV_CONVERT;
  cnvt.direction    = SV_ASCII_TO_EBCDIC;
  cnvt.char_set     = SV_AE;

  cnvt.len          = length;
  cnvt.source       = string;
  cnvt.target       = string;

  ACSSVC_C((long)(char *) (cnvtptr));
}

/*****************************************************************************/
/* CONV_E_TO_A - EBCDIC to ASCII conversion routine                          */
/*****************************************************************************/
void CONV_E_TO_A(char * string,int length)
{
  memset(cnvtptr,0,sizeof(cnvt));

  cnvt.opcode       = SV_CONVERT;
  cnvt.direction    = SV_EBCDIC_TO_ASCII;
  cnvt.char_set     = SV_AE;
  cnvt.len          = length;
  cnvt.source       = string;
  cnvt.target       = string;

  ACSSVC_C((long)(char *) (cnvtptr));
}

/*****************************************************************************/
/* StartConversation - Record start time for this conversation               */
/*****************************************************************************/
void StartConversation(CONVCB * convptr)
{
  convptr->ConvStarted = GetTickCount();
}

/*****************************************************************************/
/* EndConversation - Calculate elapsed time for this conversation            */
/*****************************************************************************/
void EndConversation(CONVCB * convptr)
{
  if (ResultPtr != NULL)
  {
    /*************************************************************************/
    /* Make sure we don't go over the end of the allocated area              */
    /*************************************************************************/
    if (NumResults < NumRcvConvs)
    {
      *ResultPtr++ = (GetTickCount() - convptr->ConvStarted);
      NumResults++;
    }
    else
    {
      SRTRACE(t,"Thread %d:%d Too many calls to EndConversation\n",
              convptr->thread,convptr->conv);
    }
  }
}

/*****************************************************************************/
/* GenerateData    - Fill in data buffer                                     */
/*****************************************************************************/
void GenerateData(CONVCB * convptr)
{
  int i;
  int div;
  int rem;
  char * dptr;

  dptr = convptr->DataPtr;
  div = convptr->SendSize / 5;
  rem = convptr->SendSize % 5;

  for (; div--;)
  {
    for (i=4; i--; *dptr++ = datach);
    *dptr++ = '.';
  }
  for (; rem--; *dptr++ = datach);

  EnterCriticalSection(&runsem);
  datach = (datach=='Z' ? 'A' : datach + 1);
  LeaveCriticalSection(&runsem);
}

/*****************************************************************************/
/* OutputResults - dump the times of conversations to file                   */
/*****************************************************************************/
void OutputResults()
{
  FILE *h = NULL;
  RESULT * ptr = NULL;
  unsigned short i = 0;

  h = fopen(FileName,"w");
  if (h != NULL)
  {
    fprintf(h,"\nMSENDRCV Results\n----------------\n\n");
    CONV_E_TO_A(LocalTPName,64);
    fprintf(h,"Local TP Name              = %.64s\n",LocalTPName);
    CONV_E_TO_A(RemoteTPName,64);
    fprintf(h,"Remote TP Name             = %.64s\n",RemoteTPName);
    fprintf(h,"Local LU Alias             = %.8s\n",LocalLUAlias);
    fprintf(h,"Remote LU Alias            = %.8s\n",RemoteLUAlias);
    CONV_E_TO_A(ModeName,8);
    fprintf(h,"ModeName                   = %.8s\n",ModeName);
    fprintf(h,"No. of receive threads     = %d\n",NumRcvThreads);
    fprintf(h,"No. of send conversations  = %d\n",NumSendConvs);
    fprintf(h,"Sends per conversation     = %d\n",NumSends);
    fprintf(h,"Sends between confirms     = %d\n",ConfirmEvery);
    fprintf(h,"Bytes per send             = %d\n",SendSize);
    fprintf(h,"No. of rcv convs started   = %d\n",NumRalcs);
    fprintf(h,"No. of rcv convs completed = %d\n",RcvConvs);
    fprintf(h,"\n");

    ptr = ResultBuf;
    while (ptr < ResultPtr)
    {
      fprintf(h,"Conversation number %d, time = %.3f seconds\n",i++,
                                                (((float) *ptr++) / 1000.0 ));
    }
    fclose(h);
  }
}

/*****************************************************************************/
/* ReadString - Get a line of text from the config file                      */
/*****************************************************************************/
int ReadString(char * lpValueName,char * lpData, int maxlen)
{
  char       buffer[200];
  char      *p = NULL;
  FILE      *h = NULL;
  BOOL       match = FALSE;
  BOOL       eof   = FALSE;
  int        rc = 0;
  int        ch = 0;
  int        i = 0;
  BOOL       gotdata = FALSE;
  char       separators[] = " =\t\n";

  GetModuleFileName( hInst, buffer, sizeof(buffer) );
  lstrcpy( buffer+lstrlen(buffer) - 4, ".CFG" );
  h = fopen( buffer, "r" );
  buffer[0] = '\0';

  if (h != NULL)
  {
    while ((!match) && (!eof))
    {
      /***********************************************************************/
      /* Use fgetc to read a line of text from the file                      */
      /***********************************************************************/
      for (i=0; (i<sizeof(buffer))     &&
                ((ch=getc(h)) != EOF)  &&
                ((char)ch != '\n');
                                   i++)
      {
        buffer[i] = (char)ch;
      }
      if ((char)ch == '\n')
      {
        buffer[i++] = (char)ch;
      }
      if (ch == EOF)
      {
        eof = TRUE;
      }
      else
      {
        /*********************************************************************/
        /* Compare the first token in the line read with the requested parm  */
        /*********************************************************************/
        if (!strcmpi(strupr(strtok(buffer, separators)), lpValueName))
        {
          match = TRUE;
          /*******************************************************************/
          /* Get a pointer to the second token (the value we want)           */
          /*******************************************************************/
          p = strtok(NULL, separators);

          /*******************************************************************/
          /* Copy the data IF there is some                                  */
          /*******************************************************************/
          if (p != NULL)
          {
            /*****************************************************************/
            /* Force a NULL after the second token                           */
            /*****************************************************************/
            strtok(NULL, separators);

            /*****************************************************************/
            /* Copy the data                                                 */
            /*****************************************************************/
            strncpy(lpData, p, maxlen);
            gotdata = TRUE;
          }
          else
          {
            gotdata = FALSE;
          }
        }
      }
    }

    if (gotdata)
    {
      rc = 1;
    }

    fclose(h);

  }

  return(rc);
}

/*****************************************************************************/
/* PadString - Remove terminating NULL  and pad on the right with spaces     */
/*****************************************************************************/
void PadString(char * string,int length)
{
  char * p;
  if ((p=memchr(string,'\0',length)) != NULL)
  {
    while (p < string+length)
    {
      *p++=' ';
    }
  }
}


MSENDRCV.H

/* msendrcv.h */
/* (C) COPYRIGHT DATA CONNECTION LIMITED 1994 */

/*****************************************************************************/
/* Change History                                                            */
/*                                                                           */
/*       11/01/94 DK  Created                                                */
/*       14/04/94 DK  Name table added for tracing                           */
/*****************************************************************************/

/*****************************************************************************/
/* static configuration constants                                            */
/* Note that MAX_RCV_PER_SERVICE cannot exceed 63, because we cannot wait    */
/*      on more than 64 events (and one extra event is needed for receive)   */
/*****************************************************************************/
#define MAX_SEND_PER_THREAD 64
#define MAX_RCV_PER_THREAD  63
#define MAX_RECEIVE_THREADS 64
#define MAXRCVBUF 4096

/*****************************************************************************/
/* global critical section                                                   */
/*****************************************************************************/
CRITICAL_SECTION runsem;

/*****************************************************************************/
/* APPC verb control block and macros for manipulation of vcb                */
/*****************************************************************************/
#define CLEARVCB memset(vcbptr,0,sizeof(VCB));
#define P_TPS(X)    ((struct tp_started *)X)
#define P_TPE(X)    ((struct tp_ended *)X)
#define P_RAL(X)    ((struct receive_allocate *)X)

#define P_M_ALC(X)  ((struct mc_allocate *)X)
#define P_M_CNF(X)  ((struct mc_confirm *)X)
#define P_M_CFD(X)  ((struct mc_confirmed *)X)
#define P_M_DAL(X)  ((struct mc_deallocate *)X)
#define P_M_RAW(X)  ((struct mc_receive_and_wait *)X)
#define P_M_SDD(X)  ((struct mc_send_data *)X)

typedef struct vcb
{
  union
  {
    struct appc_hdr             hdr;
    struct receive_allocate     rcvalloc;
    struct tp_started           tpstart;
    struct tp_ended             tpend;
    struct mc_allocate          allocate;
    struct mc_confirm           confirm;
    struct mc_confirmed         confirmed;
    struct mc_deallocate        deallocate;
    struct mc_receive_and_wait  rcvwait;
    struct mc_send_data         snddata;
  } U_VCB;
} VCB;

/*****************************************************************************/
/* structures for convert verb                                               */
/*****************************************************************************/
struct convert cnvt;
char * cnvtptr;

/*****************************************************************************/
/* for outputting results                                                    */
/*****************************************************************************/
typedef unsigned long RESULT;
RESULT * ResultPtr;
RESULT * ResultBuf;
short    NumResults;

/*****************************************************************************/
/* conversation control block - one per conversation                         */
/*****************************************************************************/
typedef struct convcb
{
  char           TPid[8];
  char           type[8];
  unsigned long  Convid;
  HANDLE         async_corr;
  char *         DataPtr;
  unsigned short thread;
  unsigned short conv;
  unsigned short SendSize;
  unsigned short SendCount;
  unsigned short RcvCount;
  unsigned short ConfirmCount;
  BOOL           TPEnded;
  BOOL           Deallocated;
  BOOL           Counted;
  RESULT         ConvStarted;
  HANDLE         hEvent;
  VCB            vcb;
} CONVCB;

/*****************************************************************************/
/* structures for receive threads                                            */
/*****************************************************************************/
typedef struct rcv_thread
{
  HANDLE event1;
  HANDLE event2;
  CONVCB * convptr;
} RCV_THREAD;

RCV_THREAD RcvThreadArray [MAX_RECEIVE_THREADS];

/*****************************************************************************/
/* File Names and handle for trace file                                      */
/*****************************************************************************/
char FileName [60];
char TraceFile[60];
FILE *t = NULL;

/*****************************************************************************/
/* APPC Names from configuration file                                        */
/*****************************************************************************/
char TPName[64];
char LocalTPName[64];
char LocalLUAlias[8];
char RemoteLUAlias[8];
char ModeName[8];
char RemoteTPName[64];

/*****************************************************************************/
/* Global Variables from configuration file                                  */
/*****************************************************************************/
unsigned short NumSends;
unsigned short SendSize;
unsigned short NumSendConvs;
unsigned short ConfirmEvery;
unsigned short NumRcvThreads;
unsigned short NumRcvConvs;
unsigned short RcvSize;

/*****************************************************************************/
/* Global Variables                                                          */
/*****************************************************************************/
unsigned short NumRalcs;                /* number of receive_allocs issued   */
unsigned short RcvConvs;                /* number of conversations completed */
unsigned short ThreadCount;             /* total thread count                */
unsigned short RcvThreads;              /* count of receive threads          */
unsigned short SendThreads;             /* count of send threads             */
unsigned short SimRcvConvs;             /* number of simultaneous rcv convs  */
unsigned short SimSendConvs;            /* number of simultaneous send convs */

/*****************************************************************************/
/* Names of verbs for tracing                                                */
/*****************************************************************************/
char *VerbName[] =
{
  "UNKNOWN_VERB",                       /* 0x0000                            */
  "MC_ALLOCATE",                        /* 0x0001                            */
  "UNKNOWN_VERB",                       /* 0x0002                            */
  "MC_CONFIRM",                         /* 0x0003                            */
  "MC_CONFIRMED",                       /* 0x0004                            */
  "MC_DEALLOCATE",                      /* 0x0005                            */
  "MC_FLUSH",                           /* 0x0006                            */
  "MC_GET_ATTRIBUTES",                  /* 0x0007                            */
  "UNKNOWN_VERB",                       /* 0x0008                            */
  "UNKNOWN_VERB",                       /* 0x0009                            */
  "MC_PREPARE_TO_RECEIVE",              /* 0x000a                            */
  "MC_RECEIVE_AND_WAIT",                /* 0x000b                            */
  "MC_RECEIVE_IMMEDIATE",               /* 0x000c                            */
  "MC_RECEIVE_AND_POST",                /* 0x000d                            */
  "MC_REQUEST_TO_SEND",                 /* 0x000e                            */
  "MC_SEND_DATA",                       /* 0x000f                            */
  "MC_SEND_ERROR",                      /* 0x0010                            */
  "UNKNOWN_VERB",                       /* 0x0011                            */
  "MC_TEST_RTS",                        /* 0x0012                            */
  "TP_ENDED",                           /* 0x0013                            */
  "TP_STARTED",                         /* 0x0014                            */
  "UNKNOWN_VERB",                       /* 0x0015                            */
  "RECEIVE_ALLOCATE",                   /* 0x0016                            */
  "UNKNOWN_VERB",                       /* 0x0017                            */
  "MC_SEND_CONVERSATION"                /* 0x0018                            */
};

/*****************************************************************************/
/* Character for filling buffer                                              */
/*****************************************************************************/
char datach;

/*****************************************************************************/
/* Windows stuff                                                             */
/*****************************************************************************/
HWND hWndMain;
char title[40]={'\0'};
#define OUTPUTNUMBER sprintf(title,"thr sms smr rdn  %d  %d  %d  %d", \
                     ThreadCount,SimSendConvs,SimRcvConvs,RcvConvs);  \
                     SetWindowText(hWndMain,title);

/*****************************************************************************/
/* Function Prototypes                                                       */
/*****************************************************************************/
int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine, int nCmdShow);
BOOL InitializeWinMain(HINSTANCE hInstance);
LONG PASCAL TPWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
void InitializeMain(void);
BOOL IssueSendVerb(CONVCB * convptr);
BOOL IssueRcvVerb(CONVCB * convptr);
BOOL ProcessReturns(CONVCB * convptr);
void ReadConfig(void);
void CONV_A_TO_E(char * string,int length);
void CONV_E_TO_A(char * string,int length);
void StartConversation(CONVCB * convptr);
void EndConversation(CONVCB * convptr);
int  ReadString(char * lpValueName, char FAR * lpData, int maxlen);
void PadString(char * string, int length);
void OutputResults(void);
void Build_RECEIVE_ALLOCATE(CONVCB * convptr);
void Build_TP_STARTED(CONVCB * convptr);
void Build_TP_ENDED(CONVCB * convptr);
void Build_MC_ALLOCATE(CONVCB * convptr);
void Build_MC_CONFIRM(CONVCB * convptr);
void Build_MC_CONFIRMED(CONVCB * convptr);
void Build_MC_DEALLOCATE(CONVCB * convptr);
void Build_MC_RECEIVE_AND_WAIT(CONVCB * convptr);
void Build_MC_SEND_CONVERSATION(CONVCB * convptr);
void Build_MC_SEND_DATA(CONVCB * convptr);
void GenerateData(CONVCB * convptr);
DWORD WINAPI RcvAllocThread();
DWORD WINAPI ReceiveThread(DWORD k);
DWORD WINAPI SendThread(DWORD k);


ADOID.H

//--------------------------------------------------------------------
// Microsoft ADO
//
// (c) 1996 Microsoft Corporation.  All Rights Reserved.
//
// @doc
//
// @moduleadoid.h | ADO Guids
//
// @devnote None
//--------------------------------------------------------------------
#ifndef _ADOID_H_
#define _ADOID_H_

// The following range of 255 guids has been reserved for the base objects.
// 00000200-0000-0010-8000-00AA006D2EA4 - 000002FF-0000-0010-8000-00AA006D2EA4
// If you need more then please take a range from daoguid.txt and update that file in

#define DEFINE_DAOGUID(name, l) \
    DEFINE_GUID(name, l, 0, 0x10, 0x80,0,0,0xAA,0,0x6D,0x2E,0xA4)

#define ADO_MAJOR2// major version of the ADO type library
#define ADO_VERSION1.5

// Type library
DEFINE_DAOGUID(LIBID_CADO10,0x00000200);
DEFINE_DAOGUID(LIBID_CADOR10,0x00000300);

// Error
DEFINE_DAOGUID(IID_IADOError,            0x00000500);
DEFINE_DAOGUID(IID_IADOErrors,           0x00000501);

// Property
DEFINE_DAOGUID(IID_IADOProperty,         0x00000503);
DEFINE_DAOGUID(IID_IADOProperties,       0x00000504);

// Field
DEFINE_DAOGUID(IID_IADOField,            0x00000505);
DEFINE_DAOGUID(IID_IADOFields,           0x00000506);

// Command
DEFINE_DAOGUID(CLSID_CADOCommand,0x00000507);
DEFINE_DAOGUID(IID_IADOCommand,0x00000508);
DEFINE_DAOGUID(IID_IADOCommands,        0x00000509);

// Parameter
DEFINE_DAOGUID(CLSID_CADOParameter,0x0000050B);
DEFINE_DAOGUID(IID_IADOParameter,        0x0000050C);
DEFINE_DAOGUID(IID_IADOParameters,       0x0000050D);

//Recordset
DEFINE_DAOGUID(CLSID_CADORecordset,  0x00000535);
DEFINE_DAOGUID(IID_IADORecordset, 0x0000050E);
DEFINE_DAOGUID(IID_IADORecordsets, 0x0000050F);
DEFINE_DAOGUID(IID_IADORecordsetConstruction,     0x00000283);

// Collections
DEFINE_DAOGUID(IID_IADOCollection,       0x00000512);
DEFINE_DAOGUID(IID_IADODynaCollection,   0x00000513);

// Connection
DEFINE_DAOGUID(CLSID_CADOConnection,   0x00000514);
DEFINE_DAOGUID(IID_IADOConnection,   0x00000515);
DEFINE_DAOGUID(IID_IADOConnections,   0x00000518);

// Enums
DEFINE_DAOGUID(IID_EnumCursorType,0x0000051B);
DEFINE_DAOGUID(IID_EnumCursorOption,0x0000051C);
DEFINE_DAOGUID(IID_EnumLockType,0x0000051D);
DEFINE_DAOGUID(IID_EnumExecuteOption,0x0000051E);
DEFINE_DAOGUID(IID_EnumDataType,0x0000051F);
DEFINE_DAOGUID(IID_EnumConnectPrompt,0x00000520);
DEFINE_DAOGUID(IID_EnumConnectMode,0x00000521);
DEFINE_DAOGUID(IID_EnumPrepareOption,0x00000522);
DEFINE_DAOGUID(IID_EnumIsolationLevel,0x00000523);
DEFINE_DAOGUID(IID_EnumXactAttribute,0x00000524);
DEFINE_DAOGUID(IID_EnumFieldAttribute,0x00000525);
DEFINE_DAOGUID(IID_EnumEditMode,0x00000526);
DEFINE_DAOGUID(IID_EnumRecordStatus,0x00000527);
DEFINE_DAOGUID(IID_EnumPosition,0x00000528);
DEFINE_DAOGUID(IID_EnumPropertyAttributes,0x00000529);
DEFINE_DAOGUID(IID_EnumErrorValue,0x0000052A);
DEFINE_DAOGUID(IID_EnumParameterAttributes,0x0000052B);
DEFINE_DAOGUID(IID_EnumParameterDirection,0x0000052C);
DEFINE_DAOGUID(IID_EnumFilterCriteria,0x0000052D);
DEFINE_DAOGUID(IID_EnumCommandType,0x0000052E);
DEFINE_DAOGUID(IID_EnumCursorLocation,0x0000052F);
DEFINE_DAOGUID(IID_EnumEventStatus,0x00000530);
DEFINE_DAOGUID(IID_EnumEventReason,0x00000531);
DEFINE_DAOGUID(IID_EnumObjectState,0x00000532);
DEFINE_DAOGUID(IID_EnumSchema,0x00000533);
DEFINE_DAOGUID(IID_EnumMarshalOptions,0x00000540);

#endif // _ADOID_H_


ADOINT.H

//--------------------------------------------------------------------
// Microsoft ADO
//
// (c) 1996 Microsoft Corporation.  All Rights Reserved.
//
// @doc
//
// @moduleadoint.h | ADO Interface header
//
// @devnote None
//--------------------------------------------------------------------
#ifndef _ADOINT_H_
#define _ADOINT_H_

#ifndef _INC_TCHAR
#include <tchar.h>
#endif

#if _MSC_VER >= 1100
#define DECLSPEC_UUID(x)    __declspec(uuid(x))
#else
#define DECLSPEC_UUID(x)
#endif

/* this ALWAYS GENERATED file contains the definitions for the interfaces */
/* File created by MIDL compiler version 3.01.75 */
/* at Tue Oct 07 23:50:20 1997
 */
/* Compiler settings for m_bobj.odl:
    Os (OptLev=s), W4, Zp8, env=Win32, ms_ext, c_ext
    error checks: none
*/
//@@MIDL_FILE_HEADING(  )
#include "rpc.h"
#include "rpcndr.h"
#ifndef __m_bobj_h__
#define __m_bobj_h__
#ifdef __cplusplus
extern "C"{
#endif
/* Forward Declarations */
#ifndef ___ADOCollection_FWD_DEFINED__
#define ___ADOCollection_FWD_DEFINED__
typedef interface _ADOCollection _ADOCollection;
#endif /* ___ADOCollection_FWD_DEFINED__ */
#ifndef ___ADODynaCollection_FWD_DEFINED__
#define ___ADODynaCollection_FWD_DEFINED__
typedef interface _ADODynaCollection _ADODynaCollection;
#endif /* ___ADODynaCollection_FWD_DEFINED__ */
#ifndef ___ADO_FWD_DEFINED__
#define ___ADO_FWD_DEFINED__
typedef interface _ADO _ADO;
#endif /* ___ADO_FWD_DEFINED__ */
#ifndef __Error_FWD_DEFINED__
#define __Error_FWD_DEFINED__
typedef interface ADOError Error;
#endif /* __Error_FWD_DEFINED__ */
#ifndef __Errors_FWD_DEFINED__
#define __Errors_FWD_DEFINED__
typedef interface ADOErrors Errors;
#endif /* __Errors_FWD_DEFINED__ */
#ifndef ___Command_FWD_DEFINED__
#define ___Command_FWD_DEFINED__
typedef interface _ADOCommand _Command;
#endif /* ___Command_FWD_DEFINED__ */
#ifndef ___Connection_FWD_DEFINED__
#define ___Connection_FWD_DEFINED__
typedef interface _ADOConnection _Connection;
#endif /* ___Connection_FWD_DEFINED__ */
#ifndef __ADOConnectionConstruction_FWD_DEFINED__
#define __ADOConnectionConstruction_FWD_DEFINED__
typedef interface ADOConnectionConstruction ADOConnectionConstruction;
#endif /* __ADOConnectionConstruction_FWD_DEFINED__ */
#ifndef __Connection_FWD_DEFINED__
#define __Connection_FWD_DEFINED__
#ifdef __cplusplus
typedef class ADOConnection Connection;
#else
typedef struct ADOConnection Connection;
#endif /* __cplusplus */
#endif /* __Connection_FWD_DEFINED__ */
#ifndef __Command_FWD_DEFINED__
#define __Command_FWD_DEFINED__
#ifdef __cplusplus
typedef class ADOCommand Command;
#else
typedef struct ADOCommand Command;
#endif /* __cplusplus */
#endif /* __Command_FWD_DEFINED__ */
#ifndef ___Recordset_FWD_DEFINED__
#define ___Recordset_FWD_DEFINED__
typedef interface _ADORecordset _Recordset;
#endif /* ___Recordset_FWD_DEFINED__ */
#ifndef __Recordset_FWD_DEFINED__
#define __Recordset_FWD_DEFINED__
#ifdef __cplusplus
typedef class ADORecordset Recordset;
#else
typedef struct ADORecordset Recordset;
#endif /* __cplusplus */
#endif /* __Recordset_FWD_DEFINED__ */
#ifndef __ADORecordsetConstruction_FWD_DEFINED__
#define __ADORecordsetConstruction_FWD_DEFINED__
typedef interface ADORecordsetConstruction ADORecordsetConstruction;
#endif /* __ADORecordsetConstruction_FWD_DEFINED__ */
#ifndef __Field_FWD_DEFINED__
#define __Field_FWD_DEFINED__
typedef interface ADOField Field;
#endif /* __Field_FWD_DEFINED__ */
#ifndef __Fields_FWD_DEFINED__
#define __Fields_FWD_DEFINED__
typedef interface ADOFields Fields;
#endif /* __Fields_FWD_DEFINED__ */
#ifndef ___Parameter_FWD_DEFINED__
#define ___Parameter_FWD_DEFINED__
typedef interface _ADOParameter _Parameter;
#endif /* ___Parameter_FWD_DEFINED__ */
#ifndef __Parameter_FWD_DEFINED__
#define __Parameter_FWD_DEFINED__
#ifdef __cplusplus
typedef class ADOParameter Parameter;
#else
typedef struct ADOParameter Parameter;
#endif /* __cplusplus */
#endif /* __Parameter_FWD_DEFINED__ */
#ifndef __Parameters_FWD_DEFINED__
#define __Parameters_FWD_DEFINED__
typedef interface ADOParameters Parameters;
#endif /* __Parameters_FWD_DEFINED__ */
#ifndef __Property_FWD_DEFINED__
#define __Property_FWD_DEFINED__
typedef interface ADOProperty Property;
#endif /* __Property_FWD_DEFINED__ */
#ifndef __Properties_FWD_DEFINED__
#define __Properties_FWD_DEFINED__
typedef interface ADOProperties Properties;
#endif /* __Properties_FWD_DEFINED__ */
void __RPC_FAR * __RPC_USER MIDL_user_allocate(size_t);
void __RPC_USER MIDL_user_free( void __RPC_FAR * );
/****************************************
 * Generated header for interface: __MIDL_itf_m_bobj_0000
 * at Tue Oct 07 23:50:20 1997
 * using MIDL 3.01.75
 ****************************************/
/* [local] */
extern RPC_IF_HANDLE __MIDL_itf_m_bobj_0000_v0_0_c_ifspec;
extern RPC_IF_HANDLE __MIDL_itf_m_bobj_0000_v0_0_s_ifspec;
#ifndef __ADODB_LIBRARY_DEFINED__
#define __ADODB_LIBRARY_DEFINED__
/****************************************
 * Generated header for library: ADODB
 * at Tue Oct 07 23:50:20 1997
 * using MIDL 3.01.75
 ****************************************/
/* [helpstring][helpcontext][version][helpfile][uuid] */
typedef /* [uuid][public] */
enum CursorTypeEnum
    {adOpenUnspecified= -1,
adOpenForwardOnly= 0,
adOpenKeyset= 1,
adOpenDynamic= 2,
adOpenStatic= 3
    }CursorTypeEnum;
typedef /* [uuid] */
enum CursorOptionEnum
    {adHoldRecords= 0x100,
adMovePrevious= 0x200,
adAddNew= 0x1000400,
adDelete= 0x1000800,
adUpdate= 0x1008000,
adBookmark= 0x2000,
adApproxPosition= 0x4000,
adUpdateBatch= 0x10000,
adResync= 0x20000
    }CursorOptionEnum;
typedef /* [uuid] */
enum LockTypeEnum
    {adLockUnspecified= -1,
adLockReadOnly= 1,
adLockPessimistic= 2,
adLockOptimistic= 3,
adLockBatchOptimistic= 4
    }LockTypeEnum;
typedef /* [hidden][uuid] */
enum ExecuteOptionEnum
    {adOptionUnspecified= -1
    }ExecuteOptionEnum;
typedef /* [uuid] */
enum ObjectStateEnum
    {adStateClosed= 0,
adStateOpen= 0x1
    }ObjectStateEnum;
typedef /* [uuid] */
enum CursorLocationEnum
    {adUseNone= 1,
adUseServer= 2,
adUseClient= 3,
adUseClientBatch= 3
    }CursorLocationEnum;
typedef /* [uuid] */
enum DataTypeEnum
    {adEmpty= 0,
adTinyInt= 16,
adSmallInt= 2,
adInteger= 3,
adBigInt= 20,
adUnsignedTinyInt= 17,
adUnsignedSmallInt= 18,
adUnsignedInt= 19,
adUnsignedBigInt= 21,
adSingle= 4,
adDouble= 5,
adCurrency= 6,
adDecimal= 14,
adNumeric= 131,
adBoolean= 11,
adError= 10,
adUserDefined= 132,
adVariant= 12,
adIDispatch= 9,
adIUnknown= 13,
adGUID= 72,
adDate= 7,
adDBDate= 133,
adDBTime= 134,
adDBTimeStamp= 135,
adBSTR= 8,
adChar= 129,
adVarChar= 200,
adLongVarChar= 201,
adWChar= 130,
adVarWChar= 202,
adLongVarWChar= 203,
adBinary= 128,
adVarBinary= 204,
adLongVarBinary= 205
    }DataTypeEnum;
typedef /* [uuid] */
enum FieldAttributeEnum
    {adFldMayDefer= 0x2,
adFldUpdatable= 0x4,
adFldUnknownUpdatable= 0x8,
adFldFixed= 0x10,
adFldIsNullable= 0x20,
adFldMayBeNull= 0x40,
adFldLong= 0x80,
adFldRowID= 0x100,
adFldRowVersion= 0x200,
adFldCacheDeferred= 0x1000
    }FieldAttributeEnum;
typedef /* [uuid] */
enum EditModeEnum
    {adEditNone= 0,
adEditInProgress= 0x1,
adEditAdd= 0x2,
adEditDelete= 0x4
    }EditModeEnum;
typedef /* [uuid] */
enum RecordStatusEnum
    {adRecOK= 0,
adRecNew= 0x1,
adRecModified= 0x2,
adRecDeleted= 0x4,
adRecUnmodified= 0x8,
adRecInvalid= 0x10,
adRecMultipleChanges= 0x40,
adRecPendingChanges= 0x80,
adRecCanceled= 0x100,
adRecCantRelease= 0x400,
adRecConcurrencyViolation= 0x800,
adRecIntegrityViolation= 0x1000,
adRecMaxChangesExceeded= 0x2000,
adRecObjectOpen= 0x4000,
adRecOutOfMemory= 0x8000,
adRecPermissionDenied= 0x10000,
adRecSchemaViolation= 0x20000,
adRecDBDeleted= 0x40000
    }RecordStatusEnum;
typedef
enum GetRowsOptionEnum
    {adGetRowsRest= -1
    }GetRowsOptionEnum;
typedef /* [uuid] */
enum PositionEnum
    {adPosUnknown= -1,
adPosBOF= -2,
adPosEOF= -3
    }PositionEnum;
typedef
enum BookmarkEnum
    {adBookmarkCurrent= 0,
adBookmarkFirst= 1,
adBookmarkLast= 2
    }BookmarkEnum;
typedef /* [uuid] */
enum MarshalOptionsEnum
    {adMarshalAll= 0,
adMarshalModifiedOnly= 1
    }MarshalOptionsEnum;
typedef
enum AffectEnum
    {adAffectCurrent= 1,
adAffectGroup= 2,
adAffectAll= 3
    }AffectEnum;
typedef
enum FilterGroupEnum
    {adFilterNone= 0,
adFilterPendingRecords= 1,
adFilterAffectedRecords= 2,
adFilterFetchedRecords= 3,
adFilterPredicate= 4
    }FilterGroupEnum;
typedef /* [hidden] */
enum SearchDirection
    {adSearchForward= 1,
adSearchBackward= -1
    }SearchDirectionEnum;
typedef /* [uuid] */
enum ConnectPromptEnum
    {adPromptAlways= 1,
adPromptComplete= 2,
adPromptCompleteRequired= 3,
adPromptNever= 4
    }ConnectPromptEnum;
typedef /* [uuid] */
enum ConnectModeEnum
    {adModeUnknown= 0,
adModeRead= 1,
adModeWrite= 2,
adModeReadWrite= 3,
adModeShareDenyRead= 4,
adModeShareDenyWrite= 8,
adModeShareExclusive= 0xc,
adModeShareDenyNone= 0x10
    }ConnectModeEnum;
typedef /* [uuid] */
enum IsolationLevelEnum
    {adXactUnspecified= 0xffffffff,
adXactChaos= 0x10,
adXactReadUncommitted= 0x100,
adXactBrowse= 0x100,
adXactCursorStability= 0x1000,
adXactReadCommitted= 0x1000,
adXactRepeatableRead= 0x10000,
adXactSerializable= 0x100000,
adXactIsolated= 0x100000
    }IsolationLevelEnum;
typedef /* [uuid] */
enum XactAttributeEnum
    {adXactCommitRetaining= 0x20000,
adXactAbortRetaining= 0x40000
    }XactAttributeEnum;
typedef /* [uuid] */
enum PropertyAttributesEnum
    {adPropNotSupported= 0,
adPropRequired= 0x1,
adPropOptional= 0x2,
adPropRead= 0x200,
adPropWrite= 0x400
    }PropertyAttributesEnum;
typedef /* [uuid] */
enum ErrorValueEnum
    {adErrInvalidArgument=     MAKE_HRESULT(SEVERITY_ERROR, FACILITY_CONTROL, 0xbb9),
adErrNoCurrentRecord=     MAKE_HRESULT(SEVERITY_ERROR, FACILITY_CONTROL, 0xbcd),
adErrIllegalOperation=     MAKE_HRESULT(SEVERITY_ERROR, FACILITY_CONTROL, 0xc93),
adErrInTransaction=     MAKE_HRESULT(SEVERITY_ERROR, FACILITY_CONTROL, 0xcae),
adErrFeatureNotAvailable=     MAKE_HRESULT(SEVERITY_ERROR, FACILITY_CONTROL, 0xcb3),
adErrItemNotFound=     MAKE_HRESULT(SEVERITY_ERROR, FACILITY_CONTROL, 0xcc1),
adErrObjectInADOCollection=     MAKE_HRESULT(SEVERITY_ERROR, FACILITY_CONTROL, 0xd27),
adErrObjectNotSet=     MAKE_HRESULT(SEVERITY_ERROR, FACILITY_CONTROL, 0xd5c),
adErrDataConversion=     MAKE_HRESULT(SEVERITY_ERROR, FACILITY_CONTROL, 0xd5d),
adErrObjectClosed=     MAKE_HRESULT(SEVERITY_ERROR, FACILITY_CONTROL, 0xe78),
adErrObjectOpen=     MAKE_HRESULT(SEVERITY_ERROR, FACILITY_CONTROL, 0xe79),
adErrProviderNotFound=     MAKE_HRESULT(SEVERITY_ERROR, FACILITY_CONTROL, 0xe7a),
adErrBoundToCommand=     MAKE_HRESULT(SEVERITY_ERROR, FACILITY_CONTROL, 0xe7b),
adErrInvalidParamInfo=     MAKE_HRESULT(SEVERITY_ERROR, FACILITY_CONTROL, 0xe7c),
adErrInvalidConnection=     MAKE_HRESULT(SEVERITY_ERROR, FACILITY_CONTROL, 0xe7d),
    }ErrorValueEnum;
typedef /* [uuid] */
enum ParameterAttributesEnum
    {adParamSigned= 0x10,
adParamNullable= 0x40,
adParamLong= 0x80
    }ParameterAttributesEnum;
typedef /* [uuid] */
enum ParameterDirectionEnum
    {adParamUnknown= 0,
adParamInput= 0x1,
adParamOutput= 0x2,
adParamInputOutput= 0x3,
adParamReturnValue= 0x4
    }ParameterDirectionEnum;
typedef /* [uuid] */
enum CommandTypeEnum
    {adCmdUnspecified= -1,
adCmdUnknown= 0x8,
adCmdText= 0x1,
adCmdTable= 0x2,
adCmdStoredProc= 0x4
    }CommandTypeEnum;
typedef /* [uuid] */
enum SchemaEnum
    {adSchemaProviderSpecific= -1,
adSchemaAsserts= 0,
adSchemaCatalogs= 1,
adSchemaCharacterSets= 2,
adSchemaCollations= 3,
adSchemaColumns= 4,
adSchemaCheckConstraints= 5,
adSchemaConstraintColumnUsage= 6,
adSchemaConstraintTableUsage= 7,
adSchemaKeyColumnUsage= 8,
adSchemaReferentialContraints= 9,
adSchemaTableConstraints= 10,
adSchemaColumnsDomainUsage= 11,
adSchemaIndexes= 12,
adSchemaColumnPrivileges= 13,
adSchemaTablePrivileges= 14,
adSchemaUsagePrivileges= 15,
adSchemaProcedures= 16,
adSchemaSchemata= 17,
adSchemaSQLLanguages= 18,
adSchemaStatistics= 19,
adSchemaTables= 20,
adSchemaTranslations= 21,
adSchemaProviderTypes= 22,
adSchemaViews= 23,
adSchemaViewColumnUsage= 24,
adSchemaViewTableUsage= 25,
adSchemaProcedureParameters= 26,
adSchemaForeignKeys= 27,
adSchemaPrimaryKeys= 28,
adSchemaProcedureColumns= 29
    }SchemaEnum;
EXTERN_C const IID LIBID_ADODB;
#ifndef ___ADOCollection_INTERFACE_DEFINED__
#define ___ADOCollection_INTERFACE_DEFINED__
/****************************************
 * Generated header for interface: _ADOCollection
 * at Tue Oct 07 23:50:20 1997
 * using MIDL 3.01.75
 ****************************************/
/* [object][uuid][nonextensible][dual] */
EXTERN_C const IID IID__ADOCollection;
#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("00000512-0000-0010-8000-00AA006D2EA4")
    _ADOCollection : public IDispatch
    {
    public:
        virtual /* [helpstring][helpcontext][propget] */ HRESULT STDMETHODCALLTYPE get_Count(
            /* [retval][out] */ long __RPC_FAR *c) = 0;

        virtual /* [id][restricted] */ HRESULT STDMETHODCALLTYPE _NewEnum(
            /* [retval][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppvObject) = 0;

        virtual /* [helpstring][helpcontext] */ HRESULT STDMETHODCALLTYPE Refresh( void) = 0;

    };

#else /* C style interface */
    typedef struct _ADOCollectionVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            _ADOCollection __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            _ADOCollection __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            _ADOCollection __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeInfoCount )(
            _ADOCollection __RPC_FAR * This,
            /* [out] */ UINT __RPC_FAR *pctinfo);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeInfo )(
            _ADOCollection __RPC_FAR * This,
            /* [in] */ UINT iTInfo,
            /* [in] */ LCID lcid,
            /* [out] */ ITypeInfo __RPC_FAR *__RPC_FAR *ppTInfo);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetIDsOfNames )(
            _ADOCollection __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [size_is][in] */ LPOLESTR __RPC_FAR *rgszNames,
            /* [in] */ UINT cNames,
            /* [in] */ LCID lcid,
            /* [size_is][out] */ DISPID __RPC_FAR *rgDispId);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Invoke )(
            _ADOCollection __RPC_FAR * This,
            /* [in] */ DISPID dispIdMember,
            /* [in] */ REFIID riid,
            /* [in] */ LCID lcid,
            /* [in] */ WORD wFlags,
            /* [out][in] */ DISPPARAMS __RPC_FAR *pDispParams,
            /* [out] */ VARIANT __RPC_FAR *pVarResult,
            /* [out] */ EXCEPINFO __RPC_FAR *pExcepInfo,
            /* [out] */ UINT __RPC_FAR *puArgErr);

        /* [helpstring][helpcontext][propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Count )(
            _ADOCollection __RPC_FAR * This,
            /* [retval][out] */ long __RPC_FAR *c);

        /* [id][restricted] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *_NewEnum )(
            _ADOCollection __RPC_FAR * This,
            /* [retval][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppvObject);

        /* [helpstring][helpcontext] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Refresh )(
            _ADOCollection __RPC_FAR * This);

        END_INTERFACE
    } _ADOCollectionVtbl;
    interface _ADOCollection
    {
        CONST_VTBL struct _ADOCollectionVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS
#define _ADOCollection_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
#define _ADOCollection_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)
#define _ADOCollection_Release(This)\
    (This)->lpVtbl -> Release(This)
#define _ADOCollection_GetTypeInfoCount(This,pctinfo)\
    (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo)
#define _ADOCollection_GetTypeInfo(This,iTInfo,lcid,ppTInfo)\
    (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo)
#define _ADOCollection_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)\
    (This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)
#define _ADOCollection_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)\
    (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)
#define _Collection_get_Count(This,c)\
    (This)->lpVtbl -> get_Count(This,c)
#define _ADOCollection__NewEnum(This,ppvObject)\
    (This)->lpVtbl -> _NewEnum(This,ppvObject)
#define _ADOCollection_Refresh(This)\
    (This)->lpVtbl -> Refresh(This)
#endif /* COBJMACROS */
#endif /* C style interface */
/* [helpstring][helpcontext][propget] */ HRESULT STDMETHODCALLTYPE _Collection_get_Count_Proxy(
    _ADOCollection __RPC_FAR * This,
    /* [retval][out] */ long __RPC_FAR *c);
void __RPC_STUB _Collection_get_Count_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [id][restricted] */ HRESULT STDMETHODCALLTYPE _ADOCollection__NewEnum_Proxy(
    _ADOCollection __RPC_FAR * This,
    /* [retval][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppvObject);
void __RPC_STUB _ADOCollection__NewEnum_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [helpstring][helpcontext] */ HRESULT STDMETHODCALLTYPE _ADOCollection_Refresh_Proxy(
    _ADOCollection __RPC_FAR * This);
void __RPC_STUB _ADOCollection_Refresh_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
#endif /* ___ADOCollection_INTERFACE_DEFINED__ */
#ifndef ___ADODynaCollection_INTERFACE_DEFINED__
#define ___ADODynaCollection_INTERFACE_DEFINED__
/****************************************
 * Generated header for interface: _ADODynaCollection
 * at Tue Oct 07 23:50:20 1997
 * using MIDL 3.01.75
 ****************************************/
/* [object][uuid][nonextensible][dual] */
EXTERN_C const IID IID__ADODynaCollection;
#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("00000513-0000-0010-8000-00AA006D2EA4")
_ADODynaCollection : public _ADOCollection
    {
    public:
        virtual /* [helpstring][helpcontext] */ HRESULT STDMETHODCALLTYPE Append(
            /* [in] */ IDispatch __RPC_FAR *Object) = 0;

        virtual /* [helpstring][helpcontext] */ HRESULT STDMETHODCALLTYPE Delete(
            /* [in] */ VARIANT Index) = 0;

    };

#else /* C style interface */
    typedef struct _ADODynaCollectionVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            _ADODynaCollection __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            _ADODynaCollection __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            _ADODynaCollection __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeInfoCount )(
            _ADODynaCollection __RPC_FAR * This,
            /* [out] */ UINT __RPC_FAR *pctinfo);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeInfo )(
            _ADODynaCollection __RPC_FAR * This,
            /* [in] */ UINT iTInfo,
            /* [in] */ LCID lcid,
            /* [out] */ ITypeInfo __RPC_FAR *__RPC_FAR *ppTInfo);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetIDsOfNames )(
            _ADODynaCollection __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [size_is][in] */ LPOLESTR __RPC_FAR *rgszNames,
            /* [in] */ UINT cNames,
            /* [in] */ LCID lcid,
            /* [size_is][out] */ DISPID __RPC_FAR *rgDispId);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Invoke )(
            _ADODynaCollection __RPC_FAR * This,
            /* [in] */ DISPID dispIdMember,
            /* [in] */ REFIID riid,
            /* [in] */ LCID lcid,
            /* [in] */ WORD wFlags,
            /* [out][in] */ DISPPARAMS __RPC_FAR *pDispParams,
            /* [out] */ VARIANT __RPC_FAR *pVarResult,
            /* [out] */ EXCEPINFO __RPC_FAR *pExcepInfo,
            /* [out] */ UINT __RPC_FAR *puArgErr);

        /* [helpstring][helpcontext][propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Count )(
            _ADODynaCollection __RPC_FAR * This,
            /* [retval][out] */ long __RPC_FAR *c);

        /* [id][restricted] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *_NewEnum )(
            _ADODynaCollection __RPC_FAR * This,
            /* [retval][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppvObject);

        /* [helpstring][helpcontext] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Refresh )(
            _ADODynaCollection __RPC_FAR * This);

        /* [helpstring][helpcontext] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Append )(
            _ADODynaCollection __RPC_FAR * This,
            /* [in] */ IDispatch __RPC_FAR *Object);

        /* [helpstring][helpcontext] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Delete )(
            _ADODynaCollection __RPC_FAR * This,
            /* [in] */ VARIANT Index);

        END_INTERFACE
    } _ADODynaCollectionVtbl;
    interface _ADODynaCollection
    {
        CONST_VTBL struct _ADODynaCollectionVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS
#define _ADODynaCollection_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
#define _ADODynaCollection_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)
#define _ADODynaCollection_Release(This)\
    (This)->lpVtbl -> Release(This)
#define _ADODynaCollection_GetTypeInfoCount(This,pctinfo)\
    (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo)
#define _ADODynaCollection_GetTypeInfo(This,iTInfo,lcid,ppTInfo)\
    (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo)
#define _ADODynaCollection_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)\
    (This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)
#define _ADODynaCollection_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)\
    (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)
#define _DynaCollection_get_Count(This,c)\
    (This)->lpVtbl -> get_Count(This,c)
#define _ADODynaCollection__NewEnum(This,ppvObject)\
    (This)->lpVtbl -> _NewEnum(This,ppvObject)
#define _ADODynaCollection_Refresh(This)\
    (This)->lpVtbl -> Refresh(This)
#define _ADODynaCollection_Append(This,Object)\
    (This)->lpVtbl -> Append(This,Object)
#define _ADODynaCollection_Delete(This,Index)\
    (This)->lpVtbl -> Delete(This,Index)
#endif /* COBJMACROS */
#endif /* C style interface */
/* [helpstring][helpcontext] */ HRESULT STDMETHODCALLTYPE _ADODynaCollection_Append_Proxy(
    _ADODynaCollection __RPC_FAR * This,
    /* [in] */ IDispatch __RPC_FAR *Object);
void __RPC_STUB _ADODynaCollection_Append_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [helpstring][helpcontext] */ HRESULT STDMETHODCALLTYPE _ADODynaCollection_Delete_Proxy(
    _ADODynaCollection __RPC_FAR * This,
    /* [in] */ VARIANT Index);
void __RPC_STUB _ADODynaCollection_Delete_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
#endif /* ___ADODynaCollection_INTERFACE_DEFINED__ */
#ifndef ___ADO_INTERFACE_DEFINED__
#define ___ADO_INTERFACE_DEFINED__
/****************************************
 * Generated header for interface: _ADO
 * at Tue Oct 07 23:50:20 1997
 * using MIDL 3.01.75
 ****************************************/
/* [object][uuid][nonextensible][dual] */
EXTERN_C const IID IID__ADO;
#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("00000534-0000-0010-8000-00AA006D2EA4")
    _ADO : public IDispatch
    {
    public:
        virtual /* [helpstring][helpcontext][id][propget] */ HRESULT STDMETHODCALLTYPE get_Properties(
            /* [retval][out] */ ADOProperties __RPC_FAR *__RPC_FAR *ppvObject) = 0;

    };

#else /* C style interface */
    typedef struct _ADOVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            _ADO __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            _ADO __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            _ADO __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeInfoCount )(
            _ADO __RPC_FAR * This,
            /* [out] */ UINT __RPC_FAR *pctinfo);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeInfo )(
            _ADO __RPC_FAR * This,
            /* [in] */ UINT iTInfo,
            /* [in] */ LCID lcid,
            /* [out] */ ITypeInfo __RPC_FAR *__RPC_FAR *ppTInfo);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetIDsOfNames )(
            _ADO __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [size_is][in] */ LPOLESTR __RPC_FAR *rgszNames,
            /* [in] */ UINT cNames,
            /* [in] */ LCID lcid,
            /* [size_is][out] */ DISPID __RPC_FAR *rgDispId);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Invoke )(
            _ADO __RPC_FAR * This,
            /* [in] */ DISPID dispIdMember,
            /* [in] */ REFIID riid,
            /* [in] */ LCID lcid,
            /* [in] */ WORD wFlags,
            /* [out][in] */ DISPPARAMS __RPC_FAR *pDispParams,
            /* [out] */ VARIANT __RPC_FAR *pVarResult,
            /* [out] */ EXCEPINFO __RPC_FAR *pExcepInfo,
            /* [out] */ UINT __RPC_FAR *puArgErr);

        /* [helpstring][helpcontext][id][propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Properties )(
            _ADO __RPC_FAR * This,
            /* [retval][out] */ ADOProperties __RPC_FAR *__RPC_FAR *ppvObject);

        END_INTERFACE
    } _ADOVtbl;
    interface _ADO
    {
        CONST_VTBL struct _ADOVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS
#define _ADO_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
#define _ADO_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)
#define _ADO_Release(This)\
    (This)->lpVtbl -> Release(This)
#define _ADO_GetTypeInfoCount(This,pctinfo)\
    (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo)
#define _ADO_GetTypeInfo(This,iTInfo,lcid,ppTInfo)\
    (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo)
#define _ADO_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)\
    (This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)
#define _ADO_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)\
    (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)
#define _ADO_get_Properties(This,ppvObject)\
    (This)->lpVtbl -> get_Properties(This,ppvObject)
#endif /* COBJMACROS */
#endif /* C style interface */
/* [helpstring][helpcontext][id][propget] */ HRESULT STDMETHODCALLTYPE _ADO_get_Properties_Proxy(
    _ADO __RPC_FAR * This,
    /* [retval][out] */ ADOProperties __RPC_FAR *__RPC_FAR *ppvObject);
void __RPC_STUB _ADO_get_Properties_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* ___ADO_INTERFACE_DEFINED__ */
#ifndef __Error_INTERFACE_DEFINED__
#define __Error_INTERFACE_DEFINED__
/****************************************
 * Generated header for interface: Error
 * at Tue Oct 07 23:50:20 1997
 * using MIDL 3.01.75
 ****************************************/
/* [object][helpstring][helpcontext][uuid][nonextensible][dual] */
EXTERN_C const IID IID_Error;
#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("00000500-0000-0010-8000-00AA006D2EA4")
    ADOError : public IDispatch
    {
    public:
        virtual /* [helpstring][helpcontext][propget] */ HRESULT STDMETHODCALLTYPE get_Number(
            /* [retval][out] */ long __RPC_FAR *pl) = 0;

        virtual /* [helpstring][helpcontext][propget] */ HRESULT STDMETHODCALLTYPE get_Source(
            /* [retval][out] */ BSTR __RPC_FAR *pbstr) = 0;

        virtual /* [helpstring][helpcontext][id][propget] */ HRESULT STDMETHODCALLTYPE get_Description(
            /* [retval][out] */ BSTR __RPC_FAR *pbstr) = 0;

        virtual /* [helpstring][helpcontext][propget] */ HRESULT STDMETHODCALLTYPE get_HelpFile(
            /* [retval][out] */ BSTR __RPC_FAR *pbstr) = 0;

        virtual /* [helpstring][helpcontext][propget] */ HRESULT STDMETHODCALLTYPE get_HelpContext(
            /* [retval][out] */ long __RPC_FAR *pl) = 0;

        virtual /* [helpstring][helpcontext][propget] */ HRESULT STDMETHODCALLTYPE get_SQLState(
            /* [retval][out] */ BSTR __RPC_FAR *pbstr) = 0;

        virtual /* [helpstring][helpcontext][propget] */ HRESULT STDMETHODCALLTYPE get_NativeError(
            /* [retval][out] */ long __RPC_FAR *pl) = 0;

    };

#else /* C style interface */
    typedef struct ErrorVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            ADOError __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            ADOError __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            ADOError __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeInfoCount )(
            ADOError __RPC_FAR * This,
            /* [out] */ UINT __RPC_FAR *pctinfo);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeInfo )(
            ADOError __RPC_FAR * This,
            /* [in] */ UINT iTInfo,
            /* [in] */ LCID lcid,
            /* [out] */ ITypeInfo __RPC_FAR *__RPC_FAR *ppTInfo);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetIDsOfNames )(
            ADOError __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [size_is][in] */ LPOLESTR __RPC_FAR *rgszNames,
            /* [in] */ UINT cNames,
            /* [in] */ LCID lcid,
            /* [size_is][out] */ DISPID __RPC_FAR *rgDispId);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Invoke )(
            ADOError __RPC_FAR * This,
            /* [in] */ DISPID dispIdMember,
            /* [in] */ REFIID riid,
            /* [in] */ LCID lcid,
            /* [in] */ WORD wFlags,
            /* [out][in] */ DISPPARAMS __RPC_FAR *pDispParams,
            /* [out] */ VARIANT __RPC_FAR *pVarResult,
            /* [out] */ EXCEPINFO __RPC_FAR *pExcepInfo,
            /* [out] */ UINT __RPC_FAR *puArgErr);

        /* [helpstring][helpcontext][propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Number )(
            ADOError __RPC_FAR * This,
            /* [retval][out] */ long __RPC_FAR *pl);

        /* [helpstring][helpcontext][propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Source )(
            ADOError __RPC_FAR * This,
            /* [retval][out] */ BSTR __RPC_FAR *pbstr);

        /* [helpstring][helpcontext][id][propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Description )(
            ADOError __RPC_FAR * This,
            /* [retval][out] */ BSTR __RPC_FAR *pbstr);

        /* [helpstring][helpcontext][propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_HelpFile )(
            ADOError __RPC_FAR * This,
            /* [retval][out] */ BSTR __RPC_FAR *pbstr);

        /* [helpstring][helpcontext][propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_HelpContext )(
            ADOError __RPC_FAR * This,
            /* [retval][out] */ long __RPC_FAR *pl);

        /* [helpstring][helpcontext][propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_SQLState )(
            ADOError __RPC_FAR * This,
            /* [retval][out] */ BSTR __RPC_FAR *pbstr);

        /* [helpstring][helpcontext][propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_NativeError )(
            ADOError __RPC_FAR * This,
            /* [retval][out] */ long __RPC_FAR *pl);

        END_INTERFACE
    } ErrorVtbl;
    interface Error
    {
        CONST_VTBL struct ErrorVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS
#define Error_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
#define Error_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)
#define Error_Release(This)\
    (This)->lpVtbl -> Release(This)
#define Error_GetTypeInfoCount(This,pctinfo)\
    (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo)
#define Error_GetTypeInfo(This,iTInfo,lcid,ppTInfo)\
    (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo)
#define Error_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)\
    (This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)
#define Error_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)\
    (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)
#define Error_get_Number(This,pl)\
    (This)->lpVtbl -> get_Number(This,pl)
#define Error_get_Source(This,pbstr)\
    (This)->lpVtbl -> get_Source(This,pbstr)
#define Error_get_Description(This,pbstr)\
    (This)->lpVtbl -> get_Description(This,pbstr)
#define Error_get_HelpFile(This,pbstr)\
    (This)->lpVtbl -> get_HelpFile(This,pbstr)
#define Error_get_HelpContext(This,pl)\
    (This)->lpVtbl -> get_HelpContext(This,pl)
#define Error_get_SQLState(This,pbstr)\
    (This)->lpVtbl -> get_SQLState(This,pbstr)
#define Error_get_NativeError(This,pl)\
    (This)->lpVtbl -> get_NativeError(This,pl)
#endif /* COBJMACROS */
#endif /* C style interface */
/* [helpstring][helpcontext][propget] */ HRESULT STDMETHODCALLTYPE Error_get_Number_Proxy(
    ADOError __RPC_FAR * This,
    /* [retval][out] */ long __RPC_FAR *pl);
void __RPC_STUB Error_get_Number_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [helpstring][helpcontext][propget] */ HRESULT STDMETHODCALLTYPE Error_get_Source_Proxy(
    ADOError __RPC_FAR * This,
    /* [retval][out] */ BSTR __RPC_FAR *pbstr);
void __RPC_STUB Error_get_Source_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [helpstring][helpcontext][id][propget] */ HRESULT STDMETHODCALLTYPE Error_get_Description_Proxy(
    ADOError __RPC_FAR * This,
    /* [retval][out] */ BSTR __RPC_FAR *pbstr);
void __RPC_STUB Error_get_Description_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [helpstring][helpcontext][propget] */ HRESULT STDMETHODCALLTYPE Error_get_HelpFile_Proxy(
    ADOError __RPC_FAR * This,
    /* [retval][out] */ BSTR __RPC_FAR *pbstr);
void __RPC_STUB Error_get_HelpFile_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [helpstring][helpcontext][propget] */ HRESULT STDMETHODCALLTYPE Error_get_HelpContext_Proxy(
    ADOError __RPC_FAR * This,
    /* [retval][out] */ long __RPC_FAR *pl);
void __RPC_STUB Error_get_HelpContext_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [helpstring][helpcontext][propget] */ HRESULT STDMETHODCALLTYPE Error_get_SQLState_Proxy(
    ADOError __RPC_FAR * This,
    /* [retval][out] */ BSTR __RPC_FAR *pbstr);
void __RPC_STUB Error_get_SQLState_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [helpstring][helpcontext][propget] */ HRESULT STDMETHODCALLTYPE Error_get_NativeError_Proxy(
    ADOError __RPC_FAR * This,
    /* [retval][out] */ long __RPC_FAR *pl);
void __RPC_STUB Error_get_NativeError_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
#endif /* __Error_INTERFACE_DEFINED__ */
#ifndef __Errors_INTERFACE_DEFINED__
#define __Errors_INTERFACE_DEFINED__
/****************************************
 * Generated header for interface: Errors
 * at Tue Oct 07 23:50:20 1997
 * using MIDL 3.01.75
 ****************************************/
/* [object][uuid][helpstring][helpcontext][nonextensible][dual] */
EXTERN_C const IID IID_Errors;
#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("00000501-0000-0010-8000-00AA006D2EA4")
    ADOErrors : public _ADOCollection
    {
    public:
        virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_Item(
            /* [in] */ VARIANT Index,
            /* [retval][out] */ ADOError __RPC_FAR *__RPC_FAR *ppvObject) = 0;

        virtual HRESULT STDMETHODCALLTYPE Clear( void) = 0;

    };

#else /* C style interface */
    typedef struct ErrorsVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            ADOErrors __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            ADOErrors __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            ADOErrors __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeInfoCount )(
            ADOErrors __RPC_FAR * This,
            /* [out] */ UINT __RPC_FAR *pctinfo);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeInfo )(
            ADOErrors __RPC_FAR * This,
            /* [in] */ UINT iTInfo,
            /* [in] */ LCID lcid,
            /* [out] */ ITypeInfo __RPC_FAR *__RPC_FAR *ppTInfo);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetIDsOfNames )(
            ADOErrors __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [size_is][in] */ LPOLESTR __RPC_FAR *rgszNames,
            /* [in] */ UINT cNames,
            /* [in] */ LCID lcid,
            /* [size_is][out] */ DISPID __RPC_FAR *rgDispId);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Invoke )(
            ADOErrors __RPC_FAR * This,
            /* [in] */ DISPID dispIdMember,
            /* [in] */ REFIID riid,
            /* [in] */ LCID lcid,
            /* [in] */ WORD wFlags,
            /* [out][in] */ DISPPARAMS __RPC_FAR *pDispParams,
            /* [out] */ VARIANT __RPC_FAR *pVarResult,
            /* [out] */ EXCEPINFO __RPC_FAR *pExcepInfo,
            /* [out] */ UINT __RPC_FAR *puArgErr);

        /* [helpstring][helpcontext][propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Count )(
            ADOErrors __RPC_FAR * This,
            /* [retval][out] */ long __RPC_FAR *c);

        /* [id][restricted] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *_NewEnum )(
            ADOErrors __RPC_FAR * This,
            /* [retval][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppvObject);

        /* [helpstring][helpcontext] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Refresh )(
            ADOErrors __RPC_FAR * This);

        /* [id][propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Item )(
            ADOErrors __RPC_FAR * This,
            /* [in] */ VARIANT Index,
            /* [retval][out] */ ADOError __RPC_FAR *__RPC_FAR *ppvObject);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Clear )(
            ADOErrors __RPC_FAR * This);

        END_INTERFACE
    } ErrorsVtbl;
    interface Errors
    {
        CONST_VTBL struct ErrorsVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS
#define Errors_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
#define Errors_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)
#define Errors_Release(This)\
    (This)->lpVtbl -> Release(This)
#define Errors_GetTypeInfoCount(This,pctinfo)\
    (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo)
#define Errors_GetTypeInfo(This,iTInfo,lcid,ppTInfo)\
    (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo)
#define Errors_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)\
    (This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)
#define Errors_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)\
    (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)
#define Errors_get_Count(This,c)\
    (This)->lpVtbl -> get_Count(This,c)
#define Errors__NewEnum(This,ppvObject)\
    (This)->lpVtbl -> _NewEnum(This,ppvObject)
#define Errors_Refresh(This)\
    (This)->lpVtbl -> Refresh(This)
#define Errors_get_Item(This,Index,ppvObject)\
    (This)->lpVtbl -> get_Item(This,Index,ppvObject)
#define Errors_Clear(This)\
    (This)->lpVtbl -> Clear(This)
#endif /* COBJMACROS */
#endif /* C style interface */
/* [id][propget] */ HRESULT STDMETHODCALLTYPE Errors_get_Item_Proxy(
    ADOErrors __RPC_FAR * This,
    /* [in] */ VARIANT Index,
    /* [retval][out] */ ADOError __RPC_FAR *__RPC_FAR *ppvObject);
void __RPC_STUB Errors_get_Item_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
HRESULT STDMETHODCALLTYPE Errors_Clear_Proxy(
    ADOErrors __RPC_FAR * This);
void __RPC_STUB Errors_Clear_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
#endif /* __Errors_INTERFACE_DEFINED__ */
#ifndef ___Command_INTERFACE_DEFINED__
#define ___Command_INTERFACE_DEFINED__
/****************************************
 * Generated header for interface: _Command
 * at Tue Oct 07 23:50:20 1997
 * using MIDL 3.01.75
 ****************************************/
/* [object][uuid][nonextensible][dual] */
EXTERN_C const IID IID__Command;
#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("00000508-0000-0010-8000-00AA006D2EA4")
    _ADOCommand : public _ADO
    {
    public:
        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_ActiveConnection(
            /* [retval][out] */ _ADOConnection __RPC_FAR *__RPC_FAR *ppvObject) = 0;

        virtual /* [propputref] */ HRESULT STDMETHODCALLTYPE putref_ActiveConnection(
            /* [in] */ _ADOConnection __RPC_FAR *pCon) = 0;

        virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_ActiveConnection(
            /* [in] */ VARIANT vConn) = 0;

        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_CommandText(
            /* [retval][out] */ BSTR __RPC_FAR *pbstr) = 0;

        virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_CommandText(
            /* [in] */ BSTR bstr) = 0;

        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_CommandTimeout(
            /* [retval][out] */ LONG __RPC_FAR *pl) = 0;

        virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_CommandTimeout(
            /* [in] */ LONG Timeout) = 0;

        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Prepared(
            /* [retval][out] */ VARIANT_BOOL __RPC_FAR *pfPrepared) = 0;

        virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_Prepared(
            /* [in] */ VARIANT_BOOL fPrepared) = 0;

        virtual /* [helpstring][helpcontext] */ HRESULT STDMETHODCALLTYPE Execute(
            /* [optional][out] */ VARIANT __RPC_FAR *RecordsAffected,
            /* [optional][in] */ VARIANT __RPC_FAR *Parameters,
            /* [defaultvalue][in] */ long Options,
            /* [retval][out] */ _ADORecordset __RPC_FAR *__RPC_FAR *ppirs) = 0;

        virtual HRESULT STDMETHODCALLTYPE CreateParameter(
            /* [defaultvalue][in] */ BSTR Name,
            /* [defaultvalue][in] */ DataTypeEnum Type,
            /* [defaultvalue][in] */ ParameterDirectionEnum Direction,
            /* [defaultvalue][in] */ long Size,
            /* [optional][in] */ VARIANT Value,
            /* [retval][out] */ _ADOParameter __RPC_FAR *__RPC_FAR *ppiprm) = 0;

        virtual /* [helpstring][helpcontext][id][propget] */ HRESULT STDMETHODCALLTYPE get_Parameters(
            /* [retval][out] */ ADOParameters __RPC_FAR *__RPC_FAR *ppvObject) = 0;

        virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_CommandType(
            /* [in] */ CommandTypeEnum lCmdType) = 0;

        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_CommandType(
            /* [retval][out] */ CommandTypeEnum __RPC_FAR *plCmdType) = 0;

        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Name(
            /* [retval][out] */ BSTR __RPC_FAR *pbstrName) = 0;

        virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_Name(
            /* [in] */ BSTR bstrName) = 0;

    };

#else /* C style interface */
    typedef struct _CommandVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            _ADOCommand __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            _ADOCommand __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            _ADOCommand __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeInfoCount )(
            _ADOCommand __RPC_FAR * This,
            /* [out] */ UINT __RPC_FAR *pctinfo);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeInfo )(
            _ADOCommand __RPC_FAR * This,
            /* [in] */ UINT iTInfo,
            /* [in] */ LCID lcid,
            /* [out] */ ITypeInfo __RPC_FAR *__RPC_FAR *ppTInfo);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetIDsOfNames )(
            _ADOCommand __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [size_is][in] */ LPOLESTR __RPC_FAR *rgszNames,
            /* [in] */ UINT cNames,
            /* [in] */ LCID lcid,
            /* [size_is][out] */ DISPID __RPC_FAR *rgDispId);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Invoke )(
            _ADOCommand __RPC_FAR * This,
            /* [in] */ DISPID dispIdMember,
            /* [in] */ REFIID riid,
            /* [in] */ LCID lcid,
            /* [in] */ WORD wFlags,
            /* [out][in] */ DISPPARAMS __RPC_FAR *pDispParams,
            /* [out] */ VARIANT __RPC_FAR *pVarResult,
            /* [out] */ EXCEPINFO __RPC_FAR *pExcepInfo,
            /* [out] */ UINT __RPC_FAR *puArgErr);

        /* [helpstring][helpcontext][id][propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Properties )(
            _ADOCommand __RPC_FAR * This,
            /* [retval][out] */ ADOProperties __RPC_FAR *__RPC_FAR *ppvObject);

        /* [propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_ActiveConnection )(
            _ADOCommand __RPC_FAR * This,
            /* [retval][out] */ _ADOConnection __RPC_FAR *__RPC_FAR *ppvObject);

        /* [propputref] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *putref_ActiveADOConnection )(
            _ADOCommand __RPC_FAR * This,
            /* [in] */ _ADOConnection __RPC_FAR *pCon);

        /* [propput] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_ActiveConnection )(
            _ADOCommand __RPC_FAR * This,
            /* [in] */ VARIANT vConn);

        /* [propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_CommandText )(
            _ADOCommand __RPC_FAR * This,
            /* [retval][out] */ BSTR __RPC_FAR *pbstr);

        /* [propput] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_CommandText )(
            _ADOCommand __RPC_FAR * This,
            /* [in] */ BSTR bstr);

        /* [propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_CommandTimeout )(
            _ADOCommand __RPC_FAR * This,
            /* [retval][out] */ LONG __RPC_FAR *pl);

        /* [propput] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_CommandTimeout )(
            _ADOCommand __RPC_FAR * This,
            /* [in] */ LONG Timeout);

        /* [propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Prepared )(
            _ADOCommand __RPC_FAR * This,
            /* [retval][out] */ VARIANT_BOOL __RPC_FAR *pfPrepared);

        /* [propput] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_Prepared )(
            _ADOCommand __RPC_FAR * This,
            /* [in] */ VARIANT_BOOL fPrepared);

        /* [helpstring][helpcontext] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Execute )(
            _ADOCommand __RPC_FAR * This,
            /* [optional][out] */ VARIANT __RPC_FAR *RecordsAffected,
            /* [optional][in] */ VARIANT __RPC_FAR *Parameters,
            /* [defaultvalue][in] */ long Options,
            /* [retval][out] */ _ADORecordset __RPC_FAR *__RPC_FAR *ppirs);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *CreateParameter )(
            _ADOCommand __RPC_FAR * This,
            /* [defaultvalue][in] */ BSTR Name,
            /* [defaultvalue][in] */ DataTypeEnum Type,
            /* [defaultvalue][in] */ ParameterDirectionEnum Direction,
            /* [defaultvalue][in] */ long Size,
            /* [optional][in] */ VARIANT Value,
            /* [retval][out] */ _ADOParameter __RPC_FAR *__RPC_FAR *ppiprm);

        /* [helpstring][helpcontext][id][propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Parameters )(
            _ADOCommand __RPC_FAR * This,
            /* [retval][out] */ ADOParameters __RPC_FAR *__RPC_FAR *ppvObject);

        /* [propput] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_CommandType )(
            _ADOCommand __RPC_FAR * This,
            /* [in] */ CommandTypeEnum lCmdType);

        /* [propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_CommandType )(
            _ADOCommand __RPC_FAR * This,
            /* [retval][out] */ CommandTypeEnum __RPC_FAR *plCmdType);

        /* [propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Name )(
            _ADOCommand __RPC_FAR * This,
            /* [retval][out] */ BSTR __RPC_FAR *pbstrName);

        /* [propput] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_Name )(
            _ADOCommand __RPC_FAR * This,
            /* [in] */ BSTR bstrName);

        END_INTERFACE
    } _CommandVtbl;
    interface _Command
    {
        CONST_VTBL struct _CommandVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS
#define _Command_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
#define _Command_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)
#define _Command_Release(This)\
    (This)->lpVtbl -> Release(This)
#define _Command_GetTypeInfoCount(This,pctinfo)\
    (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo)
#define _Command_GetTypeInfo(This,iTInfo,lcid,ppTInfo)\
    (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo)
#define _Command_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)\
    (This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)
#define _Command_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)\
    (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)
#define _Command_get_Properties(This,ppvObject)\
    (This)->lpVtbl -> get_Properties(This,ppvObject)
#define _Command_get_ActiveConnection(This,ppvObject)\
    (This)->lpVtbl -> get_ActiveConnection(This,ppvObject)
#define _Command_putref_ActiveConnection(This,pCon)\
    (This)->lpVtbl -> putref_ActiveConnection(This,pCon)
#define _Command_put_ActiveConnection(This,vConn)\
    (This)->lpVtbl -> put_ActiveConnection(This,vConn)
#define _Command_get_CommandText(This,pbstr)\
    (This)->lpVtbl -> get_CommandText(This,pbstr)
#define _Command_put_CommandText(This,bstr)\
    (This)->lpVtbl -> put_CommandText(This,bstr)
#define _Command_get_CommandTimeout(This,pl)\
    (This)->lpVtbl -> get_CommandTimeout(This,pl)
#define _Command_put_CommandTimeout(This,Timeout)\
    (This)->lpVtbl -> put_CommandTimeout(This,Timeout)
#define _Command_get_Prepared(This,pfPrepared)\
    (This)->lpVtbl -> get_Prepared(This,pfPrepared)
#define _Command_put_Prepared(This,fPrepared)\
    (This)->lpVtbl -> put_Prepared(This,fPrepared)
#define _Command_Execute(This,RecordsAffected,Parameters,Options,ppirs)\
    (This)->lpVtbl -> Execute(This,RecordsAffected,Parameters,Options,ppirs)
#define _Command_CreateParameter(This,Name,Type,Direction,Size,Value,ppiprm)\
    (This)->lpVtbl -> CreateParameter(This,Name,Type,Direction,Size,Value,ppiprm)
#define _Command_get_Parameters(This,ppvObject)\
    (This)->lpVtbl -> get_Parameters(This,ppvObject)
#define _Command_put_CommandType(This,lCmdType)\
    (This)->lpVtbl -> put_CommandType(This,lCmdType)
#define _Command_get_CommandType(This,plCmdType)\
    (This)->lpVtbl -> get_CommandType(This,plCmdType)
#define _Command_get_Name(This,pbstrName)\
    (This)->lpVtbl -> get_Name(This,pbstrName)
#define _Command_put_Name(This,bstrName)\
    (This)->lpVtbl -> put_Name(This,bstrName)
#endif /* COBJMACROS */
#endif /* C style interface */
/* [propget] */ HRESULT STDMETHODCALLTYPE _Command_get_ActiveConnection_Proxy(
    _ADOCommand __RPC_FAR * This,
    /* [retval][out] */ _ADOConnection __RPC_FAR *__RPC_FAR *ppvObject);
void __RPC_STUB _Command_get_ActiveConnection_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propputref] */ HRESULT STDMETHODCALLTYPE _Command_putref_ActiveConnection_Proxy(
    _ADOCommand __RPC_FAR * This,
    /* [in] */ _ADOConnection __RPC_FAR *pCon);
void __RPC_STUB _Command_putref_ActiveConnection_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propput] */ HRESULT STDMETHODCALLTYPE _Command_put_ActiveConnection_Proxy(
    _ADOCommand __RPC_FAR * This,
    /* [in] */ VARIANT vConn);
void __RPC_STUB _Command_put_ActiveConnection_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propget] */ HRESULT STDMETHODCALLTYPE _Command_get_CommandText_Proxy(
    _ADOCommand __RPC_FAR * This,
    /* [retval][out] */ BSTR __RPC_FAR *pbstr);
void __RPC_STUB _Command_get_CommandText_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propput] */ HRESULT STDMETHODCALLTYPE _Command_put_CommandText_Proxy(
    _ADOCommand __RPC_FAR * This,
    /* [in] */ BSTR bstr);
void __RPC_STUB _Command_put_CommandText_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propget] */ HRESULT STDMETHODCALLTYPE _Command_get_CommandTimeout_Proxy(
    _ADOCommand __RPC_FAR * This,
    /* [retval][out] */ LONG __RPC_FAR *pl);
void __RPC_STUB _Command_get_CommandTimeout_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propput] */ HRESULT STDMETHODCALLTYPE _Command_put_CommandTimeout_Proxy(
    _ADOCommand __RPC_FAR * This,
    /* [in] */ LONG Timeout);
void __RPC_STUB _Command_put_CommandTimeout_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propget] */ HRESULT STDMETHODCALLTYPE _Command_get_Prepared_Proxy(
    _ADOCommand __RPC_FAR * This,
    /* [retval][out] */ VARIANT_BOOL __RPC_FAR *pfPrepared);
void __RPC_STUB _Command_get_Prepared_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propput] */ HRESULT STDMETHODCALLTYPE _Command_put_Prepared_Proxy(
    _ADOCommand __RPC_FAR * This,
    /* [in] */ VARIANT_BOOL fPrepared);

void __RPC_STUB _Command_put_Prepared_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [helpstring][helpcontext] */ HRESULT STDMETHODCALLTYPE _Command_Execute_Proxy(
    _ADOCommand __RPC_FAR * This,
    /* [optional][out] */ VARIANT __RPC_FAR *RecordsAffected,
    /* [optional][in] */ VARIANT __RPC_FAR *Parameters,
    /* [defaultvalue][in] */ long Options,
    /* [retval][out] */ _ADORecordset __RPC_FAR *__RPC_FAR *ppirs);
void __RPC_STUB _Command_Execute_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
HRESULT STDMETHODCALLTYPE _Command_CreateParameter_Proxy(
    _ADOCommand __RPC_FAR * This,
    /* [defaultvalue][in] */ BSTR Name,
    /* [defaultvalue][in] */ DataTypeEnum Type,
    /* [defaultvalue][in] */ ParameterDirectionEnum Direction,
    /* [defaultvalue][in] */ long Size,
    /* [optional][in] */ VARIANT Value,
    /* [retval][out] */ _ADOParameter __RPC_FAR *__RPC_FAR *ppiprm);
void __RPC_STUB _Command_CreateParameter_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [helpstring][helpcontext][id][propget] */ HRESULT STDMETHODCALLTYPE _Command_get_Parameters_Proxy(
    _ADOCommand __RPC_FAR * This,
    /* [retval][out] */ ADOParameters __RPC_FAR *__RPC_FAR *ppvObject);
void __RPC_STUB _Command_get_Parameters_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propput] */ HRESULT STDMETHODCALLTYPE _Command_put_CommandType_Proxy(
    _ADOCommand __RPC_FAR * This,
    /* [in] */ CommandTypeEnum lCmdType);
void __RPC_STUB _Command_put_CommandType_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propget] */ HRESULT STDMETHODCALLTYPE _Command_get_CommandType_Proxy(
    _ADOCommand __RPC_FAR * This,
    /* [retval][out] */ CommandTypeEnum __RPC_FAR *plCmdType);
void __RPC_STUB _Command_get_CommandType_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propget] */ HRESULT STDMETHODCALLTYPE _Command_get_Name_Proxy(
    _ADOCommand __RPC_FAR * This,
    /* [retval][out] */ BSTR __RPC_FAR *pbstrName);
void __RPC_STUB _Command_get_Name_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propput] */ HRESULT STDMETHODCALLTYPE _Command_put_Name_Proxy(
    _ADOCommand __RPC_FAR * This,
    /* [in] */ BSTR bstrName);
void __RPC_STUB _Command_put_Name_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
#endif /* ___Command_INTERFACE_DEFINED__ */
#ifndef ___Connection_INTERFACE_DEFINED__
#define ___Connection_INTERFACE_DEFINED__
/****************************************
 * Generated header for interface: _Connection
 * at Tue Oct 07 23:50:20 1997
 * using MIDL 3.01.75
 ****************************************/
/* [object][helpstring][helpcontext][uuid][dual] */
EXTERN_C const IID IID__Connection;
#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("00000515-0000-0010-8000-00AA006D2EA4")
    _ADOConnection : public _ADO
    {
    public:
        virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_ConnectionString(
            /* [retval][out] */ BSTR __RPC_FAR *pbstr) = 0;

        virtual /* [id][propput] */ HRESULT STDMETHODCALLTYPE put_ConnectionString(
            /* [in] */ BSTR bstr) = 0;

        virtual /* [helpstring][helpcontext][id][propget] */ HRESULT STDMETHODCALLTYPE get_CommandTimeout(
            /* [retval][out] */ LONG __RPC_FAR *plTimeout) = 0;

        virtual /* [id][propput] */ HRESULT STDMETHODCALLTYPE put_CommandTimeout(
            /* [in] */ LONG lTimeout) = 0;

        virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_ConnectionTimeout(
            /* [retval][out] */ LONG __RPC_FAR *plTimeout) = 0;

        virtual /* [id][propput] */ HRESULT STDMETHODCALLTYPE put_ConnectionTimeout(
            /* [in] */ LONG lTimeout) = 0;

        virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_Version(
            /* [retval][out] */ BSTR __RPC_FAR *pbstr) = 0;

        virtual /* [helpstring][helpcontext][id] */ HRESULT STDMETHODCALLTYPE Close( void) = 0;

        virtual /* [helpstring][helpcontext][id] */ HRESULT STDMETHODCALLTYPE Execute(
            /* [in] */ BSTR CommandText,
            /* [optional][out] */ VARIANT __RPC_FAR *RecordsAffected,
            /* [defaultvalue][in] */ long Options,
            /* [retval][out] */ _ADORecordset __RPC_FAR *__RPC_FAR *ppiRset) = 0;

        virtual /* [helpstring][helpcontext][id] */ HRESULT STDMETHODCALLTYPE BeginTrans(
            /* [retval][out] */ long __RPC_FAR *TransactionLevel) = 0;

        virtual /* [helpstring][helpcontext][id] */ HRESULT STDMETHODCALLTYPE CommitTrans( void) = 0;

        virtual /* [helpstring][helpcontext][id] */ HRESULT STDMETHODCALLTYPE RollbackTrans( void) = 0;

        virtual /* [id] */ HRESULT STDMETHODCALLTYPE Open(
            /* [defaultvalue][in] */ BSTR ConnectionString,
            /* [defaultvalue][in] */ BSTR UserID,
            /* [defaultvalue][in] */ BSTR Password,
            /* [defaultvalue][in] */ long Options) = 0;

        virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_Errors(
            /* [retval][out] */ ADOErrors __RPC_FAR *__RPC_FAR *ppvObject) = 0;

        virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_DefaultDatabase(
            /* [retval][out] */ BSTR __RPC_FAR *pbstr) = 0;

        virtual /* [id][propput] */ HRESULT STDMETHODCALLTYPE put_DefaultDatabase(
            /* [in] */ BSTR bstr) = 0;

        virtual /* [helpstring][helpcontext][id][propget] */ HRESULT STDMETHODCALLTYPE get_IsolationLevel(
            /* [retval][out] */ IsolationLevelEnum __RPC_FAR *Level) = 0;

        virtual /* [id][propput] */ HRESULT STDMETHODCALLTYPE put_IsolationLevel(
            /* [in] */ IsolationLevelEnum Level) = 0;

        virtual /* [helpstring][helpcontext][id][propget] */ HRESULT STDMETHODCALLTYPE get_Attributes(
            /* [retval][out] */ long __RPC_FAR *plAttr) = 0;

        virtual /* [id][propput] */ HRESULT STDMETHODCALLTYPE put_Attributes(
            /* [in] */ long lAttr) = 0;

        virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_CursorLocation(
            /* [retval][out] */ CursorLocationEnum __RPC_FAR *plCursorLoc) = 0;

        virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_CursorLocation(
            /* [in] */ CursorLocationEnum lCursorLoc) = 0;

        virtual /* [helpstring][helpcontext][id][propget] */ HRESULT STDMETHODCALLTYPE get_Mode(
            /* [retval][out] */ ConnectModeEnum __RPC_FAR *plMode) = 0;

        virtual /* [helpstring][helpcontext][id][propput] */ HRESULT STDMETHODCALLTYPE put_Mode(
            /* [in] */ ConnectModeEnum lMode) = 0;

        virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_Provider(
            /* [retval][out] */ BSTR __RPC_FAR *pbstr) = 0;

        virtual /* [id][propput] */ HRESULT STDMETHODCALLTYPE put_Provider(
            /* [in] */ BSTR Provider) = 0;

        virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_State(
            /* [retval][out] */ LONG __RPC_FAR *plObjState) = 0;

        virtual /* [id] */ HRESULT STDMETHODCALLTYPE OpenSchema(
            /* [in] */ SchemaEnum Schema,
            /* [optional][in] */ VARIANT Restrictions,
            /* [optional][in] */ VARIANT SchemaID,
            /* [retval][out] */ _ADORecordset __RPC_FAR *__RPC_FAR *pprset) = 0;

    };

#else /* C style interface */
    typedef struct _ConnectionVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            _ADOConnection __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            _ADOConnection __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            _ADOConnection __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeInfoCount )(
            _ADOConnection __RPC_FAR * This,
            /* [out] */ UINT __RPC_FAR *pctinfo);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeInfo )(
            _ADOConnection __RPC_FAR * This,
            /* [in] */ UINT iTInfo,
            /* [in] */ LCID lcid,
            /* [out] */ ITypeInfo __RPC_FAR *__RPC_FAR *ppTInfo);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetIDsOfNames )(
            _ADOConnection __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [size_is][in] */ LPOLESTR __RPC_FAR *rgszNames,
            /* [in] */ UINT cNames,
            /* [in] */ LCID lcid,
            /* [size_is][out] */ DISPID __RPC_FAR *rgDispId);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Invoke )(
            _ADOConnection __RPC_FAR * This,
            /* [in] */ DISPID dispIdMember,
            /* [in] */ REFIID riid,
            /* [in] */ LCID lcid,
            /* [in] */ WORD wFlags,
            /* [out][in] */ DISPPARAMS __RPC_FAR *pDispParams,
            /* [out] */ VARIANT __RPC_FAR *pVarResult,
            /* [out] */ EXCEPINFO __RPC_FAR *pExcepInfo,
            /* [out] */ UINT __RPC_FAR *puArgErr);

        /* [helpstring][helpcontext][id][propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Properties )(
            _ADOConnection __RPC_FAR * This,
            /* [retval][out] */ ADOProperties __RPC_FAR *__RPC_FAR *ppvObject);

        /* [id][propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_ConnectionString )(
            _ADOConnection __RPC_FAR * This,
            /* [retval][out] */ BSTR __RPC_FAR *pbstr);

        /* [id][propput] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_ConnectionString )(
            _ADOConnection __RPC_FAR * This,
            /* [in] */ BSTR bstr);

        /* [helpstring][helpcontext][id][propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_CommandTimeout )(
            _ADOConnection __RPC_FAR * This,
            /* [retval][out] */ LONG __RPC_FAR *plTimeout);

        /* [id][propput] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_CommandTimeout )(
            _ADOConnection __RPC_FAR * This,
            /* [in] */ LONG lTimeout);

        /* [id][propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_ConnectionTimeout )(
            _ADOConnection __RPC_FAR * This,
            /* [retval][out] */ LONG __RPC_FAR *plTimeout);

        /* [id][propput] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_ConnectionTimeout )(
            _ADOConnection __RPC_FAR * This,
            /* [in] */ LONG lTimeout);

        /* [id][propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Version )(
            _ADOConnection __RPC_FAR * This,
            /* [retval][out] */ BSTR __RPC_FAR *pbstr);

        /* [helpstring][helpcontext][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Close )(
            _ADOConnection __RPC_FAR * This);

        /* [helpstring][helpcontext][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Execute )(
            _ADOConnection __RPC_FAR * This,
            /* [in] */ BSTR CommandText,
            /* [optional][out] */ VARIANT __RPC_FAR *RecordsAffected,
            /* [defaultvalue][in] */ long Options,
            /* [retval][out] */ _ADORecordset __RPC_FAR *__RPC_FAR *ppiRset);

        /* [helpstring][helpcontext][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *BeginTrans )(
            _ADOConnection __RPC_FAR * This,
            /* [retval][out] */ long __RPC_FAR *TransactionLevel);

        /* [helpstring][helpcontext][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *CommitTrans )(
            _ADOConnection __RPC_FAR * This);

        /* [helpstring][helpcontext][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RollbackTrans )(
            _ADOConnection __RPC_FAR * This);

        /* [id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Open )(
            _ADOConnection __RPC_FAR * This,
            /* [defaultvalue][in] */ BSTR ConnectionString,
            /* [defaultvalue][in] */ BSTR UserID,
            /* [defaultvalue][in] */ BSTR Password,
            /* [defaultvalue][in] */ long Options);

        /* [id][propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Errors )(
            _ADOConnection __RPC_FAR * This,
            /* [retval][out] */ ADOErrors __RPC_FAR *__RPC_FAR *ppvObject);

        /* [id][propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_DefaultDatabase )(
            _ADOConnection __RPC_FAR * This,
            /* [retval][out] */ BSTR __RPC_FAR *pbstr);

        /* [id][propput] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_DefaultDatabase )(
            _ADOConnection __RPC_FAR * This,
            /* [in] */ BSTR bstr);

        /* [helpstring][helpcontext][id][propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_IsolationLevel )(
            _ADOConnection __RPC_FAR * This,
            /* [retval][out] */ IsolationLevelEnum __RPC_FAR *Level);

        /* [id][propput] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_IsolationLevel )(
            _ADOConnection __RPC_FAR * This,
            /* [in] */ IsolationLevelEnum Level);

        /* [helpstring][helpcontext][id][propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Attributes )(
            _ADOConnection __RPC_FAR * This,
            /* [retval][out] */ long __RPC_FAR *plAttr);

        /* [id][propput] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_Attributes )(
            _ADOConnection __RPC_FAR * This,
            /* [in] */ long lAttr);

        /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_CursorLocation )(
            _ADOConnection __RPC_FAR * This,
            /* [retval][out] */ CursorLocationEnum __RPC_FAR *plCursorLoc);

        /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_CursorLocation )(
            _ADOConnection __RPC_FAR * This,
            /* [in] */ CursorLocationEnum lCursorLoc);

        /* [helpstring][helpcontext][id][propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Mode )(
            _ADOConnection __RPC_FAR * This,
            /* [retval][out] */ ConnectModeEnum __RPC_FAR *plMode);

        /* [helpstring][helpcontext][id][propput] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_Mode )(
            _ADOConnection __RPC_FAR * This,
            /* [in] */ ConnectModeEnum lMode);

        /* [id][propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Provider )(
            _ADOConnection __RPC_FAR * This,
            /* [retval][out] */ BSTR __RPC_FAR *pbstr);

        /* [id][propput] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_Provider )(
            _ADOConnection __RPC_FAR * This,
            /* [in] */ BSTR Provider);

        /* [id][propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_State )(
            _ADOConnection __RPC_FAR * This,
            /* [retval][out] */ LONG __RPC_FAR *plObjState);

        /* [id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OpenSchema )(
            _ADOConnection __RPC_FAR * This,
            /* [in] */ SchemaEnum Schema,
            /* [optional][in] */ VARIANT Restrictions,
            /* [optional][in] */ VARIANT SchemaID,
            /* [retval][out] */ _ADORecordset __RPC_FAR *__RPC_FAR *pprset);

        END_INTERFACE
    } _ConnectionVtbl;
    interface _Connection
    {
        CONST_VTBL struct _ConnectionVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS
#define _Connection_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
#define _Connection_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)
#define _Connection_Release(This)\
    (This)->lpVtbl -> Release(This)
#define _Connection_GetTypeInfoCount(This,pctinfo)\
    (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo)
#define _Connection_GetTypeInfo(This,iTInfo,lcid,ppTInfo)\
    (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo)
#define _Connection_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)\
    (This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)
#define _Connection_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)\
    (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)
#define _Connection_get_Properties(This,ppvObject)\
    (This)->lpVtbl -> get_Properties(This,ppvObject)
#define _Connection_get_ConnectionString(This,pbstr)\
    (This)->lpVtbl -> get_ConnectionString(This,pbstr)
#define _Connection_put_ConnectionString(This,bstr)\
    (This)->lpVtbl -> put_ConnectionString(This,bstr)
#define _Connection_get_CommandTimeout(This,plTimeout)\
    (This)->lpVtbl -> get_CommandTimeout(This,plTimeout)
#define _Connection_put_CommandTimeout(This,lTimeout)\
    (This)->lpVtbl -> put_CommandTimeout(This,lTimeout)
#define _Connection_get_ConnectionTimeout(This,plTimeout)\
    (This)->lpVtbl -> get_ConnectionTimeout(This,plTimeout)
#define _Connection_put_ConnectionTimeout(This,lTimeout)\
    (This)->lpVtbl -> put_ConnectionTimeout(This,lTimeout)
#define _Connection_get_Version(This,pbstr)\
    (This)->lpVtbl -> get_Version(This,pbstr)
#define _Connection_Close(This)\
    (This)->lpVtbl -> Close(This)
#define _Connection_Execute(This,CommandText,RecordsAffected,Options,ppiRset)\
    (This)->lpVtbl -> Execute(This,CommandText,RecordsAffected,Options,ppiRset)
#define _Connection_BeginTrans(This,TransactionLevel)\
    (This)->lpVtbl -> BeginTrans(This,TransactionLevel)
#define _Connection_CommitTrans(This)\
    (This)->lpVtbl -> CommitTrans(This)
#define _Connection_RollbackTrans(This)\
    (This)->lpVtbl -> RollbackTrans(This)
#define _Connection_Open(This,ConnectionString,UserID,Password,Options)\
    (This)->lpVtbl -> Open(This,ConnectionString,UserID,Password,Options)
#define _Connection_get_Errors(This,ppvObject)\
    (This)->lpVtbl -> get_Errors(This,ppvObject)
#define _Connection_get_DefaultDatabase(This,pbstr)\
    (This)->lpVtbl -> get_DefaultDatabase(This,pbstr)
#define _Connection_put_DefaultDatabase(This,bstr)\
    (This)->lpVtbl -> put_DefaultDatabase(This,bstr)
#define _Connection_get_IsolationLevel(This,Level)\
    (This)->lpVtbl -> get_IsolationLevel(This,Level)
#define _Connection_put_IsolationLevel(This,Level)\
    (This)->lpVtbl -> put_IsolationLevel(This,Level)
#define _Connection_get_Attributes(This,plAttr)\
    (This)->lpVtbl -> get_Attributes(This,plAttr)
#define _Connection_put_Attributes(This,lAttr)\
    (This)->lpVtbl -> put_Attributes(This,lAttr)
#define _Connection_get_CursorLocation(This,plCursorLoc)\
    (This)->lpVtbl -> get_CursorLocation(This,plCursorLoc)
#define _Connection_put_CursorLocation(This,lCursorLoc)\
    (This)->lpVtbl -> put_CursorLocation(This,lCursorLoc)
#define _Connection_get_Mode(This,plMode)\
    (This)->lpVtbl -> get_Mode(This,plMode)
#define _Connection_put_Mode(This,lMode)\
    (This)->lpVtbl -> put_Mode(This,lMode)
#define _Connection_get_Provider(This,pbstr)\
    (This)->lpVtbl -> get_Provider(This,pbstr)
#define _Connection_put_Provider(This,Provider)\
    (This)->lpVtbl -> put_Provider(This,Provider)
#define _Connection_get_State(This,plObjState)\
    (This)->lpVtbl -> get_State(This,plObjState)
#define _Connection_OpenSchema(This,Schema,Restrictions,SchemaID,pprset)\
    (This)->lpVtbl -> OpenSchema(This,Schema,Restrictions,SchemaID,pprset)
#endif /* COBJMACROS */
#endif /* C style interface */
/* [id][propget] */ HRESULT STDMETHODCALLTYPE _Connection_get_ConnectionString_Proxy(
    _ADOConnection __RPC_FAR * This,
    /* [retval][out] */ BSTR __RPC_FAR *pbstr);
void __RPC_STUB _Connection_get_ConnectionString_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [id][propput] */ HRESULT STDMETHODCALLTYPE _Connection_put_ConnectionString_Proxy(
    _ADOConnection __RPC_FAR * This,
    /* [in] */ BSTR bstr);
void __RPC_STUB _Connection_put_ConnectionString_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [helpstring][helpcontext][id][propget] */ HRESULT STDMETHODCALLTYPE _Connection_get_CommandTimeout_Proxy(
    _ADOConnection __RPC_FAR * This,
    /* [retval][out] */ LONG __RPC_FAR *plTimeout);
void __RPC_STUB _Connection_get_CommandTimeout_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [id][propput] */ HRESULT STDMETHODCALLTYPE _Connection_put_CommandTimeout_Proxy(
    _ADOConnection __RPC_FAR * This,
    /* [in] */ LONG lTimeout);
void __RPC_STUB _Connection_put_CommandTimeout_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [id][propget] */ HRESULT STDMETHODCALLTYPE _Connection_get_ConnectionTimeout_Proxy(
    _ADOConnection __RPC_FAR * This,
    /* [retval][out] */ LONG __RPC_FAR *plTimeout);
void __RPC_STUB _Connection_get_ConnectionTimeout_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [id][propput] */ HRESULT STDMETHODCALLTYPE _Connection_put_ConnectionTimeout_Proxy(
    _ADOConnection __RPC_FAR * This,
    /* [in] */ LONG lTimeout);
void __RPC_STUB _Connection_put_ConnectionTimeout_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [id][propget] */ HRESULT STDMETHODCALLTYPE _Connection_get_Version_Proxy(
    _ADOConnection __RPC_FAR * This,
    /* [retval][out] */ BSTR __RPC_FAR *pbstr);
void __RPC_STUB _Connection_get_Version_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [helpstring][helpcontext][id] */ HRESULT STDMETHODCALLTYPE _Connection_Close_Proxy(
    _ADOConnection __RPC_FAR * This);
void __RPC_STUB _Connection_Close_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [helpstring][helpcontext][id] */ HRESULT STDMETHODCALLTYPE _Connection_Execute_Proxy(
    _ADOConnection __RPC_FAR * This,
    /* [in] */ BSTR CommandText,
    /* [optional][out] */ VARIANT __RPC_FAR *RecordsAffected,
    /* [defaultvalue][in] */ long Options,
    /* [retval][out] */ _ADORecordset __RPC_FAR *__RPC_FAR *ppiRset);
void __RPC_STUB _Connection_Execute_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [helpstring][helpcontext][id] */ HRESULT STDMETHODCALLTYPE _Connection_BeginTrans_Proxy(
    _ADOConnection __RPC_FAR * This,
    /* [retval][out] */ long __RPC_FAR *TransactionLevel);
void __RPC_STUB _Connection_BeginTrans_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [helpstring][helpcontext][id] */ HRESULT STDMETHODCALLTYPE _Connection_CommitTrans_Proxy(
    _ADOConnection __RPC_FAR * This);
void __RPC_STUB _Connection_CommitTrans_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [helpstring][helpcontext][id] */ HRESULT STDMETHODCALLTYPE _Connection_RollbackTrans_Proxy(
    _ADOConnection __RPC_FAR * This);
void __RPC_STUB _Connection_RollbackTrans_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [id] */ HRESULT STDMETHODCALLTYPE _Connection_Open_Proxy(
    _ADOConnection __RPC_FAR * This,
    /* [defaultvalue][in] */ BSTR ConnectionString,
    /* [defaultvalue][in] */ BSTR UserID,
    /* [defaultvalue][in] */ BSTR Password,
    /* [defaultvalue][in] */ long Options);
void __RPC_STUB _Connection_Open_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [id][propget] */ HRESULT STDMETHODCALLTYPE _Connection_get_Errors_Proxy(
    _ADOConnection __RPC_FAR * This,
    /* [retval][out] */ ADOErrors __RPC_FAR *__RPC_FAR *ppvObject);
void __RPC_STUB _Connection_get_Errors_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [id][propget] */ HRESULT STDMETHODCALLTYPE _Connection_get_DefaultDatabase_Proxy(
    _ADOConnection __RPC_FAR * This,
    /* [retval][out] */ BSTR __RPC_FAR *pbstr);
void __RPC_STUB _Connection_get_DefaultDatabase_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [id][propput] */ HRESULT STDMETHODCALLTYPE _Connection_put_DefaultDatabase_Proxy(
    _ADOConnection __RPC_FAR * This,
    /* [in] */ BSTR bstr);
void __RPC_STUB _Connection_put_DefaultDatabase_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [helpstring][helpcontext][id][propget] */ HRESULT STDMETHODCALLTYPE _Connection_get_IsolationLevel_Proxy(
    _ADOConnection __RPC_FAR * This,
    /* [retval][out] */ IsolationLevelEnum __RPC_FAR *Level);
void __RPC_STUB _Connection_get_IsolationLevel_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [id][propput] */ HRESULT STDMETHODCALLTYPE _Connection_put_IsolationLevel_Proxy(
    _ADOConnection __RPC_FAR * This,
    /* [in] */ IsolationLevelEnum Level);
void __RPC_STUB _Connection_put_IsolationLevel_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [helpstring][helpcontext][id][propget] */ HRESULT STDMETHODCALLTYPE _Connection_get_Attributes_Proxy(
    _ADOConnection __RPC_FAR * This,
    /* [retval][out] */ long __RPC_FAR *plAttr);
void __RPC_STUB _Connection_get_Attributes_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [id][propput] */ HRESULT STDMETHODCALLTYPE _Connection_put_Attributes_Proxy(
    _ADOConnection __RPC_FAR * This,
    /* [in] */ long lAttr);
void __RPC_STUB _Connection_put_Attributes_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propget][id] */ HRESULT STDMETHODCALLTYPE _Connection_get_CursorLocation_Proxy(
    _ADOConnection __RPC_FAR * This,
    /* [retval][out] */ CursorLocationEnum __RPC_FAR *plCursorLoc);
void __RPC_STUB _Connection_get_CursorLocation_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propput][id] */ HRESULT STDMETHODCALLTYPE _Connection_put_CursorLocation_Proxy(
    _ADOConnection __RPC_FAR * This,
    /* [in] */ CursorLocationEnum lCursorLoc);
void __RPC_STUB _Connection_put_CursorLocation_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [helpstring][helpcontext][id][propget] */ HRESULT STDMETHODCALLTYPE _Connection_get_Mode_Proxy(
    _ADOConnection __RPC_FAR * This,
    /* [retval][out] */ ConnectModeEnum __RPC_FAR *plMode);
void __RPC_STUB _Connection_get_Mode_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [helpstring][helpcontext][id][propput] */ HRESULT STDMETHODCALLTYPE _Connection_put_Mode_Proxy(
    _ADOConnection __RPC_FAR * This,
    /* [in] */ ConnectModeEnum lMode);
void __RPC_STUB _Connection_put_Mode_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [id][propget] */ HRESULT STDMETHODCALLTYPE _Connection_get_Provider_Proxy(
    _ADOConnection __RPC_FAR * This,
    /* [retval][out] */ BSTR __RPC_FAR *pbstr);
void __RPC_STUB _Connection_get_Provider_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [id][propput] */ HRESULT STDMETHODCALLTYPE _Connection_put_Provider_Proxy(
    _ADOConnection __RPC_FAR * This,
    /* [in] */ BSTR Provider);
void __RPC_STUB _Connection_put_Provider_Stub(

IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [id][propget] */ HRESULT STDMETHODCALLTYPE _Connection_get_State_Proxy(
    _ADOConnection __RPC_FAR * This,
    /* [retval][out] */ LONG __RPC_FAR *plObjState);
void __RPC_STUB _Connection_get_State_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [id] */ HRESULT STDMETHODCALLTYPE _Connection_OpenSchema_Proxy(
    _ADOConnection __RPC_FAR * This,
    /* [in] */ SchemaEnum Schema,
    /* [optional][in] */ VARIANT Restrictions,
    /* [optional][in] */ VARIANT SchemaID,
    /* [retval][out] */ _ADORecordset __RPC_FAR *__RPC_FAR *pprset);
void __RPC_STUB _Connection_OpenSchema_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
#endif /* ___Connection_INTERFACE_DEFINED__ */
#ifndef __ADOConnectionConstruction_INTERFACE_DEFINED__
#define __ADOConnectionConstruction_INTERFACE_DEFINED__
/****************************************
 * Generated header for interface: ADOConnectionConstruction
 * at Tue Oct 07 23:50:20 1997
 * using MIDL 3.01.75
 ****************************************/
/* [object][uuid][restricted] */
EXTERN_C const IID IID_ADOConnectionConstruction;
#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("00000516-0000-0010-8000-00AA006D2EA4")
    ADOConnectionConstruction : public IUnknown
    {
    public:
        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_DSO(
            /* [retval][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppDSO) = 0;

        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Session(
            /* [retval][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppSession) = 0;

    };

#else /* C style interface */
    typedef struct ADOConnectionConstructionVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            ADOConnectionConstruction __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            ADOConnectionConstruction __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            ADOConnectionConstruction __RPC_FAR * This);

        /* [propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_DSO )(
            ADOConnectionConstruction __RPC_FAR * This,
            /* [retval][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppDSO);

        /* [propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Session )(
            ADOConnectionConstruction __RPC_FAR * This,
            /* [retval][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppSession);

        END_INTERFACE
    } ADOConnectionConstructionVtbl;
    interface ADOConnectionConstruction
    {
        CONST_VTBL struct ADOConnectionConstructionVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS
#define ADOConnectionConstruction_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
#define ADOConnectionConstruction_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)
#define ADOConnectionConstruction_Release(This)\
    (This)->lpVtbl -> Release(This)
#define ADOConnectionConstruction_get_DSO(This,ppDSO)\
    (This)->lpVtbl -> get_DSO(This,ppDSO)
#define ADOConnectionConstruction_get_Session(This,ppSession)\
    (This)->lpVtbl -> get_Session(This,ppSession)
#endif /* COBJMACROS */
#endif /* C style interface */
/* [propget] */ HRESULT STDMETHODCALLTYPE ADOConnectionConstruction_get_DSO_Proxy(
    ADOConnectionConstruction __RPC_FAR * This,
    /* [retval][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppDSO);
void __RPC_STUB ADOConnectionConstruction_get_DSO_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propget] */ HRESULT STDMETHODCALLTYPE ADOConnectionConstruction_get_Session_Proxy(
    ADOConnectionConstruction __RPC_FAR * This,
    /* [retval][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppSession);
void __RPC_STUB ADOConnectionConstruction_get_Session_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
#endif /* __ADOConnectionConstruction_INTERFACE_DEFINED__ */
#ifdef __cplusplus
EXTERN_C const CLSID CLSID_Connection;
Connection;
#endif
#ifdef __cplusplus
EXTERN_C const CLSID CLSID_Command;
Command;
#endif
#ifndef ___Recordset_INTERFACE_DEFINED__
#define ___Recordset_INTERFACE_DEFINED__
/****************************************
 * Generated header for interface: _Recordset
 * at Tue Oct 07 23:50:20 1997
 * using MIDL 3.01.75
 ****************************************/
/* [object][helpstring][helpcontext][uuid][nonextensible][dual] */
EXTERN_C const IID IID__Recordset;
#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0000050E-0000-0010-8000-00AA006D2EA4")
    _ADORecordset : public _ADO
    {
    public:
        virtual /* [helpstring][helpcontext][propget][id] */ HRESULT STDMETHODCALLTYPE get_AbsolutePosition(
            /* [retval][out] */ PositionEnum __RPC_FAR *pl) = 0;

        virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_AbsolutePosition(
            /* [in] */ PositionEnum Position) = 0;

        virtual /* [propputref][id] */ HRESULT STDMETHODCALLTYPE putref_ActiveConnection(
            /* [in] */ IDispatch __RPC_FAR *pconn) = 0;

        virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_ActiveConnection(
            /* [in] */ VARIANT vConn) = 0;

        virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_ActiveConnection(
            /* [retval][out] */ VARIANT __RPC_FAR *pvar) = 0;

        virtual /* [helpstring][helpcontext][propget][id] */ HRESULT STDMETHODCALLTYPE get_BOF(
            /* [retval][out] */ VARIANT_BOOL __RPC_FAR *pb) = 0;

        virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_Bookmark(
            /* [retval][out] */ VARIANT __RPC_FAR *pvBookmark) = 0;

        virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_Bookmark(
            /* [in] */ VARIANT vBookmark) = 0;

        virtual /* [helpstring][helpcontext][propget][id] */ HRESULT STDMETHODCALLTYPE get_CacheSize(
            /* [retval][out] */ long __RPC_FAR *pl) = 0;

        virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_CacheSize(
            /* [in] */ long CacheSize) = 0;

        virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_CursorType(
            /* [retval][out] */ CursorTypeEnum __RPC_FAR *plCursorType) = 0;

        virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_CursorType(
            /* [in] */ CursorTypeEnum lCursorType) = 0;

        virtual /* [helpstring][helpcontext][propget][id] */ HRESULT STDMETHODCALLTYPE get_EOF(
            /* [retval][out] */ VARIANT_BOOL __RPC_FAR *pb) = 0;

        virtual /* [helpstring][helpcontext][propget][id] */ HRESULT STDMETHODCALLTYPE get_Fields(
            /* [retval][out] */ ADOFields __RPC_FAR *__RPC_FAR *ppvObject) = 0;

        virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_LockType(
            /* [retval][out] */ LockTypeEnum __RPC_FAR *plLockType) = 0;

        virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_LockType(
            /* [in] */ LockTypeEnum lLockType) = 0;

        virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_MaxRecords(
            /* [retval][out] */ long __RPC_FAR *plMaxRecords) = 0;

        virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_MaxRecords(
            /* [in] */ long lMaxRecords) = 0;

        virtual /* [helpstring][helpcontext][propget][id] */ HRESULT STDMETHODCALLTYPE get_RecordCount(
            /* [retval][out] */ long __RPC_FAR *pl) = 0;

        virtual /* [propputref][id] */ HRESULT STDMETHODCALLTYPE putref_Source(
            /* [in] */ IDispatch __RPC_FAR *pcmd) = 0;

        virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_Source(
            /* [in] */ BSTR bstrConn) = 0;

        virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_Source(
            /* [retval][out] */ VARIANT __RPC_FAR *pvSource) = 0;

        virtual /* [id] */ HRESULT STDMETHODCALLTYPE AddNew(
            /* [optional][in] */ VARIANT FieldList,
            /* [optional][in] */ VARIANT Values) = 0;

        virtual /* [id] */ HRESULT STDMETHODCALLTYPE CancelUpdate( void) = 0;

        virtual /* [id] */ HRESULT STDMETHODCALLTYPE Close( void) = 0;

        virtual /* [id] */ HRESULT STDMETHODCALLTYPE Delete(
            /* [defaultvalue][in] */ AffectEnum AffectRecords) = 0;

        virtual /* [id] */ HRESULT STDMETHODCALLTYPE GetRows(
            /* [defaultvalue][in] */ long Rows,
            /* [optional][in] */ VARIANT Start,
            /* [optional][in] */ VARIANT Fields,
            /* [retval][out] */ VARIANT __RPC_FAR *pvar) = 0;

        virtual /* [id] */ HRESULT STDMETHODCALLTYPE Move(
            /* [in] */ long NumRecords,
            /* [optional][in] */ VARIANT Start) = 0;

        virtual /* [id] */ HRESULT STDMETHODCALLTYPE MoveNext( void) = 0;

        virtual /* [id] */ HRESULT STDMETHODCALLTYPE MovePrevious( void) = 0;

        virtual /* [id] */ HRESULT STDMETHODCALLTYPE MoveFirst( void) = 0;

        virtual /* [id] */ HRESULT STDMETHODCALLTYPE MoveLast( void) = 0;

        virtual /* [id] */ HRESULT STDMETHODCALLTYPE Open(
            /* [optional][in] */ VARIANT Source,
            /* [optional][in] */ VARIANT ActiveConnection,
            /* [defaultvalue][in] */ CursorTypeEnum CursorType,
            /* [defaultvalue][in] */ LockTypeEnum LockType,
            /* [defaultvalue][in] */ LONG Options) = 0;

        virtual /* [id] */ HRESULT STDMETHODCALLTYPE Requery(
            /* [defaultvalue][in] */ LONG Options) = 0;

        virtual /* [id] */ HRESULT STDMETHODCALLTYPE Resync(
            /* [defaultvalue][in] */ AffectEnum AffectRecords) = 0;

        virtual /* [id] */ HRESULT STDMETHODCALLTYPE Update(
            /* [optional][in] */ VARIANT Fields,
            /* [optional][in] */ VARIANT Values) = 0;

        virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_AbsolutePage(
            /* [retval][out] */ PositionEnum __RPC_FAR *pl) = 0;

        virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_AbsolutePage(
            /* [in] */ PositionEnum Page) = 0;

        virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_EditMode(
            /* [retval][out] */ EditModeEnum __RPC_FAR *pl) = 0;

        virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_Filter(
            /* [retval][out] */ VARIANT __RPC_FAR *Criteria) = 0;

        virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_Filter(
            /* [in] */ VARIANT Criteria) = 0;

        virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_PageCount(
            /* [retval][out] */ long __RPC_FAR *pl) = 0;

        virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_PageSize(
            /* [retval][out] */ long __RPC_FAR *pl) = 0;

        virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_PageSize(
            /* [in] */ long PageSize) = 0;

        virtual /* [hidden][propget][id] */ HRESULT STDMETHODCALLTYPE get_Sort(
            /* [retval][out] */ BSTR __RPC_FAR *Criteria) = 0;

        virtual /* [hidden][propput][id] */ HRESULT STDMETHODCALLTYPE put_Sort(
            /* [in] */ BSTR Criteria) = 0;

        virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_Status(
            /* [retval][out] */ long __RPC_FAR *pl) = 0;

        virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_State(
            /* [retval][out] */ LONG __RPC_FAR *plObjState) = 0;

        virtual /* [id] */ HRESULT STDMETHODCALLTYPE Clone(
            /* [retval][out] */ _ADORecordset __RPC_FAR *__RPC_FAR *ppvObject) = 0;

        virtual /* [id] */ HRESULT STDMETHODCALLTYPE UpdateBatch(
            /* [defaultvalue][in] */ AffectEnum AffectRecords) = 0;

        virtual /* [id] */ HRESULT STDMETHODCALLTYPE CancelBatch(
            /* [defaultvalue][in] */ AffectEnum AffectRecords) = 0;

        virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_CursorLocation(
            /* [retval][out] */ CursorLocationEnum __RPC_FAR *plCursorLoc) = 0;

        virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_CursorLocation(
            /* [in] */ CursorLocationEnum lCursorLoc) = 0;

        virtual /* [id] */ HRESULT STDMETHODCALLTYPE NextRecordset(
            /* [optional][out] */ VARIANT __RPC_FAR *RecordsAffected,
            /* [retval][out] */ _ADORecordset __RPC_FAR *__RPC_FAR *ppiRs) = 0;

        virtual /* [id] */ HRESULT STDMETHODCALLTYPE Supports(
            /* [in] */ CursorOptionEnum CursorOptions,
            /* [retval][out] */ VARIANT_BOOL __RPC_FAR *pb) = 0;

        virtual /* [hidden][id][propget] */ HRESULT STDMETHODCALLTYPE get_Collect(
            /* [in] */ VARIANT Index,
            /* [retval][out] */ VARIANT __RPC_FAR *pvar) = 0;

        virtual /* [hidden][id][propput] */ HRESULT STDMETHODCALLTYPE put_Collect(
            /* [in] */ VARIANT Index,
            /* [in] */ VARIANT value) = 0;

        virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_MarshalOptions(
            /* [retval][out] */ MarshalOptionsEnum __RPC_FAR *peMarshal) = 0;

        virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_MarshalOptions(
            /* [in] */ MarshalOptionsEnum eMarshal) = 0;

        virtual /* [hidden][id] */ HRESULT STDMETHODCALLTYPE Find(
            /* [in] */ BSTR Criteria,
            /* [defaultvalue][in] */ LONG SkipRecords,
            /* [defaultvalue][in] */ SearchDirectionEnum SearchDirection,
            /* [optional][in] */ VARIANT Start) = 0;

    };

#else /* C style interface */
    typedef struct _RecordsetVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            _ADORecordset __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            _ADORecordset __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            _ADORecordset __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeInfoCount )(
            _ADORecordset __RPC_FAR * This,
            /* [out] */ UINT __RPC_FAR *pctinfo);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeInfo )(
            _ADORecordset __RPC_FAR * This,
            /* [in] */ UINT iTInfo,
            /* [in] */ LCID lcid,
            /* [out] */ ITypeInfo __RPC_FAR *__RPC_FAR *ppTInfo);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetIDsOfNames )(
            _ADORecordset __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [size_is][in] */ LPOLESTR __RPC_FAR *rgszNames,
            /* [in] */ UINT cNames,
            /* [in] */ LCID lcid,
            /* [size_is][out] */ DISPID __RPC_FAR *rgDispId);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Invoke )(
            _ADORecordset __RPC_FAR * This,
            /* [in] */ DISPID dispIdMember,
            /* [in] */ REFIID riid,
            /* [in] */ LCID lcid,
            /* [in] */ WORD wFlags,
            /* [out][in] */ DISPPARAMS __RPC_FAR *pDispParams,
            /* [out] */ VARIANT __RPC_FAR *pVarResult,
            /* [out] */ EXCEPINFO __RPC_FAR *pExcepInfo,
            /* [out] */ UINT __RPC_FAR *puArgErr);

        /* [helpstring][helpcontext][id][propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Properties )(
            _ADORecordset __RPC_FAR * This,
            /* [retval][out] */ ADOProperties __RPC_FAR *__RPC_FAR *ppvObject);

        /* [helpstring][helpcontext][propget][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_AbsolutePosition )(
            _ADORecordset __RPC_FAR * This,
            /* [retval][out] */ PositionEnum __RPC_FAR *pl);

        /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_AbsolutePosition )(
            _ADORecordset __RPC_FAR * This,
            /* [in] */ PositionEnum Position);

        /* [propputref][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *putref_ActiveADOConnection )(
            _ADORecordset __RPC_FAR * This,
            /* [in] */ IDispatch __RPC_FAR *pconn);

        /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_ActiveConnection )(
            _ADORecordset __RPC_FAR * This,
            /* [in] */ VARIANT vConn);

        /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_ActiveConnection )(
            _ADORecordset __RPC_FAR * This,
            /* [retval][out] */ VARIANT __RPC_FAR *pvar);

        /* [helpstring][helpcontext][propget][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_BOF )(
            _ADORecordset __RPC_FAR * This,
            /* [retval][out] */ VARIANT_BOOL __RPC_FAR *pb);

        /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Bookmark )(
            _ADORecordset __RPC_FAR * This,
            /* [retval][out] */ VARIANT __RPC_FAR *pvBookmark);

        /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_Bookmark )(
            _ADORecordset __RPC_FAR * This,
            /* [in] */ VARIANT vBookmark);

        /* [helpstring][helpcontext][propget][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_CacheSize )(
            _ADORecordset __RPC_FAR * This,
            /* [retval][out] */ long __RPC_FAR *pl);

        /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_CacheSize )(
            _ADORecordset __RPC_FAR * This,
            /* [in] */ long CacheSize);

        /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_CursorType )(
            _ADORecordset __RPC_FAR * This,
            /* [retval][out] */ CursorTypeEnum __RPC_FAR *plCursorType);

        /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_CursorType )(
            _ADORecordset __RPC_FAR * This,
            /* [in] */ CursorTypeEnum lCursorType);

        /* [helpstring][helpcontext][propget][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_EOF )(
            _ADORecordset __RPC_FAR * This,
            /* [retval][out] */ VARIANT_BOOL __RPC_FAR *pb);

        /* [helpstring][helpcontext][propget][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Fields )(
            _ADORecordset __RPC_FAR * This,
            /* [retval][out] */ ADOFields __RPC_FAR *__RPC_FAR *ppvObject);

        /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_LockType )(
            _ADORecordset __RPC_FAR * This,
            /* [retval][out] */ LockTypeEnum __RPC_FAR *plLockType);

        /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_LockType )(
            _ADORecordset __RPC_FAR * This,
            /* [in] */ LockTypeEnum lLockType);

        /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_MaxRecords )(
            _ADORecordset __RPC_FAR * This,
            /* [retval][out] */ long __RPC_FAR *plMaxRecords);

        /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_MaxRecords )(
            _ADORecordset __RPC_FAR * This,
            /* [in] */ long lMaxRecords);

        /* [helpstring][helpcontext][propget][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_RecordCount )(
            _ADORecordset __RPC_FAR * This,
            /* [retval][out] */ long __RPC_FAR *pl);

        /* [propputref][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *putref_Source )(
            _ADORecordset __RPC_FAR * This,
            /* [in] */ IDispatch __RPC_FAR *pcmd);

        /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_Source )(
            _ADORecordset __RPC_FAR * This,
            /* [in] */ BSTR bstrConn);

        /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Source )(
            _ADORecordset __RPC_FAR * This,
            /* [retval][out] */ VARIANT __RPC_FAR *pvSource);

        /* [id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *AddNew )(
            _ADORecordset __RPC_FAR * This,
            /* [optional][in] */ VARIANT FieldList,
            /* [optional][in] */ VARIANT Values);

        /* [id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *CancelUpdate )(
            _ADORecordset __RPC_FAR * This);

        /* [id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Close )(
            _ADORecordset __RPC_FAR * This);

        /* [id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Delete )(
            _ADORecordset __RPC_FAR * This,
            /* [defaultvalue][in] */ AffectEnum AffectRecords);

        /* [id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetRows )(
            _ADORecordset __RPC_FAR * This,
            /* [defaultvalue][in] */ long Rows,
            /* [optional][in] */ VARIANT Start,
            /* [optional][in] */ VARIANT Fields,
            /* [retval][out] */ VARIANT __RPC_FAR *pvar);

        /* [id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Move )(
            _ADORecordset __RPC_FAR * This,
            /* [in] */ long NumRecords,
            /* [optional][in] */ VARIANT Start);

        /* [id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *MoveNext )(
            _ADORecordset __RPC_FAR * This);

        /* [id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *MovePrevious )(
            _ADORecordset __RPC_FAR * This);

        /* [id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *MoveFirst )(
            _ADORecordset __RPC_FAR * This);

        /* [id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *MoveLast )(
            _ADORecordset __RPC_FAR * This);

        /* [id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Open )(
            _ADORecordset __RPC_FAR * This,
            /* [optional][in] */ VARIANT Source,
            /* [optional][in] */ VARIANT ActiveConnection,
            /* [defaultvalue][in] */ CursorTypeEnum CursorType,
            /* [defaultvalue][in] */ LockTypeEnum LockType,
            /* [defaultvalue][in] */ LONG Options);

        /* [id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Requery )(
            _ADORecordset __RPC_FAR * This,
            /* [defaultvalue][in] */ LONG Options);

        /* [id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Resync )(
            _ADORecordset __RPC_FAR * This,
            /* [defaultvalue][in] */ AffectEnum AffectRecords);

        /* [id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Update )(
            _ADORecordset __RPC_FAR * This,
            /* [optional][in] */ VARIANT Fields,
            /* [optional][in] */ VARIANT Values);

        /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_AbsolutePage )(
            _ADORecordset __RPC_FAR * This,
            /* [retval][out] */ PositionEnum __RPC_FAR *pl);

        /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_AbsolutePage )(
            _ADORecordset __RPC_FAR * This,
            /* [in] */ PositionEnum Page);

        /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_EditMode )(
            _ADORecordset __RPC_FAR * This,
            /* [retval][out] */ EditModeEnum __RPC_FAR *pl);

        /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Filter )(
            _ADORecordset __RPC_FAR * This,
            /* [retval][out] */ VARIANT __RPC_FAR *Criteria);

        /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_Filter )(
            _ADORecordset __RPC_FAR * This,
            /* [in] */ VARIANT Criteria);

        /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_PageCount )(
            _ADORecordset __RPC_FAR * This,
            /* [retval][out] */ long __RPC_FAR *pl);

        /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_PageSize )(
            _ADORecordset __RPC_FAR * This,
            /* [retval][out] */ long __RPC_FAR *pl);

        /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_PageSize )(
            _ADORecordset __RPC_FAR * This,
            /* [in] */ long PageSize);

        /* [hidden][propget][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Sort )(
            _ADORecordset __RPC_FAR * This,
            /* [retval][out] */ BSTR __RPC_FAR *Criteria);

        /* [hidden][propput][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_Sort )(
            _ADORecordset __RPC_FAR * This,
            /* [in] */ BSTR Criteria);

        /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Status )(
            _ADORecordset __RPC_FAR * This,
            /* [retval][out] */ long __RPC_FAR *pl);

        /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_State )(
            _ADORecordset __RPC_FAR * This,
            /* [retval][out] */ LONG __RPC_FAR *plObjState);

        /* [id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Clone )(
            _ADORecordset __RPC_FAR * This,
            /* [retval][out] */ _ADORecordset __RPC_FAR *__RPC_FAR *ppvObject);

        /* [id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *UpdateBatch )(
            _ADORecordset __RPC_FAR * This,
            /* [defaultvalue][in] */ AffectEnum AffectRecords);

        /* [id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *CancelBatch )(
            _ADORecordset __RPC_FAR * This,
            /* [defaultvalue][in] */ AffectEnum AffectRecords);

        /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_CursorLocation )(
            _ADORecordset __RPC_FAR * This,
            /* [retval][out] */ CursorLocationEnum __RPC_FAR *plCursorLoc);

        /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_CursorLocation )(
            _ADORecordset __RPC_FAR * This,
            /* [in] */ CursorLocationEnum lCursorLoc);

        /* [id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *NextADORecordset )(
            _ADORecordset __RPC_FAR * This,
            /* [optional][out] */ VARIANT __RPC_FAR *RecordsAffected,
            /* [retval][out] */ _ADORecordset __RPC_FAR *__RPC_FAR *ppiRs);

        /* [id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Supports )(
            _ADORecordset __RPC_FAR * This,
            /* [in] */ CursorOptionEnum CursorOptions,
            /* [retval][out] */ VARIANT_BOOL __RPC_FAR *pb);

        /* [hidden][id][propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Collect )(
            _ADORecordset __RPC_FAR * This,
            /* [in] */ VARIANT Index,
            /* [retval][out] */ VARIANT __RPC_FAR *pvar);

        /* [hidden][id][propput] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_Collect )(
            _ADORecordset __RPC_FAR * This,
            /* [in] */ VARIANT Index,
            /* [in] */ VARIANT value);

        /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_MarshalOptions )(
            _ADORecordset __RPC_FAR * This,
            /* [retval][out] */ MarshalOptionsEnum __RPC_FAR *peMarshal);

        /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_MarshalOptions )(
            _ADORecordset __RPC_FAR * This,
            /* [in] */ MarshalOptionsEnum eMarshal);

        /* [hidden][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Find )(
            _ADORecordset __RPC_FAR * This,
            /* [in] */ BSTR Criteria,
            /* [defaultvalue][in] */ LONG SkipRecords,
            /* [defaultvalue][in] */ SearchDirectionEnum SearchDirection,
            /* [optional][in] */ VARIANT Start);

        END_INTERFACE
    } _RecordsetVtbl;
    interface _Recordset
    {
        CONST_VTBL struct _RecordsetVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS
#define _Recordset_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
#define _Recordset_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)
#define _Recordset_Release(This)\
    (This)->lpVtbl -> Release(This)
#define _Recordset_GetTypeInfoCount(This,pctinfo)\
    (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo)
#define _Recordset_GetTypeInfo(This,iTInfo,lcid,ppTInfo)\
    (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo)
#define _Recordset_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)\

(This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)
#define _Recordset_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)\
    (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)
#define _Recordset_get_Properties(This,ppvObject)\
    (This)->lpVtbl -> get_Properties(This,ppvObject)
#define _Recordset_get_AbsolutePosition(This,pl)\
    (This)->lpVtbl -> get_AbsolutePosition(This,pl)
#define _Recordset_put_AbsolutePosition(This,Position)\
    (This)->lpVtbl -> put_AbsolutePosition(This,Position)
#define _Recordset_putref_ActiveConnection(This,pconn)\
    (This)->lpVtbl -> putref_ActiveConnection(This,pconn)
#define _Recordset_put_ActiveConnection(This,vConn)\
    (This)->lpVtbl -> put_ActiveConnection(This,vConn)
#define _Recordset_get_ActiveConnection(This,pvar)\
    (This)->lpVtbl -> get_ActiveConnection(This,pvar)
#define _Recordset_get_BOF(This,pb)\
    (This)->lpVtbl -> get_BOF(This,pb)
#define _Recordset_get_Bookmark(This,pvBookmark)\
    (This)->lpVtbl -> get_Bookmark(This,pvBookmark)
#define _Recordset_put_Bookmark(This,vBookmark)\
    (This)->lpVtbl -> put_Bookmark(This,vBookmark)
#define _Recordset_get_CacheSize(This,pl)\
    (This)->lpVtbl -> get_CacheSize(This,pl)
#define _Recordset_put_CacheSize(This,CacheSize)\
    (This)->lpVtbl -> put_CacheSize(This,CacheSize)
#define _Recordset_get_CursorType(This,plCursorType)\
    (This)->lpVtbl -> get_CursorType(This,plCursorType)
#define _Recordset_put_CursorType(This,lCursorType)\
    (This)->lpVtbl -> put_CursorType(This,lCursorType)
#define _Recordset_get_EOF(This,pb)\
    (This)->lpVtbl -> get_EOF(This,pb)
#define _Recordset_get_Fields(This,ppvObject)\
    (This)->lpVtbl -> get_Fields(This,ppvObject)
#define _Recordset_get_LockType(This,plLockType)\
    (This)->lpVtbl -> get_LockType(This,plLockType)
#define _Recordset_put_LockType(This,lLockType)\
    (This)->lpVtbl -> put_LockType(This,lLockType)
#define _Recordset_get_MaxRecords(This,plMaxRecords)\
    (This)->lpVtbl -> get_MaxRecords(This,plMaxRecords)
#define _Recordset_put_MaxRecords(This,lMaxRecords)\
    (This)->lpVtbl -> put_MaxRecords(This,lMaxRecords)
#define _Recordset_get_RecordCount(This,pl)\
    (This)->lpVtbl -> get_RecordCount(This,pl)
#define _Recordset_putref_Source(This,pcmd)\
    (This)->lpVtbl -> putref_Source(This,pcmd)
#define _Recordset_put_Source(This,bstrConn)\
    (This)->lpVtbl -> put_Source(This,bstrConn)
#define _Recordset_get_Source(This,pvSource)\
    (This)->lpVtbl -> get_Source(This,pvSource)
#define _Recordset_AddNew(This,FieldList,Values)\
    (This)->lpVtbl -> AddNew(This,FieldList,Values)
#define _Recordset_CancelUpdate(This)\
    (This)->lpVtbl -> CancelUpdate(This)
#define _Recordset_Close(This)\
    (This)->lpVtbl -> Close(This)
#define _Recordset_Delete(This,AffectRecords)\
    (This)->lpVtbl -> Delete(This,AffectRecords)
#define _Recordset_GetRows(This,Rows,Start,Fields,pvar)\
    (This)->lpVtbl -> GetRows(This,Rows,Start,Fields,pvar)
#define _Recordset_Move(This,NumRecords,Start)\
    (This)->lpVtbl -> Move(This,NumRecords,Start)
#define _Recordset_MoveNext(This)\
    (This)->lpVtbl -> MoveNext(This)
#define _Recordset_MovePrevious(This)\
    (This)->lpVtbl -> MovePrevious(This)
#define _Recordset_MoveFirst(This)\
    (This)->lpVtbl -> MoveFirst(This)
#define _Recordset_MoveLast(This)\
    (This)->lpVtbl -> MoveLast(This)
#define _Recordset_Open(This,Source,ActiveConnection,CursorType,LockType,Options)\
    (This)->lpVtbl -> Open(This,Source,ActiveConnection,CursorType,LockType,Options)
#define _Recordset_Requery(This,Options)\
    (This)->lpVtbl -> Requery(This,Options)
#define _Recordset_Resync(This,AffectRecords)\
    (This)->lpVtbl -> Resync(This,AffectRecords)
#define _Recordset_Update(This,Fields,Values)\
    (This)->lpVtbl -> Update(This,Fields,Values)
#define _Recordset_get_AbsolutePage(This,pl)\
    (This)->lpVtbl -> get_AbsolutePage(This,pl)
#define _Recordset_put_AbsolutePage(This,Page)\
    (This)->lpVtbl -> put_AbsolutePage(This,Page)
#define _Recordset_get_EditMode(This,pl)\
    (This)->lpVtbl -> get_EditMode(This,pl)
#define _Recordset_get_Filter(This,Criteria)\
    (This)->lpVtbl -> get_Filter(This,Criteria)
#define _Recordset_put_Filter(This,Criteria)\
    (This)->lpVtbl -> put_Filter(This,Criteria)
#define _Recordset_get_PageCount(This,pl)\
    (This)->lpVtbl -> get_PageCount(This,pl)
#define _Recordset_get_PageSize(This,pl)\
    (This)->lpVtbl -> get_PageSize(This,pl)
#define _Recordset_put_PageSize(This,PageSize)\
    (This)->lpVtbl -> put_PageSize(This,PageSize)
#define _Recordset_get_Sort(This,Criteria)\
    (This)->lpVtbl -> get_Sort(This,Criteria)
#define _Recordset_put_Sort(This,Criteria)\
    (This)->lpVtbl -> put_Sort(This,Criteria)
#define _Recordset_get_Status(This,pl)\
    (This)->lpVtbl -> get_Status(This,pl)
#define _Recordset_get_State(This,plObjState)\
    (This)->lpVtbl -> get_State(This,plObjState)
#define _Recordset_Clone(This,ppvObject)\
    (This)->lpVtbl -> Clone(This,ppvObject)
#define _Recordset_UpdateBatch(This,AffectRecords)\
    (This)->lpVtbl -> UpdateBatch(This,AffectRecords)
#define _Recordset_CancelBatch(This,AffectRecords)\
    (This)->lpVtbl -> CancelBatch(This,AffectRecords)
#define _Recordset_get_CursorLocation(This,plCursorLoc)\
    (This)->lpVtbl -> get_CursorLocation(This,plCursorLoc)
#define _Recordset_put_CursorLocation(This,lCursorLoc)\
    (This)->lpVtbl -> put_CursorLocation(This,lCursorLoc)
#define _Recordset_NextRecordset(This,RecordsAffected,ppiRs)\
    (This)->lpVtbl -> NextRecordset(This,RecordsAffected,ppiRs)
#define _Recordset_Supports(This,CursorOptions,pb)\
    (This)->lpVtbl -> Supports(This,CursorOptions,pb)
#define _Recordset_get_Collect(This,Index,pvar)\
    (This)->lpVtbl -> get_Collect(This,Index,pvar)
#define _Recordset_put_Collect(This,Index,value)\
    (This)->lpVtbl -> put_Collect(This,Index,value)
#define _Recordset_get_MarshalOptions(This,peMarshal)\
    (This)->lpVtbl -> get_MarshalOptions(This,peMarshal)
#define _Recordset_put_MarshalOptions(This,eMarshal)\
    (This)->lpVtbl -> put_MarshalOptions(This,eMarshal)
#define _Recordset_Find(This,Criteria,SkipRecords,SearchDirection,Start)\
    (This)->lpVtbl -> Find(This,Criteria,SkipRecords,SearchDirection,Start)
#endif /* COBJMACROS */
#endif /* C style interface */
/* [helpstring][helpcontext][propget][id] */ HRESULT STDMETHODCALLTYPE _Recordset_get_AbsolutePosition_Proxy(
    _ADORecordset __RPC_FAR * This,
    /* [retval][out] */ PositionEnum __RPC_FAR *pl);
void __RPC_STUB _Recordset_get_AbsolutePosition_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propput][id] */ HRESULT STDMETHODCALLTYPE _Recordset_put_AbsolutePosition_Proxy(
    _ADORecordset __RPC_FAR * This,
    /* [in] */ PositionEnum Position);
void __RPC_STUB _Recordset_put_AbsolutePosition_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propputref][id] */ HRESULT STDMETHODCALLTYPE _Recordset_putref_ActiveConnection_Proxy(
    _ADORecordset __RPC_FAR * This,
    /* [in] */ IDispatch __RPC_FAR *pconn);
void __RPC_STUB _Recordset_putref_ActiveConnection_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propput][id] */ HRESULT STDMETHODCALLTYPE _Recordset_put_ActiveConnection_Proxy(
    _ADORecordset __RPC_FAR * This,
    /* [in] */ VARIANT vConn);
void __RPC_STUB _Recordset_put_ActiveConnection_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propget][id] */ HRESULT STDMETHODCALLTYPE _Recordset_get_ActiveConnection_Proxy(
    _ADORecordset __RPC_FAR * This,
    /* [retval][out] */ VARIANT __RPC_FAR *pvar);
void __RPC_STUB _Recordset_get_ActiveConnection_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [helpstring][helpcontext][propget][id] */ HRESULT STDMETHODCALLTYPE _Recordset_get_BOF_Proxy(
    _ADORecordset __RPC_FAR * This,
    /* [retval][out] */ VARIANT_BOOL __RPC_FAR *pb);
void __RPC_STUB _Recordset_get_BOF_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propget][id] */ HRESULT STDMETHODCALLTYPE _Recordset_get_Bookmark_Proxy(
    _ADORecordset __RPC_FAR * This,
    /* [retval][out] */ VARIANT __RPC_FAR *pvBookmark);
void __RPC_STUB _Recordset_get_Bookmark_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propput][id] */ HRESULT STDMETHODCALLTYPE _Recordset_put_Bookmark_Proxy(
    _ADORecordset __RPC_FAR * This,
    /* [in] */ VARIANT vBookmark);
void __RPC_STUB _Recordset_put_Bookmark_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [helpstring][helpcontext][propget][id] */ HRESULT STDMETHODCALLTYPE _Recordset_get_CacheSize_Proxy(
    _ADORecordset __RPC_FAR * This,
    /* [retval][out] */ long __RPC_FAR *pl);
void __RPC_STUB _Recordset_get_CacheSize_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propput][id] */ HRESULT STDMETHODCALLTYPE _Recordset_put_CacheSize_Proxy(
    _ADORecordset __RPC_FAR * This,
    /* [in] */ long CacheSize);
void __RPC_STUB _Recordset_put_CacheSize_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propget][id] */ HRESULT STDMETHODCALLTYPE _Recordset_get_CursorType_Proxy(
    _ADORecordset __RPC_FAR * This,
    /* [retval][out] */ CursorTypeEnum __RPC_FAR *plCursorType);
void __RPC_STUB _Recordset_get_CursorType_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propput][id] */ HRESULT STDMETHODCALLTYPE _Recordset_put_CursorType_Proxy(
    _ADORecordset __RPC_FAR * This,
    /* [in] */ CursorTypeEnum lCursorType);
void __RPC_STUB _Recordset_put_CursorType_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [helpstring][helpcontext][propget][id] */ HRESULT STDMETHODCALLTYPE _Recordset_get_EOF_Proxy(
    _ADORecordset __RPC_FAR * This,
    /* [retval][out] */ VARIANT_BOOL __RPC_FAR *pb);
void __RPC_STUB _Recordset_get_EOF_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [helpstring][helpcontext][propget][id] */ HRESULT STDMETHODCALLTYPE _Recordset_get_Fields_Proxy(
    _ADORecordset __RPC_FAR * This,
    /* [retval][out] */ ADOFields __RPC_FAR *__RPC_FAR *ppvObject);
void __RPC_STUB _Recordset_get_Fields_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propget][id] */ HRESULT STDMETHODCALLTYPE _Recordset_get_LockType_Proxy(
    _ADORecordset __RPC_FAR * This,
    /* [retval][out] */ LockTypeEnum __RPC_FAR *plLockType);
void __RPC_STUB _Recordset_get_LockType_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propput][id] */ HRESULT STDMETHODCALLTYPE _Recordset_put_LockType_Proxy(
    _ADORecordset __RPC_FAR * This,
    /* [in] */ LockTypeEnum lLockType);
void __RPC_STUB _Recordset_put_LockType_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propget][id] */ HRESULT STDMETHODCALLTYPE _Recordset_get_MaxRecords_Proxy(
    _ADORecordset __RPC_FAR * This,
    /* [retval][out] */ long __RPC_FAR *plMaxRecords);
void __RPC_STUB _Recordset_get_MaxRecords_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propput][id] */ HRESULT STDMETHODCALLTYPE _Recordset_put_MaxRecords_Proxy(
    _ADORecordset __RPC_FAR * This,
    /* [in] */ long lMaxRecords);
void __RPC_STUB _Recordset_put_MaxRecords_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [helpstring][helpcontext][propget][id] */ HRESULT STDMETHODCALLTYPE _Recordset_get_RecordCount_Proxy(
    _ADORecordset __RPC_FAR * This,
    /* [retval][out] */ long __RPC_FAR *pl);
void __RPC_STUB _Recordset_get_RecordCount_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propputref][id] */ HRESULT STDMETHODCALLTYPE _Recordset_putref_Source_Proxy(
    _ADORecordset __RPC_FAR * This,
    /* [in] */ IDispatch __RPC_FAR *pcmd);
void __RPC_STUB _Recordset_putref_Source_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propput][id] */ HRESULT STDMETHODCALLTYPE _Recordset_put_Source_Proxy(
    _ADORecordset __RPC_FAR * This,
    /* [in] */ BSTR bstrConn);
void __RPC_STUB _Recordset_put_Source_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propget][id] */ HRESULT STDMETHODCALLTYPE _Recordset_get_Source_Proxy(
    _ADORecordset __RPC_FAR * This,
    /* [retval][out] */ VARIANT __RPC_FAR *pvSource);
void __RPC_STUB _Recordset_get_Source_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [id] */ HRESULT STDMETHODCALLTYPE _Recordset_AddNew_Proxy(
    _ADORecordset __RPC_FAR * This,
    /* [optional][in] */ VARIANT FieldList,
    /* [optional][in] */ VARIANT Values);
void __RPC_STUB _Recordset_AddNew_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [id] */ HRESULT STDMETHODCALLTYPE _Recordset_CancelUpdate_Proxy(
    _ADORecordset __RPC_FAR * This);
void __RPC_STUB _Recordset_CancelUpdate_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [id] */ HRESULT STDMETHODCALLTYPE _Recordset_Close_Proxy(
    _ADORecordset __RPC_FAR * This);
void __RPC_STUB _Recordset_Close_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [id] */ HRESULT STDMETHODCALLTYPE _Recordset_Delete_Proxy(
    _ADORecordset __RPC_FAR * This,
    /* [defaultvalue][in] */ AffectEnum AffectRecords);
void __RPC_STUB _Recordset_Delete_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [id] */ HRESULT STDMETHODCALLTYPE _Recordset_GetRows_Proxy(
    _ADORecordset __RPC_FAR * This,
    /* [defaultvalue][in] */ long Rows,
    /* [optional][in] */ VARIANT Start,
    /* [optional][in] */ VARIANT Fields,
    /* [retval][out] */ VARIANT __RPC_FAR *pvar);
void __RPC_STUB _Recordset_GetRows_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [id] */ HRESULT STDMETHODCALLTYPE _Recordset_Move_Proxy(
    _ADORecordset __RPC_FAR * This,
    /* [in] */ long NumRecords,
    /* [optional][in] */ VARIANT Start);
void __RPC_STUB _Recordset_Move_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [id] */ HRESULT STDMETHODCALLTYPE _Recordset_MoveNext_Proxy(
    _ADORecordset __RPC_FAR * This);
void __RPC_STUB _Recordset_MoveNext_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [id] */ HRESULT STDMETHODCALLTYPE _Recordset_MovePrevious_Proxy(
    _ADORecordset __RPC_FAR * This);
void __RPC_STUB _Recordset_MovePrevious_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [id] */ HRESULT STDMETHODCALLTYPE _Recordset_MoveFirst_Proxy(
    _ADORecordset __RPC_FAR * This);
void __RPC_STUB _Recordset_MoveFirst_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [id] */ HRESULT STDMETHODCALLTYPE _Recordset_MoveLast_Proxy(
    _ADORecordset __RPC_FAR * This);
void __RPC_STUB _Recordset_MoveLast_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [id] */ HRESULT STDMETHODCALLTYPE _Recordset_Open_Proxy(
    _ADORecordset __RPC_FAR * This,
    /* [optional][in] */ VARIANT Source,
    /* [optional][in] */ VARIANT ActiveConnection,
    /* [defaultvalue][in] */ CursorTypeEnum CursorType,
    /* [defaultvalue][in] */ LockTypeEnum LockType,
    /* [defaultvalue][in] */ LONG Options);
void __RPC_STUB _Recordset_Open_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [id] */ HRESULT STDMETHODCALLTYPE _Recordset_Requery_Proxy(
    _ADORecordset __RPC_FAR * This,
    /* [defaultvalue][in] */ LONG Options);
void __RPC_STUB _Recordset_Requery_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [id] */ HRESULT STDMETHODCALLTYPE _Recordset_Resync_Proxy(
    _ADORecordset __RPC_FAR * This,
    /* [defaultvalue][in] */ AffectEnum AffectRecords);
void __RPC_STUB _Recordset_Resync_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [id] */ HRESULT STDMETHODCALLTYPE _Recordset_Update_Proxy(
    _ADORecordset __RPC_FAR * This,
    /* [optional][in] */ VARIANT Fields,
    /* [optional][in] */ VARIANT Values);
void __RPC_STUB _Recordset_Update_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propget][id] */ HRESULT STDMETHODCALLTYPE _Recordset_get_AbsolutePage_Proxy(
    _ADORecordset __RPC_FAR * This,
    /* [retval][out] */ PositionEnum __RPC_FAR *pl);
void __RPC_STUB _Recordset_get_AbsolutePage_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propput][id] */ HRESULT STDMETHODCALLTYPE _Recordset_put_AbsolutePage_Proxy(
    _ADORecordset __RPC_FAR * This,
    /* [in] */ PositionEnum Page);
void __RPC_STUB _Recordset_put_AbsolutePage_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propget][id] */ HRESULT STDMETHODCALLTYPE _Recordset_get_EditMode_Proxy(
    _ADORecordset __RPC_FAR * This,
    /* [retval][out] */ EditModeEnum __RPC_FAR *pl);
void __RPC_STUB _Recordset_get_EditMode_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propget][id] */ HRESULT STDMETHODCALLTYPE _Recordset_get_Filter_Proxy(
    _ADORecordset __RPC_FAR * This,
    /* [retval][out] */ VARIANT __RPC_FAR *Criteria);
void __RPC_STUB _Recordset_get_Filter_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propput][id] */ HRESULT STDMETHODCALLTYPE _Recordset_put_Filter_Proxy(
    _ADORecordset __RPC_FAR * This,
    /* [in] */ VARIANT Criteria);
void __RPC_STUB _Recordset_put_Filter_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propget][id] */ HRESULT STDMETHODCALLTYPE _Recordset_get_PageCount_Proxy(
    _ADORecordset __RPC_FAR * This,
    /* [retval][out] */ long __RPC_FAR *pl);
void __RPC_STUB _Recordset_get_PageCount_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propget][id] */ HRESULT STDMETHODCALLTYPE _Recordset_get_PageSize_Proxy(
    _ADORecordset __RPC_FAR * This,
    /* [retval][out] */ long __RPC_FAR *pl);
void __RPC_STUB _Recordset_get_PageSize_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propput][id] */ HRESULT STDMETHODCALLTYPE _Recordset_put_PageSize_Proxy(
    _ADORecordset __RPC_FAR * This,
    /* [in] */ long PageSize);
void __RPC_STUB _Recordset_put_PageSize_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [hidden][propget][id] */ HRESULT STDMETHODCALLTYPE _Recordset_get_Sort_Proxy(
    _ADORecordset __RPC_FAR * This,
    /* [retval][out] */ BSTR __RPC_FAR *Criteria);
void __RPC_STUB _Recordset_get_Sort_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [hidden][propput][id] */ HRESULT STDMETHODCALLTYPE _Recordset_put_Sort_Proxy(
    _ADORecordset __RPC_FAR * This,
    /* [in] */ BSTR Criteria);
void __RPC_STUB _Recordset_put_Sort_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propget][id] */ HRESULT STDMETHODCALLTYPE _Recordset_get_Status_Proxy(
    _ADORecordset __RPC_FAR * This,
    /* [retval][out] */ long __RPC_FAR *pl);
void __RPC_STUB _Recordset_get_Status_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propget][id] */ HRESULT STDMETHODCALLTYPE _Recordset_get_State_Proxy(
    _ADORecordset __RPC_FAR * This,
    /* [retval][out] */ LONG __RPC_FAR *plObjState);
void __RPC_STUB _Recordset_get_State_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [id] */ HRESULT STDMETHODCALLTYPE _Recordset_Clone_Proxy(
    _ADORecordset __RPC_FAR * This,
    /* [retval][out] */ _ADORecordset __RPC_FAR *__RPC_FAR *ppvObject);
void __RPC_STUB _Recordset_Clone_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [id] */ HRESULT STDMETHODCALLTYPE _Recordset_UpdateBatch_Proxy(
    _ADORecordset __RPC_FAR * This,
    /* [defaultvalue][in] */ AffectEnum AffectRecords);
void __RPC_STUB _Recordset_UpdateBatch_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [id] */ HRESULT STDMETHODCALLTYPE _Recordset_CancelBatch_Proxy(
    _ADORecordset __RPC_FAR * This,
    /* [defaultvalue][in] */ AffectEnum AffectRecords);
void __RPC_STUB _Recordset_CancelBatch_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propget][id] */ HRESULT STDMETHODCALLTYPE _Recordset_get_CursorLocation_Proxy(
    _ADORecordset __RPC_FAR * This,
    /* [retval][out] */ CursorLocationEnum __RPC_FAR *plCursorLoc);
void __RPC_STUB _Recordset_get_CursorLocation_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propput][id] */ HRESULT STDMETHODCALLTYPE _Recordset_put_CursorLocation_Proxy(
    _ADORecordset __RPC_FAR * This,
    /* [in] */ CursorLocationEnum lCursorLoc);
void __RPC_STUB _Recordset_put_CursorLocation_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [id] */ HRESULT STDMETHODCALLTYPE _Recordset_NextRecordset_Proxy(
    _ADORecordset __RPC_FAR * This,
    /* [optional][out] */ VARIANT __RPC_FAR *RecordsAffected,
    /* [retval][out] */ _ADORecordset __RPC_FAR *__RPC_FAR *ppiRs);
void __RPC_STUB _Recordset_NextRecordset_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [id] */ HRESULT STDMETHODCALLTYPE _Recordset_Supports_Proxy(
    _ADORecordset __RPC_FAR * This,
    /* [in] */ CursorOptionEnum CursorOptions,
    /* [retval][out] */ VARIANT_BOOL __RPC_FAR *pb);
void __RPC_STUB _Recordset_Supports_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [hidden][id][propget] */ HRESULT STDMETHODCALLTYPE _Recordset_get_Collect_Proxy(
    _ADORecordset __RPC_FAR * This,
    /* [in] */ VARIANT Index,
    /* [retval][out] */ VARIANT __RPC_FAR *pvar);
void __RPC_STUB _Recordset_get_Collect_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [hidden][id][propput] */ HRESULT STDMETHODCALLTYPE _Recordset_put_Collect_Proxy(
    _ADORecordset __RPC_FAR * This,
    /* [in] */ VARIANT Index,
    /* [in] */ VARIANT value);
void __RPC_STUB _Recordset_put_Collect_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propget][id] */ HRESULT STDMETHODCALLTYPE _Recordset_get_MarshalOptions_Proxy(
    _ADORecordset __RPC_FAR * This,
    /* [retval][out] */ MarshalOptionsEnum __RPC_FAR *peMarshal);
void __RPC_STUB _Recordset_get_MarshalOptions_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propput][id] */ HRESULT STDMETHODCALLTYPE _Recordset_put_MarshalOptions_Proxy(
    _ADORecordset __RPC_FAR * This,
    /* [in] */ MarshalOptionsEnum eMarshal);
void __RPC_STUB _Recordset_put_MarshalOptions_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [hidden][id] */ HRESULT STDMETHODCALLTYPE _Recordset_Find_Proxy(
    _ADORecordset __RPC_FAR * This,
    /* [in] */ BSTR Criteria,
    /* [defaultvalue][in] */ LONG SkipRecords,
    /* [defaultvalue][in] */ SearchDirectionEnum SearchDirection,
    /* [optional][in] */ VARIANT Start);
void __RPC_STUB _Recordset_Find_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
#endif /* ___Recordset_INTERFACE_DEFINED__ */
#ifdef __cplusplus
EXTERN_C const CLSID CLSID_Recordset;
Recordset;
#endif
#ifndef __ADORecordsetConstruction_INTERFACE_DEFINED__
#define __ADORecordsetConstruction_INTERFACE_DEFINED__
/****************************************
 * Generated header for interface: ADORecordsetConstruction
 * at Tue Oct 07 23:50:20 1997
 * using MIDL 3.01.75
 ****************************************/
/* [object][uuid][restricted] */
EXTERN_C const IID IID_ADORecordsetConstruction;
#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("00000283-0000-0010-8000-00AA006D2EA4")
    ADORecordsetConstruction : public IDispatch
    {
    public:
        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Rowset(
            /* [retval][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppRowset) = 0;

        virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_Rowset(
            /* [in] */ IUnknown __RPC_FAR *pRowset) = 0;

        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Chapter(
            /* [retval][out] */ LONG __RPC_FAR *plChapter) = 0;

        virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_Chapter(
            /* [in] */ LONG lChapter) = 0;

        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_RowPosition(
            /* [retval][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppRowPos) = 0;

        virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_RowPosition(
            /* [in] */ IUnknown __RPC_FAR *pRowPos) = 0;

    };

#else /* C style interface */
    typedef struct ADORecordsetConstructionVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            ADORecordsetConstruction __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            ADORecordsetConstruction __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            ADORecordsetConstruction __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeInfoCount )(
            ADORecordsetConstruction __RPC_FAR * This,

/* [out] */ UINT __RPC_FAR *pctinfo);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeInfo )(
            ADORecordsetConstruction __RPC_FAR * This,
            /* [in] */ UINT iTInfo,
            /* [in] */ LCID lcid,
            /* [out] */ ITypeInfo __RPC_FAR *__RPC_FAR *ppTInfo);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetIDsOfNames )(
            ADORecordsetConstruction __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [size_is][in] */ LPOLESTR __RPC_FAR *rgszNames,
            /* [in] */ UINT cNames,
            /* [in] */ LCID lcid,
            /* [size_is][out] */ DISPID __RPC_FAR *rgDispId);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Invoke )(
            ADORecordsetConstruction __RPC_FAR * This,
            /* [in] */ DISPID dispIdMember,
            /* [in] */ REFIID riid,
            /* [in] */ LCID lcid,
            /* [in] */ WORD wFlags,
            /* [out][in] */ DISPPARAMS __RPC_FAR *pDispParams,
            /* [out] */ VARIANT __RPC_FAR *pVarResult,
            /* [out] */ EXCEPINFO __RPC_FAR *pExcepInfo,
            /* [out] */ UINT __RPC_FAR *puArgErr);

        /* [propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Rowset )(
            ADORecordsetConstruction __RPC_FAR * This,
            /* [retval][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppRowset);

        /* [propput] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_Rowset )(
            ADORecordsetConstruction __RPC_FAR * This,
            /* [in] */ IUnknown __RPC_FAR *pRowset);

        /* [propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Chapter )(
            ADORecordsetConstruction __RPC_FAR * This,
            /* [retval][out] */ LONG __RPC_FAR *plChapter);

        /* [propput] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_Chapter )(
            ADORecordsetConstruction __RPC_FAR * This,
            /* [in] */ LONG lChapter);

        /* [propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_RowPosition )(
            ADORecordsetConstruction __RPC_FAR * This,
            /* [retval][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppRowPos);

        /* [propput] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_RowPosition )(
            ADORecordsetConstruction __RPC_FAR * This,
            /* [in] */ IUnknown __RPC_FAR *pRowPos);

        END_INTERFACE
    } ADORecordsetConstructionVtbl;
    interface ADORecordsetConstruction
    {
        CONST_VTBL struct ADORecordsetConstructionVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS
#define ADORecordsetConstruction_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
#define ADORecordsetConstruction_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)
#define ADORecordsetConstruction_Release(This)\
    (This)->lpVtbl -> Release(This)
#define ADORecordsetConstruction_GetTypeInfoCount(This,pctinfo)\
    (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo)
#define ADORecordsetConstruction_GetTypeInfo(This,iTInfo,lcid,ppTInfo)\
    (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo)
#define ADORecordsetConstruction_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)\
    (This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)
#define ADORecordsetConstruction_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)\
    (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)
#define ADORecordsetConstruction_get_Rowset(This,ppRowset)\
    (This)->lpVtbl -> get_Rowset(This,ppRowset)
#define ADORecordsetConstruction_put_Rowset(This,pRowset)\
    (This)->lpVtbl -> put_Rowset(This,pRowset)
#define ADORecordsetConstruction_get_Chapter(This,plChapter)\
    (This)->lpVtbl -> get_Chapter(This,plChapter)
#define ADORecordsetConstruction_put_Chapter(This,lChapter)\
    (This)->lpVtbl -> put_Chapter(This,lChapter)
#define ADORecordsetConstruction_get_RowPosition(This,ppRowPos)\
    (This)->lpVtbl -> get_RowPosition(This,ppRowPos)
#define ADORecordsetConstruction_put_RowPosition(This,pRowPos)\
    (This)->lpVtbl -> put_RowPosition(This,pRowPos)
#endif /* COBJMACROS */
#endif /* C style interface */
/* [propget] */ HRESULT STDMETHODCALLTYPE ADORecordsetConstruction_get_Rowset_Proxy(
    ADORecordsetConstruction __RPC_FAR * This,
    /* [retval][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppRowset);
void __RPC_STUB ADORecordsetConstruction_get_Rowset_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propput] */ HRESULT STDMETHODCALLTYPE ADORecordsetConstruction_put_Rowset_Proxy(
    ADORecordsetConstruction __RPC_FAR * This,
    /* [in] */ IUnknown __RPC_FAR *pRowset);
void __RPC_STUB ADORecordsetConstruction_put_Rowset_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propget] */ HRESULT STDMETHODCALLTYPE ADORecordsetConstruction_get_Chapter_Proxy(
    ADORecordsetConstruction __RPC_FAR * This,
    /* [retval][out] */ LONG __RPC_FAR *plChapter);
void __RPC_STUB ADORecordsetConstruction_get_Chapter_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propput] */ HRESULT STDMETHODCALLTYPE ADORecordsetConstruction_put_Chapter_Proxy(
    ADORecordsetConstruction __RPC_FAR * This,
    /* [in] */ LONG lChapter);
void __RPC_STUB ADORecordsetConstruction_put_Chapter_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propget] */ HRESULT STDMETHODCALLTYPE ADORecordsetConstruction_get_RowPosition_Proxy(
    ADORecordsetConstruction __RPC_FAR * This,
    /* [retval][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppRowPos);
void __RPC_STUB ADORecordsetConstruction_get_RowPosition_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propput] */ HRESULT STDMETHODCALLTYPE ADORecordsetConstruction_put_RowPosition_Proxy(
    ADORecordsetConstruction __RPC_FAR * This,
    /* [in] */ IUnknown __RPC_FAR *pRowPos);
void __RPC_STUB ADORecordsetConstruction_put_RowPosition_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
#endif /* __ADORecordsetConstruction_INTERFACE_DEFINED__ */
#ifndef __Field_INTERFACE_DEFINED__
#define __Field_INTERFACE_DEFINED__
/****************************************
 * Generated header for interface: Field
 * at Tue Oct 07 23:50:20 1997
 * using MIDL 3.01.75
 ****************************************/
/* [object][helpstring][helpcontext][uuid][nonextensible][dual] */
EXTERN_C const IID IID_Field;
#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("00000505-0000-0010-8000-00AA006D2EA4")
    ADOField : public _ADO
    {
    public:
        virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_ActualSize(
            /* [retval][out] */ long __RPC_FAR *pl) = 0;

        virtual /* [helpstring][helpcontext][propget][id] */ HRESULT STDMETHODCALLTYPE get_Attributes(
            /* [retval][out] */ long __RPC_FAR *pl) = 0;

        virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_DefinedSize(
            /* [retval][out] */ long __RPC_FAR *pl) = 0;

        virtual /* [helpstring][helpcontext][propget][id] */ HRESULT STDMETHODCALLTYPE get_Name(
            /* [retval][out] */ BSTR __RPC_FAR *pbstr) = 0;

        virtual /* [helpstring][helpcontext][propget][id] */ HRESULT STDMETHODCALLTYPE get_Type(
            /* [retval][out] */ DataTypeEnum __RPC_FAR *pDataType) = 0;

        virtual /* [helpstring][helpcontext][propget][id] */ HRESULT STDMETHODCALLTYPE get_Value(
            /* [retval][out] */ VARIANT __RPC_FAR *pvar) = 0;

        virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_Value(
            /* [in] */ VARIANT Val) = 0;

        virtual /* [helpstring][helpcontext][propget] */ HRESULT STDMETHODCALLTYPE get_Precision(
            /* [retval][out] */ BYTE __RPC_FAR *pbPrecision) = 0;

        virtual /* [helpstring][helpcontext][propget] */ HRESULT STDMETHODCALLTYPE get_NumericScale(
            /* [retval][out] */ BYTE __RPC_FAR *pbNumericScale) = 0;

        virtual /* [helpstring][helpcontext][id] */ HRESULT STDMETHODCALLTYPE AppendChunk(
            /* [in] */ VARIANT Data) = 0;

        virtual /* [helpstring][helpcontext][id] */ HRESULT STDMETHODCALLTYPE GetChunk(
            /* [in] */ long Length,
            /* [retval][out] */ VARIANT __RPC_FAR *pvar) = 0;

        virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_OriginalValue(
            /* [retval][out] */ VARIANT __RPC_FAR *pvar) = 0;

        virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_UnderlyingValue(
            /* [retval][out] */ VARIANT __RPC_FAR *pvar) = 0;

    };

#else /* C style interface */
    typedef struct FieldVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            ADOField __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            ADOField __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            ADOField __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeInfoCount )(
            ADOField __RPC_FAR * This,
            /* [out] */ UINT __RPC_FAR *pctinfo);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeInfo )(
            ADOField __RPC_FAR * This,
            /* [in] */ UINT iTInfo,
            /* [in] */ LCID lcid,
            /* [out] */ ITypeInfo __RPC_FAR *__RPC_FAR *ppTInfo);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetIDsOfNames )(
            ADOField __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [size_is][in] */ LPOLESTR __RPC_FAR *rgszNames,
            /* [in] */ UINT cNames,
            /* [in] */ LCID lcid,
            /* [size_is][out] */ DISPID __RPC_FAR *rgDispId);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Invoke )(
            ADOField __RPC_FAR * This,
            /* [in] */ DISPID dispIdMember,
            /* [in] */ REFIID riid,
            /* [in] */ LCID lcid,
            /* [in] */ WORD wFlags,
            /* [out][in] */ DISPPARAMS __RPC_FAR *pDispParams,
            /* [out] */ VARIANT __RPC_FAR *pVarResult,
            /* [out] */ EXCEPINFO __RPC_FAR *pExcepInfo,
            /* [out] */ UINT __RPC_FAR *puArgErr);

        /* [helpstring][helpcontext][id][propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Properties )(
            ADOField __RPC_FAR * This,
            /* [retval][out] */ ADOProperties __RPC_FAR *__RPC_FAR *ppvObject);

        /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_ActualSize )(
            ADOField __RPC_FAR * This,
            /* [retval][out] */ long __RPC_FAR *pl);

        /* [helpstring][helpcontext][propget][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Attributes )(
            ADOField __RPC_FAR * This,
            /* [retval][out] */ long __RPC_FAR *pl);

        /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_DefinedSize )(
            ADOField __RPC_FAR * This,
            /* [retval][out] */ long __RPC_FAR *pl);

        /* [helpstring][helpcontext][propget][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Name )(
            ADOField __RPC_FAR * This,
            /* [retval][out] */ BSTR __RPC_FAR *pbstr);

        /* [helpstring][helpcontext][propget][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Type )(
            ADOField __RPC_FAR * This,
            /* [retval][out] */ DataTypeEnum __RPC_FAR *pDataType);

        /* [helpstring][helpcontext][propget][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Value )(
            ADOField __RPC_FAR * This,
            /* [retval][out] */ VARIANT __RPC_FAR *pvar);

        /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_Value )(
            ADOField __RPC_FAR * This,
            /* [in] */ VARIANT Val);

        /* [helpstring][helpcontext][propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Precision )(
            ADOField __RPC_FAR * This,
            /* [retval][out] */ BYTE __RPC_FAR *pbPrecision);

        /* [helpstring][helpcontext][propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_NumericScale )(
            ADOField __RPC_FAR * This,
            /* [retval][out] */ BYTE __RPC_FAR *pbNumericScale);

        /* [helpstring][helpcontext][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *AppendChunk )(
            ADOField __RPC_FAR * This,
            /* [in] */ VARIANT Data);

        /* [helpstring][helpcontext][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetChunk )(
            ADOField __RPC_FAR * This,
            /* [in] */ long Length,
            /* [retval][out] */ VARIANT __RPC_FAR *pvar);

        /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_OriginalValue )(
            ADOField __RPC_FAR * This,
            /* [retval][out] */ VARIANT __RPC_FAR *pvar);

        /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_UnderlyingValue )(
            ADOField __RPC_FAR * This,
            /* [retval][out] */ VARIANT __RPC_FAR *pvar);

        END_INTERFACE
    } FieldVtbl;
    interface Field
    {
        CONST_VTBL struct FieldVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS
#define Field_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
#define Field_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)
#define Field_Release(This)\
    (This)->lpVtbl -> Release(This)
#define Field_GetTypeInfoCount(This,pctinfo)\
    (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo)
#define Field_GetTypeInfo(This,iTInfo,lcid,ppTInfo)\
    (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo)
#define Field_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)\
    (This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)
#define Field_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)\
    (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)
#define Field_get_Properties(This,ppvObject)\
    (This)->lpVtbl -> get_Properties(This,ppvObject)
#define Field_get_ActualSize(This,pl)\
    (This)->lpVtbl -> get_ActualSize(This,pl)
#define Field_get_Attributes(This,pl)\
    (This)->lpVtbl -> get_Attributes(This,pl)
#define Field_get_DefinedSize(This,pl)\
    (This)->lpVtbl -> get_DefinedSize(This,pl)
#define Field_get_Name(This,pbstr)\
    (This)->lpVtbl -> get_Name(This,pbstr)
#define Field_get_Type(This,pDataType)\
    (This)->lpVtbl -> get_Type(This,pDataType)
#define Field_get_Value(This,pvar)\
    (This)->lpVtbl -> get_Value(This,pvar)
#define Field_put_Value(This,Val)\
    (This)->lpVtbl -> put_Value(This,Val)
#define Field_get_Precision(This,pbPrecision)\
    (This)->lpVtbl -> get_Precision(This,pbPrecision)
#define Field_get_NumericScale(This,pbNumericScale)\
    (This)->lpVtbl -> get_NumericScale(This,pbNumericScale)
#define Field_AppendChunk(This,Data)\
    (This)->lpVtbl -> AppendChunk(This,Data)
#define Field_GetChunk(This,Length,pvar)\
    (This)->lpVtbl -> GetChunk(This,Length,pvar)
#define Field_get_OriginalValue(This,pvar)\
    (This)->lpVtbl -> get_OriginalValue(This,pvar)
#define Field_get_UnderlyingValue(This,pvar)\
    (This)->lpVtbl -> get_UnderlyingValue(This,pvar)
#endif /* COBJMACROS */
#endif /* C style interface */
/* [propget][id] */ HRESULT STDMETHODCALLTYPE Field_get_ActualSize_Proxy(
    ADOField __RPC_FAR * This,
    /* [retval][out] */ long __RPC_FAR *pl);
void __RPC_STUB Field_get_ActualSize_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [helpstring][helpcontext][propget][id] */ HRESULT STDMETHODCALLTYPE Field_get_Attributes_Proxy(
    ADOField __RPC_FAR * This,
    /* [retval][out] */ long __RPC_FAR *pl);
void __RPC_STUB Field_get_Attributes_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propget][id] */ HRESULT STDMETHODCALLTYPE Field_get_DefinedSize_Proxy(
    ADOField __RPC_FAR * This,
    /* [retval][out] */ long __RPC_FAR *pl);
void __RPC_STUB Field_get_DefinedSize_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [helpstring][helpcontext][propget][id] */ HRESULT STDMETHODCALLTYPE Field_get_Name_Proxy(
    ADOField __RPC_FAR * This,
    /* [retval][out] */ BSTR __RPC_FAR *pbstr);
void __RPC_STUB Field_get_Name_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [helpstring][helpcontext][propget][id] */ HRESULT STDMETHODCALLTYPE Field_get_Type_Proxy(
    ADOField __RPC_FAR * This,
    /* [retval][out] */ DataTypeEnum __RPC_FAR *pDataType);
void __RPC_STUB Field_get_Type_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [helpstring][helpcontext][propget][id] */ HRESULT STDMETHODCALLTYPE Field_get_Value_Proxy(
    ADOField __RPC_FAR * This,
    /* [retval][out] */ VARIANT __RPC_FAR *pvar);
void __RPC_STUB Field_get_Value_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propput][id] */ HRESULT STDMETHODCALLTYPE Field_put_Value_Proxy(
    ADOField __RPC_FAR * This,
    /* [in] */ VARIANT Val);
void __RPC_STUB Field_put_Value_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [helpstring][helpcontext][propget] */ HRESULT STDMETHODCALLTYPE Field_get_Precision_Proxy(
    ADOField __RPC_FAR * This,
    /* [retval][out] */ BYTE __RPC_FAR *pbPrecision);
void __RPC_STUB Field_get_Precision_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [helpstring][helpcontext][propget] */ HRESULT STDMETHODCALLTYPE Field_get_NumericScale_Proxy(
    ADOField __RPC_FAR * This,
    /* [retval][out] */ BYTE __RPC_FAR *pbNumericScale);
void __RPC_STUB Field_get_NumericScale_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [helpstring][helpcontext][id] */ HRESULT STDMETHODCALLTYPE Field_AppendChunk_Proxy(
    ADOField __RPC_FAR * This,
    /* [in] */ VARIANT Data);
void __RPC_STUB Field_AppendChunk_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [helpstring][helpcontext][id] */ HRESULT STDMETHODCALLTYPE Field_GetChunk_Proxy(
    ADOField __RPC_FAR * This,
    /* [in] */ long Length,
    /* [retval][out] */ VARIANT __RPC_FAR *pvar);
void __RPC_STUB Field_GetChunk_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propget][id] */ HRESULT STDMETHODCALLTYPE Field_get_OriginalValue_Proxy(
    ADOField __RPC_FAR * This,
    /* [retval][out] */ VARIANT __RPC_FAR *pvar);
void __RPC_STUB Field_get_OriginalValue_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propget][id] */ HRESULT STDMETHODCALLTYPE Field_get_UnderlyingValue_Proxy(
    ADOField __RPC_FAR * This,
    /* [retval][out] */ VARIANT __RPC_FAR *pvar);
void __RPC_STUB Field_get_UnderlyingValue_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
#endif /* __Field_INTERFACE_DEFINED__ */
#ifndef __Fields_INTERFACE_DEFINED__
#define __Fields_INTERFACE_DEFINED__
/****************************************
 * Generated header for interface: Fields
 * at Tue Oct 07 23:50:20 1997
 * using MIDL 3.01.75
 ****************************************/
/* [object][uuid][helpstring][helpcontext][nonextensible][dual] */
EXTERN_C const IID IID_Fields;
#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("00000506-0000-0010-8000-00AA006D2EA4")
    ADOFields : public _ADOCollection
    {
    public:
        virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_Item(
            /* [in] */ VARIANT Index,
            /* [retval][out] */ ADOField __RPC_FAR *__RPC_FAR *ppvObject) = 0;

    };

#else /* C style interface */
    typedef struct FieldsVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            ADOFields __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            ADOFields __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            ADOFields __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeInfoCount )(
            ADOFields __RPC_FAR * This,
            /* [out] */ UINT __RPC_FAR *pctinfo);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeInfo )(
            ADOFields __RPC_FAR * This,
            /* [in] */ UINT iTInfo,
            /* [in] */ LCID lcid,
            /* [out] */ ITypeInfo __RPC_FAR *__RPC_FAR *ppTInfo);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetIDsOfNames )(
            ADOFields __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [size_is][in] */ LPOLESTR __RPC_FAR *rgszNames,
            /* [in] */ UINT cNames,
            /* [in] */ LCID lcid,
            /* [size_is][out] */ DISPID __RPC_FAR *rgDispId);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Invoke )(
            ADOFields __RPC_FAR * This,
            /* [in] */ DISPID dispIdMember,
            /* [in] */ REFIID riid,
            /* [in] */ LCID lcid,
            /* [in] */ WORD wFlags,
            /* [out][in] */ DISPPARAMS __RPC_FAR *pDispParams,
            /* [out] */ VARIANT __RPC_FAR *pVarResult,
            /* [out] */ EXCEPINFO __RPC_FAR *pExcepInfo,
            /* [out] */ UINT __RPC_FAR *puArgErr);

        /* [helpstring][helpcontext][propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Count )(
            ADOFields __RPC_FAR * This,
            /* [retval][out] */ long __RPC_FAR *c);

        /* [id][restricted] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *_NewEnum )(
            ADOFields __RPC_FAR * This,
            /* [retval][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppvObject);

        /* [helpstring][helpcontext] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Refresh )(
            ADOFields __RPC_FAR * This);

        /* [id][propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Item )(
            ADOFields __RPC_FAR * This,
            /* [in] */ VARIANT Index,
            /* [retval][out] */ ADOField __RPC_FAR *__RPC_FAR *ppvObject);

        END_INTERFACE
    } FieldsVtbl;
    interface Fields
    {
        CONST_VTBL struct FieldsVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS
#define Fields_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
#define Fields_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)
#define Fields_Release(This)\
    (This)->lpVtbl -> Release(This)
#define Fields_GetTypeInfoCount(This,pctinfo)\
    (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo)
#define Fields_GetTypeInfo(This,iTInfo,lcid,ppTInfo)\
    (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo)
#define Fields_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)\
    (This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)
#define Fields_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)\
    (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)
#define Fields_get_Count(This,c)\
    (This)->lpVtbl -> get_Count(This,c)
#define Fields__NewEnum(This,ppvObject)\
    (This)->lpVtbl -> _NewEnum(This,ppvObject)
#define Fields_Refresh(This)\
    (This)->lpVtbl -> Refresh(This)
#define Fields_get_Item(This,Index,ppvObject)\
    (This)->lpVtbl -> get_Item(This,Index,ppvObject)
#endif /* COBJMACROS */
#endif /* C style interface */
/* [id][propget] */ HRESULT STDMETHODCALLTYPE Fields_get_Item_Proxy(
    ADOFields __RPC_FAR * This,
    /* [in] */ VARIANT Index,
    /* [retval][out] */ ADOField __RPC_FAR *__RPC_FAR *ppvObject);
void __RPC_STUB Fields_get_Item_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
#endif /* __Fields_INTERFACE_DEFINED__ */
#ifndef ___Parameter_INTERFACE_DEFINED__
#define ___Parameter_INTERFACE_DEFINED__
/****************************************
 * Generated header for interface: _Parameter
 * at Tue Oct 07 23:50:20 1997
 * using MIDL 3.01.75
 ****************************************/
/* [object][helpstring][helpcontext][uuid][nonextensible][dual] */
EXTERN_C const IID IID__Parameter;
#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0000050C-0000-0010-8000-00AA006D2EA4")
    _ADOParameter : public _ADO
    {
    public:
        virtual /* [helpstring][helpcontext][propget] */ HRESULT STDMETHODCALLTYPE get_Name(
            /* [retval][out] */ BSTR __RPC_FAR *pbstr) = 0;

        virtual /* [helpstring][helpcontext][propput] */ HRESULT STDMETHODCALLTYPE put_Name(
            /* [in] */ BSTR bstr) = 0;

        virtual /* [helpstring][helpcontext][id][propget] */ HRESULT STDMETHODCALLTYPE get_Value(
            /* [retval][out] */ VARIANT __RPC_FAR *pvar) = 0;

        virtual /* [id][propput] */ HRESULT STDMETHODCALLTYPE put_Value(
            /* [in] */ VARIANT val) = 0;

        virtual /* [helpstring][helpcontext][propget] */ HRESULT STDMETHODCALLTYPE get_Type(
            /* [retval][out] */ DataTypeEnum __RPC_FAR *psDataType) = 0;

        virtual /* [helpstring][helpcontext][propput] */ HRESULT STDMETHODCALLTYPE put_Type(
            /* [in] */ DataTypeEnum sDataType) = 0;

        virtual /* [helpstring][helpcontext][propput] */ HRESULT STDMETHODCALLTYPE put_Direction(
            /* [in] */ ParameterDirectionEnum lParmDirection) = 0;

        virtual /* [helpstring][helpcontext][propget] */ HRESULT STDMETHODCALLTYPE get_Direction(
            /* [retval][out] */ ParameterDirectionEnum __RPC_FAR *plParmDirection) = 0;

        virtual /* [helpstring][helpcontext][propput] */ HRESULT STDMETHODCALLTYPE put_Precision(
            /* [in] */ BYTE bPrecision) = 0;

        virtual /* [helpstring][helpcontext][propget] */ HRESULT STDMETHODCALLTYPE get_Precision(
            /* [retval][out] */ BYTE __RPC_FAR *pbPrecision) = 0;

        virtual /* [helpstring][helpcontext][propput] */ HRESULT STDMETHODCALLTYPE put_NumericScale(
            /* [in] */ BYTE bScale) = 0;

        virtual /* [helpstring][helpcontext][propget] */ HRESULT STDMETHODCALLTYPE get_NumericScale(
            /* [retval][out] */ BYTE __RPC_FAR *pbScale) = 0;

        virtual /* [helpstring][helpcontext][propput] */ HRESULT STDMETHODCALLTYPE put_Size(
            /* [in] */ long l) = 0;

        virtual /* [helpstring][helpcontext][propget] */ HRESULT STDMETHODCALLTYPE get_Size(
            /* [retval][out] */ long __RPC_FAR *pl) = 0;

        virtual HRESULT STDMETHODCALLTYPE AppendChunk(
            /* [in] */ VARIANT Val) = 0;

        virtual /* [helpstring][helpcontext][propget] */ HRESULT STDMETHODCALLTYPE get_Attributes(
            /* [retval][out] */ LONG __RPC_FAR *plParmAttribs) = 0;

        virtual /* [helpstring][helpcontext][propput] */ HRESULT STDMETHODCALLTYPE put_Attributes(
            /* [in] */ LONG lParmAttribs) = 0;

    };

#else /* C style interface */
    typedef struct _ParameterVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            _ADOParameter __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(

_ADOParameter __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            _ADOParameter __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeInfoCount )(
            _ADOParameter __RPC_FAR * This,
            /* [out] */ UINT __RPC_FAR *pctinfo);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeInfo )(
            _ADOParameter __RPC_FAR * This,
            /* [in] */ UINT iTInfo,
            /* [in] */ LCID lcid,
            /* [out] */ ITypeInfo __RPC_FAR *__RPC_FAR *ppTInfo);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetIDsOfNames )(
            _ADOParameter __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [size_is][in] */ LPOLESTR __RPC_FAR *rgszNames,
            /* [in] */ UINT cNames,
            /* [in] */ LCID lcid,
            /* [size_is][out] */ DISPID __RPC_FAR *rgDispId);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Invoke )(
            _ADOParameter __RPC_FAR * This,
            /* [in] */ DISPID dispIdMember,
            /* [in] */ REFIID riid,
            /* [in] */ LCID lcid,
            /* [in] */ WORD wFlags,
            /* [out][in] */ DISPPARAMS __RPC_FAR *pDispParams,
            /* [out] */ VARIANT __RPC_FAR *pVarResult,
            /* [out] */ EXCEPINFO __RPC_FAR *pExcepInfo,
            /* [out] */ UINT __RPC_FAR *puArgErr);

        /* [helpstring][helpcontext][id][propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Properties )(
            _ADOParameter __RPC_FAR * This,
            /* [retval][out] */ ADOProperties __RPC_FAR *__RPC_FAR *ppvObject);

        /* [helpstring][helpcontext][propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Name )(
            _ADOParameter __RPC_FAR * This,
            /* [retval][out] */ BSTR __RPC_FAR *pbstr);

        /* [helpstring][helpcontext][propput] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_Name )(
            _ADOParameter __RPC_FAR * This,
            /* [in] */ BSTR bstr);

        /* [helpstring][helpcontext][id][propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Value )(
            _ADOParameter __RPC_FAR * This,
            /* [retval][out] */ VARIANT __RPC_FAR *pvar);

        /* [id][propput] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_Value )(
            _ADOParameter __RPC_FAR * This,
            /* [in] */ VARIANT val);

        /* [helpstring][helpcontext][propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Type )(
            _ADOParameter __RPC_FAR * This,
            /* [retval][out] */ DataTypeEnum __RPC_FAR *psDataType);

        /* [helpstring][helpcontext][propput] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_Type )(
            _ADOParameter __RPC_FAR * This,
            /* [in] */ DataTypeEnum sDataType);

        /* [helpstring][helpcontext][propput] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_Direction )(
            _ADOParameter __RPC_FAR * This,
            /* [in] */ ParameterDirectionEnum lParmDirection);

        /* [helpstring][helpcontext][propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Direction )(
            _ADOParameter __RPC_FAR * This,
            /* [retval][out] */ ParameterDirectionEnum __RPC_FAR *plParmDirection);

        /* [helpstring][helpcontext][propput] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_Precision )(
            _ADOParameter __RPC_FAR * This,
            /* [in] */ BYTE bPrecision);

        /* [helpstring][helpcontext][propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Precision )(
            _ADOParameter __RPC_FAR * This,
            /* [retval][out] */ BYTE __RPC_FAR *pbPrecision);

        /* [helpstring][helpcontext][propput] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_NumericScale )(
            _ADOParameter __RPC_FAR * This,
            /* [in] */ BYTE bScale);

        /* [helpstring][helpcontext][propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_NumericScale )(
            _ADOParameter __RPC_FAR * This,
            /* [retval][out] */ BYTE __RPC_FAR *pbScale);

        /* [helpstring][helpcontext][propput] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_Size )(
            _ADOParameter __RPC_FAR * This,
            /* [in] */ long l);

        /* [helpstring][helpcontext][propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Size )(
            _ADOParameter __RPC_FAR * This,
            /* [retval][out] */ long __RPC_FAR *pl);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *AppendChunk )(
            _ADOParameter __RPC_FAR * This,
            /* [in] */ VARIANT Val);

        /* [helpstring][helpcontext][propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Attributes )(
            _ADOParameter __RPC_FAR * This,
            /* [retval][out] */ LONG __RPC_FAR *plParmAttribs);

        /* [helpstring][helpcontext][propput] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_Attributes )(
            _ADOParameter __RPC_FAR * This,
            /* [in] */ LONG lParmAttribs);

        END_INTERFACE
    } _ParameterVtbl;
    interface _Parameter
    {
        CONST_VTBL struct _ParameterVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS
#define _Parameter_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
#define _Parameter_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)
#define _Parameter_Release(This)\
    (This)->lpVtbl -> Release(This)
#define _Parameter_GetTypeInfoCount(This,pctinfo)\
    (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo)
#define _Parameter_GetTypeInfo(This,iTInfo,lcid,ppTInfo)\
    (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo)
#define _Parameter_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)\
    (This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)
#define _Parameter_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)\
    (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)
#define _Parameter_get_Properties(This,ppvObject)\
    (This)->lpVtbl -> get_Properties(This,ppvObject)
#define _Parameter_get_Name(This,pbstr)\
    (This)->lpVtbl -> get_Name(This,pbstr)
#define _Parameter_put_Name(This,bstr)\
    (This)->lpVtbl -> put_Name(This,bstr)
#define _Parameter_get_Value(This,pvar)\
    (This)->lpVtbl -> get_Value(This,pvar)
#define _Parameter_put_Value(This,val)\
    (This)->lpVtbl -> put_Value(This,val)
#define _Parameter_get_Type(This,psDataType)\
    (This)->lpVtbl -> get_Type(This,psDataType)
#define _Parameter_put_Type(This,sDataType)\
    (This)->lpVtbl -> put_Type(This,sDataType)
#define _Parameter_put_Direction(This,lParmDirection)\
    (This)->lpVtbl -> put_Direction(This,lParmDirection)
#define _Parameter_get_Direction(This,plParmDirection)\
    (This)->lpVtbl -> get_Direction(This,plParmDirection)
#define _Parameter_put_Precision(This,bPrecision)\
    (This)->lpVtbl -> put_Precision(This,bPrecision)
#define _Parameter_get_Precision(This,pbPrecision)\
    (This)->lpVtbl -> get_Precision(This,pbPrecision)
#define _Parameter_put_NumericScale(This,bScale)\
    (This)->lpVtbl -> put_NumericScale(This,bScale)
#define _Parameter_get_NumericScale(This,pbScale)\
    (This)->lpVtbl -> get_NumericScale(This,pbScale)
#define _Parameter_put_Size(This,l)\
    (This)->lpVtbl -> put_Size(This,l)
#define _Parameter_get_Size(This,pl)\
    (This)->lpVtbl -> get_Size(This,pl)
#define _Parameter_AppendChunk(This,Val)\
    (This)->lpVtbl -> AppendChunk(This,Val)
#define _Parameter_get_Attributes(This,plParmAttribs)\
    (This)->lpVtbl -> get_Attributes(This,plParmAttribs)
#define _Parameter_put_Attributes(This,lParmAttribs)\
    (This)->lpVtbl -> put_Attributes(This,lParmAttribs)
#endif /* COBJMACROS */
#endif /* C style interface */
/* [helpstring][helpcontext][propget] */ HRESULT STDMETHODCALLTYPE _Parameter_get_Name_Proxy(
    _ADOParameter __RPC_FAR * This,
    /* [retval][out] */ BSTR __RPC_FAR *pbstr);
void __RPC_STUB _Parameter_get_Name_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [helpstring][helpcontext][propput] */ HRESULT STDMETHODCALLTYPE _Parameter_put_Name_Proxy(
    _ADOParameter __RPC_FAR * This,
    /* [in] */ BSTR bstr);
void __RPC_STUB _Parameter_put_Name_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [helpstring][helpcontext][id][propget] */ HRESULT STDMETHODCALLTYPE _Parameter_get_Value_Proxy(
    _ADOParameter __RPC_FAR * This,
    /* [retval][out] */ VARIANT __RPC_FAR *pvar);
void __RPC_STUB _Parameter_get_Value_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [id][propput] */ HRESULT STDMETHODCALLTYPE _Parameter_put_Value_Proxy(
    _ADOParameter __RPC_FAR * This,
    /* [in] */ VARIANT val);
void __RPC_STUB _Parameter_put_Value_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [helpstring][helpcontext][propget] */ HRESULT STDMETHODCALLTYPE _Parameter_get_Type_Proxy(
    _ADOParameter __RPC_FAR * This,
    /* [retval][out] */ DataTypeEnum __RPC_FAR *psDataType);
void __RPC_STUB _Parameter_get_Type_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [helpstring][helpcontext][propput] */ HRESULT STDMETHODCALLTYPE _Parameter_put_Type_Proxy(
    _ADOParameter __RPC_FAR * This,
    /* [in] */ DataTypeEnum sDataType);
void __RPC_STUB _Parameter_put_Type_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [helpstring][helpcontext][propput] */ HRESULT STDMETHODCALLTYPE _Parameter_put_Direction_Proxy(
    _ADOParameter __RPC_FAR * This,
    /* [in] */ ParameterDirectionEnum lParmDirection);
void __RPC_STUB _Parameter_put_Direction_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [helpstring][helpcontext][propget] */ HRESULT STDMETHODCALLTYPE _Parameter_get_Direction_Proxy(
    _ADOParameter __RPC_FAR * This,
    /* [retval][out] */ ParameterDirectionEnum __RPC_FAR *plParmDirection);
void __RPC_STUB _Parameter_get_Direction_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [helpstring][helpcontext][propput] */ HRESULT STDMETHODCALLTYPE _Parameter_put_Precision_Proxy(
    _ADOParameter __RPC_FAR * This,
    /* [in] */ BYTE bPrecision);
void __RPC_STUB _Parameter_put_Precision_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [helpstring][helpcontext][propget] */ HRESULT STDMETHODCALLTYPE _Parameter_get_Precision_Proxy(
    _ADOParameter __RPC_FAR * This,
    /* [retval][out] */ BYTE __RPC_FAR *pbPrecision);
void __RPC_STUB _Parameter_get_Precision_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [helpstring][helpcontext][propput] */ HRESULT STDMETHODCALLTYPE _Parameter_put_NumericScale_Proxy(
    _ADOParameter __RPC_FAR * This,
    /* [in] */ BYTE bScale);
void __RPC_STUB _Parameter_put_NumericScale_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [helpstring][helpcontext][propget] */ HRESULT STDMETHODCALLTYPE _Parameter_get_NumericScale_Proxy(
    _ADOParameter __RPC_FAR * This,
    /* [retval][out] */ BYTE __RPC_FAR *pbScale);
void __RPC_STUB _Parameter_get_NumericScale_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [helpstring][helpcontext][propput] */ HRESULT STDMETHODCALLTYPE _Parameter_put_Size_Proxy(
    _ADOParameter __RPC_FAR * This,
    /* [in] */ long l);
void __RPC_STUB _Parameter_put_Size_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [helpstring][helpcontext][propget] */ HRESULT STDMETHODCALLTYPE _Parameter_get_Size_Proxy(
    _ADOParameter __RPC_FAR * This,
    /* [retval][out] */ long __RPC_FAR *pl);
void __RPC_STUB _Parameter_get_Size_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
HRESULT STDMETHODCALLTYPE _Parameter_AppendChunk_Proxy(
    _ADOParameter __RPC_FAR * This,
    /* [in] */ VARIANT Val);
void __RPC_STUB _Parameter_AppendChunk_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [helpstring][helpcontext][propget] */ HRESULT STDMETHODCALLTYPE _Parameter_get_Attributes_Proxy(
    _ADOParameter __RPC_FAR * This,
    /* [retval][out] */ LONG __RPC_FAR *plParmAttribs);
void __RPC_STUB _Parameter_get_Attributes_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [helpstring][helpcontext][propput] */ HRESULT STDMETHODCALLTYPE _Parameter_put_Attributes_Proxy(
    _ADOParameter __RPC_FAR * This,
    /* [in] */ LONG lParmAttribs);
void __RPC_STUB _Parameter_put_Attributes_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
#endif /* ___Parameter_INTERFACE_DEFINED__ */
#ifdef __cplusplus
EXTERN_C const CLSID CLSID_Parameter;
Parameter;
#endif
#ifndef __Parameters_INTERFACE_DEFINED__
#define __Parameters_INTERFACE_DEFINED__
/****************************************
 * Generated header for interface: Parameters
 * at Tue Oct 07 23:50:20 1997
 * using MIDL 3.01.75
 ****************************************/
/* [object][uuid][helpstring][helpcontext][nonextensible][dual] */
EXTERN_C const IID IID_Parameters;
#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0000050D-0000-0010-8000-00AA006D2EA4")
    ADOParameters : public _ADODynaCollection
    {
    public:
        virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_Item(
            /* [in] */ VARIANT Index,
            /* [retval][out] */ _ADOParameter __RPC_FAR *__RPC_FAR *ppvObject) = 0;

    };

#else /* C style interface */
    typedef struct ParametersVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            ADOParameters __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            ADOParameters __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            ADOParameters __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeInfoCount )(
            ADOParameters __RPC_FAR * This,
            /* [out] */ UINT __RPC_FAR *pctinfo);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeInfo )(
            ADOParameters __RPC_FAR * This,
            /* [in] */ UINT iTInfo,
            /* [in] */ LCID lcid,
            /* [out] */ ITypeInfo __RPC_FAR *__RPC_FAR *ppTInfo);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetIDsOfNames )(
            ADOParameters __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [size_is][in] */ LPOLESTR __RPC_FAR *rgszNames,
            /* [in] */ UINT cNames,
            /* [in] */ LCID lcid,
            /* [size_is][out] */ DISPID __RPC_FAR *rgDispId);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Invoke )(
            ADOParameters __RPC_FAR * This,
            /* [in] */ DISPID dispIdMember,
            /* [in] */ REFIID riid,
            /* [in] */ LCID lcid,
            /* [in] */ WORD wFlags,
            /* [out][in] */ DISPPARAMS __RPC_FAR *pDispParams,
            /* [out] */ VARIANT __RPC_FAR *pVarResult,
            /* [out] */ EXCEPINFO __RPC_FAR *pExcepInfo,
            /* [out] */ UINT __RPC_FAR *puArgErr);

        /* [helpstring][helpcontext][propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Count )(
            ADOParameters __RPC_FAR * This,
            /* [retval][out] */ long __RPC_FAR *c);

        /* [id][restricted] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *_NewEnum )(
            ADOParameters __RPC_FAR * This,
            /* [retval][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppvObject);

        /* [helpstring][helpcontext] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Refresh )(
            ADOParameters __RPC_FAR * This);

        /* [helpstring][helpcontext] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Append )(
            ADOParameters __RPC_FAR * This,
            /* [in] */ IDispatch __RPC_FAR *Object);

        /* [helpstring][helpcontext] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Delete )(
            ADOParameters __RPC_FAR * This,
            /* [in] */ VARIANT Index);

        /* [id][propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Item )(
            ADOParameters __RPC_FAR * This,
            /* [in] */ VARIANT Index,
            /* [retval][out] */ _ADOParameter __RPC_FAR *__RPC_FAR *ppvObject);

        END_INTERFACE
    } ParametersVtbl;
    interface Parameters
    {
        CONST_VTBL struct ParametersVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS
#define Parameters_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
#define Parameters_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)
#define Parameters_Release(This)\
    (This)->lpVtbl -> Release(This)
#define Parameters_GetTypeInfoCount(This,pctinfo)\
    (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo)
#define Parameters_GetTypeInfo(This,iTInfo,lcid,ppTInfo)\
    (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo)
#define Parameters_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)\
    (This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)
#define Parameters_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)\
    (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)
#define Parameters_get_Count(This,c)\
    (This)->lpVtbl -> get_Count(This,c)
#define Parameters__NewEnum(This,ppvObject)\
    (This)->lpVtbl -> _NewEnum(This,ppvObject)
#define Parameters_Refresh(This)\
    (This)->lpVtbl -> Refresh(This)
#define Parameters_Append(This,Object)\
    (This)->lpVtbl -> Append(This,Object)
#define Parameters_Delete(This,Index)\
    (This)->lpVtbl -> Delete(This,Index)
#define Parameters_get_Item(This,Index,ppvObject)\
    (This)->lpVtbl -> get_Item(This,Index,ppvObject)
#endif /* COBJMACROS */
#endif /* C style interface */
/* [id][propget] */ HRESULT STDMETHODCALLTYPE Parameters_get_Item_Proxy(
    ADOParameters __RPC_FAR * This,
    /* [in] */ VARIANT Index,
    /* [retval][out] */ _ADOParameter __RPC_FAR *__RPC_FAR *ppvObject);
void __RPC_STUB Parameters_get_Item_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
#endif /* __Parameters_INTERFACE_DEFINED__ */
#ifndef __Property_INTERFACE_DEFINED__
#define __Property_INTERFACE_DEFINED__
/****************************************
 * Generated header for interface: Property
 * at Tue Oct 07 23:50:20 1997
 * using MIDL 3.01.75
 ****************************************/
/* [object][helpstring][helpcontext][uuid][nonextensible][dual] */
EXTERN_C const IID IID_Property;
#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("00000503-0000-0010-8000-00AA006D2EA4")
    ADOProperty : public IDispatch
    {
    public:
        virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_Value(
            /* [retval][out] */ VARIANT __RPC_FAR *pval) = 0;

        virtual /* [id][propput] */ HRESULT STDMETHODCALLTYPE put_Value(
            /* [in] */ VARIANT val) = 0;

        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Name(
            /* [retval][out] */ BSTR __RPC_FAR *pbstr) = 0;

        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Type(
            /* [retval][out] */ DataTypeEnum __RPC_FAR *ptype) = 0;

        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Attributes(
            /* [retval][out] */ long __RPC_FAR *plAttributes) = 0;

        virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_Attributes(
            /* [in] */ long lAttributes) = 0;

    };

#else /* C style interface */
    typedef struct PropertyVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            ADOProperty __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            ADOProperty __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            ADOProperty __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeInfoCount )(
            ADOProperty __RPC_FAR * This,
            /* [out] */ UINT __RPC_FAR *pctinfo);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeInfo )(
            ADOProperty __RPC_FAR * This,
            /* [in] */ UINT iTInfo,
            /* [in] */ LCID lcid,
            /* [out] */ ITypeInfo __RPC_FAR *__RPC_FAR *ppTInfo);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetIDsOfNames )(
            ADOProperty __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [size_is][in] */ LPOLESTR __RPC_FAR *rgszNames,
            /* [in] */ UINT cNames,
            /* [in] */ LCID lcid,
            /* [size_is][out] */ DISPID __RPC_FAR *rgDispId);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Invoke )(
            ADOProperty __RPC_FAR * This,
            /* [in] */ DISPID dispIdMember,
            /* [in] */ REFIID riid,
            /* [in] */ LCID lcid,
            /* [in] */ WORD wFlags,
            /* [out][in] */ DISPPARAMS __RPC_FAR *pDispParams,
            /* [out] */ VARIANT __RPC_FAR *pVarResult,
            /* [out] */ EXCEPINFO __RPC_FAR *pExcepInfo,
            /* [out] */ UINT __RPC_FAR *puArgErr);

        /* [id][propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Value )(
            ADOProperty __RPC_FAR * This,
            /* [retval][out] */ VARIANT __RPC_FAR *pval);

        /* [id][propput] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_Value )(
            ADOProperty __RPC_FAR * This,
            /* [in] */ VARIANT val);

        /* [propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Name )(
            ADOProperty __RPC_FAR * This,
            /* [retval][out] */ BSTR __RPC_FAR *pbstr);

        /* [propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Type )(
            ADOProperty __RPC_FAR * This,
            /* [retval][out] */ DataTypeEnum __RPC_FAR *ptype);

        /* [propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Attributes )(
            ADOProperty __RPC_FAR * This,
            /* [retval][out] */ long __RPC_FAR *plAttributes);

        /* [propput] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_Attributes )(
            ADOProperty __RPC_FAR * This,
            /* [in] */ long lAttributes);

        END_INTERFACE
    } PropertyVtbl;
    interface Property
    {
        CONST_VTBL struct PropertyVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS
#define Property_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
#define Property_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)
#define Property_Release(This)\
    (This)->lpVtbl -> Release(This)
#define Property_GetTypeInfoCount(This,pctinfo)\
    (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo)
#define Property_GetTypeInfo(This,iTInfo,lcid,ppTInfo)\
    (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo)
#define Property_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)\
    (This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)
#define Property_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)\
    (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)
#define Property_get_Value(This,pval)\
    (This)->lpVtbl -> get_Value(This,pval)
#define Property_put_Value(This,val)\
    (This)->lpVtbl -> put_Value(This,val)
#define Property_get_Name(This,pbstr)\
    (This)->lpVtbl -> get_Name(This,pbstr)
#define Property_get_Type(This,ptype)\
    (This)->lpVtbl -> get_Type(This,ptype)
#define Property_get_Attributes(This,plAttributes)\
    (This)->lpVtbl -> get_Attributes(This,plAttributes)
#define Property_put_Attributes(This,lAttributes)\
    (This)->lpVtbl -> put_Attributes(This,lAttributes)
#endif /* COBJMACROS */
#endif /* C style interface */
/* [id][propget] */ HRESULT STDMETHODCALLTYPE Property_get_Value_Proxy(
    ADOProperty __RPC_FAR * This,
    /* [retval][out] */ VARIANT __RPC_FAR *pval);
void __RPC_STUB Property_get_Value_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [id][propput] */ HRESULT STDMETHODCALLTYPE Property_put_Value_Proxy(
    ADOProperty __RPC_FAR * This,
    /* [in] */ VARIANT val);
void __RPC_STUB Property_put_Value_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propget] */ HRESULT STDMETHODCALLTYPE Property_get_Name_Proxy(
    ADOProperty __RPC_FAR * This,
    /* [retval][out] */ BSTR __RPC_FAR *pbstr);
void __RPC_STUB Property_get_Name_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propget] */ HRESULT STDMETHODCALLTYPE Property_get_Type_Proxy(
    ADOProperty __RPC_FAR * This,
    /* [retval][out] */ DataTypeEnum __RPC_FAR *ptype);
void __RPC_STUB Property_get_Type_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propget] */ HRESULT STDMETHODCALLTYPE Property_get_Attributes_Proxy(
    ADOProperty __RPC_FAR * This,
    /* [retval][out] */ long __RPC_FAR *plAttributes);
void __RPC_STUB Property_get_Attributes_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
/* [propput] */ HRESULT STDMETHODCALLTYPE Property_put_Attributes_Proxy(
    ADOProperty __RPC_FAR * This,
    /* [in] */ long lAttributes);
void __RPC_STUB Property_put_Attributes_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
#endif /* __Property_INTERFACE_DEFINED__ */
#ifndef __Properties_INTERFACE_DEFINED__
#define __Properties_INTERFACE_DEFINED__
/****************************************
 * Generated header for interface: Properties
 * at Tue Oct 07 23:50:20 1997
 * using MIDL 3.01.75
 ****************************************/
/* [object][uuid][helpstring][helpcontext][nonextensible][dual] */
EXTERN_C const IID IID_Properties;
#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("00000504-0000-0010-8000-00AA006D2EA4")
    ADOProperties : public _ADOCollection
    {
    public:
        virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_Item(
            /* [in] */ VARIANT Index,
            /* [retval][out] */ ADOProperty __RPC_FAR *__RPC_FAR *ppvObject) = 0;

    };

#else /* C style interface */
    typedef struct PropertiesVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            ADOProperties __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            ADOProperties __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            ADOProperties __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeInfoCount )(
            ADOProperties __RPC_FAR * This,
            /* [out] */ UINT __RPC_FAR *pctinfo);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeInfo )(
            ADOProperties __RPC_FAR * This,

/* [in] */ UINT iTInfo,
            /* [in] */ LCID lcid,
            /* [out] */ ITypeInfo __RPC_FAR *__RPC_FAR *ppTInfo);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetIDsOfNames )(
            ADOProperties __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [size_is][in] */ LPOLESTR __RPC_FAR *rgszNames,
            /* [in] */ UINT cNames,
            /* [in] */ LCID lcid,
            /* [size_is][out] */ DISPID __RPC_FAR *rgDispId);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Invoke )(
            ADOProperties __RPC_FAR * This,
            /* [in] */ DISPID dispIdMember,
            /* [in] */ REFIID riid,
            /* [in] */ LCID lcid,
            /* [in] */ WORD wFlags,
            /* [out][in] */ DISPPARAMS __RPC_FAR *pDispParams,
            /* [out] */ VARIANT __RPC_FAR *pVarResult,
            /* [out] */ EXCEPINFO __RPC_FAR *pExcepInfo,
            /* [out] */ UINT __RPC_FAR *puArgErr);

        /* [helpstring][helpcontext][propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Count )(
            ADOProperties __RPC_FAR * This,
            /* [retval][out] */ long __RPC_FAR *c);

        /* [id][restricted] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *_NewEnum )(
            ADOProperties __RPC_FAR * This,
            /* [retval][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppvObject);

        /* [helpstring][helpcontext] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Refresh )(
            ADOProperties __RPC_FAR * This);

        /* [id][propget] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Item )(
            ADOProperties __RPC_FAR * This,
            /* [in] */ VARIANT Index,
            /* [retval][out] */ ADOProperty __RPC_FAR *__RPC_FAR *ppvObject);

        END_INTERFACE
    } PropertiesVtbl;
    interface Properties
    {
        CONST_VTBL struct PropertiesVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS
#define Properties_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
#define Properties_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)
#define Properties_Release(This)\
    (This)->lpVtbl -> Release(This)
#define Properties_GetTypeInfoCount(This,pctinfo)\
    (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo)
#define Properties_GetTypeInfo(This,iTInfo,lcid,ppTInfo)\
    (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo)
#define Properties_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)\
    (This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)
#define Properties_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)\
    (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)
#define Properties_get_Count(This,c)\
    (This)->lpVtbl -> get_Count(This,c)
#define Properties__NewEnum(This,ppvObject)\
    (This)->lpVtbl -> _NewEnum(This,ppvObject)
#define Properties_Refresh(This)\
    (This)->lpVtbl -> Refresh(This)
#define Properties_get_Item(This,Index,ppvObject)\
    (This)->lpVtbl -> get_Item(This,Index,ppvObject)
#endif /* COBJMACROS */
#endif /* C style interface */
/* [id][propget] */ HRESULT STDMETHODCALLTYPE Properties_get_Item_Proxy(
    ADOProperties __RPC_FAR * This,
    /* [in] */ VARIANT Index,
    /* [retval][out] */ ADOProperty __RPC_FAR *__RPC_FAR *ppvObject);
void __RPC_STUB Properties_get_Item_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);
#endif /* __Properties_INTERFACE_DEFINED__ */
#endif /* __ADODB_LIBRARY_DEFINED__ */
/* Additional Prototypes for ALL interfaces */
/* end of Additional Prototypes */
#ifdef __cplusplus
}
#endif
#endif
#define ADOCommand _ADOCommand
#define ADORecordset _ADORecordset
#define ADOParameter _ADOParameter
#define ADOConnection _ADOConnection
#define ADOCollection _ADOCollection
#define ADODynaCollection _ADODynaCollection

#endif // _ADOINT_H_


MSDADC.H

/* this ALWAYS GENERATED file contains the definitions for the interfaces */

/* File created by MIDL compiler version 3.01.75 */
/* at Tue Aug 05 15:23:57 1997
 */
/* Compiler settings for .\msdadc.idl:
    Oicf (OptLev=i2), W1, Zp2, env=Win32, ms_ext, c_ext
    error checks: none
*/
//@@MIDL_FILE_HEADING(  )
#include "rpc.h"
#include "rpcndr.h"
#ifndef COM_NO_WINDOWS_H
#include "windows.h"
#include "ole2.h"
#endif /*COM_NO_WINDOWS_H*/

#ifndef __msdadc_h__
#define __msdadc_h__

#ifdef __cplusplus
extern "C"{
#endif

/* Forward Declarations */

#ifndef __IDataConvert_FWD_DEFINED__
#define __IDataConvert_FWD_DEFINED__
typedef interface IDataConvert IDataConvert;
#endif /* __IDataConvert_FWD_DEFINED__ */

#ifndef __IDCInfo_FWD_DEFINED__
#define __IDCInfo_FWD_DEFINED__
typedef interface IDCInfo IDCInfo;
#endif /* __IDCInfo_FWD_DEFINED__ */

/* header files for imported files */
#include "oaidl.h"
#include "ocidl.h"

#ifndef __IDataConvert_INTERFACE_DEFINED__
#define __IDataConvert_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IDataConvert
 * at Tue Aug 05 15:23:57 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][helpstring][uuid][object] */

typedef DWORD DBDATACONVERT;

enum DBDATACONVERTENUM
    {DBDATACONVERT_DEFAULT= 0,
DBDATACONVERT_SETDATABEHAVIOR= 0x1,
DBDATACONVERT_LENGTHFROMNTS= 0x2
    };

DEFINE_GUID(IID_IDataConvert,0x0c733a8dL,0x2a1c,0x11ce,0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d);

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a8d-2a1c-11ce-ade5-00aa0044773d")
    IDataConvert : public IUnknown
    {
    public:
        virtual /* [local] */ HRESULT STDMETHODCALLTYPE DataConvert(
            /* [in] */ DBTYPE wSrcType,
            /* [in] */ DBTYPE wDstType,
            /* [in] */ ULONG cbSrcLength,
            /* [out][in] */ ULONG __RPC_FAR *pcbDstLength,
            /* [in] */ void __RPC_FAR *pSrc,
            /* [out] */ void __RPC_FAR *pDst,
            /* [in] */ ULONG cbDstMaxLength,
            /* [in] */ DBSTATUS dbsSrcStatus,
            /* [out] */ DBSTATUS __RPC_FAR *pdbsStatus,
            /* [in] */ BYTE bPrecision,
            /* [in] */ BYTE bScale,
            /* [in] */ DBDATACONVERT dwFlags) = 0;

        virtual HRESULT STDMETHODCALLTYPE CanConvert(
            /* [in] */ DBTYPE wSrcType,
            /* [in] */ DBTYPE wDstType) = 0;

        virtual /* [local] */ HRESULT STDMETHODCALLTYPE GetConversionSize(
            /* [in] */ DBTYPE wSrcType,
            /* [in] */ DBTYPE wDstType,
            /* [in] */ ULONG __RPC_FAR *pcbSrcLength,
            /* [out] */ ULONG __RPC_FAR *pcbDstLength,
            /* [size_is][in] */ void __RPC_FAR *pSrc) = 0;

    };

#else /* C style interface */

    typedef struct IDataConvertVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IDataConvert __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IDataConvert __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IDataConvert __RPC_FAR * This);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *DataConvert )(
            IDataConvert __RPC_FAR * This,
            /* [in] */ DBTYPE wSrcType,
            /* [in] */ DBTYPE wDstType,
            /* [in] */ ULONG cbSrcLength,
            /* [out][in] */ ULONG __RPC_FAR *pcbDstLength,
            /* [in] */ void __RPC_FAR *pSrc,
            /* [out] */ void __RPC_FAR *pDst,
            /* [in] */ ULONG cbDstMaxLength,
            /* [in] */ DBSTATUS dbsSrcStatus,
            /* [out] */ DBSTATUS __RPC_FAR *pdbsStatus,
            /* [in] */ BYTE bPrecision,
            /* [in] */ BYTE bScale,
            /* [in] */ DBDATACONVERT dwFlags);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *CanConvert )(
            IDataConvert __RPC_FAR * This,
            /* [in] */ DBTYPE wSrcType,
            /* [in] */ DBTYPE wDstType);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetConversionSize )(
            IDataConvert __RPC_FAR * This,
            /* [in] */ DBTYPE wSrcType,
            /* [in] */ DBTYPE wDstType,
            /* [in] */ ULONG __RPC_FAR *pcbSrcLength,
            /* [out] */ ULONG __RPC_FAR *pcbDstLength,
            /* [size_is][in] */ void __RPC_FAR *pSrc);

        END_INTERFACE
    } IDataConvertVtbl;

    interface IDataConvert
    {
        CONST_VTBL struct IDataConvertVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IDataConvert_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IDataConvert_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IDataConvert_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IDataConvert_DataConvert(This,wSrcType,wDstType,cbSrcLength,pcbDstLength,pSrc,pDst,cbDstMaxLength,dbsSrcStatus,pdbsStatus,bPrecision,bScale,dwFlags)\
    (This)->lpVtbl -> DataConvert(This,wSrcType,wDstType,cbSrcLength,pcbDstLength,pSrc,pDst,cbDstMaxLength,dbsSrcStatus,pdbsStatus,bPrecision,bScale,dwFlags)

#define IDataConvert_CanConvert(This,wSrcType,wDstType)\
    (This)->lpVtbl -> CanConvert(This,wSrcType,wDstType)

#define IDataConvert_GetConversionSize(This,wSrcType,wDstType,pcbSrcLength,pcbDstLength,pSrc)\
    (This)->lpVtbl -> GetConversionSize(This,wSrcType,wDstType,pcbSrcLength,pcbDstLength,pSrc)

#endif /* COBJMACROS */

#endif /* C style interface */

/* [local] */ HRESULT STDMETHODCALLTYPE IDataConvert_DataConvert_Proxy(
    IDataConvert __RPC_FAR * This,
    /* [in] */ DBTYPE wSrcType,
    /* [in] */ DBTYPE wDstType,
    /* [in] */ ULONG cbSrcLength,
    /* [out][in] */ ULONG __RPC_FAR *pcbDstLength,
    /* [in] */ void __RPC_FAR *pSrc,
    /* [out] */ void __RPC_FAR *pDst,
    /* [in] */ ULONG cbDstMaxLength,
    /* [in] */ DBSTATUS dbsSrcStatus,
    /* [out] */ DBSTATUS __RPC_FAR *pdbsStatus,
    /* [in] */ BYTE bPrecision,
    /* [in] */ BYTE bScale,
    /* [in] */ DBDATACONVERT dwFlags);

void __RPC_STUB IDataConvert_DataConvert_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IDataConvert_CanConvert_Proxy(
    IDataConvert __RPC_FAR * This,
    /* [in] */ DBTYPE wSrcType,
    /* [in] */ DBTYPE wDstType);

void __RPC_STUB IDataConvert_CanConvert_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

/* [local] */ HRESULT STDMETHODCALLTYPE IDataConvert_GetConversionSize_Proxy(
    IDataConvert __RPC_FAR * This,
    /* [in] */ DBTYPE wSrcType,
    /* [in] */ DBTYPE wDstType,
    /* [in] */ ULONG __RPC_FAR *pcbSrcLength,
    /* [out] */ ULONG __RPC_FAR *pcbDstLength,
    /* [size_is][in] */ void __RPC_FAR *pSrc);

void __RPC_STUB IDataConvert_GetConversionSize_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IDataConvert_INTERFACE_DEFINED__ */

#ifndef __IDCInfo_INTERFACE_DEFINED__
#define __IDCInfo_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IDCInfo
 * at Tue Aug 05 15:23:57 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][helpstring][uuid][object] */

typedef DWORD DCINFOTYPE;

enum DCINFOTYPEENUM
    {DCINFOTYPE_VERSION= 1
    };
typedef struct  tagDCINFO
    {
    DCINFOTYPE eInfoType;
    VARIANT vData;
    }DCINFO;

DEFINE_GUID(IID_IDCInfo,0x0c733a9cL,0x2a1c,0x11ce,0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d);

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a9c-2a1c-11ce-ade5-00aa0044773d")
    IDCInfo : public IUnknown
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE GetInfo(
            /* [in] */ ULONG cInfo,
            /* [size_is][in] */ DCINFOTYPE __RPC_FAR rgeInfoType[  ],
            /* [size_is][out] */ DCINFO __RPC_FAR *__RPC_FAR *prgInfo) = 0;

        virtual HRESULT STDMETHODCALLTYPE SetInfo(
            /* [in] */ ULONG cInfo,
            /* [size_is][in] */ DCINFO __RPC_FAR rgInfo[  ]) = 0;

    };

#else /* C style interface */

    typedef struct IDCInfoVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IDCInfo __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IDCInfo __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IDCInfo __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetInfo )(
            IDCInfo __RPC_FAR * This,
            /* [in] */ ULONG cInfo,
            /* [size_is][in] */ DCINFOTYPE __RPC_FAR rgeInfoType[  ],
            /* [size_is][out] */ DCINFO __RPC_FAR *__RPC_FAR *prgInfo);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetInfo )(
            IDCInfo __RPC_FAR * This,
            /* [in] */ ULONG cInfo,
            /* [size_is][in] */ DCINFO __RPC_FAR rgInfo[  ]);

        END_INTERFACE
    } IDCInfoVtbl;

    interface IDCInfo
    {
        CONST_VTBL struct IDCInfoVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IDCInfo_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IDCInfo_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IDCInfo_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IDCInfo_GetInfo(This,cInfo,rgeInfoType,prgInfo)\
    (This)->lpVtbl -> GetInfo(This,cInfo,rgeInfoType,prgInfo)

#define IDCInfo_SetInfo(This,cInfo,rgInfo)\
    (This)->lpVtbl -> SetInfo(This,cInfo,rgInfo)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE IDCInfo_GetInfo_Proxy(
    IDCInfo __RPC_FAR * This,
    /* [in] */ ULONG cInfo,
    /* [size_is][in] */ DCINFOTYPE __RPC_FAR rgeInfoType[  ],
    /* [size_is][out] */ DCINFO __RPC_FAR *__RPC_FAR *prgInfo);

void __RPC_STUB IDCInfo_GetInfo_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IDCInfo_SetInfo_Proxy(
    IDCInfo __RPC_FAR * This,
    /* [in] */ ULONG cInfo,
    /* [size_is][in] */ DCINFO __RPC_FAR rgInfo[  ]);

void __RPC_STUB IDCInfo_SetInfo_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IDCInfo_INTERFACE_DEFINED__ */

/* Additional Prototypes for ALL interfaces */

/* end of Additional Prototypes */

#ifdef __cplusplus
}
#endif

#endif


MSDAGUID.H

//--------------------------------------------------------------------
// Microsoft OLE DB Implementation
// (C) Copyright 1994 - 1997 By Microsoft Corporation.
//
// @doc
//
// @module MSDAGUID.H | Microsoft Data Access GUID defintions
//--------------------------------------------------------------------

// CLSID_EXTENDEDERRORINFO
DEFINE_GUID(CLSID_EXTENDEDERRORINFO,0xc8b522cfL,0x5cf3,0x11ce,0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d);

// CLSID_OLEDB_ENUMERATOR
DEFINE_GUID(CLSID_OLEDB_ENUMERATOR,0xc8b522d0L,0x5cf3,0x11ce,0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d);

// CLSID_MSDAVTM
DEFINE_GUID(CLSID_MSDAVTM,0x0c733a8eL,0x2a1c,0x11ce,0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d);

// CLSID_OLEDB_CONVERSIONLIBRARY
DEFINE_GUID(CLSID_OLEDB_CONVERSIONLIBRARY,0xc8b522d1L,0x5cf3,0x11ce,0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d);

// CLSID_OLEDB_ROWPOSITIONLIBRARY
DEFINE_GUID(CLSID_OLEDB_ROWPOSITIONLIBRARY,0x2048eee6l,0x7fa2,0x11d0,0x9e,0x6a,0x00,0xa0,0xc9,0x13,0x8c,0x29);


MSDASQL.H

//--------------------------------------------------------------------
// Microsoft OLE DB Provider for ODBC data
// (C) Copyright 1994 - 1997 By Microsoft Corporation.
//
// @doc
//
// @module MSDASQL.H | Provider Specific definitions
//
//--------------------------------------------------------------------

#ifndef  _MSDASQL_H_
#define  _MSDASQL_H_

// Provider Class Id
#ifdef DBINITCONSTANTS
// IID_ISQLRequestDiagFields {228972F0-B5FF-11d0-8A80-00C04FD611CD}
extern const GUID IID_ISQLRequestDiagFields = { 0x228972f0, 0xb5ff, 0x11d0, { 0x8a, 0x80, 0x0, 0xc0, 0x4f, 0xd6, 0x11, 0xcd } };
// IID_ISQLGetDiagField {228972F1-B5FF-11d0-8A80-00C04FD611CD}
extern const GUID IID_ISQLGetDiagField = { 0x228972f1, 0xb5ff, 0x11d0, { 0x8a, 0x80, 0x0, 0xc0, 0x4f, 0xd6, 0x11, 0xcd } };
// @msg IID_IRowsetChangeExtInfo | {0c733a8f-2a1c-11ce-ade5-00aa0044773d}
extern const GUID IID_IRowsetChangeExtInfo    = {0x0C733A8FL,0x2A1C,0x11CE,{0xAD,0xE5,0x00,0xAA,0x00,0x44,0x77,0x3D}};
extern const GUID CLSID_MSDASQL               = {0xC8B522CBL,0x5CF3,0x11CE,{0xAD,0xE5,0x00,0xAA,0x00,0x44,0x77,0x3D}};
extern const GUID CLSID_MSDASQL_ENUMERATOR    = {0xC8B522CDL,0x5CF3,0x11CE,{0xAD,0xE5,0x00,0xAA,0x00,0x44,0x77,0x3D}};
#else // !DBINITCONSTANTS
extern const GUID IID_ISQLRequestDiagFields;
extern const GUID IID_ISQLGetDiagField;
extern const GUID IID_IRowsetChangeExtInfo;
extern const GUID CLSID_MSDASQL;
extern const GUID CLSID_MSDASQL_ENUMERATOR;
#endif // DBINITCONSTANTS

//----------------------------------------------------------------------------
// MSDASQL specific properties
#ifdef DBINITCONSTANTS
extern const GUID DBPROPSET_PROVIDERDATASOURCEINFO= {0x497c60e0,0x7123,0x11cf,{0xb1,0x71,0x0,0xaa,0x0,0x57,0x59,0x9e}};
extern const GUID DBPROPSET_PROVIDERROWSET  = {0x497c60e1,0x7123,0x11cf,{0xb1,0x71,0x0,0xaa,0x0,0x57,0x59,0x9e}};
extern const GUID DBPROPSET_PROVIDERDBINIT  = {0x497c60e2,0x7123,0x11cf,{0xb1,0x71,0x0,0xaa,0x0,0x57,0x59,0x9e}};
extern const GUID DBPROPSET_PROVIDERSTMTATTR  = {0x497c60e3,0x7123,0x11cf,{0xb1,0x71,0x0,0xaa,0x0,0x57,0x59,0x9e}};
extern const GUID DBPROPSET_PROVIDERCONNATTR  = {0x497c60e4,0x7123,0x11cf,{0xb1,0x71,0x0,0xaa,0x0,0x57,0x59,0x9e}};
#else // !DBINITCONSTANTS
extern const GUID DBPROPSET_PROVIDERDATASOURCEINFO;
extern const GUID DBPROPSET_PROVIDERROWSET;
extern const GUID DBPROPSET_PROVIDERDBINIT;
extern const GUID DBPROPSET_PROVIDERSTMTATTR;
extern const GUID DBPROPSET_PROVIDERCONNATTR;
#endif // DBINITCONSTANTS

// PropIds under DBPROPSET_PROVIDERROWSET
#define KAGPROP_QUERYBASEDUPDATES2
#define KAGPROP_MARSHALLABLE3
#define KAGPROP_POSITIONONNEWROW4
#defineKAGPROP_IRowsetChangeExtInfo5
#define KAGPROP_CURSOR6
#define KAGPROP_CONCURRENCY7
#define KAGPROP_BLOBSONFOCURSOR8
#define KAGPROP_INCLUDENONEXACT9
#define KAGPROP_FORCESSFIREHOSEMODE10
#define KAGPROP_FORCENOPARAMETERREBIND11
#define KAGPROP_FORCENOPREPARE12
#define KAGPROP_FORCENOREEXECUTE13

// PropIds under DPBROPSET_PROVIDERDATASOURCEINFO
#define KAGPROP_ACCESSIBLEPROCEDURES2
#define KAGPROP_ACCESSIBLETABLES3
#define KAGPROP_ODBCSQLOPTIEF4
#define KAGPROP_OJCAPABILITY5
#define KAGPROP_PROCEDURES6
#define KAGPROP_DRIVERNAME7
#define KAGPROP_DRIVERVER8
#define KAGPROP_DRIVERODBCVER9
#define KAGPROP_LIKEESCAPECLAUSE10
#define KAGPROP_SPECIALCHARACTERS11
#define KAGPROP_MAXCOLUMNSINGROUPBY12
#define KAGPROP_MAXCOLUMNSININDEX13
#define KAGPROP_MAXCOLUMNSINORDERBY14
#define KAGPROP_MAXCOLUMNSINSELECT15
#define KAGPROP_MAXCOLUMNSINTABLE16
#define KAGPROP_NUMERICFUNCTIONS17
#define KAGPROP_ODBCSQLCONFORMANCE18
#define KAGPROP_OUTERJOINS19
#define KAGPROP_STRINGFUNCTIONS20
#define KAGPROP_SYSTEMFUNCTIONS21
#define KAGPROP_TIMEDATEFUNCTIONS22
#define KAGPROP_FILEUSAGE23
#define KAGPROP_ACTIVESTATEMENTS24

// PropIds under DBPROPSET_PROVIDERDBINIT
#define KAGPROP_AUTH_TRUSTEDCONNECTION2
#define KAGPROP_AUTH_SERVERINTEGRATED3

// Bitmask values for KAGPROP_CONCURRENCY
#define KAGPROPVAL_CONCUR_ROWVER0x00000001
#define KAGPROPVAL_CONCUR_VALUES0x00000002
#define KAGPROPVAL_CONCUR_LOCK0x00000004
#define KAGPROPVAL_CONCUR_READ_ONLY0x00000008

/* this ALWAYS GENERATED file contains the definitions for the interfaces */

/* File created by MIDL compiler version 3.00.15 */
/* at Tue Sep 24 13:38:00 1996
 */
/* Compiler settings for rstcei.idl:
    Os, W1, Zp8, env=Win32, ms_ext, c_ext
    error checks: none
*/
//@@MIDL_FILE_HEADING(  )
#include "rpc.h"
#include "rpcndr.h"
#ifndef COM_NO_WINDOWS_H
#include "windows.h"
#include "ole2.h"
#endif /*COM_NO_WINDOWS_H*/

#ifndef __rstcei_h__
#define __rstcei_h__

#ifdef __cplusplus
extern "C"{
#endif

/* Forward Declarations */

#ifndef __IRowsetChangeExtInfo_FWD_DEFINED__
#define __IRowsetChangeExtInfo_FWD_DEFINED__
typedef interface IRowsetChangeExtInfo IRowsetChangeExtInfo;
#endif /* __IRowsetChangeExtInfo_FWD_DEFINED__ */

void __RPC_FAR * __RPC_USER MIDL_user_allocate(size_t);
void __RPC_USER MIDL_user_free( void __RPC_FAR * );

#ifndef __IRowsetChangeExtInfo_INTERFACE_DEFINED__
#define __IRowsetChangeExtInfo_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IRowsetChangeExtInfo
 * at Tue Sep 24 13:38:00 1996
 * using MIDL 3.00.15
 ****************************************/
/* [unique][uuid][object][local] */

EXTERN_C const IID IID_IRowsetChangeExtInfo;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface IRowsetChangeExtInfo : public IUnknown
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE GetOriginalRow(
            /* [in] */ HCHAPTER hReserved,
            /* [in] */ HROW hRow,
            /* [out] */ HROW __RPC_FAR *phRowOriginal) = 0;

        virtual HRESULT STDMETHODCALLTYPE GetPendingColumns(
            /* [in] */ HCHAPTER hReserved,
            /* [in] */ HROW hRow,
            /* [in] */ ULONG cColumnOrdinals,
            /* [size_is][in] */ const ULONG __RPC_FAR rgiOrdinals[  ],
            /* [size_is][out] */ DBPENDINGSTATUS __RPC_FAR rgColumnStatus[  ]) = 0;

    };

#else /* C style interface */

    typedef struct IRowsetChangeExtInfoVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IRowsetChangeExtInfo __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IRowsetChangeExtInfo __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IRowsetChangeExtInfo __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetOriginalRow )(
            IRowsetChangeExtInfo __RPC_FAR * This,
            /* [in] */ HCHAPTER hReserved,
            /* [in] */ HROW hRow,
            /* [out] */ HROW __RPC_FAR *phRowOriginal);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetPendingColumns )(
            IRowsetChangeExtInfo __RPC_FAR * This,
            /* [in] */ HCHAPTER hReserved,
            /* [in] */ HROW hRow,
            /* [in] */ ULONG cColumnOrdinals,
            /* [size_is][in] */ const ULONG __RPC_FAR rgiOrdinals[  ],
            /* [size_is][out] */ DBPENDINGSTATUS __RPC_FAR rgColumnStatus[  ]);

        END_INTERFACE
    } IRowsetChangeExtInfoVtbl;

    interface IRowsetChangeExtInfo
    {
        CONST_VTBL struct IRowsetChangeExtInfoVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IRowsetChangeExtInfo_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IRowsetChangeExtInfo_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IRowsetChangeExtInfo_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IRowsetChangeExtInfo_GetOriginalRow(This,hReserved,hRow,phRowOriginal)\
    (This)->lpVtbl -> GetOriginalRow(This,hReserved,hRow,phRowOriginal)

#define IRowsetChangeExtInfo_GetPendingColumns(This,hReserved,hRow,cColumnOrdinals,rgiOrdinals,rgColumnStatus)\
    (This)->lpVtbl -> GetPendingColumns(This,hReserved,hRow,cColumnOrdinals,rgiOrdinals,rgColumnStatus)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE IRowsetChangeExtInfo_GetOriginalRow_Proxy(
    IRowsetChangeExtInfo __RPC_FAR * This,
    /* [in] */ HCHAPTER hReserved,
    /* [in] */ HROW hRow,
    /* [out] */ HROW __RPC_FAR *phRowOriginal);

void __RPC_STUB IRowsetChangeExtInfo_GetOriginalRow_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IRowsetChangeExtInfo_GetPendingColumns_Proxy(
    IRowsetChangeExtInfo __RPC_FAR * This,
    /* [in] */ HCHAPTER hReserved,
    /* [in] */ HROW hRow,
    /* [in] */ ULONG cColumnOrdinals,
    /* [size_is][in] */ const ULONG __RPC_FAR rgiOrdinals[  ],
    /* [size_is][out] */ DBPENDINGSTATUS __RPC_FAR rgColumnStatus[  ]);

void __RPC_STUB IRowsetChangeExtInfo_GetPendingColumns_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IRowsetChangeExtInfo_INTERFACE_DEFINED__ */

/* Additional Prototypes for ALL interfaces */

/* end of Additional Prototypes */

#ifdef __cplusplus
}
#endif

#endif

/* this ALWAYS GENERATED file contains the definitions for the interfaces */

/* File created by MIDL compiler version 3.00.44 */
/* at Sun May 11 15:03:47 1997
 */
/* Compiler settings for KAGDIAG.IDL:
    Os (OptLev=s), W1, Zp8, env=Win32, ms_ext, c_ext
    error checks: none
*/
//@@MIDL_FILE_HEADING(  )
#include "rpc.h"
#include "rpcndr.h"
#ifndef COM_NO_WINDOWS_H
#include "windows.h"
#include "ole2.h"
#endif /*COM_NO_WINDOWS_H*/

#ifndef __KAGDIAG_H__
#define __KAGDIAG_H__

#ifdef __cplusplus
extern "C"{
#endif

/* Forward Declarations */

#ifndef __ISQLRequestDiagFields_FWD_DEFINED__
#define __ISQLRequestDiagFields_FWD_DEFINED__
typedef interface ISQLRequestDiagFields ISQLRequestDiagFields;
#endif /* __ISQLRequestDiagFields_FWD_DEFINED__ */

#ifndef __ISQLGetDiagField_FWD_DEFINED__
#define __ISQLGetDiagField_FWD_DEFINED__
typedef interface ISQLGetDiagField ISQLGetDiagField;
#endif /* __ISQLGetDiagField_FWD_DEFINED__ */

/* header files for imported files */
#include "unknwn.h"
#include "oaidl.h"

void __RPC_FAR * __RPC_USER MIDL_user_allocate(size_t);
void __RPC_USER MIDL_user_free( void __RPC_FAR * );

/****************************************
 * Generated header for interface: __MIDL__intf_0000
 * at Sun May 11 15:03:47 1997
 * using MIDL 3.00.44
 ****************************************/
/* [local] */

enum KAGREQDIAGFLAGSENUM
    {KAGREQDIAGFLAGS_HEADER= 0x1,
KAGREQDIAGFLAGS_RECORD= 0x2
    };
// the structure passed in in IRequestDiagFields::RequestDiagFields
typedef struct  tagKAGREQDIAG
    {
    ULONG ulDiagFlags;
    VARTYPE vt;
    SHORT sDiagField;
    }KAGREQDIAG;

// the structure passed in in IGetDiagField::GetDiagField
typedef struct  tagKAGGETDIAG
    {
    ULONG ulSize;
    VARIANTARG vDiagInfo;
    SHORT sDiagField;
    }KAGGETDIAG;

extern RPC_IF_HANDLE __MIDL__intf_0000_v0_0_c_ifspec;
extern RPC_IF_HANDLE __MIDL__intf_0000_v0_0_s_ifspec;

#ifndef __ISQLRequestDiagFields_INTERFACE_DEFINED__
#define __ISQLRequestDiagFields_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: ISQLRequestDiagFields
 * at Sun May 11 15:03:47 1997
 * using MIDL 3.00.44
 ****************************************/
/* [object][uuid] */

EXTERN_C const IID IID_ISQLRequestDiagFields;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface ISQLRequestDiagFields : public IUnknown
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE RequestDiagFields(
            /* [in] */ ULONG cDiagFields,
            /* [size_is][in] */ KAGREQDIAG __RPC_FAR rgDiagFields[  ]) = 0;

    };

#else /* C style interface */

    typedef struct ISQLRequestDiagFieldsVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            ISQLRequestDiagFields __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            ISQLRequestDiagFields __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            ISQLRequestDiagFields __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RequestDiagFields )(
            ISQLRequestDiagFields __RPC_FAR * This,
            /* [in] */ ULONG cDiagFields,
            /* [size_is][in] */ KAGREQDIAG __RPC_FAR rgDiagFields[  ]);

        END_INTERFACE
    } ISQLRequestDiagFieldsVtbl;

    interface ISQLRequestDiagFields
    {
        CONST_VTBL struct ISQLRequestDiagFieldsVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define ISQLRequestDiagFields_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define ISQLRequestDiagFields_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define ISQLRequestDiagFields_Release(This)\
    (This)->lpVtbl -> Release(This)

#define ISQLRequestDiagFields_RequestDiagFields(This,cDiagFields,rgDiagFields)\
    (This)->lpVtbl -> RequestDiagFields(This,cDiagFields,rgDiagFields)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE ISQLRequestDiagFields_RequestDiagFields_Proxy(
    ISQLRequestDiagFields __RPC_FAR * This,
    /* [in] */ ULONG cDiagFields,
    /* [size_is][in] */ KAGREQDIAG __RPC_FAR rgDiagFields[  ]);

void __RPC_STUB ISQLRequestDiagFields_RequestDiagFields_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __ISQLRequestDiagFields_INTERFACE_DEFINED__ */

#ifndef __ISQLGetDiagField_INTERFACE_DEFINED__
#define __ISQLGetDiagField_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: ISQLGetDiagField
 * at Sun May 11 15:03:47 1997
 * using MIDL 3.00.44
 ****************************************/
/* [object][uuid] */

EXTERN_C const IID IID_ISQLGetDiagField;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface ISQLGetDiagField : public IUnknown
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE GetDiagField(
            /* [unique][out][in] */ KAGGETDIAG __RPC_FAR *pDiagInfo) = 0;

    };

#else /* C style interface */

    typedef struct ISQLGetDiagFieldVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            ISQLGetDiagField __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            ISQLGetDiagField __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            ISQLGetDiagField __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetDiagField )(
            ISQLGetDiagField __RPC_FAR * This,
            /* [unique][out][in] */ KAGGETDIAG __RPC_FAR *pDiagInfo);

        END_INTERFACE
    } ISQLGetDiagFieldVtbl;

    interface ISQLGetDiagField
    {
        CONST_VTBL struct ISQLGetDiagFieldVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define ISQLGetDiagField_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define ISQLGetDiagField_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define ISQLGetDiagField_Release(This)\
    (This)->lpVtbl -> Release(This)

#define ISQLGetDiagField_GetDiagField(This,pDiagInfo)\
    (This)->lpVtbl -> GetDiagField(This,pDiagInfo)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE ISQLGetDiagField_GetDiagField_Proxy(
    ISQLGetDiagField __RPC_FAR * This,
    /* [unique][out][in] */ KAGGETDIAG __RPC_FAR *pDiagInfo);

void __RPC_STUB ISQLGetDiagField_GetDiagField_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __ISQLGetDiagField_INTERFACE_DEFINED__ */

/* Additional Prototypes for ALL interfaces */

unsigned long             __RPC_USER  VARIANT_UserSize(     unsigned long __RPC_FAR *, unsigned long            , VARIANT __RPC_FAR * );
unsigned char __RPC_FAR * __RPC_USER  VARIANT_UserMarshal(  unsigned long __RPC_FAR *, unsigned char __RPC_FAR *, VARIANT __RPC_FAR * );
unsigned char __RPC_FAR * __RPC_USER  VARIANT_UserUnmarshal(unsigned long __RPC_FAR *, unsigned char __RPC_FAR *, VARIANT __RPC_FAR * );
void                      __RPC_USER  VARIANT_UserFree(     unsigned long __RPC_FAR *, VARIANT __RPC_FAR * );

/* end of Additional Prototypes */

#ifdef __cplusplus
}
#endif

#endif

#endif
//----


OLEDB.H

/* this ALWAYS GENERATED file contains the definitions for the interfaces */

/* File created by MIDL compiler version 3.01.75 */
/* at Mon Nov 03 18:14:46 1997
 */
/* Compiler settings for .\idl\oledb.idl:
    Oicf (OptLev=i2), W1, Zp2, env=Win32, ms_ext, c_ext
    error checks: none
*/
//@@MIDL_FILE_HEADING(  )
#include "rpc.h"
#include "rpcndr.h"
#ifndef COM_NO_WINDOWS_H
#include "windows.h"
#include "ole2.h"
#endif /*COM_NO_WINDOWS_H*/

#ifndef __oledb_h__
#define __oledb_h__

#ifdef __cplusplus
extern "C"{
#endif

/* Forward Declarations */

#ifndef __IAccessor_FWD_DEFINED__
#define __IAccessor_FWD_DEFINED__
typedef interface IAccessor IAccessor;
#endif /* __IAccessor_FWD_DEFINED__ */

#ifndef __IRowset_FWD_DEFINED__
#define __IRowset_FWD_DEFINED__
typedef interface IRowset IRowset;
#endif /* __IRowset_FWD_DEFINED__ */

#ifndef __IRowsetInfo_FWD_DEFINED__
#define __IRowsetInfo_FWD_DEFINED__
typedef interface IRowsetInfo IRowsetInfo;
#endif /* __IRowsetInfo_FWD_DEFINED__ */

#ifndef __IRowsetLocate_FWD_DEFINED__
#define __IRowsetLocate_FWD_DEFINED__
typedef interface IRowsetLocate IRowsetLocate;
#endif /* __IRowsetLocate_FWD_DEFINED__ */

#ifndef __IRowsetResynch_FWD_DEFINED__
#define __IRowsetResynch_FWD_DEFINED__
typedef interface IRowsetResynch IRowsetResynch;
#endif /* __IRowsetResynch_FWD_DEFINED__ */

#ifndef __IRowsetScroll_FWD_DEFINED__
#define __IRowsetScroll_FWD_DEFINED__
typedef interface IRowsetScroll IRowsetScroll;
#endif /* __IRowsetScroll_FWD_DEFINED__ */

#ifndef __IChapteredRowset_FWD_DEFINED__
#define __IChapteredRowset_FWD_DEFINED__
typedef interface IChapteredRowset IChapteredRowset;
#endif /* __IChapteredRowset_FWD_DEFINED__ */

#ifndef __IRowsetFind_FWD_DEFINED__
#define __IRowsetFind_FWD_DEFINED__
typedef interface IRowsetFind IRowsetFind;
#endif /* __IRowsetFind_FWD_DEFINED__ */

#ifndef __IRowPosition_FWD_DEFINED__
#define __IRowPosition_FWD_DEFINED__
typedef interface IRowPosition IRowPosition;
#endif /* __IRowPosition_FWD_DEFINED__ */

#ifndef __IRowPositionChange_FWD_DEFINED__
#define __IRowPositionChange_FWD_DEFINED__
typedef interface IRowPositionChange IRowPositionChange;
#endif /* __IRowPositionChange_FWD_DEFINED__ */

#ifndef __IViewRowset_FWD_DEFINED__
#define __IViewRowset_FWD_DEFINED__
typedef interface IViewRowset IViewRowset;
#endif /* __IViewRowset_FWD_DEFINED__ */

#ifndef __IViewChapter_FWD_DEFINED__
#define __IViewChapter_FWD_DEFINED__
typedef interface IViewChapter IViewChapter;
#endif /* __IViewChapter_FWD_DEFINED__ */

#ifndef __IViewSort_FWD_DEFINED__
#define __IViewSort_FWD_DEFINED__
typedef interface IViewSort IViewSort;
#endif /* __IViewSort_FWD_DEFINED__ */

#ifndef __IViewFilter_FWD_DEFINED__
#define __IViewFilter_FWD_DEFINED__
typedef interface IViewFilter IViewFilter;
#endif /* __IViewFilter_FWD_DEFINED__ */

#ifndef __IRowsetView_FWD_DEFINED__
#define __IRowsetView_FWD_DEFINED__
typedef interface IRowsetView IRowsetView;
#endif /* __IRowsetView_FWD_DEFINED__ */

#ifndef __IRowsetExactScroll_FWD_DEFINED__
#define __IRowsetExactScroll_FWD_DEFINED__
typedef interface IRowsetExactScroll IRowsetExactScroll;
#endif /* __IRowsetExactScroll_FWD_DEFINED__ */

#ifndef __IRowsetChange_FWD_DEFINED__
#define __IRowsetChange_FWD_DEFINED__
typedef interface IRowsetChange IRowsetChange;
#endif /* __IRowsetChange_FWD_DEFINED__ */

#ifndef __IRowsetUpdate_FWD_DEFINED__
#define __IRowsetUpdate_FWD_DEFINED__
typedef interface IRowsetUpdate IRowsetUpdate;
#endif /* __IRowsetUpdate_FWD_DEFINED__ */

#ifndef __IRowsetNextRowset_FWD_DEFINED__
#define __IRowsetNextRowset_FWD_DEFINED__
typedef interface IRowsetNextRowset IRowsetNextRowset;
#endif /* __IRowsetNextRowset_FWD_DEFINED__ */

#ifndef __IRowsetIdentity_FWD_DEFINED__
#define __IRowsetIdentity_FWD_DEFINED__
typedef interface IRowsetIdentity IRowsetIdentity;
#endif /* __IRowsetIdentity_FWD_DEFINED__ */

#ifndef __IRowsetNewRowAfter_FWD_DEFINED__
#define __IRowsetNewRowAfter_FWD_DEFINED__
typedef interface IRowsetNewRowAfter IRowsetNewRowAfter;
#endif /* __IRowsetNewRowAfter_FWD_DEFINED__ */

#ifndef __IRowsetWithParameters_FWD_DEFINED__
#define __IRowsetWithParameters_FWD_DEFINED__
typedef interface IRowsetWithParameters IRowsetWithParameters;
#endif /* __IRowsetWithParameters_FWD_DEFINED__ */

#ifndef __IRowsetAsynch_FWD_DEFINED__
#define __IRowsetAsynch_FWD_DEFINED__
typedef interface IRowsetAsynch IRowsetAsynch;
#endif /* __IRowsetAsynch_FWD_DEFINED__ */

#ifndef __IRowsetKeys_FWD_DEFINED__
#define __IRowsetKeys_FWD_DEFINED__
typedef interface IRowsetKeys IRowsetKeys;
#endif /* __IRowsetKeys_FWD_DEFINED__ */

#ifndef __IRowsetNotify_FWD_DEFINED__
#define __IRowsetNotify_FWD_DEFINED__
typedef interface IRowsetNotify IRowsetNotify;
#endif /* __IRowsetNotify_FWD_DEFINED__ */

#ifndef __IRowsetIndex_FWD_DEFINED__
#define __IRowsetIndex_FWD_DEFINED__
typedef interface IRowsetIndex IRowsetIndex;
#endif /* __IRowsetIndex_FWD_DEFINED__ */

#ifndef __IRowsetWatchAll_FWD_DEFINED__
#define __IRowsetWatchAll_FWD_DEFINED__
typedef interface IRowsetWatchAll IRowsetWatchAll;
#endif /* __IRowsetWatchAll_FWD_DEFINED__ */

#ifndef __IRowsetWatchNotify_FWD_DEFINED__
#define __IRowsetWatchNotify_FWD_DEFINED__
typedef interface IRowsetWatchNotify IRowsetWatchNotify;
#endif /* __IRowsetWatchNotify_FWD_DEFINED__ */

#ifndef __IRowsetWatchRegion_FWD_DEFINED__
#define __IRowsetWatchRegion_FWD_DEFINED__
typedef interface IRowsetWatchRegion IRowsetWatchRegion;
#endif /* __IRowsetWatchRegion_FWD_DEFINED__ */

#ifndef __IRowsetCopyRows_FWD_DEFINED__
#define __IRowsetCopyRows_FWD_DEFINED__
typedef interface IRowsetCopyRows IRowsetCopyRows;
#endif /* __IRowsetCopyRows_FWD_DEFINED__ */

#ifndef __IReadData_FWD_DEFINED__
#define __IReadData_FWD_DEFINED__
typedef interface IReadData IReadData;
#endif /* __IReadData_FWD_DEFINED__ */

#ifndef __ICommand_FWD_DEFINED__
#define __ICommand_FWD_DEFINED__
typedef interface ICommand ICommand;
#endif /* __ICommand_FWD_DEFINED__ */

#ifndef __IMultipleResults_FWD_DEFINED__
#define __IMultipleResults_FWD_DEFINED__
typedef interface IMultipleResults IMultipleResults;
#endif /* __IMultipleResults_FWD_DEFINED__ */

#ifndef __IConvertType_FWD_DEFINED__
#define __IConvertType_FWD_DEFINED__
typedef interface IConvertType IConvertType;
#endif /* __IConvertType_FWD_DEFINED__ */

#ifndef __ICommandCost_FWD_DEFINED__
#define __ICommandCost_FWD_DEFINED__
typedef interface ICommandCost ICommandCost;
#endif /* __ICommandCost_FWD_DEFINED__ */

#ifndef __ICommandPrepare_FWD_DEFINED__
#define __ICommandPrepare_FWD_DEFINED__
typedef interface ICommandPrepare ICommandPrepare;
#endif /* __ICommandPrepare_FWD_DEFINED__ */

#ifndef __ICommandProperties_FWD_DEFINED__
#define __ICommandProperties_FWD_DEFINED__
typedef interface ICommandProperties ICommandProperties;
#endif /* __ICommandProperties_FWD_DEFINED__ */

#ifndef __ICommandText_FWD_DEFINED__
#define __ICommandText_FWD_DEFINED__
typedef interface ICommandText ICommandText;
#endif /* __ICommandText_FWD_DEFINED__ */

#ifndef __ICommandTree_FWD_DEFINED__
#define __ICommandTree_FWD_DEFINED__
typedef interface ICommandTree ICommandTree;
#endif /* __ICommandTree_FWD_DEFINED__ */

#ifndef __ICommandValidate_FWD_DEFINED__
#define __ICommandValidate_FWD_DEFINED__
typedef interface ICommandValidate ICommandValidate;
#endif /* __ICommandValidate_FWD_DEFINED__ */

#ifndef __ICommandWithParameters_FWD_DEFINED__
#define __ICommandWithParameters_FWD_DEFINED__
typedef interface ICommandWithParameters ICommandWithParameters;
#endif /* __ICommandWithParameters_FWD_DEFINED__ */

#ifndef __IQuery_FWD_DEFINED__
#define __IQuery_FWD_DEFINED__
typedef interface IQuery IQuery;
#endif /* __IQuery_FWD_DEFINED__ */

#ifndef __IColumnsRowset_FWD_DEFINED__
#define __IColumnsRowset_FWD_DEFINED__
typedef interface IColumnsRowset IColumnsRowset;
#endif /* __IColumnsRowset_FWD_DEFINED__ */

#ifndef __IColumnsInfo_FWD_DEFINED__
#define __IColumnsInfo_FWD_DEFINED__
typedef interface IColumnsInfo IColumnsInfo;
#endif /* __IColumnsInfo_FWD_DEFINED__ */

#ifndef __IDBCreateCommand_FWD_DEFINED__
#define __IDBCreateCommand_FWD_DEFINED__
typedef interface IDBCreateCommand IDBCreateCommand;
#endif /* __IDBCreateCommand_FWD_DEFINED__ */

#ifndef __IDBCreateSession_FWD_DEFINED__
#define __IDBCreateSession_FWD_DEFINED__
typedef interface IDBCreateSession IDBCreateSession;
#endif /* __IDBCreateSession_FWD_DEFINED__ */

#ifndef __ISourcesRowset_FWD_DEFINED__
#define __ISourcesRowset_FWD_DEFINED__
typedef interface ISourcesRowset ISourcesRowset;
#endif /* __ISourcesRowset_FWD_DEFINED__ */

#ifndef __IDBProperties_FWD_DEFINED__
#define __IDBProperties_FWD_DEFINED__
typedef interface IDBProperties IDBProperties;
#endif /* __IDBProperties_FWD_DEFINED__ */

#ifndef __IDBInitialize_FWD_DEFINED__
#define __IDBInitialize_FWD_DEFINED__
typedef interface IDBInitialize IDBInitialize;
#endif /* __IDBInitialize_FWD_DEFINED__ */

#ifndef __IDBInfo_FWD_DEFINED__
#define __IDBInfo_FWD_DEFINED__
typedef interface IDBInfo IDBInfo;
#endif /* __IDBInfo_FWD_DEFINED__ */

#ifndef __IDBDataSourceAdmin_FWD_DEFINED__
#define __IDBDataSourceAdmin_FWD_DEFINED__
typedef interface IDBDataSourceAdmin IDBDataSourceAdmin;
#endif /* __IDBDataSourceAdmin_FWD_DEFINED__ */

#ifndef __IDBAsynchNotify_FWD_DEFINED__
#define __IDBAsynchNotify_FWD_DEFINED__
typedef interface IDBAsynchNotify IDBAsynchNotify;
#endif /* __IDBAsynchNotify_FWD_DEFINED__ */

#ifndef __IDBAsynchStatus_FWD_DEFINED__
#define __IDBAsynchStatus_FWD_DEFINED__
typedef interface IDBAsynchStatus IDBAsynchStatus;
#endif /* __IDBAsynchStatus_FWD_DEFINED__ */

#ifndef __ISessionProperties_FWD_DEFINED__
#define __ISessionProperties_FWD_DEFINED__
typedef interface ISessionProperties ISessionProperties;
#endif /* __ISessionProperties_FWD_DEFINED__ */

#ifndef __IIndexDefinition_FWD_DEFINED__
#define __IIndexDefinition_FWD_DEFINED__
typedef interface IIndexDefinition IIndexDefinition;
#endif /* __IIndexDefinition_FWD_DEFINED__ */

#ifndef __ITableDefinition_FWD_DEFINED__
#define __ITableDefinition_FWD_DEFINED__
typedef interface ITableDefinition ITableDefinition;
#endif /* __ITableDefinition_FWD_DEFINED__ */

#ifndef __IOpenRowset_FWD_DEFINED__
#define __IOpenRowset_FWD_DEFINED__
typedef interface IOpenRowset IOpenRowset;
#endif /* __IOpenRowset_FWD_DEFINED__ */

#ifndef __ITableRename_FWD_DEFINED__
#define __ITableRename_FWD_DEFINED__
typedef interface ITableRename ITableRename;
#endif /* __ITableRename_FWD_DEFINED__ */

#ifndef __IDBSchemaCommand_FWD_DEFINED__
#define __IDBSchemaCommand_FWD_DEFINED__
typedef interface IDBSchemaCommand IDBSchemaCommand;
#endif /* __IDBSchemaCommand_FWD_DEFINED__ */

#ifndef __IDBSecurityInfo_FWD_DEFINED__
#define __IDBSecurityInfo_FWD_DEFINED__
typedef interface IDBSecurityInfo IDBSecurityInfo;
#endif /* __IDBSecurityInfo_FWD_DEFINED__ */

#ifndef __IDBSchemaRowset_FWD_DEFINED__
#define __IDBSchemaRowset_FWD_DEFINED__
typedef interface IDBSchemaRowset IDBSchemaRowset;
#endif /* __IDBSchemaRowset_FWD_DEFINED__ */

#ifndef __IProvideMoniker_FWD_DEFINED__
#define __IProvideMoniker_FWD_DEFINED__
typedef interface IProvideMoniker IProvideMoniker;
#endif /* __IProvideMoniker_FWD_DEFINED__ */

#ifndef __IMDDataset_FWD_DEFINED__
#define __IMDDataset_FWD_DEFINED__
typedef interface IMDDataset IMDDataset;
#endif /* __IMDDataset_FWD_DEFINED__ */

#ifndef __IMDFind_FWD_DEFINED__
#define __IMDFind_FWD_DEFINED__
typedef interface IMDFind IMDFind;
#endif /* __IMDFind_FWD_DEFINED__ */

#ifndef __IMDRangeRowset_FWD_DEFINED__
#define __IMDRangeRowset_FWD_DEFINED__
typedef interface IMDRangeRowset IMDRangeRowset;
#endif /* __IMDRangeRowset_FWD_DEFINED__ */

#ifndef __IErrorRecords_FWD_DEFINED__
#define __IErrorRecords_FWD_DEFINED__
typedef interface IErrorRecords IErrorRecords;
#endif /* __IErrorRecords_FWD_DEFINED__ */

#ifndef __IErrorLookup_FWD_DEFINED__
#define __IErrorLookup_FWD_DEFINED__
typedef interface IErrorLookup IErrorLookup;
#endif /* __IErrorLookup_FWD_DEFINED__ */

#ifndef __ISQLErrorInfo_FWD_DEFINED__
#define __ISQLErrorInfo_FWD_DEFINED__
typedef interface ISQLErrorInfo ISQLErrorInfo;
#endif /* __ISQLErrorInfo_FWD_DEFINED__ */

#ifndef __IGetDataSource_FWD_DEFINED__
#define __IGetDataSource_FWD_DEFINED__
typedef interface IGetDataSource IGetDataSource;
#endif /* __IGetDataSource_FWD_DEFINED__ */

#ifndef __ITransactionLocal_FWD_DEFINED__
#define __ITransactionLocal_FWD_DEFINED__
typedef interface ITransactionLocal ITransactionLocal;
#endif /* __ITransactionLocal_FWD_DEFINED__ */

#ifndef __ITransactionJoin_FWD_DEFINED__
#define __ITransactionJoin_FWD_DEFINED__
typedef interface ITransactionJoin ITransactionJoin;
#endif /* __ITransactionJoin_FWD_DEFINED__ */

#ifndef __ITransactionObject_FWD_DEFINED__
#define __ITransactionObject_FWD_DEFINED__
typedef interface ITransactionObject ITransactionObject;
#endif /* __ITransactionObject_FWD_DEFINED__ */

/* header files for imported files */
#include "wtypes.h"
#include "oaidl.h"
#include "transact.h"

void __RPC_FAR * __RPC_USER MIDL_user_allocate(size_t);
void __RPC_USER MIDL_user_free( void __RPC_FAR * );

/****************************************
 * Generated header for interface: __MIDL_itf_oledb_0000
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [local] */

//+---------------------------------------------------------------------------
//
//  Microsoft OLE DB
//  Copyright (C) Microsoft Corporation, 1994 - 1997.
//
//----------------------------------------------------------------------------

#include <pshpack2.h>// 2-byte structure packing

//
// OLEDBVER
//OLE DB version number is 1.5 (0x0150); to force a particular version,
//#define OLEDBVER as required before including this file.
//

// If OLEDBVER is not defined, assume version 1.5
#ifndef OLEDBVER
#define OLEDBVER 0x0150
#endif

extern RPC_IF_HANDLE __MIDL_itf_oledb_0000_v0_0_c_ifspec;
extern RPC_IF_HANDLE __MIDL_itf_oledb_0000_v0_0_s_ifspec;

#ifndef __DBStructureDefinitions_INTERFACE_DEFINED__
#define __DBStructureDefinitions_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: DBStructureDefinitions
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [auto_handle][unique][uuid] */

typedef DWORD DBKIND;

enum DBKINDENUM
    {DBKIND_GUID_NAME= 0,
DBKIND_GUID_PROPID= DBKIND_GUID_NAME + 1,
DBKIND_NAME= DBKIND_GUID_PROPID + 1,
DBKIND_PGUID_NAME= DBKIND_NAME + 1,
DBKIND_PGUID_PROPID= DBKIND_PGUID_NAME + 1,
DBKIND_PROPID= DBKIND_PGUID_PROPID + 1,
DBKIND_GUID= DBKIND_PROPID + 1
    };
typedef struct  tagDBID
    {
    /* [switch_is][switch_type] */ union
        {
        /* [case()] */ GUID guid;
        /* [case()] */ GUID __RPC_FAR *pguid;
        /* [default] */  /* Empty union arm */
        }uGuid;
    DBKIND eKind;
    /* [switch_is][switch_type] */ union
        {
        /* [case()] */ LPOLESTR pwszName;
        /* [case()] */ ULONG ulPropid;
        /* [default] */  /* Empty union arm */
        }uName;
    }DBID;

typedef struct  tagDB_NUMERIC
    {
    BYTE precision;
    BYTE scale;
    BYTE sign;
    BYTE val[ 16 ];
    }DB_NUMERIC;

#ifndef _ULONGLONG_
typedef hyper LONGLONG;

typedef MIDL_uhyper ULONGLONG;

typedef LONGLONG __RPC_FAR *PLONGLONG;

typedef ULONGLONG __RPC_FAR *PULONGLONG;

#endif // _ULONGLONG_
#ifndef DECIMAL_NEG
#ifndef DECIMAL_SETZERO
typedef struct tagDEC {
    USHORT wReserved;
    union {
        struct {
            BYTE scale;
            BYTE sign;
        };
        USHORT signscale;
    };
    ULONG Hi32;
    union {
        struct {
#ifdef _MAC
            ULONG Mid32;
            ULONG Lo32;
#else
            ULONG Lo32;
            ULONG Mid32;
#endif
        };
        ULONGLONG Lo64;
    };
} DECIMAL;
#define DECIMAL_NEG ((BYTE)0x80)
#define DECIMAL_SETZERO(dec) {(dec).Lo64 = 0; (dec).Hi32 = 0; (dec).signscale = 0;}
#endif // DECIMAL_SETZERO
#endif // DECIMAL_NEG
typedef struct  tagDBVECTOR
    {
    ULONG size;
    /* [size_is] */ void __RPC_FAR *ptr;
    }DBVECTOR;

typedef struct  tagDBDATE
    {
    SHORT year;
    USHORT month;
    USHORT day;
    }DBDATE;

typedef struct  tagDBTIME
    {
    USHORT hour;
    USHORT minute;
    USHORT second;
    }DBTIME;

typedef struct  tagDBTIMESTAMP
    {
    SHORT year;
    USHORT month;
    USHORT day;
    USHORT hour;
    USHORT minute;
    USHORT second;
    ULONG fraction;
    }DBTIMESTAMP;

typedef WORD DBTYPE;

enum DBTYPEENUM
    {DBTYPE_EMPTY= 0,
DBTYPE_NULL= 1,
DBTYPE_I2= 2,
DBTYPE_I4= 3,
DBTYPE_R4= 4,
DBTYPE_R8= 5,
DBTYPE_CY= 6,
DBTYPE_DATE= 7,
DBTYPE_BSTR= 8,
DBTYPE_IDISPATCH= 9,
DBTYPE_ERROR= 10,
DBTYPE_BOOL= 11,
DBTYPE_VARIANT= 12,
DBTYPE_IUNKNOWN= 13,
DBTYPE_DECIMAL= 14,
DBTYPE_UI1= 17,
DBTYPE_ARRAY= 0x2000,
DBTYPE_BYREF= 0x4000,
DBTYPE_I1= 16,
DBTYPE_UI2= 18,
DBTYPE_UI4= 19,
DBTYPE_I8= 20,
DBTYPE_UI8= 21,
DBTYPE_GUID= 72,
DBTYPE_VECTOR= 0x1000,
DBTYPE_RESERVED= 0x8000,
DBTYPE_BYTES= 128,
DBTYPE_STR= 129,
DBTYPE_WSTR= 130,
DBTYPE_NUMERIC= 131,
DBTYPE_UDT= 132,
DBTYPE_DBDATE= 133,
DBTYPE_DBTIME= 134,
DBTYPE_DBTIMESTAMP= 135
    };
//@@@+ V1.5
#if( OLEDBVER >= 0x0150 )

enum DBTYPEENUM15
    {DBTYPE_HCHAPTER= 136
    };
#endif // OLEDBVER >= 0x0150
//@@@- V1.5
typedef DWORD DBPART;

enum DBPARTENUM
    {DBPART_INVALID= 0,
DBPART_VALUE= 0x1,
DBPART_LENGTH= 0x2,
DBPART_STATUS= 0x4
    };
typedef DWORD DBPARAMIO;

enum DBPARAMIOENUM
    {DBPARAMIO_NOTPARAM= 0,
DBPARAMIO_INPUT= 0x1,
DBPARAMIO_OUTPUT= 0x2
    };
typedef DWORD DBMEMOWNER;

enum DBMEMOWNERENUM
    {DBMEMOWNER_CLIENTOWNED= 0,
DBMEMOWNER_PROVIDEROWNED= 0x1
    };
typedef struct  tagDBOBJECT
    {
    DWORD dwFlags;
    IID iid;
    }DBOBJECT;

typedef DWORD DBSTATUS;

enum DBSTATUSENUM
    {DBSTATUS_S_OK= 0,
DBSTATUS_E_BADACCESSOR= 1,
DBSTATUS_E_CANTCONVERTVALUE= 2,
DBSTATUS_S_ISNULL= 3,
DBSTATUS_S_TRUNCATED= 4,
DBSTATUS_E_SIGNMISMATCH= 5,
DBSTATUS_E_DATAOVERFLOW= 6,
DBSTATUS_E_CANTCREATE= 7,
DBSTATUS_E_UNAVAILABLE= 8,
DBSTATUS_E_PERMISSIONDENIED= 9,
DBSTATUS_E_INTEGRITYVIOLATION= 10,
DBSTATUS_E_SCHEMAVIOLATION= 11,
DBSTATUS_E_BADSTATUS= 12,
DBSTATUS_S_DEFAULT= 13
    };
//@@@+ V2.0
#if( OLEDBVER >= 0x0200 )

enum DBSTATUSENUM20
    {DBSTATUS_S_NOTONAXIS= 14,
DBSTATUS_S_CELLEMPTY= 15
    };
#endif // OLEDBVER >= 0x0200
//@@@- V2.0
typedef struct  tagDBBINDEXT
    {
    /* [size_is] */ BYTE __RPC_FAR *pExtension;
    ULONG ulExtension;
    }DBBINDEXT;

typedef struct  tagDBBINDING
    {
    ULONG iOrdinal;
    ULONG obValue;
    ULONG obLength;
    ULONG obStatus;
    ITypeInfo __RPC_FAR *pTypeInfo;
    DBOBJECT __RPC_FAR *pObject;
    DBBINDEXT __RPC_FAR *pBindExt;
    DBPART dwPart;
    DBMEMOWNER dwMemOwner;
    DBPARAMIO eParamIO;
    ULONG cbMaxLen;
    DWORD dwFlags;
    DBTYPE wType;
    BYTE bPrecision;
    BYTE bScale;
    }DBBINDING;

typedef DWORD DBROWSTATUS;

enum DBROWSTATUSENUM
    {DBROWSTATUS_S_OK= 0,
DBROWSTATUS_S_MULTIPLECHANGES= 2,
DBROWSTATUS_S_PENDINGCHANGES= 3,
DBROWSTATUS_E_CANCELED= 4,
DBROWSTATUS_E_CANTRELEASE= 6,
DBROWSTATUS_E_CONCURRENCYVIOLATION= 7,
DBROWSTATUS_E_DELETED= 8,
DBROWSTATUS_E_PENDINGINSERT= 9,
DBROWSTATUS_E_NEWLYINSERTED= 10,
DBROWSTATUS_E_INTEGRITYVIOLATION= 11,
DBROWSTATUS_E_INVALID= 12,
DBROWSTATUS_E_MAXPENDCHANGESEXCEEDED= 13,
DBROWSTATUS_E_OBJECTOPEN= 14,
DBROWSTATUS_E_OUTOFMEMORY= 15,
DBROWSTATUS_E_PERMISSIONDENIED= 16,
DBROWSTATUS_E_LIMITREACHED= 17,
DBROWSTATUS_E_SCHEMAVIOLATION= 18
    };
typedef ULONG HACCESSOR;

#define DB_NULL_HACCESSOR 0x00 // deprecated; use DB_INVALID_HACCESSOR instead
#define DB_INVALID_HACCESSOR 0x00
typedef ULONG HROW;

#define DB_NULL_HROW 0x00
typedef ULONG HWATCHREGION;

#define DBWATCHREGION_NULL NULL
typedef ULONG HCHAPTER;

#define DB_NULL_HCHAPTER 0x00
#define DB_INVALID_HCHAPTER 0x00// deprecated; use DB_NULL_HCHAPTER instead
typedef struct  tagDBFAILUREINFO
    {
    HROW hRow;
    ULONG iColumn;
    HRESULT failure;
    }DBFAILUREINFO;

typedef DWORD DBCOLUMNFLAGS;

enum DBCOLUMNFLAGSENUM
    {DBCOLUMNFLAGS_ISBOOKMARK= 0x1,
DBCOLUMNFLAGS_MAYDEFER= 0x2,
DBCOLUMNFLAGS_WRITE= 0x4,
DBCOLUMNFLAGS_WRITEUNKNOWN= 0x8,
DBCOLUMNFLAGS_ISFIXEDLENGTH= 0x10,
DBCOLUMNFLAGS_ISNULLABLE= 0x20,
DBCOLUMNFLAGS_MAYBENULL= 0x40,
DBCOLUMNFLAGS_ISLONG= 0x80,
DBCOLUMNFLAGS_ISROWID= 0x100,
DBCOLUMNFLAGS_ISROWVER= 0x200,
DBCOLUMNFLAGS_CACHEDEFERRED= 0x1000
    };
//@@@+ V2.0
#if( OLEDBVER >= 0x0200 )

enum DBCOLUMNFLAGS2ENUM
    {DBCOLUMNFLAGS_ISCHAPTER= 0x2000
    };
#endif // OLEDBVER >= 0x0200
//@@@- V2.0
typedef struct  tagDBCOLUMNINFO
    {
    LPOLESTR pwszName;
    ITypeInfo __RPC_FAR *pTypeInfo;
    ULONG iOrdinal;
    DBCOLUMNFLAGS dwFlags;
    ULONG ulColumnSize;
    DBTYPE wType;
    BYTE bPrecision;
    BYTE bScale;
    DBID columnid;
    }DBCOLUMNINFO;

typedef
enum tagDBBOOKMARK
    {DBBMK_INVALID= 0,
DBBMK_FIRST= DBBMK_INVALID + 1,
DBBMK_LAST= DBBMK_FIRST + 1
    }DBBOOKMARK;

#define STD_BOOKMARKLENGTH 1
#ifdef __cplusplus
inline BOOL IsEqualGUIDBase(const GUID &rguid1, const GUID &rguid2)
{ return !memcmp(&(rguid1.Data2), &(rguid2.Data2), sizeof(GUID) - sizeof(rguid1.Data1)); }
#else // !__cplusplus
#define IsEqualGuidBase(rguid1, rguid2) (!memcmp(&((rguid1).Data2), &((rguid2).Data2), sizeof(GUID) - sizeof((rguid1).Data1)))
#endif // __cplusplus
#define DB_INVALIDCOLUMN ULONG_MAX
#define DBCIDGUID   {0x0C733A81L,0x2A1C,0x11CE,{0xAD,0xE5,0x00,0xAA,0x00,0x44,0x77,0x3D}}
#define DB_NULLGUID {0x00000000L,0x0000,0x0000,{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}
#ifdef DBINITCONSTANTS
extern const DBID DB_NULLID                      = {DB_NULLGUID, 0, (LPOLESTR)0};
extern const DBID DBCOLUMN_IDNAME                = {DBCIDGUID, DBKIND_GUID_PROPID, (LPOLESTR)2};
extern const DBID DBCOLUMN_NAME                  = {DBCIDGUID, DBKIND_GUID_PROPID, (LPOLESTR)3};
extern const DBID DBCOLUMN_NUMBER                = {DBCIDGUID, DBKIND_GUID_PROPID, (LPOLESTR)4};
extern const DBID DBCOLUMN_TYPE                  = {DBCIDGUID, DBKIND_GUID_PROPID, (LPOLESTR)5};
extern const DBID DBCOLUMN_PRECISION             = {DBCIDGUID, DBKIND_GUID_PROPID, (LPOLESTR)7};
extern const DBID DBCOLUMN_SCALE                 = {DBCIDGUID, DBKIND_GUID_PROPID, (LPOLESTR)8};
extern const DBID DBCOLUMN_FLAGS                 = {DBCIDGUID, DBKIND_GUID_PROPID, (LPOLESTR)9};
extern const DBID DBCOLUMN_BASECOLUMNNAME        = {DBCIDGUID, DBKIND_GUID_PROPID, (LPOLESTR)10};
extern const DBID DBCOLUMN_BASETABLENAME         = {DBCIDGUID, DBKIND_GUID_PROPID, (LPOLESTR)11};
extern const DBID DBCOLUMN_COLLATINGSEQUENCE     = {DBCIDGUID, DBKIND_GUID_PROPID, (LPOLESTR)12};
extern const DBID DBCOLUMN_COMPUTEMODE           = {DBCIDGUID, DBKIND_GUID_PROPID, (LPOLESTR)13};
extern const DBID DBCOLUMN_DEFAULTVALUE          = {DBCIDGUID, DBKIND_GUID_PROPID, (LPOLESTR)14};
extern const DBID DBCOLUMN_DOMAINNAME            = {DBCIDGUID, DBKIND_GUID_PROPID, (LPOLESTR)15};
extern const DBID DBCOLUMN_HASDEFAULT            = {DBCIDGUID, DBKIND_GUID_PROPID, (LPOLESTR)16};
extern const DBID DBCOLUMN_ISAUTOINCREMENT       = {DBCIDGUID, DBKIND_GUID_PROPID, (LPOLESTR)17};
extern const DBID DBCOLUMN_ISCASESENSITIVE       = {DBCIDGUID, DBKIND_GUID_PROPID, (LPOLESTR)18};
extern const DBID DBCOLUMN_ISSEARCHABLE          = {DBCIDGUID, DBKIND_GUID_PROPID, (LPOLESTR)20};
extern const DBID DBCOLUMN_ISUNIQUE              = {DBCIDGUID, DBKIND_GUID_PROPID, (LPOLESTR)21};
extern const DBID DBCOLUMN_BASECATALOGNAME       = {DBCIDGUID, DBKIND_GUID_PROPID, (LPOLESTR)23};
extern const DBID DBCOLUMN_BASESCHEMANAME        = {DBCIDGUID, DBKIND_GUID_PROPID, (LPOLESTR)24};
extern const DBID DBCOLUMN_GUID                  = {DBCIDGUID, DBKIND_GUID_PROPID, (LPOLESTR)29};
extern const DBID DBCOLUMN_PROPID                = {DBCIDGUID, DBKIND_GUID_PROPID, (LPOLESTR)30};
extern const DBID DBCOLUMN_TYPEINFO              = {DBCIDGUID, DBKIND_GUID_PROPID, (LPOLESTR)31};
extern const DBID DBCOLUMN_DOMAINCATALOG         = {DBCIDGUID, DBKIND_GUID_PROPID, (LPOLESTR)32};
extern const DBID DBCOLUMN_DOMAINSCHEMA          = {DBCIDGUID, DBKIND_GUID_PROPID, (LPOLESTR)33};
extern const DBID DBCOLUMN_DATETIMEPRECISION     = {DBCIDGUID, DBKIND_GUID_PROPID, (LPOLESTR)34};
extern const DBID DBCOLUMN_NUMERICPRECISIONRADIX = {DBCIDGUID, DBKIND_GUID_PROPID, (LPOLESTR)35};
extern const DBID DBCOLUMN_OCTETLENGTH           = {DBCIDGUID, DBKIND_GUID_PROPID, (LPOLESTR)36};
extern const DBID DBCOLUMN_COLUMNSIZE            = {DBCIDGUID, DBKIND_GUID_PROPID, (LPOLESTR)37};
extern const DBID DBCOLUMN_CLSID                 = {DBCIDGUID, DBKIND_GUID_PROPID, (LPOLESTR)38};
//@@@+ V1.5
#if( OLEDBVER >= 0x0150 )
extern const DBID DBCOLUMN_MAYSORT               = {DBCIDGUID, DBKIND_GUID_PROPID, (LPOLESTR)39};
#endif // OLEDBVER >= 0x0150
//@@@- V1.5
#else // !DBINITCONSTANTS
extern const DBID DB_NULLID;
extern const DBID DBCOLUMN_IDNAME;
extern const DBID DBCOLUMN_NAME;
extern const DBID DBCOLUMN_NUMBER;
extern const DBID DBCOLUMN_TYPE;
extern const DBID DBCOLUMN_PRECISION;
extern const DBID DBCOLUMN_SCALE;
extern const DBID DBCOLUMN_FLAGS;
extern const DBID DBCOLUMN_BASECOLUMNNAME;
extern const DBID DBCOLUMN_BASETABLENAME;
extern const DBID DBCOLUMN_COLLATINGSEQUENCE;
extern const DBID DBCOLUMN_COMPUTEMODE;
extern const DBID DBCOLUMN_DEFAULTVALUE;
extern const DBID DBCOLUMN_DOMAINNAME;
extern const DBID DBCOLUMN_HASDEFAULT;
extern const DBID DBCOLUMN_ISAUTOINCREMENT;
extern const DBID DBCOLUMN_ISCASESENSITIVE;
extern const DBID DBCOLUMN_ISSEARCHABLE;
extern const DBID DBCOLUMN_ISUNIQUE;
extern const DBID DBCOLUMN_BASECATALOGNAME;
extern const DBID DBCOLUMN_BASESCHEMANAME;
extern const DBID DBCOLUMN_GUID;
extern const DBID DBCOLUMN_PROPID;
extern const DBID DBCOLUMN_TYPEINFO;
extern const DBID DBCOLUMN_DOMAINCATALOG;
extern const DBID DBCOLUMN_DOMAINSCHEMA;
extern const DBID DBCOLUMN_DATETIMEPRECISION;
extern const DBID DBCOLUMN_NUMERICPRECISIONRADIX;
extern const DBID DBCOLUMN_OCTETLENGTH;
extern const DBID DBCOLUMN_COLUMNSIZE;
extern const DBID DBCOLUMN_CLSID;
//@@@+ V1.5
#if( OLEDBVER >= 0x0150 )
extern const DBID DBCOLUMN_MAYSORT;
#endif // OLEDBVER >= 0x0150
//@@@- V1.5
#endif // DBINITCONSTANTS
#ifdef DBINITCONSTANTS
//@@@+ V2.0
#if( OLEDBVER >= 0x0200 )
extern const GUID DB_PROPERTY_CHECK_OPTION               = {0xc8b5220b,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DB_PROPERTY_CONSTRAINT_CHECK_DEFERRED  = {0xc8b521f0,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DB_PROPERTY_DROP_CASCADE               = {0xc8b521f3,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DB_PROPERTY_UNIQUE                     = {0xc8b521f5,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DB_PROPERTY_ON_COMMIT_PRESERVE_ROWS    = {0xc8b52230,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DB_PROPERTY_PRIMARY                    = {0xc8b521fc,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DB_PROPERTY_CLUSTERED                  = {0xc8b521ff,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DB_PROPERTY_NONCLUSTERED               = {0xc8b52200,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DB_PROPERTY_BTREE                      = {0xc8b52201,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DB_PROPERTY_HASH                       = {0xc8b52202,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DB_PROPERTY_FILLFACTOR                 = {0xc8b52203,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DB_PROPERTY_INITIALSIZE                = {0xc8b52204,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DB_PROPERTY_DISALLOWNULL               = {0xc8b52205,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};

extern const GUID DB_PROPERTY_IGNORENULL                 = {0xc8b52206,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DB_PROPERTY_IGNOREANYNULL              = {0xc8b52207,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DB_PROPERTY_SORTBOOKMARKS              = {0xc8b52208,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DB_PROPERTY_AUTOMATICUPDATE            = {0xc8b52209,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DB_PROPERTY_EXPLICITUPDATE             = {0xc8b5220a,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DBGUID_LIKE_SQL                        = {0xc8b521f6,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DBGUID_LIKE_DOS                        = {0xc8b521f7,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DBGUID_LIKE_OFS                        = {0xc8b521f8,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DBGUID_LIKE_MAPI                       = {0xc8b521f9,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID MDGUID_MDX                             = {0xe66451b0,0xd62f,0x11d0,{0xb5,0x0c,0x00,0x80,0xc7,0x5e,0x09,0x8a}};
extern const GUID MDSCHEMA_CUBES                         = {0xc8b522d8,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID MDSCHEMA_DIMENSIONS                    = {0xc8b522d9,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID MDSCHEMA_HIERARCHIES                   = {0xc8b522da,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID MDSCHEMA_LEVELS                        = {0xc8b522db,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID MDSCHEMA_MEASURES                      = {0xc8b522dc,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID MDSCHEMA_PROPERTIES                    = {0xc8b522dd,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID MDSCHEMA_MEMBERS                       = {0xc8b522de,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
#endif // OLEDBVER >= 0x0200
//@@@- V2.0
extern const GUID DBSCHEMA_ASSERTIONS                    = {0xc8b52210,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DBSCHEMA_CATALOGS                      = {0xc8b52211,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DBSCHEMA_CHARACTER_SETS                = {0xc8b52212,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DBSCHEMA_COLLATIONS                    = {0xc8b52213,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DBSCHEMA_COLUMNS                       = {0xc8b52214,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DBSCHEMA_CHECK_CONSTRAINTS             = {0xc8b52215,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DBSCHEMA_CONSTRAINT_COLUMN_USAGE       = {0xc8b52216,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DBSCHEMA_CONSTRAINT_TABLE_USAGE        = {0xc8b52217,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DBSCHEMA_KEY_COLUMN_USAGE              = {0xc8b52218,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DBSCHEMA_REFERENTIAL_CONSTRAINTS       = {0xc8b52219,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DBSCHEMA_TABLE_CONSTRAINTS             = {0xc8b5221a,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DBSCHEMA_COLUMN_DOMAIN_USAGE           = {0xc8b5221b,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DBSCHEMA_INDEXES                       = {0xc8b5221e,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DBSCHEMA_COLUMN_PRIVILEGES             = {0xc8b52221,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DBSCHEMA_TABLE_PRIVILEGES              = {0xc8b52222,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DBSCHEMA_USAGE_PRIVILEGES              = {0xc8b52223,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DBSCHEMA_PROCEDURES                    = {0xc8b52224,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DBSCHEMA_SCHEMATA                      = {0xc8b52225,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DBSCHEMA_SQL_LANGUAGES                 = {0xc8b52226,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DBSCHEMA_STATISTICS                    = {0xc8b52227,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DBSCHEMA_TABLES                        = {0xc8b52229,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DBSCHEMA_TRANSLATIONS                  = {0xc8b5222a,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DBSCHEMA_PROVIDER_TYPES                = {0xc8b5222c,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DBSCHEMA_VIEWS                         = {0xc8b5222d,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DBSCHEMA_VIEW_COLUMN_USAGE             = {0xc8b5222e,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DBSCHEMA_VIEW_TABLE_USAGE              = {0xc8b5222f,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DBSCHEMA_PROCEDURE_PARAMETERS          = {0xc8b522b8,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DBSCHEMA_FOREIGN_KEYS                  = {0xc8b522c4,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DBSCHEMA_PRIMARY_KEYS                  = {0xc8b522c5,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DBSCHEMA_PROCEDURE_COLUMNS             = {0xc8b522c9,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DBCOL_SELFCOLUMNS                      = {0xc8b52231,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DBCOL_SPECIALCOL                       = {0xc8b52232,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID PSGUID_QUERY                           = {0x49691c90,0x7e17,0x101a,{0xa9,0x1c,0x08,0x00,0x2b,0x2e,0xcd,0xa9}};
extern const GUID DBPROPSET_COLUMN                       = {0xc8b522b9,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DBPROPSET_DATASOURCE                   = {0xc8b522ba,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DBPROPSET_DATASOURCEINFO               = {0xc8b522bb,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DBPROPSET_DBINIT                       = {0xc8b522bc,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DBPROPSET_INDEX                        = {0xc8b522bd,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DBPROPSET_ROWSET                       = {0xc8b522be,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DBPROPSET_TABLE                        = {0xc8b522bf,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DBPROPSET_DATASOURCEALL                = {0xc8b522c0,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DBPROPSET_DATASOURCEINFOALL            = {0xc8b522c1,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DBPROPSET_ROWSETALL                    = {0xc8b522c2,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DBPROPSET_SESSION                      = {0xc8b522c6,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DBPROPSET_SESSIONALL                   = {0xc8b522c7,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DBPROPSET_DBINITALL                    = {0xc8b522ca,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DBPROPSET_PROPERTIESINERROR            = {0xc8b522d4,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
//@@@+ V1.5
#if( OLEDBVER >= 0x0150 )
extern const GUID DBPROPSET_VIEW                         = {0xc8b522df,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
#endif // OLEDBVER >= 0x0150
//@@@- V1.5
// DBGUID_DBSQL is deprecated; use DBGUID_DEFAULT instead
extern const GUID DBGUID_DBSQL                           = {0xc8b521fb,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DBGUID_DEFAULT                         = {0xc8b521fb,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
extern const GUID DBGUID_SQL                             = {0xc8b522d7,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
#else // !DBINITCONSTANTS
//@@@+ V2.0
#if( OLEDBVER >= 0x0200 )
extern const GUID DB_PROPERTY_CHECK_OPTION;
extern const GUID DB_PROPERTY_CONSTRAINT_CHECK_DEFERRED;
extern const GUID DB_PROPERTY_DROP_CASCADE;
extern const GUID DB_PROPERTY_ON_COMMIT_PRESERVE_ROWS;
extern const GUID DB_PROPERTY_UNIQUE;
extern const GUID DB_PROPERTY_PRIMARY;
extern const GUID DB_PROPERTY_CLUSTERED;
extern const GUID DB_PROPERTY_NONCLUSTERED;
extern const GUID DB_PROPERTY_BTREE;
extern const GUID DB_PROPERTY_HASH;
extern const GUID DB_PROPERTY_FILLFACTOR;
extern const GUID DB_PROPERTY_INITIALSIZE;
extern const GUID DB_PROPERTY_DISALLOWNULL;
extern const GUID DB_PROPERTY_IGNORENULL;
extern const GUID DB_PROPERTY_IGNOREANYNULL;
extern const GUID DB_PROPERTY_SORTBOOKMARKS;
extern const GUID DB_PROPERTY_AUTOMATICUPDATE;
extern const GUID DB_PROPERTY_EXPLICITUPDATE;
extern const GUID DBGUID_LIKE_SQL;
extern const GUID DBGUID_LIKE_DOS;
extern const GUID DBGUID_LIKE_OFS;
extern const GUID DBGUID_LIKE_MAPI;
extern const GUID MDGUID_MDX;
extern const GUID MDSCHEMA_CUBES;
extern const GUID MDSCHEMA_DIMENSIONS;
extern const GUID MDSCHEMA_HIERARCHIES;
extern const GUID MDSCHEMA_LEVELS;
extern const GUID MDSCHEMA_MEASURES;
extern const GUID MDSCHEMA_PROPERTIES;
extern const GUID MDSCHEMA_MEMBERS;
#endif // OLEDBVER >= 0x0200
//@@@- V2.0
extern const GUID DBSCHEMA_ASSERTIONS;
extern const GUID DBSCHEMA_CATALOGS;
extern const GUID DBSCHEMA_CHARACTER_SETS;
extern const GUID DBSCHEMA_COLLATIONS;
extern const GUID DBSCHEMA_COLUMNS;
extern const GUID DBSCHEMA_CHECK_CONSTRAINTS;
extern const GUID DBSCHEMA_CONSTRAINT_COLUMN_USAGE;
extern const GUID DBSCHEMA_CONSTRAINT_TABLE_USAGE;
extern const GUID DBSCHEMA_KEY_COLUMN_USAGE;
extern const GUID DBSCHEMA_REFERENTIAL_CONSTRAINTS;
extern const GUID DBSCHEMA_TABLE_CONSTRAINTS;
extern const GUID DBSCHEMA_COLUMN_DOMAIN_USAGE;
extern const GUID DBSCHEMA_INDEXES;
extern const GUID DBSCHEMA_COLUMN_PRIVILEGES;
extern const GUID DBSCHEMA_TABLE_PRIVILEGES;
extern const GUID DBSCHEMA_USAGE_PRIVILEGES;
extern const GUID DBSCHEMA_PROCEDURES;
extern const GUID DBSCHEMA_SCHEMATA;
extern const GUID DBSCHEMA_SQL_LANGUAGES;
extern const GUID DBSCHEMA_STATISTICS;
extern const GUID DBSCHEMA_TABLES;
extern const GUID DBSCHEMA_TRANSLATIONS;
extern const GUID DBSCHEMA_PROVIDER_TYPES;
extern const GUID DBSCHEMA_VIEWS;
extern const GUID DBSCHEMA_VIEW_COLUMN_USAGE;
extern const GUID DBSCHEMA_VIEW_TABLE_USAGE;
extern const GUID DBSCHEMA_PROCEDURE_PARAMETERS;
extern const GUID DBSCHEMA_FOREIGN_KEYS;
extern const GUID DBSCHEMA_PRIMARY_KEYS;
extern const GUID DBSCHEMA_PROCEDURE_COLUMNS;
extern const GUID DBCOL_SELFCOLUMNS;
extern const GUID DBCOL_SPECIALCOL;
extern const GUID PSGUID_QUERY;
extern const GUID DBPROPSET_COLUMN;
extern const GUID DBPROPSET_DATASOURCE;
extern const GUID DBPROPSET_DATASOURCEINFO;
extern const GUID DBPROPSET_DBINIT;
extern const GUID DBPROPSET_INDEX;
extern const GUID DBPROPSET_ROWSET;
extern const GUID DBPROPSET_TABLE;
extern const GUID DBPROPSET_DATASOURCEALL;
extern const GUID DBPROPSET_DATASOURCEINFOALL;
extern const GUID DBPROPSET_ROWSETALL;
extern const GUID DBPROPSET_SESSION;
extern const GUID DBPROPSET_SESSIONALL;
extern const GUID DBPROPSET_DBINITALL;
extern const GUID DBPROPSET_PROPERTIESINERROR;
//@@@+ V1.5
#if( OLEDBVER >= 0x0150 )
extern const GUID DBPROPSET_VIEW;
#endif // OLEDBVER >= 0x0150
//@@@- V1.5
// DBGUID_DBSQL is deprecated; use DBGUID_DEFAULT instead
extern const GUID DBGUID_DBSQL;
extern const GUID DBGUID_DEFAULT;
extern const GUID DBGUID_SQL;
#endif // DBINITCONSTANTS

enum DBPROPENUM
    {DBPROP_ABORTPRESERVE= 0x2L,
DBPROP_ACTIVESESSIONS= 0x3L,
DBPROP_APPENDONLY= 0xbbL,
DBPROP_ASYNCTXNABORT= 0xa8L,
DBPROP_ASYNCTXNCOMMIT= 0x4L,
DBPROP_AUTH_CACHE_AUTHINFO= 0x5L,
DBPROP_AUTH_ENCRYPT_PASSWORD= 0x6L,
DBPROP_AUTH_INTEGRATED= 0x7L,
DBPROP_AUTH_MASK_PASSWORD= 0x8L,
DBPROP_AUTH_PASSWORD= 0x9L,
DBPROP_AUTH_PERSIST_ENCRYPTED= 0xaL,
DBPROP_AUTH_PERSIST_SENSITIVE_AUTHINFO= 0xbL,
DBPROP_AUTH_USERID= 0xcL,
DBPROP_BLOCKINGSTORAGEOBJECTS= 0xdL,
DBPROP_BOOKMARKS= 0xeL,
DBPROP_BOOKMARKSKIPPED= 0xfL,
DBPROP_BOOKMARKTYPE= 0x10L,
DBPROP_BYREFACCESSORS= 0x78L,
DBPROP_CACHEDEFERRED= 0x11L,
DBPROP_CANFETCHBACKWARDS= 0x12L,
DBPROP_CANHOLDROWS= 0x13L,
DBPROP_CANSCROLLBACKWARDS= 0x15L,
DBPROP_CATALOGLOCATION= 0x16L,
DBPROP_CATALOGTERM= 0x17L,
DBPROP_CATALOGUSAGE= 0x18L,
DBPROP_CHANGEINSERTEDROWS= 0xbcL,
DBPROP_COL_AUTOINCREMENT= 0x1aL,
DBPROP_COL_DEFAULT= 0x1bL,
DBPROP_COL_DESCRIPTION= 0x1cL,
DBPROP_COL_FIXEDLENGTH= 0xa7L,
DBPROP_COL_NULLABLE= 0x1dL,
DBPROP_COL_PRIMARYKEY= 0x1eL,
DBPROP_COL_UNIQUE= 0x1fL,
DBPROP_COLUMNDEFINITION= 0x20L,
DBPROP_COLUMNRESTRICT= 0x21L,
DBPROP_COMMANDTIMEOUT= 0x22L,
DBPROP_COMMITPRESERVE= 0x23L,
DBPROP_CONCATNULLBEHAVIOR= 0x24L,
DBPROP_CURRENTCATALOG= 0x25L,
DBPROP_DATASOURCENAME= 0x26L,
DBPROP_DATASOURCEREADONLY= 0x27L,
DBPROP_DBMSNAME= 0x28L,
DBPROP_DBMSVER= 0x29L,
DBPROP_DEFERRED= 0x2aL,
DBPROP_DELAYSTORAGEOBJECTS= 0x2bL,
DBPROP_DSOTHREADMODEL= 0xa9L,
DBPROP_GROUPBY= 0x2cL,
DBPROP_HETEROGENEOUSTABLES= 0x2dL,
DBPROP_IAccessor= 0x79L,
DBPROP_IColumnsInfo= 0x7aL,
DBPROP_IColumnsRowset= 0x7bL,
DBPROP_IConnectionPointContainer= 0x7cL,
DBPROP_IConvertType= 0xc2L,
DBPROP_IRowset= 0x7eL,
DBPROP_IRowsetChange= 0x7fL,
DBPROP_IRowsetIdentity= 0x80L,
DBPROP_IRowsetIndex= 0x9fL,
DBPROP_IRowsetInfo= 0x81L,
DBPROP_IRowsetLocate= 0x82L,
DBPROP_IRowsetResynch= 0x84L,
DBPROP_IRowsetScroll= 0x85L,
DBPROP_IRowsetUpdate= 0x86L,
DBPROP_ISupportErrorInfo= 0x87L,
DBPROP_ILockBytes= 0x88L,
DBPROP_ISequentialStream= 0x89L,
DBPROP_IStorage= 0x8aL,
DBPROP_IStream= 0x8bL,
DBPROP_IDENTIFIERCASE= 0x2eL,
DBPROP_IMMOBILEROWS= 0x2fL,
DBPROP_INDEX_AUTOUPDATE= 0x30L,
DBPROP_INDEX_CLUSTERED= 0x31L,
DBPROP_INDEX_FILLFACTOR= 0x32L,
DBPROP_INDEX_INITIALSIZE= 0x33L,
DBPROP_INDEX_NULLCOLLATION= 0x34L,
DBPROP_INDEX_NULLS= 0x35L,
DBPROP_INDEX_PRIMARYKEY= 0x36L,
DBPROP_INDEX_SORTBOOKMARKS= 0x37L,
DBPROP_INDEX_TEMPINDEX= 0xa3L,
DBPROP_INDEX_TYPE= 0x38L,
DBPROP_INDEX_UNIQUE= 0x39L,
DBPROP_INIT_DATASOURCE= 0x3bL,
DBPROP_INIT_HWND= 0x3cL,
DBPROP_INIT_IMPERSONATION_LEVEL= 0x3dL,
DBPROP_INIT_LCID= 0xbaL,
DBPROP_INIT_LOCATION= 0x3eL,
DBPROP_INIT_MODE= 0x3fL,
DBPROP_INIT_PROMPT= 0x40L,
DBPROP_INIT_PROTECTION_LEVEL= 0x41L,
DBPROP_INIT_PROVIDERSTRING= 0xa0L,
DBPROP_INIT_TIMEOUT= 0x42L,
DBPROP_LITERALBOOKMARKS= 0x43L,
DBPROP_LITERALIDENTITY= 0x44L,
DBPROP_MARSHALLABLE= 0xc5L,
DBPROP_MAXINDEXSIZE= 0x46L,
DBPROP_MAXOPENROWS= 0x47L,
DBPROP_MAXPENDINGROWS= 0x48L,
DBPROP_MAXROWS= 0x49L,
DBPROP_MAXROWSIZE= 0x4aL,
DBPROP_MAXROWSIZEINCLUDESBLOB= 0x4bL,
DBPROP_MAXTABLESINSELECT= 0x4cL,
DBPROP_MAYWRITECOLUMN= 0x4dL,
DBPROP_MEMORYUSAGE= 0x4eL,
DBPROP_MULTIPLEPARAMSETS= 0xbfL,
DBPROP_MULTIPLERESULTS= 0xc4L,
DBPROP_MULTIPLESTORAGEOBJECTS= 0x50L,
DBPROP_MULTITABLEUPDATE= 0x51L,
DBPROP_NOTIFICATIONGRANULARITY= 0xc6L,
DBPROP_NOTIFICATIONPHASES= 0x52L,
DBPROP_NOTIFYCOLUMNSET= 0xabL,
DBPROP_NOTIFYROWDELETE= 0xadL,
DBPROP_NOTIFYROWFIRSTCHANGE= 0xaeL,
DBPROP_NOTIFYROWINSERT= 0xafL,
DBPROP_NOTIFYROWRESYNCH= 0xb1L,
DBPROP_NOTIFYROWSETCHANGED= 0xd3L,
DBPROP_NOTIFYROWSETRELEASE= 0xb2L,
DBPROP_NOTIFYROWSETFETCHPOSITIONCHANGE= 0xb3L,
DBPROP_NOTIFYROWUNDOCHANGE= 0xb4L,
DBPROP_NOTIFYROWUNDODELETE= 0xb5L,
DBPROP_NOTIFYROWUNDOINSERT= 0xb6L,
DBPROP_NOTIFYROWUPDATE= 0xb7L,
DBPROP_NULLCOLLATION= 0x53L,
DBPROP_OLEOBJECTS= 0x54L,
DBPROP_ORDERBYCOLUMNSINSELECT= 0x55L,
DBPROP_ORDEREDBOOKMARKS= 0x56L,
DBPROP_OTHERINSERT= 0x57L,
DBPROP_OTHERUPDATEDELETE= 0x58L,
DBPROP_OUTPUTPARAMETERAVAILABILITY= 0xb8L,
DBPROP_OWNINSERT= 0x59L,
DBPROP_OWNUPDATEDELETE= 0x5aL,
DBPROP_PERSISTENTIDTYPE= 0xb9L,
DBPROP_PREPAREABORTBEHAVIOR= 0x5bL,
DBPROP_PREPARECOMMITBEHAVIOR= 0x5cL,
DBPROP_PROCEDURETERM= 0x5dL,
DBPROP_PROVIDERNAME= 0x60L,
DBPROP_PROVIDEROLEDBVER= 0x61L,
DBPROP_PROVIDERVER= 0x62L,
DBPROP_QUICKRESTART= 0x63L,
DBPROP_QUOTEDIDENTIFIERCASE= 0x64L,
DBPROP_REENTRANTEVENTS= 0x65L,
DBPROP_REMOVEDELETED= 0x66L,
DBPROP_REPORTMULTIPLECHANGES= 0x67L,
DBPROP_RETURNPENDINGINSERTS= 0xbdL,
DBPROP_ROWRESTRICT= 0x68L,
DBPROP_ROWSETCONVERSIONSONCOMMAND= 0xc0L,
DBPROP_ROWTHREADMODEL= 0x69L,
DBPROP_SCHEMATERM= 0x6aL,
DBPROP_SCHEMAUSAGE= 0x6bL,
DBPROP_SERVERCURSOR= 0x6cL,
DBPROP_SESS_AUTOCOMMITISOLEVELS= 0xbeL,
DBPROP_SQLSUPPORT= 0x6dL,
DBPROP_STRONGIDENTITY= 0x77L,
DBPROP_STRUCTUREDSTORAGE= 0x6fL,
DBPROP_SUBQUERIES= 0x70L,
DBPROP_SUPPORTEDTXNDDL= 0xa1L,
DBPROP_SUPPORTEDTXNISOLEVELS= 0x71L,
DBPROP_SUPPORTEDTXNISORETAIN= 0x72L,
DBPROP_TABLETERM= 0x73L,
DBPROP_TBL_TEMPTABLE= 0x8cL,
DBPROP_TRANSACTEDOBJECT= 0x74L,
DBPROP_UPDATABILITY= 0x75L,
DBPROP_USERNAME= 0x76L
    };
//@@@+ V1.5
#if( OLEDBVER >= 0x0150 )

enum DBPROPENUM15
    {DBPROP_FILTEROPS= 0xd0L,
DBPROP_FILTERCOMPAREOPS= 0xd1L,
DBPROP_FINDCOMPAREOPS= 0xd2L,
DBPROP_IChapteredRowset= 0xcaL,
DBPROP_IDBAsynchStatus= 0xcbL,
DBPROP_IRowsetFind= 0xccL,
DBPROP_IRowsetView= 0xd4L,
DBPROP_IViewChapter= 0xd5L,
DBPROP_IViewFilter= 0xd6L,
DBPROP_IViewRowset= 0xd7L,
DBPROP_IViewSort= 0xd8L,
DBPROP_INIT_ASYNCH= 0xc8L,
DBPROP_MAXOPENCHAPTERS= 0xc7L,
DBPROP_MAXORSINFILTER= 0xcdL,
DBPROP_MAXSORTCOLUMNS= 0xceL,
DBPROP_ROWSET_ASYNCH= 0xc9L,
DBPROP_SORTONINDEX= 0xcfL
    };
#endif // OLEDBVER >= 0x0150
//@@@- V1.5
//@@@+ V2.0
#if( OLEDBVER >= 0x0200 )

enum DBPROPENUM20
    {DBPROP_DATASOURCE_TYPE= 0xd4L,
DBPROP_ICommandCost= 0x8dL,
DBPROP_ICommandTree= 0x8eL,
DBPROP_ICommandValidate= 0x8fL,
DBPROP_IDBSchemaCommand= 0x90L,
DBPROP_IDBSecurityInfo= 0x91L,
DBPROP_IProvideMoniker= 0x7dL,
DBPROP_IQuery= 0x92L,
DBPROP_IReadData= 0x93L,
DBPROP_IRowsetAsynch= 0x94L,
DBPROP_IRowsetCopyRows= 0x95L,
DBPROP_IRowsetKeys= 0x97L,
DBPROP_IRowsetNewRowAfter= 0x98L,
DBPROP_IRowsetNextRowset= 0x99L,
DBPROP_IRowsetExactScroll= 0x9aL,
DBPROP_IRowsetWatchAll= 0x9bL,
DBPROP_IRowsetWatchNotify= 0x9cL,
DBPROP_IRowsetWatchRegion= 0x9dL,
DBPROP_IRowsetWithParameters= 0x9eL,
MDPROP_AXES= 0xd5L,
MDPROP_CELLUPDATE= 0xd6L,
MDPROP_FLATTENING_SUPPORT= 0xd7L,
MDPROP_JOINCUBES= 0xd8L,
MDPROP_NAMED_LEVELS= 0xd9L,
MDPROP_SLICER= 0xdaL
    };
#endif // OLEDBVER >= 0x0200
//@@@- V2.0
#define DBPROPVAL_BMK_NUMERIC 0x00000001L
#define DBPROPVAL_BMK_KEY 0x00000002L
#define DBPROPVAL_CL_START 0x00000001L
#define DBPROPVAL_CL_END 0x00000002L
#define DBPROPVAL_CU_DML_STATEMENTS 0x00000001L
#define DBPROPVAL_CU_TABLE_DEFINITION 0x00000002L
#define DBPROPVAL_CU_INDEX_DEFINITION 0x00000004L
#define DBPROPVAL_CU_PRIVILEGE_DEFINITION 0x00000008L
#define DBPROPVAL_CD_NOTNULL 0x00000001L
#define DBPROPVAL_CB_NULL 0x00000001L
#define DBPROPVAL_CB_NON_NULL 0x00000002L
#define DBPROPVAL_FU_NOT_SUPPORTED 0x00000001L
#define DBPROPVAL_FU_COLUMN 0x00000002L
#define DBPROPVAL_FU_TABLE 0x00000004L
#define DBPROPVAL_FU_CATALOG 0x00000008L
#define DBPROPVAL_GB_NOT_SUPPORTED 0x00000001L
#define DBPROPVAL_GB_EQUALS_SELECT 0x00000002L
#define DBPROPVAL_GB_CONTAINS_SELECT 0x00000004L
#define DBPROPVAL_GB_NO_RELATION 0x00000008L
#define DBPROPVAL_HT_DIFFERENT_CATALOGS 0x00000001L
#define DBPROPVAL_HT_DIFFERENT_PROVIDERS 0x00000002L
#define DBPROPVAL_IC_UPPER 0x00000001L
#define DBPROPVAL_IC_LOWER 0x00000002L
#define DBPROPVAL_IC_SENSITIVE 0x00000004L
#define DBPROPVAL_IC_MIXED 0x00000008L
#define DBPROPVAL_LM_NONE 0x00000001L
#define DBPROPVAL_LM_READ 0x00000002L
#define DBPROPVAL_LM_INTENT 0x00000004L
#define DBPROPVAL_LM_WRITE 0x00000008L
#define DBPROPVAL_NP_OKTODO 0x00000001L
#define DBPROPVAL_NP_ABOUTTODO 0x00000002L
#define DBPROPVAL_NP_SYNCHAFTER 0x00000004L
#define DBPROPVAL_NP_FAILEDTODO 0x00000008L
#define DBPROPVAL_NP_DIDEVENT 0x00000010L
#define DBPROPVAL_NC_END 0x00000001L
#define DBPROPVAL_NC_HIGH 0x00000002L
#define DBPROPVAL_NC_LOW 0x00000004L
#define DBPROPVAL_NC_START 0x00000008L
#define DBPROPVAL_OO_BLOB 0x00000001L
#define DBPROPVAL_OO_IPERSIST 0x00000002L
#define DBPROPVAL_CB_DELETE 0x00000001L
#define DBPROPVAL_CB_PRESERVE 0x00000002L
#define DBPROPVAL_SU_DML_STATEMENTS 0x00000001L
#define DBPROPVAL_SU_TABLE_DEFINITION 0x00000002L
#define DBPROPVAL_SU_INDEX_DEFINITION 0x00000004L
#define DBPROPVAL_SU_PRIVILEGE_DEFINITION 0x00000008L
#define DBPROPVAL_SQ_CORRELATEDSUBQUERIES 0x00000001L
#define DBPROPVAL_SQ_COMPARISON 0x00000002L
#define DBPROPVAL_SQ_EXISTS 0x00000004L
#define DBPROPVAL_SQ_IN 0x00000008L
#define DBPROPVAL_SQ_QUANTIFIED 0x00000010L
#define DBPROPVAL_SS_ISEQUENTIALSTREAM 0x00000001L
#define DBPROPVAL_SS_ISTREAM 0x00000002L
#define DBPROPVAL_SS_ISTORAGE 0x00000004L
#define DBPROPVAL_SS_ILOCKBYTES 0x00000008L
#define DBPROPVAL_TI_CHAOS 0x00000010L
#define DBPROPVAL_TI_READUNCOMMITTED 0x00000100L
#define DBPROPVAL_TI_BROWSE 0x00000100L
#define DBPROPVAL_TI_CURSORSTABILITY 0x00001000L
#define DBPROPVAL_TI_READCOMMITTED 0x00001000L
#define DBPROPVAL_TI_REPEATABLEREAD 0x00010000L
#define DBPROPVAL_TI_SERIALIZABLE 0x00100000L
#define DBPROPVAL_TI_ISOLATED 0x00100000L
#define DBPROPVAL_TR_COMMIT_DC 0x00000001L
#define DBPROPVAL_TR_COMMIT 0x00000002L
#define DBPROPVAL_TR_COMMIT_NO 0x00000004L
#define DBPROPVAL_TR_ABORT_DC 0x00000008L
#define DBPROPVAL_TR_ABORT 0x00000010L
#define DBPROPVAL_TR_ABORT_NO 0x00000020L
#define DBPROPVAL_TR_DONTCARE 0x00000040L
#define DBPROPVAL_TR_BOTH 0x00000080L
#define DBPROPVAL_TR_NONE 0x00000100L
#define DBPROPVAL_TR_OPTIMISTIC 0x00000200L
#define DBPROPVAL_RT_FREETHREAD 0x00000001L
#define DBPROPVAL_RT_APTMTTHREAD 0x00000002L
#define DBPROPVAL_RT_SINGLETHREAD 0x00000004L
#define DBPROPVAL_UP_CHANGE 0x00000001L
#define DBPROPVAL_UP_DELETE 0x00000002L
#define DBPROPVAL_UP_INSERT 0x00000004L
#define DBPROPVAL_SQL_NONE 0x00000000L
#define DBPROPVAL_SQL_ODBC_MINIMUM 0x00000001L
#define DBPROPVAL_SQL_ODBC_CORE 0x00000002L
#define DBPROPVAL_SQL_ODBC_EXTENDED 0x00000004L
#define DBPROPVAL_SQL_ANSI89_IEF 0x00000008L
#define DBPROPVAL_SQL_ANSI92_ENTRY 0x00000010L
#define DBPROPVAL_SQL_FIPS_TRANSITIONAL 0x00000020L
#define DBPROPVAL_SQL_ANSI92_INTERMEDIATE 0x00000040L
#define DBPROPVAL_SQL_ANSI92_FULL 0x00000080L
#define DBPROPVAL_SQL_ESCAPECLAUSES 0x00000100L
#define DBPROPVAL_IT_BTREE                                0x00000001L
#define DBPROPVAL_IT_HASH                                 0x00000002L
#define DBPROPVAL_IT_CONTENT                              0x00000003L
#define DBPROPVAL_IT_OTHER                                0x00000004L
#define DBPROPVAL_IN_DISALLOWNULL                         0x00000001L
#define DBPROPVAL_IN_IGNORENULL                           0x00000002L
#define DBPROPVAL_IN_IGNOREANYNULL                        0x00000004L
#define DBPROPVAL_TC_NONE                                 0x00000000L
#define DBPROPVAL_TC_DML                                  0x00000001L
#define DBPROPVAL_TC_DDL_COMMIT                           0x00000002L
#define DBPROPVAL_TC_DDL_IGNORE                           0x00000004L
#define DBPROPVAL_TC_ALL                                  0x00000008L
#define DBPROPVAL_NP_OKTODO                               0x00000001L
#define DBPROPVAL_NP_ABOUTTODO                            0x00000002L
#define DBPROPVAL_NP_SYNCHAFTER                           0x00000004L
#define DBPROPVAL_OA_NOTSUPPORTED                         0x00000001L
#define DBPROPVAL_OA_ATEXECUTE                            0x00000002L
#define DBPROPVAL_OA_ATROWRELEASE                         0x00000004L
#define DBPROPVAL_MR_NOTSUPPORTED                         0x00000000L
#define DBPROPVAL_MR_SUPPORTED                            0x00000001L
#define DBPROPVAL_MR_CONCURRENT                           0x00000002L
#define DBPROPVAL_PT_GUID_NAME                            0x00000001L
#define DBPROPVAL_PT_GUID_PROPID                          0x00000002L
#define DBPROPVAL_PT_NAME                                 0x00000004L
#define DBPROPVAL_PT_GUID                                 0x00000008L
#define DBPROPVAL_NT_SINGLEROW                            0x00000001L
#define DBPROPVAL_NT_MULTIPLEROWS                         0x00000002L
//@@@+ V1.5
#if( OLEDBVER >= 0x0150 )
#define DBPROPVAL_ASYNCH_INITIALIZE                       0x00000001L
#define DBPROPVAL_ASYNCH_SEQUENTIALPOPULATION             0x00000002L
#define DBPROPVAL_ASYNCH_RANDOMPOPULATION                 0x00000004L
#define DBPROPVAL_OP_EQUAL                                0x00000001L
#define DBPROPVAL_OP_RELATIVE                             0x00000002L
#define DBPROPVAL_OP_STRING                               0x00000004L
#define DBPROPVAL_CO_EQUALITY                             0x00000001L
#define DBPROPVAL_CO_STRING                               0x00000002L
#define DBPROPVAL_CO_CASESENSITIVE                        0x00000004L
#define DBPROPVAL_CO_CASEINSENSITIVE                      0x00000008L
#endif // OLEDBVER >= 0x0150
//@@@- V1.5
//@@@+ V2.0
#if( OLEDBVER >= 0x0200 )
#define DBPROPVAL_DST_TDP                                 0x00000001L
#define DBPROPVAL_DST_MDP                                 0x00000002L
#define DBPROPVAL_DST_TDPANDMDP                           0x00000003L
#define MDPROPVAL_FS_FULL_SUPPORT                         0x00000001L
#define MDPROPVAL_FS_GENERATED_COLUMN                     0x00000002L
#define MDPROPVAL_FS_GENERATED_DIMENSION                  0x00000003l
#define MDPROPVAL_FS_NO_SUPPORT                           0x00000004L
#define MDPROPVAL_NL_NO_SUPPORT                           0x00000000L
#define MDPROPVAL_NL_SUPPORTED                            0x00000001L
#define MDPROPVAL_JC_SINGLECUBE                           0x00000001L
#define MDPROPVAL_JC_MULTICUBES                           0x00000002L
#define MDPROPVAL_RR_NORANGEROWSET                        0x00000001L
#define MDPROPVAL_RR_READONLY                             0x00000002L
#define MDPROPVAL_RR_UPDATE                               0x00000004L
#define MDPROPVAL_S_SINGLETUPLE                           0x00000001L
#define MDPROPVAL_S_MULTIPLETUPLES                        0x00000002L
#endif // OLEDBVER >= 0x0200
//@@@- V2.0
#define DB_IMP_LEVEL_ANONYMOUS0x00
#define DB_IMP_LEVEL_IDENTIFY0x01
#define DB_IMP_LEVEL_IMPERSONATE0x02
#define DB_IMP_LEVEL_DELEGATE0x03
#define DBPROMPT_PROMPT0x01
#define DBPROMPT_COMPLETE0x02
#define DBPROMPT_COMPLETEREQUIRED0x03
#define DBPROMPT_NOPROMPT0x04
#define DB_PROT_LEVEL_NONE0x00
#define DB_PROT_LEVEL_CONNECT0x01
#define DB_PROT_LEVEL_CALL0x02
#define DB_PROT_LEVEL_PKT0x03
#define DB_PROT_LEVEL_PKT_INTEGRITY0x04
#define DB_PROT_LEVEL_PKT_PRIVACY0x05
#define DB_MODE_READ0x01
#define DB_MODE_WRITE0x02
#define DB_MODE_READWRITE0x03
#define DB_MODE_SHARE_DENY_READ0x04
#define DB_MODE_SHARE_DENY_WRITE0x08
#define DB_MODE_SHARE_EXCLUSIVE0x0c
#define DB_MODE_SHARE_DENY_NONE0x10
#define DBCOMPUTEMODE_COMPUTED       0x01
#define DBCOMPUTEMODE_DYNAMIC        0x02
#define DBCOMPUTEMODE_NOTCOMPUTED    0x03
//@@@+ V2.0
#if( OLEDBVER >= 0x0200 )
typedef WORD DBCOMMANDOP;

enum DBCOMMANDOPENUM
    {DBOP_scalar_constant= 0,
DBOP_DEFAULT= DBOP_scalar_constant + 1,
DBOP_NULL= DBOP_DEFAULT + 1,
DBOP_bookmark_name= DBOP_NULL + 1,
DBOP_catalog_name= DBOP_bookmark_name + 1,
DBOP_column_name= DBOP_catalog_name + 1,
DBOP_schema_name= DBOP_column_name + 1,
DBOP_outall_name= DBOP_schema_name + 1,
DBOP_qualifier_name= DBOP_outall_name + 1,
DBOP_qualified_column_name= DBOP_qualifier_name + 1,
DBOP_table_name= DBOP_qualified_column_name + 1,
DBOP_nested_table_name= DBOP_table_name + 1,
DBOP_nested_column_name= DBOP_nested_table_name + 1,
DBOP_row= DBOP_nested_column_name + 1,
DBOP_table= DBOP_row + 1,
DBOP_sort= DBOP_table + 1,
DBOP_distinct= DBOP_sort + 1,
DBOP_distinct_order_preserving= DBOP_distinct + 1,

DBOP_alias= DBOP_distinct_order_preserving + 1,
DBOP_cross_join= DBOP_alias + 1,
DBOP_union_join= DBOP_cross_join + 1,
DBOP_inner_join= DBOP_union_join + 1,
DBOP_left_semi_join= DBOP_inner_join + 1,
DBOP_right_semi_join= DBOP_left_semi_join + 1,
DBOP_left_anti_semi_join= DBOP_right_semi_join + 1,
DBOP_right_anti_semi_join= DBOP_left_anti_semi_join + 1,
DBOP_left_outer_join= DBOP_right_anti_semi_join + 1,
DBOP_right_outer_join= DBOP_left_outer_join + 1,
DBOP_full_outer_join= DBOP_right_outer_join + 1,
DBOP_natural_join= DBOP_full_outer_join + 1,
DBOP_natural_left_outer_join= DBOP_natural_join + 1,
DBOP_natural_right_outer_join= DBOP_natural_left_outer_join + 1,
DBOP_natural_full_outer_join= DBOP_natural_right_outer_join + 1,
DBOP_set_intersection= DBOP_natural_full_outer_join + 1,
DBOP_set_union= DBOP_set_intersection + 1,
DBOP_set_left_difference= DBOP_set_union + 1,
DBOP_set_right_difference= DBOP_set_left_difference + 1,
DBOP_set_anti_difference= DBOP_set_right_difference + 1,
DBOP_bag_intersection= DBOP_set_anti_difference + 1,
DBOP_bag_union= DBOP_bag_intersection + 1,
DBOP_bag_left_difference= DBOP_bag_union + 1,
DBOP_bag_right_difference= DBOP_bag_left_difference + 1,
DBOP_bag_anti_difference= DBOP_bag_right_difference + 1,
DBOP_division= DBOP_bag_anti_difference + 1,
DBOP_relative_sampling= DBOP_division + 1,
DBOP_absolute_sampling= DBOP_relative_sampling + 1,
DBOP_transitive_closure= DBOP_absolute_sampling + 1,
DBOP_recursive_union= DBOP_transitive_closure + 1,
DBOP_aggregate= DBOP_recursive_union + 1,
DBOP_remote_table= DBOP_aggregate + 1,
DBOP_select= DBOP_remote_table + 1,
DBOP_order_preserving_select= DBOP_select + 1,
DBOP_project= DBOP_order_preserving_select + 1,
DBOP_project_order_preserving= DBOP_project + 1,
DBOP_top= DBOP_project_order_preserving + 1,
DBOP_top_percent= DBOP_top + 1,
DBOP_top_plus_ties= DBOP_top_percent + 1,
DBOP_top_percent_plus_ties= DBOP_top_plus_ties + 1,
DBOP_rank= DBOP_top_percent_plus_ties + 1,
DBOP_rank_ties_equally= DBOP_rank + 1,
DBOP_rank_ties_equally_and_skip= DBOP_rank_ties_equally + 1,
DBOP_navigate= DBOP_rank_ties_equally_and_skip + 1,
DBOP_nesting= DBOP_navigate + 1,
DBOP_unnesting= DBOP_nesting + 1,
DBOP_nested_apply= DBOP_unnesting + 1,
DBOP_cross_tab= DBOP_nested_apply + 1,
DBOP_is_NULL= DBOP_cross_tab + 1,
DBOP_is_NOT_NULL= DBOP_is_NULL + 1,
DBOP_equal= DBOP_is_NOT_NULL + 1,
DBOP_not_equal= DBOP_equal + 1,
DBOP_less= DBOP_not_equal + 1,
DBOP_less_equal= DBOP_less + 1,
DBOP_greater= DBOP_less_equal + 1,
DBOP_greater_equal= DBOP_greater + 1,
DBOP_equal_all= DBOP_greater_equal + 1,
DBOP_not_equal_all= DBOP_equal_all + 1,
DBOP_less_all= DBOP_not_equal_all + 1,
DBOP_less_equal_all= DBOP_less_all + 1,
DBOP_greater_all= DBOP_less_equal_all + 1,
DBOP_greater_equal_all= DBOP_greater_all + 1,
DBOP_equal_any= DBOP_greater_equal_all + 1,
DBOP_not_equal_any= DBOP_equal_any + 1,
DBOP_less_any= DBOP_not_equal_any + 1,
DBOP_less_equal_any= DBOP_less_any + 1,
DBOP_greater_any= DBOP_less_equal_any + 1,
DBOP_greater_equal_any= DBOP_greater_any + 1,
DBOP_anybits= DBOP_greater_equal_any + 1,
DBOP_allbits= DBOP_anybits + 1,
DBOP_anybits_any= DBOP_allbits + 1,
DBOP_allbits_any= DBOP_anybits_any + 1,
DBOP_anybits_all= DBOP_allbits_any + 1,
DBOP_allbits_all= DBOP_anybits_all + 1,
DBOP_between= DBOP_allbits_all + 1,
DBOP_between_unordered= DBOP_between + 1,
DBOP_match= DBOP_between_unordered + 1,
DBOP_match_unique= DBOP_match + 1,
DBOP_match_partial= DBOP_match_unique + 1,
DBOP_match_partial_unique= DBOP_match_partial + 1,
DBOP_match_full= DBOP_match_partial_unique + 1,
DBOP_match_full_unique= DBOP_match_full + 1,
DBOP_scalar_parameter= DBOP_match_full_unique + 1,
DBOP_scalar_function= DBOP_scalar_parameter + 1,
DBOP_plus= DBOP_scalar_function + 1,
DBOP_minus= DBOP_plus + 1,
DBOP_times= DBOP_minus + 1,
DBOP_over= DBOP_times + 1,
DBOP_div= DBOP_over + 1,
DBOP_modulo= DBOP_div + 1,
DBOP_power= DBOP_modulo + 1,
DBOP_like= DBOP_power + 1,
DBOP_sounds_like= DBOP_like + 1,
DBOP_like_any= DBOP_sounds_like + 1,
DBOP_like_all= DBOP_like_any + 1,
DBOP_is_INVALID= DBOP_like_all + 1,
DBOP_is_TRUE= DBOP_is_INVALID + 1,
DBOP_is_FALSE= DBOP_is_TRUE + 1,
DBOP_and= DBOP_is_FALSE + 1,
DBOP_or= DBOP_and + 1,
DBOP_xor= DBOP_or + 1,
DBOP_equivalent= DBOP_xor + 1,
DBOP_not= DBOP_equivalent + 1,
DBOP_implies= DBOP_not + 1,
DBOP_overlaps= DBOP_implies + 1,
DBOP_case_condition= DBOP_overlaps + 1,
DBOP_case_value= DBOP_case_condition + 1,
DBOP_nullif= DBOP_case_value + 1,
DBOP_cast= DBOP_nullif + 1,
DBOP_coalesce= DBOP_cast + 1,
DBOP_position= DBOP_coalesce + 1,
DBOP_extract= DBOP_position + 1,
DBOP_char_length= DBOP_extract + 1,
DBOP_octet_length= DBOP_char_length + 1,
DBOP_bit_length= DBOP_octet_length + 1,
DBOP_substring= DBOP_bit_length + 1,
DBOP_upper= DBOP_substring + 1,
DBOP_lower= DBOP_upper + 1,
DBOP_trim= DBOP_lower + 1,
DBOP_translate= DBOP_trim + 1,
DBOP_convert= DBOP_translate + 1,
DBOP_string_concat= DBOP_convert + 1,
DBOP_current_date= DBOP_string_concat + 1,
DBOP_current_time= DBOP_current_date + 1,
DBOP_current_timestamp= DBOP_current_time + 1,
DBOP_content_select= DBOP_current_timestamp + 1,
DBOP_content= DBOP_content_select + 1,
DBOP_content_freetext= DBOP_content + 1,
DBOP_content_proximity= DBOP_content_freetext + 1,
DBOP_content_vector_or= DBOP_content_proximity + 1,
DBOP_delete= DBOP_content_vector_or + 1,
DBOP_update= DBOP_delete + 1,
DBOP_insert= DBOP_update + 1,
DBOP_min= DBOP_insert + 1,
DBOP_max= DBOP_min + 1,
DBOP_count= DBOP_max + 1,
DBOP_sum= DBOP_count + 1,
DBOP_avg= DBOP_sum + 1,
DBOP_any_sample= DBOP_avg + 1,
DBOP_stddev= DBOP_any_sample + 1,
DBOP_stddev_pop= DBOP_stddev + 1,
DBOP_var= DBOP_stddev_pop + 1,
DBOP_var_pop= DBOP_var + 1,
DBOP_first= DBOP_var_pop + 1,
DBOP_last= DBOP_first + 1,
DBOP_in= DBOP_last + 1,
DBOP_exists= DBOP_in + 1,
DBOP_unique= DBOP_exists + 1,
DBOP_subset= DBOP_unique + 1,
DBOP_proper_subset= DBOP_subset + 1,
DBOP_superset= DBOP_proper_subset + 1,
DBOP_proper_superset= DBOP_superset + 1,
DBOP_disjoint= DBOP_proper_superset + 1,
DBOP_pass_through= DBOP_disjoint + 1,
DBOP_defined_by_GUID= DBOP_pass_through + 1,
DBOP_text_command= DBOP_defined_by_GUID + 1,
DBOP_SQL_select= DBOP_text_command + 1,
DBOP_prior_command_tree= DBOP_SQL_select + 1,
DBOP_add_columns= DBOP_prior_command_tree + 1,
DBOP_column_list_anchor= DBOP_add_columns + 1,
DBOP_column_list_element= DBOP_column_list_anchor + 1,
DBOP_command_list_anchor= DBOP_column_list_element + 1,
DBOP_command_list_element= DBOP_command_list_anchor + 1,
DBOP_from_list_anchor= DBOP_command_list_element + 1,
DBOP_from_list_element= DBOP_from_list_anchor + 1,
DBOP_project_list_anchor= DBOP_from_list_element + 1,
DBOP_project_list_element= DBOP_project_list_anchor + 1,
DBOP_row_list_anchor= DBOP_project_list_element + 1,
DBOP_row_list_element= DBOP_row_list_anchor + 1,
DBOP_scalar_list_anchor= DBOP_row_list_element + 1,
DBOP_scalar_list_element= DBOP_scalar_list_anchor + 1,
DBOP_set_list_anchor= DBOP_scalar_list_element + 1,
DBOP_set_list_element= DBOP_set_list_anchor + 1,
DBOP_sort_list_anchor= DBOP_set_list_element + 1,
DBOP_sort_list_element= DBOP_sort_list_anchor + 1,
DBOP_alter_character_set= DBOP_sort_list_element + 1,
DBOP_alter_collation= DBOP_alter_character_set + 1,
DBOP_alter_domain= DBOP_alter_collation + 1,
DBOP_alter_index= DBOP_alter_domain + 1,
DBOP_alter_procedure= DBOP_alter_index + 1,
DBOP_alter_schema= DBOP_alter_procedure + 1,
DBOP_alter_table= DBOP_alter_schema + 1,
DBOP_alter_trigger= DBOP_alter_table + 1,
DBOP_alter_view= DBOP_alter_trigger + 1,
DBOP_coldef_list_anchor= DBOP_alter_view + 1,
DBOP_coldef_list_element= DBOP_coldef_list_anchor + 1,
DBOP_create_assertion= DBOP_coldef_list_element + 1,
DBOP_create_character_set= DBOP_create_assertion + 1,
DBOP_create_collation= DBOP_create_character_set + 1,
DBOP_create_domain= DBOP_create_collation + 1,
DBOP_create_index= DBOP_create_domain + 1,
DBOP_create_procedure= DBOP_create_index + 1,
DBOP_create_schema= DBOP_create_procedure + 1,
DBOP_create_synonym= DBOP_create_schema + 1,
DBOP_create_table= DBOP_create_synonym + 1,
DBOP_create_temporary_table= DBOP_create_table + 1,
DBOP_create_translation= DBOP_create_temporary_table + 1,
DBOP_create_trigger= DBOP_create_translation + 1,
DBOP_create_view= DBOP_create_trigger + 1,
DBOP_drop_assertion= DBOP_create_view + 1,
DBOP_drop_character_set= DBOP_drop_assertion + 1,
DBOP_drop_collation= DBOP_drop_character_set + 1,
DBOP_drop_domain= DBOP_drop_collation + 1,
DBOP_drop_index= DBOP_drop_domain + 1,
DBOP_drop_procedure= DBOP_drop_index + 1,
DBOP_drop_schema= DBOP_drop_procedure + 1,
DBOP_drop_synonym= DBOP_drop_schema + 1,
DBOP_drop_table= DBOP_drop_synonym + 1,
DBOP_drop_translation= DBOP_drop_table + 1,
DBOP_drop_trigger= DBOP_drop_translation + 1,
DBOP_drop_view= DBOP_drop_trigger + 1,
DBOP_foreign_key= DBOP_drop_view + 1,
DBOP_grant_privileges= DBOP_foreign_key + 1,
DBOP_index_list_anchor= DBOP_grant_privileges + 1,
DBOP_index_list_element= DBOP_index_list_anchor + 1,
DBOP_primary_key= DBOP_index_list_element + 1,
DBOP_property_list_anchor= DBOP_primary_key + 1,
DBOP_property_list_element= DBOP_property_list_anchor + 1,
DBOP_referenced_table= DBOP_property_list_element + 1,
DBOP_rename_object= DBOP_referenced_table + 1,
DBOP_revoke_privileges= DBOP_rename_object + 1,
DBOP_schema_authorization= DBOP_revoke_privileges + 1,
DBOP_unique_key= DBOP_schema_authorization + 1
    };
#endif // OLEDBVER >= 0x0200
//@@@- V2.0
typedef struct  tagDBPARAMS
    {
    void __RPC_FAR *pData;
    ULONG cParamSets;
    HACCESSOR hAccessor;
    }DBPARAMS;

typedef DWORD DBPARAMFLAGS;

enum DBPARAMFLAGSENUM
    {DBPARAMFLAGS_ISINPUT= 0x1,
DBPARAMFLAGS_ISOUTPUT= 0x2,
DBPARAMFLAGS_ISSIGNED= 0x10,
DBPARAMFLAGS_ISNULLABLE= 0x40,
DBPARAMFLAGS_ISLONG= 0x80
    };
typedef struct  tagDBPARAMINFO
    {
    DBPARAMFLAGS dwFlags;
    ULONG iOrdinal;
    LPOLESTR pwszName;
    ITypeInfo __RPC_FAR *pTypeInfo;
    ULONG ulParamSize;
    DBTYPE wType;
    BYTE bPrecision;
    BYTE bScale;
    }DBPARAMINFO;

typedef DWORD DBPROPID;

typedef struct  tagDBPROPIDSET
    {
    /* [size_is] */ DBPROPID __RPC_FAR *rgPropertyIDs;
    ULONG cPropertyIDs;
    GUID guidPropertySet;
    }DBPROPIDSET;

typedef DWORD DBPROPFLAGS;

enum DBPROPFLAGSENUM
    {DBPROPFLAGS_NOTSUPPORTED= 0,
DBPROPFLAGS_COLUMN= 0x1,
DBPROPFLAGS_DATASOURCE= 0x2,
DBPROPFLAGS_DATASOURCECREATE= 0x4,
DBPROPFLAGS_DATASOURCEINFO= 0x8,
DBPROPFLAGS_DBINIT= 0x10,
DBPROPFLAGS_INDEX= 0x20,
DBPROPFLAGS_ROWSET= 0x40,
DBPROPFLAGS_TABLE= 0x80,
DBPROPFLAGS_COLUMNOK= 0x100,
DBPROPFLAGS_READ= 0x200,
DBPROPFLAGS_WRITE= 0x400,
DBPROPFLAGS_REQUIRED= 0x800,
DBPROPFLAGS_SESSION= 0x1000
    };
typedef struct  tagDBPROPINFO
    {
    LPOLESTR pwszDescription;
    DBPROPID dwPropertyID;
    DBPROPFLAGS dwFlags;
    VARTYPE vtType;
    VARIANT vValues;
    }DBPROPINFO;

typedef struct  tagDBPROPINFOSET
    {
    /* [size_is] */ DBPROPINFO __RPC_FAR *rgPropertyInfos;
    ULONG cPropertyInfos;
    GUID guidPropertySet;
    }DBPROPINFOSET;

typedef DWORD DBPROPOPTIONS;

// DBPROPOPTIONS_SETIFCHEAP is deprecated; use DBPROPOPTIONS_OPTIONAL instead.

enum DBPROPOPTIONSENUM
    {DBPROPOPTIONS_REQUIRED= 0,
DBPROPOPTIONS_SETIFCHEAP= 0x1,
DBPROPOPTIONS_OPTIONAL= 0x1
    };
typedef DWORD DBPROPSTATUS;

enum DBPROPSTATUSENUM
    {DBPROPSTATUS_OK= 0,
DBPROPSTATUS_NOTSUPPORTED= 1,
DBPROPSTATUS_BADVALUE= 2,
DBPROPSTATUS_BADOPTION= 3,
DBPROPSTATUS_BADCOLUMN= 4,
DBPROPSTATUS_NOTALLSETTABLE= 5,
DBPROPSTATUS_NOTSETTABLE= 6,
DBPROPSTATUS_NOTSET= 7,
DBPROPSTATUS_CONFLICTING= 8
    };
typedef struct  tagDBPROP
    {
    DBPROPID dwPropertyID;
    DBPROPOPTIONS dwOptions;
    DBPROPSTATUS dwStatus;
    DBID colid;
    VARIANT vValue;
    }DBPROP;

typedef struct  tagDBPROPSET
    {
    /* [size_is] */ DBPROP __RPC_FAR *rgProperties;
    ULONG cProperties;
    GUID guidPropertySet;
    }DBPROPSET;

#define DBPARAMTYPE_INPUT0x01
#define DBPARAMTYPE_INPUTOUTPUT0x02
#define DBPARAMTYPE_OUTPUT0x03
#define DBPARAMTYPE_RETURNVALUE0x04
#define DB_PT_UNKNOWN0x01
#define DB_PT_PROCEDURE0x02
#define DB_PT_FUNCTION0x03
#define DB_REMOTE0x01
#define DB_LOCAL_SHARED0x02
#define DB_LOCAL_EXCLUSIVE0x03
#define DB_COLLATION_ASC0x01
#define DB_COLLATION_DESC0x02
#define DB_UNSEARCHABLE0x01
#define DB_LIKE_ONLY0x02
#define DB_ALL_EXCEPT_LIKE0x03
#define DB_SEARCHABLE0x04
//@@@+ V2.0
#if( OLEDBVER >= 0x0200 )
#define MDTREEOP_CHILDREN            0x01
#define MDTREEOP_SIBLINGS            0x02
#define MDTREEOP_PARENT              0x04
#define MDTREEOP_SELD                0x08
#define MD_DIMTYPE_UNKNOWN           0x00
#define MD_DIMTYPE_TIME              0x01
#define MD_DIMTYPE_MEASURE           0x02
#define MD_DIMTYPE_OTHER             0x03
#define LEVEL_TYPE_UNKNOWN           0x0000
#define LEVEL_TYPE_REGULAR           0x0000
#define LEVEL_TYPE_ALL               0x0001
#define LEVEL_TYPE_CALCULATED        0x0002
#define LEVEL_TYPE_TIME              0x0004
#define LEVEL_TYPE_RESERVED1         0x0008
#define LEVEL_TYPE_TIME_YEARS        0x0014
#define LEVEL_TYPE_TIME_HALF_YEAR    0x0024
#define LEVEL_TYPE_TIME_QUARTERS     0x0044
#define LEVEL_TYPE_TIME_MONTHS       0x0084
#define LEVEL_TYPE_TIME_WEEKS        0x0104
#define LEVEL_TYPE_TIME_DAYS         0x0204
#define LEVEL_TYPE_TIME_HOURS        0x0304
#define LEVEL_TYPE_TIME_MINUTES      0x0404
#define LEVEL_TYPE_TIME_SECONDS      0x0804
#define LEVEL_TYPE_TIME_UNDEFINED    0x1004
#define MEASURE_AGGR_UNKNOWN         0x00
#define MEASURE_AGGR_SUM             0x01
#define MEASURE_AGGR_COUNT           0x02
#define MEASURE_AGGR_MIN             0x03
#define MEASURE_AGGR_MAX             0x04
#define MEASURE_AGGR_AVG             0x05
#define MEASURE_AGGR_VAR             0x06
#define MEASURE_AGGR_STD             0x07
#define MEASURE_AGGR_CALCULATED      0x7f
#define MDPROP_MEMBER                0x01
#define MDPROP_CELL                  0x02
#define MEMBER_TYPE_UNKNOWN          0x00
#define MEMBER_TYPE_REGULAR          0x01
#define MEMBER_TYPE_ALL              0x02
#define MEMBER_TYPE_MEASURE          0x03
#define MEMBER_TYPE_FORMULA          0x04
#define MEMBER_TYPE_RESERVE1         0x05
#define MEMBER_TYPE_RESERVE2         0x06
#define MEMBER_TYPE_RESERVE3         0x07
#define MEMBER_TYPE_RESERVE4         0x08
#endif // OLEDBVER >= 0x0200
//@@@- V2.0
typedef DWORD DBINDEX_COL_ORDER;

enum DBINDEX_COL_ORDERENUM
    {DBINDEX_COL_ORDER_ASC= 0,
DBINDEX_COL_ORDER_DESC= DBINDEX_COL_ORDER_ASC + 1
    };
typedef struct  tagDBINDEXCOLUMNDESC
    {
    DBID __RPC_FAR *pColumnID;
    DBINDEX_COL_ORDER eIndexColOrder;
    }DBINDEXCOLUMNDESC;

typedef struct  tagDBCOLUMNDESC
    {
    LPOLESTR pwszTypeName;
    ITypeInfo __RPC_FAR *pTypeInfo;
    /* [size_is] */ DBPROPSET __RPC_FAR *rgPropertySets;
    CLSID __RPC_FAR *pclsid;
    ULONG cPropertySets;
    ULONG ulColumnSize;
    DBID dbcid;
    DBTYPE wType;
    BYTE bPrecision;
    BYTE bScale;
    }DBCOLUMNDESC;

typedef DWORD DBEVENTPHASE;

enum DBEVENTPHASEENUM
    {DBEVENTPHASE_OKTODO= 0,
DBEVENTPHASE_ABOUTTODO= DBEVENTPHASE_OKTODO + 1,
DBEVENTPHASE_SYNCHAFTER= DBEVENTPHASE_ABOUTTODO + 1,
DBEVENTPHASE_FAILEDTODO= DBEVENTPHASE_SYNCHAFTER + 1,
DBEVENTPHASE_DIDEVENT= DBEVENTPHASE_FAILEDTODO + 1
    };
typedef DWORD DBREASON;

enum DBREASONENUM
    {DBREASON_ROWSET_FETCHPOSITIONCHANGE= 0,
DBREASON_ROWSET_RELEASE= DBREASON_ROWSET_FETCHPOSITIONCHANGE + 1,
DBREASON_COLUMN_SET= DBREASON_ROWSET_RELEASE + 1,
DBREASON_COLUMN_RECALCULATED= DBREASON_COLUMN_SET + 1,
DBREASON_ROW_ACTIVATE= DBREASON_COLUMN_RECALCULATED + 1,
DBREASON_ROW_RELEASE= DBREASON_ROW_ACTIVATE + 1,
DBREASON_ROW_DELETE= DBREASON_ROW_RELEASE + 1,
DBREASON_ROW_FIRSTCHANGE= DBREASON_ROW_DELETE + 1,
DBREASON_ROW_INSERT= DBREASON_ROW_FIRSTCHANGE + 1,
DBREASON_ROW_RESYNCH= DBREASON_ROW_INSERT + 1,
DBREASON_ROW_UNDOCHANGE= DBREASON_ROW_RESYNCH + 1,
DBREASON_ROW_UNDOINSERT= DBREASON_ROW_UNDOCHANGE + 1,
DBREASON_ROW_UNDODELETE= DBREASON_ROW_UNDOINSERT + 1,
DBREASON_ROW_UPDATE= DBREASON_ROW_UNDODELETE + 1,
DBREASON_ROWSET_CHANGED= DBREASON_ROW_UPDATE + 1
    };
//@@@+ V1.5
#if( OLEDBVER >= 0x0150 )

enum DBREASONENUM15
    {DBREASON_ROWPOSITION_CHANGED= DBREASON_ROWSET_CHANGED + 1,
DBREASON_ROWPOSITION_CHAPTERCHANGED= DBREASON_ROWPOSITION_CHANGED + 1,
DBREASON_ROWPOSITION_CLEARED= DBREASON_ROWPOSITION_CHAPTERCHANGED + 1,
DBREASON_ROW_ASYNCHINSERT= DBREASON_ROWPOSITION_CLEARED + 1
    };
#endif // OLEDBVER >= 0x0150
//@@@- V1.5
//@@@+ V2.0
#if( OLEDBVER >= 0x0200 )

enum DBREASONENUM20
    {DBREASON_ROWSET_ROWSADDED= DBREASON_ROW_ASYNCHINSERT + 1,
DBREASON_ROWSET_POPULATIONCOMPLETE= DBREASON_ROWSET_ROWSADDED + 1,
DBREASON_ROWSET_POPULATIONSTOPPED= DBREASON_ROWSET_POPULATIONCOMPLETE + 1
    };
#endif // OLEDBVER >= 0x0200
//@@@- V2.0
//@@@+ V1.5
#if( OLEDBVER >= 0x0150 )
typedef DWORD DBCOMPAREOP;

enum DBCOMPAREOPSENUM
    {DBCOMPAREOPS_LT= 0,
DBCOMPAREOPS_LE= DBCOMPAREOPS_LT + 1,
DBCOMPAREOPS_EQ= DBCOMPAREOPS_LE + 1,
DBCOMPAREOPS_GE= DBCOMPAREOPS_EQ + 1,
DBCOMPAREOPS_GT= DBCOMPAREOPS_GE + 1,
DBCOMPAREOPS_BEGINSWITH= DBCOMPAREOPS_GT + 1,
DBCOMPAREOPS_CONTAINS= DBCOMPAREOPS_BEGINSWITH + 1,
DBCOMPAREOPS_NE= DBCOMPAREOPS_CONTAINS + 1,
DBCOMPAREOPS_IGNORE= DBCOMPAREOPS_NE + 1,
DBCOMPAREOPS_CASESENSITIVE= 0x1000,
DBCOMPAREOPS_CASEINSENSITIVE= 0x2000
    };

enum DBASYNCHOPENUM
    {DBASYNCHOP_OPEN= 0
    };

enum DBASYNCHPHASEENUM
    {DBASYNCHPHASE_INITIALIZATION= 0,
DBASYNCHPHASE_POPULATION= DBASYNCHPHASE_INITIALIZATION + 1,
DBASYNCHPHASE_COMPLETE= DBASYNCHPHASE_POPULATION + 1,
DBASYNCHPHASE_CANCELED= DBASYNCHPHASE_COMPLETE + 1
    };
#define DB_COUNTUNAVAILABLE -1
#endif // OLEDBVER >= 0x0150
//@@@- V1.5
typedef DWORD DBSORT;

enum DBSORTENUM
    {DBSORT_ASCENDING= 0,
DBSORT_DESCENDING= DBSORT_ASCENDING + 1
    };
//@@@+ V2.0
#if( OLEDBVER >= 0x0200 )
#define MDFF_BOLD                    0x01
#define MDFF_ITALIC                  0x02
#define MDFF_UNDERLINE               0x04
#define MDFF_STRIKEOUT               0x08
typedef struct  tagMDAXISINFO
    {
    ULONG cbAxis;
    ULONG iAxis;
    ULONG cDimensions;
    ULONG cCoordinates;
    ULONG __RPC_FAR *rgcColumns;
    LPOLESTR __RPC_FAR *rgpwszDimensionNames;
    }MDAXISINFO;

#define PMDAXISINFO_GETAT(rgAxisInfo, iAxis) ((MDAXISINFO *)(((BYTE *)rgAxisInfo) + iAxis * rgAxisInfo[0].cbSize))
#define MDAXISINFO_GETAT(rgAxisInfo, iAxis) (*PMDAXISINFO_GETAT(rgAxisInfo, iAxis))
#define MDAXIS_COLUMNS               0x00000000
#define MDAXIS_ROWS                  0x00000001
#define MDAXIS_PAGES                 0x00000002
#define MDAXIS_SECTIONS              0x00000003
#define MDAXIS_CHAPTERS              0x00000004
#define MDAXIS_SLICERS               0xffffffff
#endif // OLEDBVER >= 0x0200
//@@@- V2.0

extern RPC_IF_HANDLE DBStructureDefinitions_v0_0_c_ifspec;
extern RPC_IF_HANDLE DBStructureDefinitions_v0_0_s_ifspec;
#endif /* __DBStructureDefinitions_INTERFACE_DEFINED__ */

#ifndef __IAccessor_INTERFACE_DEFINED__
#define __IAccessor_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IAccessor
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object] */

typedef DWORD DBACCESSORFLAGS;

enum DBACCESSORFLAGSENUM
    {DBACCESSOR_INVALID= 0,
DBACCESSOR_PASSBYREF= 0x1,
DBACCESSOR_ROWDATA= 0x2,
DBACCESSOR_PARAMETERDATA= 0x4,
DBACCESSOR_OPTIMIZED= 0x8
    };
typedef DWORD DBBINDSTATUS;

enum DBBINDSTATUSENUM
    {DBBINDSTATUS_OK= 0,
DBBINDSTATUS_BADORDINAL= 1,
DBBINDSTATUS_UNSUPPORTEDCONVERSION= 2,
DBBINDSTATUS_BADBINDINFO= 3,
DBBINDSTATUS_BADSTORAGEFLAGS= 4,
DBBINDSTATUS_NOINTERFACE= 5,
DBBINDSTATUS_MULTIPLESTORAGE= 6
    };

EXTERN_C const IID IID_IAccessor;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a8c-2a1c-11ce-ade5-00aa0044773d")
    IAccessor : public IUnknown
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE AddRefAccessor(
            /* [in] */ HACCESSOR hAccessor,
            /* [unique][out][in] */ ULONG __RPC_FAR *pcRefCount) = 0;

        virtual /* [local] */ HRESULT STDMETHODCALLTYPE CreateAccessor(
            /* [in] */ DBACCESSORFLAGS dwAccessorFlags,
            /* [in] */ ULONG cBindings,
            /* [size_is][in] */ const DBBINDING __RPC_FAR rgBindings[  ],
            /* [in] */ ULONG cbRowSize,
            /* [out] */ HACCESSOR __RPC_FAR *phAccessor,
            /* [size_is][out] */ DBBINDSTATUS __RPC_FAR rgStatus[  ]) = 0;

        virtual /* [local] */ HRESULT STDMETHODCALLTYPE GetBindings(
            /* [in] */ HACCESSOR hAccessor,
            /* [out] */ DBACCESSORFLAGS __RPC_FAR *pdwAccessorFlags,
            /* [out][in] */ ULONG __RPC_FAR *pcBindings,
            /* [size_is][size_is][out] */ DBBINDING __RPC_FAR *__RPC_FAR *prgBindings) = 0;

        virtual HRESULT STDMETHODCALLTYPE ReleaseAccessor(
            /* [in] */ HACCESSOR hAccessor,
            /* [unique][out][in] */ ULONG __RPC_FAR *pcRefCount) = 0;

    };

#else /* C style interface */

    typedef struct IAccessorVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IAccessor __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IAccessor __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IAccessor __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *AddRefAccessor )(
            IAccessor __RPC_FAR * This,
            /* [in] */ HACCESSOR hAccessor,
            /* [unique][out][in] */ ULONG __RPC_FAR *pcRefCount);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *CreateAccessor )(
            IAccessor __RPC_FAR * This,
            /* [in] */ DBACCESSORFLAGS dwAccessorFlags,
            /* [in] */ ULONG cBindings,
            /* [size_is][in] */ const DBBINDING __RPC_FAR rgBindings[  ],
            /* [in] */ ULONG cbRowSize,
            /* [out] */ HACCESSOR __RPC_FAR *phAccessor,
            /* [size_is][out] */ DBBINDSTATUS __RPC_FAR rgStatus[  ]);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetBindings )(
            IAccessor __RPC_FAR * This,
            /* [in] */ HACCESSOR hAccessor,
            /* [out] */ DBACCESSORFLAGS __RPC_FAR *pdwAccessorFlags,
            /* [out][in] */ ULONG __RPC_FAR *pcBindings,
            /* [size_is][size_is][out] */ DBBINDING __RPC_FAR *__RPC_FAR *prgBindings);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *ReleaseAccessor )(
            IAccessor __RPC_FAR * This,
            /* [in] */ HACCESSOR hAccessor,
            /* [unique][out][in] */ ULONG __RPC_FAR *pcRefCount);

        END_INTERFACE
    } IAccessorVtbl;

    interface IAccessor
    {
        CONST_VTBL struct IAccessorVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IAccessor_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IAccessor_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IAccessor_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IAccessor_AddRefAccessor(This,hAccessor,pcRefCount)\
    (This)->lpVtbl -> AddRefAccessor(This,hAccessor,pcRefCount)

#define IAccessor_CreateAccessor(This,dwAccessorFlags,cBindings,rgBindings,cbRowSize,phAccessor,rgStatus)\
    (This)->lpVtbl -> CreateAccessor(This,dwAccessorFlags,cBindings,rgBindings,cbRowSize,phAccessor,rgStatus)

#define IAccessor_GetBindings(This,hAccessor,pdwAccessorFlags,pcBindings,prgBindings)\
    (This)->lpVtbl -> GetBindings(This,hAccessor,pdwAccessorFlags,pcBindings,prgBindings)

#define IAccessor_ReleaseAccessor(This,hAccessor,pcRefCount)\
    (This)->lpVtbl -> ReleaseAccessor(This,hAccessor,pcRefCount)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE IAccessor_AddRefAccessor_Proxy(
    IAccessor __RPC_FAR * This,
    /* [in] */ HACCESSOR hAccessor,
    /* [unique][out][in] */ ULONG __RPC_FAR *pcRefCount);

void __RPC_STUB IAccessor_AddRefAccessor_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

/* [call_as] */ HRESULT STDMETHODCALLTYPE IAccessor_RemoteCreateAccessor_Proxy(
    IAccessor __RPC_FAR * This,
    /* [in] */ DBACCESSORFLAGS dwAccessorFlags,
    /* [in] */ ULONG cBindings,
    /* [size_is][unique][in] */ DBBINDING __RPC_FAR *rgBindings,
    /* [in] */ ULONG cbRowSize,
    /* [out] */ HACCESSOR __RPC_FAR *phAccessor,
    /* [size_is][unique][out][in] */ DBBINDSTATUS __RPC_FAR *rgStatus);

void __RPC_STUB IAccessor_RemoteCreateAccessor_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

/* [call_as] */ HRESULT STDMETHODCALLTYPE IAccessor_RemoteGetBindings_Proxy(
    IAccessor __RPC_FAR * This,
    /* [in] */ HACCESSOR hAccessor,
    /* [out] */ DBACCESSORFLAGS __RPC_FAR *pdwAccessorFlags,
    /* [out][in] */ ULONG __RPC_FAR *pcBindings,
    /* [size_is][size_is][out] */ DBBINDING __RPC_FAR *__RPC_FAR *prgBindings);

void __RPC_STUB IAccessor_RemoteGetBindings_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IAccessor_ReleaseAccessor_Proxy(
    IAccessor __RPC_FAR * This,
    /* [in] */ HACCESSOR hAccessor,
    /* [unique][out][in] */ ULONG __RPC_FAR *pcRefCount);

void __RPC_STUB IAccessor_ReleaseAccessor_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IAccessor_INTERFACE_DEFINED__ */

#ifndef __IRowset_INTERFACE_DEFINED__
#define __IRowset_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IRowset
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object][local] */

typedef DWORD DBROWOPTIONS;

EXTERN_C const IID IID_IRowset;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a7c-2a1c-11ce-ade5-00aa0044773d")
    IRowset : public IUnknown
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE AddRefRows(
            /* [in] */ ULONG cRows,
            /* [size_is][in] */ const HROW __RPC_FAR rghRows[  ],
            /* [size_is][out] */ ULONG __RPC_FAR rgRefCounts[  ],
            /* [size_is][out] */ DBROWSTATUS __RPC_FAR rgRowStatus[  ]) = 0;

        virtual HRESULT STDMETHODCALLTYPE GetData(
            /* [in] */ HROW hRow,
            /* [in] */ HACCESSOR hAccessor,
            /* [out] */ void __RPC_FAR *pData) = 0;

        virtual HRESULT STDMETHODCALLTYPE GetNextRows(
            /* [in] */ HCHAPTER hReserved,
            /* [in] */ LONG lRowsOffset,
            /* [in] */ LONG cRows,
            /* [out] */ ULONG __RPC_FAR *pcRowsObtained,
            /* [size_is][size_is][out] */ HROW __RPC_FAR *__RPC_FAR *prghRows) = 0;

        virtual HRESULT STDMETHODCALLTYPE ReleaseRows(
            /* [in] */ ULONG cRows,
            /* [size_is][in] */ const HROW __RPC_FAR rghRows[  ],
            /* [size_is][in] */ DBROWOPTIONS __RPC_FAR rgRowOptions[  ],
            /* [size_is][out] */ ULONG __RPC_FAR rgRefCounts[  ],
            /* [size_is][out] */ DBROWSTATUS __RPC_FAR rgRowStatus[  ]) = 0;

        virtual HRESULT STDMETHODCALLTYPE RestartPosition(
            /* [in] */ HCHAPTER hReserved) = 0;

    };

#else /* C style interface */

    typedef struct IRowsetVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IRowset __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IRowset __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IRowset __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *AddRefRows )(
            IRowset __RPC_FAR * This,
            /* [in] */ ULONG cRows,
            /* [size_is][in] */ const HROW __RPC_FAR rghRows[  ],
            /* [size_is][out] */ ULONG __RPC_FAR rgRefCounts[  ],
            /* [size_is][out] */ DBROWSTATUS __RPC_FAR rgRowStatus[  ]);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetData )(
            IRowset __RPC_FAR * This,
            /* [in] */ HROW hRow,
            /* [in] */ HACCESSOR hAccessor,
            /* [out] */ void __RPC_FAR *pData);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetNextRows )(
            IRowset __RPC_FAR * This,
            /* [in] */ HCHAPTER hReserved,
            /* [in] */ LONG lRowsOffset,
            /* [in] */ LONG cRows,
            /* [out] */ ULONG __RPC_FAR *pcRowsObtained,
            /* [size_is][size_is][out] */ HROW __RPC_FAR *__RPC_FAR *prghRows);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *ReleaseRows )(
            IRowset __RPC_FAR * This,
            /* [in] */ ULONG cRows,
            /* [size_is][in] */ const HROW __RPC_FAR rghRows[  ],
            /* [size_is][in] */ DBROWOPTIONS __RPC_FAR rgRowOptions[  ],
            /* [size_is][out] */ ULONG __RPC_FAR rgRefCounts[  ],
            /* [size_is][out] */ DBROWSTATUS __RPC_FAR rgRowStatus[  ]);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RestartPosition )(
            IRowset __RPC_FAR * This,
            /* [in] */ HCHAPTER hReserved);

        END_INTERFACE
    } IRowsetVtbl;

    interface IRowset
    {
        CONST_VTBL struct IRowsetVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IRowset_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IRowset_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IRowset_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IRowset_AddRefRows(This,cRows,rghRows,rgRefCounts,rgRowStatus)\
    (This)->lpVtbl -> AddRefRows(This,cRows,rghRows,rgRefCounts,rgRowStatus)

#define IRowset_GetData(This,hRow,hAccessor,pData)\
    (This)->lpVtbl -> GetData(This,hRow,hAccessor,pData)

#define IRowset_GetNextRows(This,hReserved,lRowsOffset,cRows,pcRowsObtained,prghRows)\
    (This)->lpVtbl -> GetNextRows(This,hReserved,lRowsOffset,cRows,pcRowsObtained,prghRows)

#define IRowset_ReleaseRows(This,cRows,rghRows,rgRowOptions,rgRefCounts,rgRowStatus)\
    (This)->lpVtbl -> ReleaseRows(This,cRows,rghRows,rgRowOptions,rgRefCounts,rgRowStatus)

#define IRowset_RestartPosition(This,hReserved)\
    (This)->lpVtbl -> RestartPosition(This,hReserved)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE IRowset_AddRefRows_Proxy(
    IRowset __RPC_FAR * This,
    /* [in] */ ULONG cRows,
    /* [size_is][in] */ const HROW __RPC_FAR rghRows[  ],
    /* [size_is][out] */ ULONG __RPC_FAR rgRefCounts[  ],
    /* [size_is][out] */ DBROWSTATUS __RPC_FAR rgRowStatus[  ]);

void __RPC_STUB IRowset_AddRefRows_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IRowset_GetData_Proxy(
    IRowset __RPC_FAR * This,
    /* [in] */ HROW hRow,
    /* [in] */ HACCESSOR hAccessor,
    /* [out] */ void __RPC_FAR *pData);

void __RPC_STUB IRowset_GetData_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IRowset_GetNextRows_Proxy(
    IRowset __RPC_FAR * This,
    /* [in] */ HCHAPTER hReserved,
    /* [in] */ LONG lRowsOffset,
    /* [in] */ LONG cRows,
    /* [out] */ ULONG __RPC_FAR *pcRowsObtained,
    /* [size_is][size_is][out] */ HROW __RPC_FAR *__RPC_FAR *prghRows);

void __RPC_STUB IRowset_GetNextRows_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IRowset_ReleaseRows_Proxy(
    IRowset __RPC_FAR * This,
    /* [in] */ ULONG cRows,
    /* [size_is][in] */ const HROW __RPC_FAR rghRows[  ],
    /* [size_is][in] */ DBROWOPTIONS __RPC_FAR rgRowOptions[  ],
    /* [size_is][out] */ ULONG __RPC_FAR rgRefCounts[  ],
    /* [size_is][out] */ DBROWSTATUS __RPC_FAR rgRowStatus[  ]);

void __RPC_STUB IRowset_ReleaseRows_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IRowset_RestartPosition_Proxy(
    IRowset __RPC_FAR * This,
    /* [in] */ HCHAPTER hReserved);

void __RPC_STUB IRowset_RestartPosition_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IRowset_INTERFACE_DEFINED__ */

#ifndef __IRowsetInfo_INTERFACE_DEFINED__
#define __IRowsetInfo_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IRowsetInfo
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object] */

EXTERN_C const IID IID_IRowsetInfo;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a55-2a1c-11ce-ade5-00aa0044773d")
    IRowsetInfo : public IUnknown
    {
    public:
        virtual /* [local] */ HRESULT STDMETHODCALLTYPE GetProperties(
            /* [in] */ const ULONG cPropertyIDSets,
            /* [size_is][in] */ const DBPROPIDSET __RPC_FAR rgPropertyIDSets[  ],
            /* [out][in] */ ULONG __RPC_FAR *pcPropertySets,
            /* [size_is][size_is][out] */ DBPROPSET __RPC_FAR *__RPC_FAR *prgPropertySets) = 0;

        virtual /* [local] */ HRESULT STDMETHODCALLTYPE GetReferencedRowset(
            /* [in] */ ULONG iOrdinal,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppReferencedRowset) = 0;

        virtual /* [local] */ HRESULT STDMETHODCALLTYPE GetSpecification(
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppSpecification) = 0;

    };

#else /* C style interface */

    typedef struct IRowsetInfoVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IRowsetInfo __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IRowsetInfo __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IRowsetInfo __RPC_FAR * This);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetProperties )(
            IRowsetInfo __RPC_FAR * This,
            /* [in] */ const ULONG cPropertyIDSets,
            /* [size_is][in] */ const DBPROPIDSET __RPC_FAR rgPropertyIDSets[  ],
            /* [out][in] */ ULONG __RPC_FAR *pcPropertySets,
            /* [size_is][size_is][out] */ DBPROPSET __RPC_FAR *__RPC_FAR *prgPropertySets);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetReferencedRowset )(
            IRowsetInfo __RPC_FAR * This,
            /* [in] */ ULONG iOrdinal,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppReferencedRowset);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetSpecification )(
            IRowsetInfo __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppSpecification);

        END_INTERFACE
    } IRowsetInfoVtbl;

    interface IRowsetInfo
    {
        CONST_VTBL struct IRowsetInfoVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IRowsetInfo_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IRowsetInfo_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IRowsetInfo_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IRowsetInfo_GetProperties(This,cPropertyIDSets,rgPropertyIDSets,pcPropertySets,prgPropertySets)\
    (This)->lpVtbl -> GetProperties(This,cPropertyIDSets,rgPropertyIDSets,pcPropertySets,prgPropertySets)

#define IRowsetInfo_GetReferencedRowset(This,iOrdinal,riid,ppReferencedRowset)\
    (This)->lpVtbl -> GetReferencedRowset(This,iOrdinal,riid,ppReferencedRowset)

#define IRowsetInfo_GetSpecification(This,riid,ppSpecification)\
    (This)->lpVtbl -> GetSpecification(This,riid,ppSpecification)

#endif /* COBJMACROS */

#endif /* C style interface */

/* [call_as] */ HRESULT STDMETHODCALLTYPE IRowsetInfo_RemoteGetProperties_Proxy(
    IRowsetInfo __RPC_FAR * This,
    /* [in] */ ULONG cPropertyIDSets,
    /* [size_is][unique][in] */ const DBPROPIDSET __RPC_FAR *rgPropertyIDSets,
    /* [out][in] */ ULONG __RPC_FAR *pcPropertySets,
    /* [size_is][size_is][out] */ DBPROPSET __RPC_FAR *__RPC_FAR *prgPropertySets);

void __RPC_STUB IRowsetInfo_RemoteGetProperties_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

/* [call_as] */ HRESULT STDMETHODCALLTYPE IRowsetInfo_RemoteGetReferencedRowset_Proxy(
    IRowsetInfo __RPC_FAR * This,
    /* [in] */ ULONG iOrdinal,
    /* [in] */ REFIID riid,
    /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppReferencedRowset);

void __RPC_STUB IRowsetInfo_RemoteGetReferencedRowset_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

/* [call_as] */ HRESULT STDMETHODCALLTYPE IRowsetInfo_RemoteGetSpecification_Proxy(
    IRowsetInfo __RPC_FAR * This,
    /* [in] */ REFIID riid,
    /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppSpecification);

void __RPC_STUB IRowsetInfo_RemoteGetSpecification_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IRowsetInfo_INTERFACE_DEFINED__ */

#ifndef __IRowsetLocate_INTERFACE_DEFINED__
#define __IRowsetLocate_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IRowsetLocate
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object][local] */

typedef DWORD DBCOMPARE;

enum DBCOMPAREENUM
    {DBCOMPARE_LT= 0,
DBCOMPARE_EQ= DBCOMPARE_LT + 1,
DBCOMPARE_GT= DBCOMPARE_EQ + 1,
DBCOMPARE_NE= DBCOMPARE_GT + 1,
DBCOMPARE_NOTCOMPARABLE= DBCOMPARE_NE + 1
    };

EXTERN_C const IID IID_IRowsetLocate;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a7d-2a1c-11ce-ade5-00aa0044773d")
    IRowsetLocate : public IRowset
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE Compare(
            /* [in] */ HCHAPTER hReserved,
            /* [in] */ ULONG cbBookmark1,
            /* [size_is][in] */ const BYTE __RPC_FAR *pBookmark1,
            /* [in] */ ULONG cbBookmark2,
            /* [size_is][in] */ const BYTE __RPC_FAR *pBookmark2,
            /* [out] */ DBCOMPARE __RPC_FAR *pComparison) = 0;

        virtual HRESULT STDMETHODCALLTYPE GetRowsAt(
            /* [in] */ HWATCHREGION hReserved1,
            /* [in] */ HCHAPTER hReserved2,
            /* [in] */ ULONG cbBookmark,
            /* [size_is][in] */ const BYTE __RPC_FAR *pBookmark,
            /* [in] */ LONG lRowsOffset,
            /* [in] */ LONG cRows,
            /* [out] */ ULONG __RPC_FAR *pcRowsObtained,
            /* [size_is][size_is][out] */ HROW __RPC_FAR *__RPC_FAR *prghRows) = 0;

        virtual HRESULT STDMETHODCALLTYPE GetRowsByBookmark(
            /* [in] */ HCHAPTER hReserved,
            /* [in] */ ULONG cRows,
            /* [size_is][in] */ const ULONG __RPC_FAR rgcbBookmarks[  ],
            /* [size_is][in] */ const BYTE __RPC_FAR *__RPC_FAR rgpBookmarks[  ],
            /* [size_is][out] */ HROW __RPC_FAR rghRows[  ],
            /* [size_is][out] */ DBROWSTATUS __RPC_FAR rgRowStatus[  ]) = 0;

        virtual HRESULT STDMETHODCALLTYPE Hash(
            /* [in] */ HCHAPTER hReserved,
            /* [in] */ ULONG cBookmarks,
            /* [size_is][in] */ const ULONG __RPC_FAR rgcbBookmarks[  ],
            /* [size_is][in] */ const BYTE __RPC_FAR *__RPC_FAR rgpBookmarks[  ],
            /* [size_is][out] */ DWORD __RPC_FAR rgHashedValues[  ],
            /* [size_is][out] */ DBROWSTATUS __RPC_FAR rgBookmarkStatus[  ]) = 0;

    };

#else /* C style interface */

    typedef struct IRowsetLocateVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IRowsetLocate __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IRowsetLocate __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IRowsetLocate __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *AddRefRows )(
            IRowsetLocate __RPC_FAR * This,
            /* [in] */ ULONG cRows,
            /* [size_is][in] */ const HROW __RPC_FAR rghRows[  ],
            /* [size_is][out] */ ULONG __RPC_FAR rgRefCounts[  ],
            /* [size_is][out] */ DBROWSTATUS __RPC_FAR rgRowStatus[  ]);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetData )(
            IRowsetLocate __RPC_FAR * This,
            /* [in] */ HROW hRow,
            /* [in] */ HACCESSOR hAccessor,
            /* [out] */ void __RPC_FAR *pData);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetNextRows )(
            IRowsetLocate __RPC_FAR * This,
            /* [in] */ HCHAPTER hReserved,
            /* [in] */ LONG lRowsOffset,
            /* [in] */ LONG cRows,
            /* [out] */ ULONG __RPC_FAR *pcRowsObtained,
            /* [size_is][size_is][out] */ HROW __RPC_FAR *__RPC_FAR *prghRows);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *ReleaseRows )(
            IRowsetLocate __RPC_FAR * This,
            /* [in] */ ULONG cRows,
            /* [size_is][in] */ const HROW __RPC_FAR rghRows[  ],
            /* [size_is][in] */ DBROWOPTIONS __RPC_FAR rgRowOptions[  ],
            /* [size_is][out] */ ULONG __RPC_FAR rgRefCounts[  ],
            /* [size_is][out] */ DBROWSTATUS __RPC_FAR rgRowStatus[  ]);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RestartPosition )(
            IRowsetLocate __RPC_FAR * This,
            /* [in] */ HCHAPTER hReserved);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Compare )(
            IRowsetLocate __RPC_FAR * This,
            /* [in] */ HCHAPTER hReserved,
            /* [in] */ ULONG cbBookmark1,
            /* [size_is][in] */ const BYTE __RPC_FAR *pBookmark1,
            /* [in] */ ULONG cbBookmark2,
            /* [size_is][in] */ const BYTE __RPC_FAR *pBookmark2,
            /* [out] */ DBCOMPARE __RPC_FAR *pComparison);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetRowsAt )(
            IRowsetLocate __RPC_FAR * This,
            /* [in] */ HWATCHREGION hReserved1,
            /* [in] */ HCHAPTER hReserved2,
            /* [in] */ ULONG cbBookmark,
            /* [size_is][in] */ const BYTE __RPC_FAR *pBookmark,
            /* [in] */ LONG lRowsOffset,
            /* [in] */ LONG cRows,
            /* [out] */ ULONG __RPC_FAR *pcRowsObtained,
            /* [size_is][size_is][out] */ HROW __RPC_FAR *__RPC_FAR *prghRows);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetRowsByBookmark )(
            IRowsetLocate __RPC_FAR * This,
            /* [in] */ HCHAPTER hReserved,
            /* [in] */ ULONG cRows,
            /* [size_is][in] */ const ULONG __RPC_FAR rgcbBookmarks[  ],
            /* [size_is][in] */ const BYTE __RPC_FAR *__RPC_FAR rgpBookmarks[  ],
            /* [size_is][out] */ HROW __RPC_FAR rghRows[  ],
            /* [size_is][out] */ DBROWSTATUS __RPC_FAR rgRowStatus[  ]);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Hash )(
            IRowsetLocate __RPC_FAR * This,
            /* [in] */ HCHAPTER hReserved,
            /* [in] */ ULONG cBookmarks,
            /* [size_is][in] */ const ULONG __RPC_FAR rgcbBookmarks[  ],
            /* [size_is][in] */ const BYTE __RPC_FAR *__RPC_FAR rgpBookmarks[  ],
            /* [size_is][out] */ DWORD __RPC_FAR rgHashedValues[  ],
            /* [size_is][out] */ DBROWSTATUS __RPC_FAR rgBookmarkStatus[  ]);

        END_INTERFACE
    } IRowsetLocateVtbl;

    interface IRowsetLocate
    {
        CONST_VTBL struct IRowsetLocateVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IRowsetLocate_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IRowsetLocate_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IRowsetLocate_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IRowsetLocate_AddRefRows(This,cRows,rghRows,rgRefCounts,rgRowStatus)\
    (This)->lpVtbl -> AddRefRows(This,cRows,rghRows,rgRefCounts,rgRowStatus)

#define IRowsetLocate_GetData(This,hRow,hAccessor,pData)\
    (This)->lpVtbl -> GetData(This,hRow,hAccessor,pData)

#define IRowsetLocate_GetNextRows(This,hReserved,lRowsOffset,cRows,pcRowsObtained,prghRows)\
    (This)->lpVtbl -> GetNextRows(This,hReserved,lRowsOffset,cRows,pcRowsObtained,prghRows)

#define IRowsetLocate_ReleaseRows(This,cRows,rghRows,rgRowOptions,rgRefCounts,rgRowStatus)\
    (This)->lpVtbl -> ReleaseRows(This,cRows,rghRows,rgRowOptions,rgRefCounts,rgRowStatus)

#define IRowsetLocate_RestartPosition(This,hReserved)\
    (This)->lpVtbl -> RestartPosition(This,hReserved)

#define IRowsetLocate_Compare(This,hReserved,cbBookmark1,pBookmark1,cbBookmark2,pBookmark2,pComparison)\
    (This)->lpVtbl -> Compare(This,hReserved,cbBookmark1,pBookmark1,cbBookmark2,pBookmark2,pComparison)

#define IRowsetLocate_GetRowsAt(This,hReserved1,hReserved2,cbBookmark,pBookmark,lRowsOffset,cRows,pcRowsObtained,prghRows)\
    (This)->lpVtbl -> GetRowsAt(This,hReserved1,hReserved2,cbBookmark,pBookmark,lRowsOffset,cRows,pcRowsObtained,prghRows)

#define IRowsetLocate_GetRowsByBookmark(This,hReserved,cRows,rgcbBookmarks,rgpBookmarks,rghRows,rgRowStatus)\
    (This)->lpVtbl -> GetRowsByBookmark(This,hReserved,cRows,rgcbBookmarks,rgpBookmarks,rghRows,rgRowStatus)

#define IRowsetLocate_Hash(This,hReserved,cBookmarks,rgcbBookmarks,rgpBookmarks,rgHashedValues,rgBookmarkStatus)\
    (This)->lpVtbl -> Hash(This,hReserved,cBookmarks,rgcbBookmarks,rgpBookmarks,rgHashedValues,rgBookmarkStatus)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE IRowsetLocate_Compare_Proxy(
    IRowsetLocate __RPC_FAR * This,
    /* [in] */ HCHAPTER hReserved,
    /* [in] */ ULONG cbBookmark1,
    /* [size_is][in] */ const BYTE __RPC_FAR *pBookmark1,
    /* [in] */ ULONG cbBookmark2,
    /* [size_is][in] */ const BYTE __RPC_FAR *pBookmark2,
    /* [out] */ DBCOMPARE __RPC_FAR *pComparison);

void __RPC_STUB IRowsetLocate_Compare_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IRowsetLocate_GetRowsAt_Proxy(
    IRowsetLocate __RPC_FAR * This,
    /* [in] */ HWATCHREGION hReserved1,
    /* [in] */ HCHAPTER hReserved2,
    /* [in] */ ULONG cbBookmark,
    /* [size_is][in] */ const BYTE __RPC_FAR *pBookmark,
    /* [in] */ LONG lRowsOffset,
    /* [in] */ LONG cRows,
    /* [out] */ ULONG __RPC_FAR *pcRowsObtained,
    /* [size_is][size_is][out] */ HROW __RPC_FAR *__RPC_FAR *prghRows);

void __RPC_STUB IRowsetLocate_GetRowsAt_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IRowsetLocate_GetRowsByBookmark_Proxy(
    IRowsetLocate __RPC_FAR * This,
    /* [in] */ HCHAPTER hReserved,
    /* [in] */ ULONG cRows,
    /* [size_is][in] */ const ULONG __RPC_FAR rgcbBookmarks[  ],
    /* [size_is][in] */ const BYTE __RPC_FAR *__RPC_FAR rgpBookmarks[  ],
    /* [size_is][out] */ HROW __RPC_FAR rghRows[  ],
    /* [size_is][out] */ DBROWSTATUS __RPC_FAR rgRowStatus[  ]);

void __RPC_STUB IRowsetLocate_GetRowsByBookmark_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IRowsetLocate_Hash_Proxy(
    IRowsetLocate __RPC_FAR * This,
    /* [in] */ HCHAPTER hReserved,
    /* [in] */ ULONG cBookmarks,
    /* [size_is][in] */ const ULONG __RPC_FAR rgcbBookmarks[  ],
    /* [size_is][in] */ const BYTE __RPC_FAR *__RPC_FAR rgpBookmarks[  ],
    /* [size_is][out] */ DWORD __RPC_FAR rgHashedValues[  ],
    /* [size_is][out] */ DBROWSTATUS __RPC_FAR rgBookmarkStatus[  ]);

void __RPC_STUB IRowsetLocate_Hash_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IRowsetLocate_INTERFACE_DEFINED__ */

#ifndef __IRowsetResynch_INTERFACE_DEFINED__
#define __IRowsetResynch_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IRowsetResynch
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object][local] */

EXTERN_C const IID IID_IRowsetResynch;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a84-2a1c-11ce-ade5-00aa0044773d")
    IRowsetResynch : public IUnknown
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE GetVisibleData(
            /* [in] */ HROW hRow,
            /* [in] */ HACCESSOR hAccessor,
            /* [out] */ void __RPC_FAR *pData) = 0;

        virtual HRESULT STDMETHODCALLTYPE ResynchRows(
            /* [in] */ ULONG cRows,
            /* [size_is][in] */ const HROW __RPC_FAR rghRows[  ],
            /* [out] */ ULONG __RPC_FAR *pcRowsResynched,
            /* [size_is][size_is][out] */ HROW __RPC_FAR *__RPC_FAR *prghRowsResynched,
            /* [size_is][size_is][out] */ DBROWSTATUS __RPC_FAR *__RPC_FAR *prgRowStatus) = 0;

    };

#else /* C style interface */

    typedef struct IRowsetResynchVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IRowsetResynch __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IRowsetResynch __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IRowsetResynch __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetVisibleData )(
            IRowsetResynch __RPC_FAR * This,
            /* [in] */ HROW hRow,
            /* [in] */ HACCESSOR hAccessor,
            /* [out] */ void __RPC_FAR *pData);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *ResynchRows )(
            IRowsetResynch __RPC_FAR * This,
            /* [in] */ ULONG cRows,
            /* [size_is][in] */ const HROW __RPC_FAR rghRows[  ],
            /* [out] */ ULONG __RPC_FAR *pcRowsResynched,
            /* [size_is][size_is][out] */ HROW __RPC_FAR *__RPC_FAR *prghRowsResynched,
            /* [size_is][size_is][out] */ DBROWSTATUS __RPC_FAR *__RPC_FAR *prgRowStatus);

        END_INTERFACE
    } IRowsetResynchVtbl;

    interface IRowsetResynch
    {
        CONST_VTBL struct IRowsetResynchVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IRowsetResynch_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IRowsetResynch_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IRowsetResynch_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IRowsetResynch_GetVisibleData(This,hRow,hAccessor,pData)\
    (This)->lpVtbl -> GetVisibleData(This,hRow,hAccessor,pData)

#define IRowsetResynch_ResynchRows(This,cRows,rghRows,pcRowsResynched,prghRowsResynched,prgRowStatus)\
    (This)->lpVtbl -> ResynchRows(This,cRows,rghRows,pcRowsResynched,prghRowsResynched,prgRowStatus)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE IRowsetResynch_GetVisibleData_Proxy(
    IRowsetResynch __RPC_FAR * This,
    /* [in] */ HROW hRow,
    /* [in] */ HACCESSOR hAccessor,
    /* [out] */ void __RPC_FAR *pData);

void __RPC_STUB IRowsetResynch_GetVisibleData_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IRowsetResynch_ResynchRows_Proxy(
    IRowsetResynch __RPC_FAR * This,
    /* [in] */ ULONG cRows,
    /* [size_is][in] */ const HROW __RPC_FAR rghRows[  ],
    /* [out] */ ULONG __RPC_FAR *pcRowsResynched,
    /* [size_is][size_is][out] */ HROW __RPC_FAR *__RPC_FAR *prghRowsResynched,
    /* [size_is][size_is][out] */ DBROWSTATUS __RPC_FAR *__RPC_FAR *prgRowStatus);

void __RPC_STUB IRowsetResynch_ResynchRows_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IRowsetResynch_INTERFACE_DEFINED__ */

#ifndef __IRowsetScroll_INTERFACE_DEFINED__
#define __IRowsetScroll_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IRowsetScroll
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object][local] */

EXTERN_C const IID IID_IRowsetScroll;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a7e-2a1c-11ce-ade5-00aa0044773d")
    IRowsetScroll : public IRowsetLocate
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE GetApproximatePosition(
            /* [in] */ HCHAPTER hReserved,
            /* [in] */ ULONG cbBookmark,
            /* [size_is][in] */ const BYTE __RPC_FAR *pBookmark,
            /* [out] */ ULONG __RPC_FAR *pulPosition,
            /* [out] */ ULONG __RPC_FAR *pcRows) = 0;

        virtual HRESULT STDMETHODCALLTYPE GetRowsAtRatio(
            /* [in] */ HWATCHREGION hReserved1,
            /* [in] */ HCHAPTER hReserved2,
            /* [in] */ ULONG ulNumerator,
            /* [in] */ ULONG ulDenominator,
            /* [in] */ LONG cRows,
            /* [out] */ ULONG __RPC_FAR *pcRowsObtained,
            /* [size_is][size_is][out] */ HROW __RPC_FAR *__RPC_FAR *prghRows) = 0;

    };

#else /* C style interface */

    typedef struct IRowsetScrollVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IRowsetScroll __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IRowsetScroll __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IRowsetScroll __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *AddRefRows )(
            IRowsetScroll __RPC_FAR * This,
            /* [in] */ ULONG cRows,
            /* [size_is][in] */ const HROW __RPC_FAR rghRows[  ],
            /* [size_is][out] */ ULONG __RPC_FAR rgRefCounts[  ],
            /* [size_is][out] */ DBROWSTATUS __RPC_FAR rgRowStatus[  ]);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetData )(
            IRowsetScroll __RPC_FAR * This,
            /* [in] */ HROW hRow,
            /* [in] */ HACCESSOR hAccessor,
            /* [out] */ void __RPC_FAR *pData);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetNextRows )(
            IRowsetScroll __RPC_FAR * This,
            /* [in] */ HCHAPTER hReserved,
            /* [in] */ LONG lRowsOffset,
            /* [in] */ LONG cRows,
            /* [out] */ ULONG __RPC_FAR *pcRowsObtained,
            /* [size_is][size_is][out] */ HROW __RPC_FAR *__RPC_FAR *prghRows);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *ReleaseRows )(
            IRowsetScroll __RPC_FAR * This,
            /* [in] */ ULONG cRows,
            /* [size_is][in] */ const HROW __RPC_FAR rghRows[  ],
            /* [size_is][in] */ DBROWOPTIONS __RPC_FAR rgRowOptions[  ],
            /* [size_is][out] */ ULONG __RPC_FAR rgRefCounts[  ],
            /* [size_is][out] */ DBROWSTATUS __RPC_FAR rgRowStatus[  ]);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RestartPosition )(
            IRowsetScroll __RPC_FAR * This,
            /* [in] */ HCHAPTER hReserved);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Compare )(
            IRowsetScroll __RPC_FAR * This,
            /* [in] */ HCHAPTER hReserved,
            /* [in] */ ULONG cbBookmark1,
            /* [size_is][in] */ const BYTE __RPC_FAR *pBookmark1,
            /* [in] */ ULONG cbBookmark2,
            /* [size_is][in] */ const BYTE __RPC_FAR *pBookmark2,
            /* [out] */ DBCOMPARE __RPC_FAR *pComparison);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetRowsAt )(
            IRowsetScroll __RPC_FAR * This,
            /* [in] */ HWATCHREGION hReserved1,
            /* [in] */ HCHAPTER hReserved2,
            /* [in] */ ULONG cbBookmark,
            /* [size_is][in] */ const BYTE __RPC_FAR *pBookmark,
            /* [in] */ LONG lRowsOffset,
            /* [in] */ LONG cRows,
            /* [out] */ ULONG __RPC_FAR *pcRowsObtained,
            /* [size_is][size_is][out] */ HROW __RPC_FAR *__RPC_FAR *prghRows);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetRowsByBookmark )(
            IRowsetScroll __RPC_FAR * This,
            /* [in] */ HCHAPTER hReserved,
            /* [in] */ ULONG cRows,
            /* [size_is][in] */ const ULONG __RPC_FAR rgcbBookmarks[  ],
            /* [size_is][in] */ const BYTE __RPC_FAR *__RPC_FAR rgpBookmarks[  ],
            /* [size_is][out] */ HROW __RPC_FAR rghRows[  ],
            /* [size_is][out] */ DBROWSTATUS __RPC_FAR rgRowStatus[  ]);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Hash )(
            IRowsetScroll __RPC_FAR * This,
            /* [in] */ HCHAPTER hReserved,
            /* [in] */ ULONG cBookmarks,
            /* [size_is][in] */ const ULONG __RPC_FAR rgcbBookmarks[  ],
            /* [size_is][in] */ const BYTE __RPC_FAR *__RPC_FAR rgpBookmarks[  ],
            /* [size_is][out] */ DWORD __RPC_FAR rgHashedValues[  ],
            /* [size_is][out] */ DBROWSTATUS __RPC_FAR rgBookmarkStatus[  ]);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetApproximatePosition )(
            IRowsetScroll __RPC_FAR * This,
            /* [in] */ HCHAPTER hReserved,
            /* [in] */ ULONG cbBookmark,
            /* [size_is][in] */ const BYTE __RPC_FAR *pBookmark,
            /* [out] */ ULONG __RPC_FAR *pulPosition,
            /* [out] */ ULONG __RPC_FAR *pcRows);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetRowsAtRatio )(
            IRowsetScroll __RPC_FAR * This,
            /* [in] */ HWATCHREGION hReserved1,
            /* [in] */ HCHAPTER hReserved2,
            /* [in] */ ULONG ulNumerator,
            /* [in] */ ULONG ulDenominator,
            /* [in] */ LONG cRows,
            /* [out] */ ULONG __RPC_FAR *pcRowsObtained,
            /* [size_is][size_is][out] */ HROW __RPC_FAR *__RPC_FAR *prghRows);

        END_INTERFACE
    } IRowsetScrollVtbl;

    interface IRowsetScroll
    {
        CONST_VTBL struct IRowsetScrollVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IRowsetScroll_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IRowsetScroll_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IRowsetScroll_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IRowsetScroll_AddRefRows(This,cRows,rghRows,rgRefCounts,rgRowStatus)\
    (This)->lpVtbl -> AddRefRows(This,cRows,rghRows,rgRefCounts,rgRowStatus)

#define IRowsetScroll_GetData(This,hRow,hAccessor,pData)\
    (This)->lpVtbl -> GetData(This,hRow,hAccessor,pData)

#define IRowsetScroll_GetNextRows(This,hReserved,lRowsOffset,cRows,pcRowsObtained,prghRows)\
    (This)->lpVtbl -> GetNextRows(This,hReserved,lRowsOffset,cRows,pcRowsObtained,prghRows)

#define IRowsetScroll_ReleaseRows(This,cRows,rghRows,rgRowOptions,rgRefCounts,rgRowStatus)\
    (This)->lpVtbl -> ReleaseRows(This,cRows,rghRows,rgRowOptions,rgRefCounts,rgRowStatus)

#define IRowsetScroll_RestartPosition(This,hReserved)\
    (This)->lpVtbl -> RestartPosition(This,hReserved)

#define IRowsetScroll_Compare(This,hReserved,cbBookmark1,pBookmark1,cbBookmark2,pBookmark2,pComparison)\
    (This)->lpVtbl -> Compare(This,hReserved,cbBookmark1,pBookmark1,cbBookmark2,pBookmark2,pComparison)

#define IRowsetScroll_GetRowsAt(This,hReserved1,hReserved2,cbBookmark,pBookmark,lRowsOffset,cRows,pcRowsObtained,prghRows)\
    (This)->lpVtbl -> GetRowsAt(This,hReserved1,hReserved2,cbBookmark,pBookmark,lRowsOffset,cRows,pcRowsObtained,prghRows)

#define IRowsetScroll_GetRowsByBookmark(This,hReserved,cRows,rgcbBookmarks,rgpBookmarks,rghRows,rgRowStatus)\
    (This)->lpVtbl -> GetRowsByBookmark(This,hReserved,cRows,rgcbBookmarks,rgpBookmarks,rghRows,rgRowStatus)

#define IRowsetScroll_Hash(This,hReserved,cBookmarks,rgcbBookmarks,rgpBookmarks,rgHashedValues,rgBookmarkStatus)\
    (This)->lpVtbl -> Hash(This,hReserved,cBookmarks,rgcbBookmarks,rgpBookmarks,rgHashedValues,rgBookmarkStatus)

#define IRowsetScroll_GetApproximatePosition(This,hReserved,cbBookmark,pBookmark,pulPosition,pcRows)\
    (This)->lpVtbl -> GetApproximatePosition(This,hReserved,cbBookmark,pBookmark,pulPosition,pcRows)

#define IRowsetScroll_GetRowsAtRatio(This,hReserved1,hReserved2,ulNumerator,ulDenominator,cRows,pcRowsObtained,prghRows)\
    (This)->lpVtbl -> GetRowsAtRatio(This,hReserved1,hReserved2,ulNumerator,ulDenominator,cRows,pcRowsObtained,prghRows)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE IRowsetScroll_GetApproximatePosition_Proxy(
    IRowsetScroll __RPC_FAR * This,
    /* [in] */ HCHAPTER hReserved,
    /* [in] */ ULONG cbBookmark,
    /* [size_is][in] */ const BYTE __RPC_FAR *pBookmark,
    /* [out] */ ULONG __RPC_FAR *pulPosition,
    /* [out] */ ULONG __RPC_FAR *pcRows);

void __RPC_STUB IRowsetScroll_GetApproximatePosition_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IRowsetScroll_GetRowsAtRatio_Proxy(
    IRowsetScroll __RPC_FAR * This,
    /* [in] */ HWATCHREGION hReserved1,
    /* [in] */ HCHAPTER hReserved2,
    /* [in] */ ULONG ulNumerator,
    /* [in] */ ULONG ulDenominator,
    /* [in] */ LONG cRows,
    /* [out] */ ULONG __RPC_FAR *pcRowsObtained,
    /* [size_is][size_is][out] */ HROW __RPC_FAR *__RPC_FAR *prghRows);

void __RPC_STUB IRowsetScroll_GetRowsAtRatio_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IRowsetScroll_INTERFACE_DEFINED__ */

/****************************************
 * Generated header for interface: __MIDL_itf_oledb_0078
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [local] */

//@@@+ V1.5
#if( OLEDBVER >= 0x0150 )

extern RPC_IF_HANDLE __MIDL_itf_oledb_0078_v0_0_c_ifspec;
extern RPC_IF_HANDLE __MIDL_itf_oledb_0078_v0_0_s_ifspec;

#ifndef __IChapteredRowset_INTERFACE_DEFINED__
#define __IChapteredRowset_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IChapteredRowset
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object] */

EXTERN_C const IID IID_IChapteredRowset;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a93-2a1c-11ce-ade5-00aa0044773d")
    IChapteredRowset : public IUnknown
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE AddRefChapter(
            /* [in] */ HCHAPTER hChapter,
            /* [out] */ ULONG __RPC_FAR *pcRefCount) = 0;

        virtual HRESULT STDMETHODCALLTYPE ReleaseChapter(
            /* [in] */ HCHAPTER hChapter,
            /* [out] */ ULONG __RPC_FAR *pcRefCount) = 0;

    };

#else /* C style interface */

    typedef struct IChapteredRowsetVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IChapteredRowset __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IChapteredRowset __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IChapteredRowset __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *AddRefChapter )(
            IChapteredRowset __RPC_FAR * This,
            /* [in] */ HCHAPTER hChapter,
            /* [out] */ ULONG __RPC_FAR *pcRefCount);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *ReleaseChapter )(
            IChapteredRowset __RPC_FAR * This,
            /* [in] */ HCHAPTER hChapter,
            /* [out] */ ULONG __RPC_FAR *pcRefCount);

        END_INTERFACE
    } IChapteredRowsetVtbl;

    interface IChapteredRowset
    {
        CONST_VTBL struct IChapteredRowsetVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IChapteredRowset_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IChapteredRowset_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IChapteredRowset_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IChapteredRowset_AddRefChapter(This,hChapter,pcRefCount)\
    (This)->lpVtbl -> AddRefChapter(This,hChapter,pcRefCount)

#define IChapteredRowset_ReleaseChapter(This,hChapter,pcRefCount)\
    (This)->lpVtbl -> ReleaseChapter(This,hChapter,pcRefCount)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE IChapteredRowset_AddRefChapter_Proxy(
    IChapteredRowset __RPC_FAR * This,
    /* [in] */ HCHAPTER hChapter,
    /* [out] */ ULONG __RPC_FAR *pcRefCount);

void __RPC_STUB IChapteredRowset_AddRefChapter_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IChapteredRowset_ReleaseChapter_Proxy(
    IChapteredRowset __RPC_FAR * This,
    /* [in] */ HCHAPTER hChapter,
    /* [out] */ ULONG __RPC_FAR *pcRefCount);

void __RPC_STUB IChapteredRowset_ReleaseChapter_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IChapteredRowset_INTERFACE_DEFINED__ */

#ifndef __IRowsetFind_INTERFACE_DEFINED__
#define __IRowsetFind_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IRowsetFind
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object][local] */

EXTERN_C const IID IID_IRowsetFind;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a9d-2a1c-11ce-ade5-00aa0044773d")
    IRowsetFind : public IUnknown
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE FindNextRow(
            /* [in] */ HCHAPTER hChapter,
            /* [in] */ HACCESSOR hAccessor,
            /* [in] */ void __RPC_FAR *pFindValue,
            /* [in] */ DBCOMPAREOP CompareOp,
            /* [in] */ ULONG cbBookmark,
            /* [size_is][in] */ const BYTE __RPC_FAR *pBookmark,
            /* [in] */ LONG lRowsOffset,
            /* [in] */ LONG cRows,
            /* [out][in] */ ULONG __RPC_FAR *pcRowsObtained,
            /* [size_is][size_is][out] */ HROW __RPC_FAR *__RPC_FAR *prghRows) = 0;

    };

#else /* C style interface */

    typedef struct IRowsetFindVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IRowsetFind __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IRowsetFind __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IRowsetFind __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *FindNextRow )(
            IRowsetFind __RPC_FAR * This,
            /* [in] */ HCHAPTER hChapter,
            /* [in] */ HACCESSOR hAccessor,
            /* [in] */ void __RPC_FAR *pFindValue,
            /* [in] */ DBCOMPAREOP CompareOp,
            /* [in] */ ULONG cbBookmark,
            /* [size_is][in] */ const BYTE __RPC_FAR *pBookmark,
            /* [in] */ LONG lRowsOffset,
            /* [in] */ LONG cRows,
            /* [out][in] */ ULONG __RPC_FAR *pcRowsObtained,
            /* [size_is][size_is][out] */ HROW __RPC_FAR *__RPC_FAR *prghRows);

        END_INTERFACE
    } IRowsetFindVtbl;

    interface IRowsetFind
    {
        CONST_VTBL struct IRowsetFindVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IRowsetFind_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IRowsetFind_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IRowsetFind_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IRowsetFind_FindNextRow(This,hChapter,hAccessor,pFindValue,CompareOp,cbBookmark,pBookmark,lRowsOffset,cRows,pcRowsObtained,prghRows)\
    (This)->lpVtbl -> FindNextRow(This,hChapter,hAccessor,pFindValue,CompareOp,cbBookmark,pBookmark,lRowsOffset,cRows,pcRowsObtained,prghRows)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE IRowsetFind_FindNextRow_Proxy(
    IRowsetFind __RPC_FAR * This,
    /* [in] */ HCHAPTER hChapter,
    /* [in] */ HACCESSOR hAccessor,
    /* [in] */ void __RPC_FAR *pFindValue,
    /* [in] */ DBCOMPAREOP CompareOp,
    /* [in] */ ULONG cbBookmark,
    /* [size_is][in] */ const BYTE __RPC_FAR *pBookmark,
    /* [in] */ LONG lRowsOffset,
    /* [in] */ LONG cRows,
    /* [out][in] */ ULONG __RPC_FAR *pcRowsObtained,
    /* [size_is][size_is][out] */ HROW __RPC_FAR *__RPC_FAR *prghRows);

void __RPC_STUB IRowsetFind_FindNextRow_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IRowsetFind_INTERFACE_DEFINED__ */

#ifndef __IRowPosition_INTERFACE_DEFINED__
#define __IRowPosition_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IRowPosition
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object] */

typedef DWORD DBPOSITIONFLAGS;

enum DBPOSITIONFLAGSENUM
    {DBPOSITION_OK= 0,
DBPOSITION_NOROW= DBPOSITION_OK + 1,
DBPOSITION_BOF= DBPOSITION_NOROW + 1,
DBPOSITION_EOF= DBPOSITION_BOF + 1
    };

EXTERN_C const IID IID_IRowPosition;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a94-2a1c-11ce-ade5-00aa0044773d")
    IRowPosition : public IUnknown
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE ClearRowPosition( void) = 0;

        virtual HRESULT STDMETHODCALLTYPE GetRowPosition(
            /* [out] */ HCHAPTER __RPC_FAR *phChapter,
            /* [out] */ HROW __RPC_FAR *phRow,
            /* [out] */ DBPOSITIONFLAGS __RPC_FAR *pdwPositionFlags) = 0;

        virtual HRESULT STDMETHODCALLTYPE GetRowset(
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppRowset) = 0;

        virtual HRESULT STDMETHODCALLTYPE Initialize(
            /* [in] */ IUnknown __RPC_FAR *pRowset) = 0;

        virtual HRESULT STDMETHODCALLTYPE SetRowPosition(
            /* [in] */ HCHAPTER hChapter,
            /* [in] */ HROW hRow,
            /* [in] */ DBPOSITIONFLAGS dwPositionFlags) = 0;

    };

#else /* C style interface */

    typedef struct IRowPositionVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IRowPosition __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IRowPosition __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IRowPosition __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *ClearRowPosition )(
            IRowPosition __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetRowPosition )(
            IRowPosition __RPC_FAR * This,
            /* [out] */ HCHAPTER __RPC_FAR *phChapter,
            /* [out] */ HROW __RPC_FAR *phRow,
            /* [out] */ DBPOSITIONFLAGS __RPC_FAR *pdwPositionFlags);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetRowset )(
            IRowPosition __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppRowset);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Initialize )(
            IRowPosition __RPC_FAR * This,
            /* [in] */ IUnknown __RPC_FAR *pRowset);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetRowPosition )(
            IRowPosition __RPC_FAR * This,
            /* [in] */ HCHAPTER hChapter,
            /* [in] */ HROW hRow,
            /* [in] */ DBPOSITIONFLAGS dwPositionFlags);

        END_INTERFACE
    } IRowPositionVtbl;

    interface IRowPosition
    {
        CONST_VTBL struct IRowPositionVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IRowPosition_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IRowPosition_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IRowPosition_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IRowPosition_ClearRowPosition(This)\
    (This)->lpVtbl -> ClearRowPosition(This)

#define IRowPosition_GetRowPosition(This,phChapter,phRow,pdwPositionFlags)\
    (This)->lpVtbl -> GetRowPosition(This,phChapter,phRow,pdwPositionFlags)

#define IRowPosition_GetRowset(This,riid,ppRowset)\
    (This)->lpVtbl -> GetRowset(This,riid,ppRowset)

#define IRowPosition_Initialize(This,pRowset)\
    (This)->lpVtbl -> Initialize(This,pRowset)

#define IRowPosition_SetRowPosition(This,hChapter,hRow,dwPositionFlags)\
    (This)->lpVtbl -> SetRowPosition(This,hChapter,hRow,dwPositionFlags)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE IRowPosition_ClearRowPosition_Proxy(
    IRowPosition __RPC_FAR * This);

void __RPC_STUB IRowPosition_ClearRowPosition_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IRowPosition_GetRowPosition_Proxy(
    IRowPosition __RPC_FAR * This,
    /* [out] */ HCHAPTER __RPC_FAR *phChapter,
    /* [out] */ HROW __RPC_FAR *phRow,
    /* [out] */ DBPOSITIONFLAGS __RPC_FAR *pdwPositionFlags);

void __RPC_STUB IRowPosition_GetRowPosition_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IRowPosition_GetRowset_Proxy(
    IRowPosition __RPC_FAR * This,
    /* [in] */ REFIID riid,
    /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppRowset);

void __RPC_STUB IRowPosition_GetRowset_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IRowPosition_Initialize_Proxy(
    IRowPosition __RPC_FAR * This,
    /* [in] */ IUnknown __RPC_FAR *pRowset);

void __RPC_STUB IRowPosition_Initialize_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IRowPosition_SetRowPosition_Proxy(
    IRowPosition __RPC_FAR * This,
    /* [in] */ HCHAPTER hChapter,
    /* [in] */ HROW hRow,
    /* [in] */ DBPOSITIONFLAGS dwPositionFlags);

void __RPC_STUB IRowPosition_SetRowPosition_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IRowPosition_INTERFACE_DEFINED__ */

#ifndef __IRowPositionChange_INTERFACE_DEFINED__
#define __IRowPositionChange_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IRowPositionChange
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object] */

EXTERN_C const IID IID_IRowPositionChange;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0997a571-126e-11d0-9f8a-00a0c9a0631e")
    IRowPositionChange : public IUnknown
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE OnRowPositionChange(
            /* [in] */ DBREASON eReason,
            /* [in] */ DBEVENTPHASE ePhase,
            /* [in] */ BOOL fCantDeny) = 0;

    };

#else /* C style interface */

    typedef struct IRowPositionChangeVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IRowPositionChange __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IRowPositionChange __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IRowPositionChange __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnRowPositionChange )(
            IRowPositionChange __RPC_FAR * This,
            /* [in] */ DBREASON eReason,
            /* [in] */ DBEVENTPHASE ePhase,
            /* [in] */ BOOL fCantDeny);

        END_INTERFACE
    } IRowPositionChangeVtbl;

    interface IRowPositionChange
    {
        CONST_VTBL struct IRowPositionChangeVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IRowPositionChange_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IRowPositionChange_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IRowPositionChange_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IRowPositionChange_OnRowPositionChange(This,eReason,ePhase,fCantDeny)\
    (This)->lpVtbl -> OnRowPositionChange(This,eReason,ePhase,fCantDeny)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE IRowPositionChange_OnRowPositionChange_Proxy(
    IRowPositionChange __RPC_FAR * This,
    /* [in] */ DBREASON eReason,
    /* [in] */ DBEVENTPHASE ePhase,
    /* [in] */ BOOL fCantDeny);

void __RPC_STUB IRowPositionChange_OnRowPositionChange_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IRowPositionChange_INTERFACE_DEFINED__ */

#ifndef __IViewRowset_INTERFACE_DEFINED__
#define __IViewRowset_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IViewRowset
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object] */

EXTERN_C const IID IID_IViewRowset;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a97-2a1c-11ce-ade5-00aa0044773d")
    IViewRowset : public IUnknown
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE GetSpecification(
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppObject) = 0;

        virtual HRESULT STDMETHODCALLTYPE OpenViewRowset(
            /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppRowset) = 0;

    };

#else /* C style interface */

    typedef struct IViewRowsetVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IViewRowset __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IViewRowset __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IViewRowset __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetSpecification )(
            IViewRowset __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppObject);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OpenViewRowset )(
            IViewRowset __RPC_FAR * This,
            /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppRowset);

        END_INTERFACE
    } IViewRowsetVtbl;

    interface IViewRowset
    {
        CONST_VTBL struct IViewRowsetVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IViewRowset_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IViewRowset_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IViewRowset_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IViewRowset_GetSpecification(This,riid,ppObject)\
    (This)->lpVtbl -> GetSpecification(This,riid,ppObject)

#define IViewRowset_OpenViewRowset(This,pUnkOuter,riid,ppRowset)\
    (This)->lpVtbl -> OpenViewRowset(This,pUnkOuter,riid,ppRowset)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE IViewRowset_GetSpecification_Proxy(
    IViewRowset __RPC_FAR * This,
    /* [in] */ REFIID riid,
    /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppObject);

void __RPC_STUB IViewRowset_GetSpecification_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IViewRowset_OpenViewRowset_Proxy(
    IViewRowset __RPC_FAR * This,
    /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
    /* [in] */ REFIID riid,
    /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppRowset);

void __RPC_STUB IViewRowset_OpenViewRowset_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IViewRowset_INTERFACE_DEFINED__ */

#ifndef __IViewChapter_INTERFACE_DEFINED__
#define __IViewChapter_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IViewChapter
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object] */

EXTERN_C const IID IID_IViewChapter;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a98-2a1c-11ce-ade5-00aa0044773d")
    IViewChapter : public IUnknown
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE GetSpecification(
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppRowset) = 0;

        virtual HRESULT STDMETHODCALLTYPE OpenViewChapter(
            /* [in] */ HCHAPTER hSource,
            /* [out] */ HCHAPTER __RPC_FAR *phViewChapter) = 0;

    };

#else /* C style interface */

    typedef struct IViewChapterVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IViewChapter __RPC_FAR * This,

/* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IViewChapter __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IViewChapter __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetSpecification )(
            IViewChapter __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppRowset);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OpenViewChapter )(
            IViewChapter __RPC_FAR * This,
            /* [in] */ HCHAPTER hSource,
            /* [out] */ HCHAPTER __RPC_FAR *phViewChapter);

        END_INTERFACE
    } IViewChapterVtbl;

    interface IViewChapter
    {
        CONST_VTBL struct IViewChapterVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IViewChapter_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IViewChapter_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IViewChapter_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IViewChapter_GetSpecification(This,riid,ppRowset)\
    (This)->lpVtbl -> GetSpecification(This,riid,ppRowset)

#define IViewChapter_OpenViewChapter(This,hSource,phViewChapter)\
    (This)->lpVtbl -> OpenViewChapter(This,hSource,phViewChapter)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE IViewChapter_GetSpecification_Proxy(
    IViewChapter __RPC_FAR * This,
    /* [in] */ REFIID riid,
    /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppRowset);

void __RPC_STUB IViewChapter_GetSpecification_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IViewChapter_OpenViewChapter_Proxy(
    IViewChapter __RPC_FAR * This,
    /* [in] */ HCHAPTER hSource,
    /* [out] */ HCHAPTER __RPC_FAR *phViewChapter);

void __RPC_STUB IViewChapter_OpenViewChapter_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IViewChapter_INTERFACE_DEFINED__ */

#ifndef __IViewSort_INTERFACE_DEFINED__
#define __IViewSort_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IViewSort
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object] */

EXTERN_C const IID IID_IViewSort;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a9a-2a1c-11ce-ade5-00aa0044773d")
    IViewSort : public IUnknown
    {
    public:
        virtual /* [local] */ HRESULT STDMETHODCALLTYPE GetSortOrder(
            /* [out] */ ULONG __RPC_FAR *pcValues,
            /* [out] */ ULONG __RPC_FAR *__RPC_FAR prgColumns[  ],
            /* [out] */ DBSORT __RPC_FAR *__RPC_FAR prgOrders[  ]) = 0;

        virtual HRESULT STDMETHODCALLTYPE SetSortOrder(
            /* [in] */ ULONG cValues,
            /* [size_is][in] */ const ULONG __RPC_FAR rgColumns[  ],
            /* [size_is][in] */ const DBSORT __RPC_FAR rgOrders[  ]) = 0;

    };

#else /* C style interface */

    typedef struct IViewSortVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IViewSort __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IViewSort __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IViewSort __RPC_FAR * This);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetSortOrder )(
            IViewSort __RPC_FAR * This,
            /* [out] */ ULONG __RPC_FAR *pcValues,
            /* [out] */ ULONG __RPC_FAR *__RPC_FAR prgColumns[  ],
            /* [out] */ DBSORT __RPC_FAR *__RPC_FAR prgOrders[  ]);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetSortOrder )(
            IViewSort __RPC_FAR * This,
            /* [in] */ ULONG cValues,
            /* [size_is][in] */ const ULONG __RPC_FAR rgColumns[  ],
            /* [size_is][in] */ const DBSORT __RPC_FAR rgOrders[  ]);

        END_INTERFACE
    } IViewSortVtbl;

    interface IViewSort
    {
        CONST_VTBL struct IViewSortVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IViewSort_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IViewSort_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IViewSort_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IViewSort_GetSortOrder(This,pcValues,prgColumns,prgOrders)\
    (This)->lpVtbl -> GetSortOrder(This,pcValues,prgColumns,prgOrders)

#define IViewSort_SetSortOrder(This,cValues,rgColumns,rgOrders)\
    (This)->lpVtbl -> SetSortOrder(This,cValues,rgColumns,rgOrders)

#endif /* COBJMACROS */

#endif /* C style interface */

/* [call_as] */ HRESULT STDMETHODCALLTYPE IViewSort_RemoteGetSortOrder_Proxy(
    IViewSort __RPC_FAR * This,
    /* [out][in] */ ULONG __RPC_FAR *pcValues,
    /* [size_is][size_is][out] */ ULONG __RPC_FAR *__RPC_FAR *prgColumns,
    /* [size_is][size_is][out] */ ULONG __RPC_FAR *__RPC_FAR *prgOrders);

void __RPC_STUB IViewSort_RemoteGetSortOrder_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IViewSort_SetSortOrder_Proxy(
    IViewSort __RPC_FAR * This,
    /* [in] */ ULONG cValues,
    /* [size_is][in] */ const ULONG __RPC_FAR rgColumns[  ],
    /* [size_is][in] */ const DBSORT __RPC_FAR rgOrders[  ]);

void __RPC_STUB IViewSort_SetSortOrder_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IViewSort_INTERFACE_DEFINED__ */

#ifndef __IViewFilter_INTERFACE_DEFINED__
#define __IViewFilter_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IViewFilter
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object] */

EXTERN_C const IID IID_IViewFilter;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a9b-2a1c-11ce-ade5-00aa0044773d")
    IViewFilter : public IUnknown
    {
    public:
        virtual /* [local] */ HRESULT STDMETHODCALLTYPE GetFilter(
            /* [in] */ HACCESSOR hAccessor,
            /* [out] */ ULONG __RPC_FAR *pcRows,
            /* [out] */ DBCOMPAREOP __RPC_FAR *__RPC_FAR pCompareOps[  ],
            /* [out] */ void __RPC_FAR *pCriteriaData) = 0;

        virtual /* [local] */ HRESULT STDMETHODCALLTYPE GetFilterBindings(
            /* [out] */ ULONG __RPC_FAR *pcBindings,
            /* [out] */ DBBINDING __RPC_FAR *__RPC_FAR *prgBindings) = 0;

        virtual /* [local] */ HRESULT STDMETHODCALLTYPE SetFilter(
            /* [in] */ HACCESSOR hAccessor,
            /* [in] */ ULONG cRows,
            /* [in] */ DBCOMPAREOP __RPC_FAR CompareOps[  ],
            /* [in] */ void __RPC_FAR *pCriteriaData) = 0;

    };

#else /* C style interface */

    typedef struct IViewFilterVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IViewFilter __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IViewFilter __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IViewFilter __RPC_FAR * This);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetFilter )(
            IViewFilter __RPC_FAR * This,
            /* [in] */ HACCESSOR hAccessor,
            /* [out] */ ULONG __RPC_FAR *pcRows,
            /* [out] */ DBCOMPAREOP __RPC_FAR *__RPC_FAR pCompareOps[  ],
            /* [out] */ void __RPC_FAR *pCriteriaData);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetFilterBindings )(
            IViewFilter __RPC_FAR * This,
            /* [out] */ ULONG __RPC_FAR *pcBindings,
            /* [out] */ DBBINDING __RPC_FAR *__RPC_FAR *prgBindings);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetFilter )(
            IViewFilter __RPC_FAR * This,
            /* [in] */ HACCESSOR hAccessor,
            /* [in] */ ULONG cRows,
            /* [in] */ DBCOMPAREOP __RPC_FAR CompareOps[  ],
            /* [in] */ void __RPC_FAR *pCriteriaData);

        END_INTERFACE
    } IViewFilterVtbl;

    interface IViewFilter
    {
        CONST_VTBL struct IViewFilterVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IViewFilter_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IViewFilter_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IViewFilter_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IViewFilter_GetFilter(This,hAccessor,pcRows,pCompareOps,pCriteriaData)\
    (This)->lpVtbl -> GetFilter(This,hAccessor,pcRows,pCompareOps,pCriteriaData)

#define IViewFilter_GetFilterBindings(This,pcBindings,prgBindings)\
    (This)->lpVtbl -> GetFilterBindings(This,pcBindings,prgBindings)

#define IViewFilter_SetFilter(This,hAccessor,cRows,CompareOps,pCriteriaData)\
    (This)->lpVtbl -> SetFilter(This,hAccessor,cRows,CompareOps,pCriteriaData)

#endif /* COBJMACROS */

#endif /* C style interface */

/* [local] */ HRESULT STDMETHODCALLTYPE IViewFilter_GetFilter_Proxy(
    IViewFilter __RPC_FAR * This,
    /* [in] */ HACCESSOR hAccessor,
    /* [out] */ ULONG __RPC_FAR *pcRows,
    /* [out] */ DBCOMPAREOP __RPC_FAR *__RPC_FAR pCompareOps[  ],
    /* [out] */ void __RPC_FAR *pCriteriaData);

void __RPC_STUB IViewFilter_GetFilter_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

/* [call_as] */ HRESULT STDMETHODCALLTYPE IViewFilter_RemoteGetFilterBindings_Proxy(
    IViewFilter __RPC_FAR * This,
    /* [out][in] */ ULONG __RPC_FAR *pcBindings,
    /* [size_is][size_is][out] */ DBBINDING __RPC_FAR *__RPC_FAR *prgBindings);

void __RPC_STUB IViewFilter_RemoteGetFilterBindings_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

/* [local] */ HRESULT STDMETHODCALLTYPE IViewFilter_SetFilter_Proxy(
    IViewFilter __RPC_FAR * This,
    /* [in] */ HACCESSOR hAccessor,
    /* [in] */ ULONG cRows,
    /* [in] */ DBCOMPAREOP __RPC_FAR CompareOps[  ],
    /* [in] */ void __RPC_FAR *pCriteriaData);

void __RPC_STUB IViewFilter_SetFilter_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IViewFilter_INTERFACE_DEFINED__ */

#ifndef __IRowsetView_INTERFACE_DEFINED__
#define __IRowsetView_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IRowsetView
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object] */

EXTERN_C const IID IID_IRowsetView;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a99-2a1c-11ce-ade5-00aa0044773d")
    IRowsetView : public IUnknown
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE CreateView(
            /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppView) = 0;

        virtual HRESULT STDMETHODCALLTYPE GetView(
            /* [in] */ HCHAPTER hChapter,
            /* [in] */ REFIID riid,
            /* [out] */ HCHAPTER __RPC_FAR *phChapterSource,
            /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppView) = 0;

    };

#else /* C style interface */

    typedef struct IRowsetViewVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IRowsetView __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IRowsetView __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IRowsetView __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *CreateView )(
            IRowsetView __RPC_FAR * This,
            /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppView);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetView )(
            IRowsetView __RPC_FAR * This,
            /* [in] */ HCHAPTER hChapter,
            /* [in] */ REFIID riid,
            /* [out] */ HCHAPTER __RPC_FAR *phChapterSource,
            /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppView);

        END_INTERFACE
    } IRowsetViewVtbl;

    interface IRowsetView
    {
        CONST_VTBL struct IRowsetViewVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IRowsetView_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IRowsetView_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IRowsetView_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IRowsetView_CreateView(This,pUnkOuter,riid,ppView)\
    (This)->lpVtbl -> CreateView(This,pUnkOuter,riid,ppView)

#define IRowsetView_GetView(This,hChapter,riid,phChapterSource,ppView)\
    (This)->lpVtbl -> GetView(This,hChapter,riid,phChapterSource,ppView)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE IRowsetView_CreateView_Proxy(
    IRowsetView __RPC_FAR * This,
    /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
    /* [in] */ REFIID riid,
    /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppView);

void __RPC_STUB IRowsetView_CreateView_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IRowsetView_GetView_Proxy(
    IRowsetView __RPC_FAR * This,
    /* [in] */ HCHAPTER hChapter,
    /* [in] */ REFIID riid,
    /* [out] */ HCHAPTER __RPC_FAR *phChapterSource,
    /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppView);

void __RPC_STUB IRowsetView_GetView_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IRowsetView_INTERFACE_DEFINED__ */

/****************************************
 * Generated header for interface: __MIDL_itf_oledb_0087
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [local] */

#endif // OLEDBVER >= 0x0150
//@@@- V1.5
//@@@+ V2.0
#if( OLEDBVER >= 0x0200 )

extern RPC_IF_HANDLE __MIDL_itf_oledb_0087_v0_0_c_ifspec;
extern RPC_IF_HANDLE __MIDL_itf_oledb_0087_v0_0_s_ifspec;

#ifndef __IRowsetExactScroll_INTERFACE_DEFINED__
#define __IRowsetExactScroll_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IRowsetExactScroll
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object][local] */

EXTERN_C const IID IID_IRowsetExactScroll;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a7f-2a1c-11ce-ade5-00aa0044773d")
    IRowsetExactScroll : public IRowsetScroll
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE GetExactPosition(
            /* [in] */ HCHAPTER hChapter,
            /* [in] */ ULONG cbBookmark,
            /* [size_is][in] */ const BYTE __RPC_FAR *pBookmark,
            /* [out] */ ULONG __RPC_FAR *pulPosition,
            /* [out] */ ULONG __RPC_FAR *pcRows) = 0;

    };

#else /* C style interface */

    typedef struct IRowsetExactScrollVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IRowsetExactScroll __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IRowsetExactScroll __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IRowsetExactScroll __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *AddRefRows )(
            IRowsetExactScroll __RPC_FAR * This,
            /* [in] */ ULONG cRows,
            /* [size_is][in] */ const HROW __RPC_FAR rghRows[  ],
            /* [size_is][out] */ ULONG __RPC_FAR rgRefCounts[  ],
            /* [size_is][out] */ DBROWSTATUS __RPC_FAR rgRowStatus[  ]);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetData )(
            IRowsetExactScroll __RPC_FAR * This,
            /* [in] */ HROW hRow,
            /* [in] */ HACCESSOR hAccessor,
            /* [out] */ void __RPC_FAR *pData);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetNextRows )(
            IRowsetExactScroll __RPC_FAR * This,
            /* [in] */ HCHAPTER hReserved,
            /* [in] */ LONG lRowsOffset,
            /* [in] */ LONG cRows,
            /* [out] */ ULONG __RPC_FAR *pcRowsObtained,
            /* [size_is][size_is][out] */ HROW __RPC_FAR *__RPC_FAR *prghRows);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *ReleaseRows )(
            IRowsetExactScroll __RPC_FAR * This,
            /* [in] */ ULONG cRows,
            /* [size_is][in] */ const HROW __RPC_FAR rghRows[  ],
            /* [size_is][in] */ DBROWOPTIONS __RPC_FAR rgRowOptions[  ],
            /* [size_is][out] */ ULONG __RPC_FAR rgRefCounts[  ],
            /* [size_is][out] */ DBROWSTATUS __RPC_FAR rgRowStatus[  ]);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RestartPosition )(
            IRowsetExactScroll __RPC_FAR * This,
            /* [in] */ HCHAPTER hReserved);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Compare )(
            IRowsetExactScroll __RPC_FAR * This,
            /* [in] */ HCHAPTER hReserved,
            /* [in] */ ULONG cbBookmark1,
            /* [size_is][in] */ const BYTE __RPC_FAR *pBookmark1,
            /* [in] */ ULONG cbBookmark2,
            /* [size_is][in] */ const BYTE __RPC_FAR *pBookmark2,
            /* [out] */ DBCOMPARE __RPC_FAR *pComparison);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetRowsAt )(
            IRowsetExactScroll __RPC_FAR * This,
            /* [in] */ HWATCHREGION hReserved1,
            /* [in] */ HCHAPTER hReserved2,
            /* [in] */ ULONG cbBookmark,
            /* [size_is][in] */ const BYTE __RPC_FAR *pBookmark,
            /* [in] */ LONG lRowsOffset,
            /* [in] */ LONG cRows,
            /* [out] */ ULONG __RPC_FAR *pcRowsObtained,
            /* [size_is][size_is][out] */ HROW __RPC_FAR *__RPC_FAR *prghRows);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetRowsByBookmark )(
            IRowsetExactScroll __RPC_FAR * This,
            /* [in] */ HCHAPTER hReserved,
            /* [in] */ ULONG cRows,
            /* [size_is][in] */ const ULONG __RPC_FAR rgcbBookmarks[  ],
            /* [size_is][in] */ const BYTE __RPC_FAR *__RPC_FAR rgpBookmarks[  ],
            /* [size_is][out] */ HROW __RPC_FAR rghRows[  ],
            /* [size_is][out] */ DBROWSTATUS __RPC_FAR rgRowStatus[  ]);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Hash )(
            IRowsetExactScroll __RPC_FAR * This,
            /* [in] */ HCHAPTER hReserved,
            /* [in] */ ULONG cBookmarks,
            /* [size_is][in] */ const ULONG __RPC_FAR rgcbBookmarks[  ],
            /* [size_is][in] */ const BYTE __RPC_FAR *__RPC_FAR rgpBookmarks[  ],
            /* [size_is][out] */ DWORD __RPC_FAR rgHashedValues[  ],
            /* [size_is][out] */ DBROWSTATUS __RPC_FAR rgBookmarkStatus[  ]);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetApproximatePosition )(
            IRowsetExactScroll __RPC_FAR * This,
            /* [in] */ HCHAPTER hReserved,
            /* [in] */ ULONG cbBookmark,
            /* [size_is][in] */ const BYTE __RPC_FAR *pBookmark,
            /* [out] */ ULONG __RPC_FAR *pulPosition,
            /* [out] */ ULONG __RPC_FAR *pcRows);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetRowsAtRatio )(
            IRowsetExactScroll __RPC_FAR * This,
            /* [in] */ HWATCHREGION hReserved1,
            /* [in] */ HCHAPTER hReserved2,
            /* [in] */ ULONG ulNumerator,
            /* [in] */ ULONG ulDenominator,
            /* [in] */ LONG cRows,
            /* [out] */ ULONG __RPC_FAR *pcRowsObtained,
            /* [size_is][size_is][out] */ HROW __RPC_FAR *__RPC_FAR *prghRows);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetExactPosition )(
            IRowsetExactScroll __RPC_FAR * This,
            /* [in] */ HCHAPTER hChapter,
            /* [in] */ ULONG cbBookmark,
            /* [size_is][in] */ const BYTE __RPC_FAR *pBookmark,
            /* [out] */ ULONG __RPC_FAR *pulPosition,
            /* [out] */ ULONG __RPC_FAR *pcRows);

        END_INTERFACE
    } IRowsetExactScrollVtbl;

    interface IRowsetExactScroll
    {
        CONST_VTBL struct IRowsetExactScrollVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IRowsetExactScroll_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IRowsetExactScroll_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IRowsetExactScroll_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IRowsetExactScroll_AddRefRows(This,cRows,rghRows,rgRefCounts,rgRowStatus)\
    (This)->lpVtbl -> AddRefRows(This,cRows,rghRows,rgRefCounts,rgRowStatus)

#define IRowsetExactScroll_GetData(This,hRow,hAccessor,pData)\
    (This)->lpVtbl -> GetData(This,hRow,hAccessor,pData)

#define IRowsetExactScroll_GetNextRows(This,hReserved,lRowsOffset,cRows,pcRowsObtained,prghRows)\
    (This)->lpVtbl -> GetNextRows(This,hReserved,lRowsOffset,cRows,pcRowsObtained,prghRows)

#define IRowsetExactScroll_ReleaseRows(This,cRows,rghRows,rgRowOptions,rgRefCounts,rgRowStatus)\
    (This)->lpVtbl -> ReleaseRows(This,cRows,rghRows,rgRowOptions,rgRefCounts,rgRowStatus)

#define IRowsetExactScroll_RestartPosition(This,hReserved)\
    (This)->lpVtbl -> RestartPosition(This,hReserved)

#define IRowsetExactScroll_Compare(This,hReserved,cbBookmark1,pBookmark1,cbBookmark2,pBookmark2,pComparison)\
    (This)->lpVtbl -> Compare(This,hReserved,cbBookmark1,pBookmark1,cbBookmark2,pBookmark2,pComparison)

#define IRowsetExactScroll_GetRowsAt(This,hReserved1,hReserved2,cbBookmark,pBookmark,lRowsOffset,cRows,pcRowsObtained,prghRows)\
    (This)->lpVtbl -> GetRowsAt(This,hReserved1,hReserved2,cbBookmark,pBookmark,lRowsOffset,cRows,pcRowsObtained,prghRows)

#define IRowsetExactScroll_GetRowsByBookmark(This,hReserved,cRows,rgcbBookmarks,rgpBookmarks,rghRows,rgRowStatus)\
    (This)->lpVtbl -> GetRowsByBookmark(This,hReserved,cRows,rgcbBookmarks,rgpBookmarks,rghRows,rgRowStatus)

#define IRowsetExactScroll_Hash(This,hReserved,cBookmarks,rgcbBookmarks,rgpBookmarks,rgHashedValues,rgBookmarkStatus)\
    (This)->lpVtbl -> Hash(This,hReserved,cBookmarks,rgcbBookmarks,rgpBookmarks,rgHashedValues,rgBookmarkStatus)

#define IRowsetExactScroll_GetApproximatePosition(This,hReserved,cbBookmark,pBookmark,pulPosition,pcRows)\
    (This)->lpVtbl -> GetApproximatePosition(This,hReserved,cbBookmark,pBookmark,pulPosition,pcRows)

#define IRowsetExactScroll_GetRowsAtRatio(This,hReserved1,hReserved2,ulNumerator,ulDenominator,cRows,pcRowsObtained,prghRows)\
    (This)->lpVtbl -> GetRowsAtRatio(This,hReserved1,hReserved2,ulNumerator,ulDenominator,cRows,pcRowsObtained,prghRows)

#define IRowsetExactScroll_GetExactPosition(This,hChapter,cbBookmark,pBookmark,pulPosition,pcRows)\
    (This)->lpVtbl -> GetExactPosition(This,hChapter,cbBookmark,pBookmark,pulPosition,pcRows)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE IRowsetExactScroll_GetExactPosition_Proxy(
    IRowsetExactScroll __RPC_FAR * This,
    /* [in] */ HCHAPTER hChapter,
    /* [in] */ ULONG cbBookmark,
    /* [size_is][in] */ const BYTE __RPC_FAR *pBookmark,
    /* [out] */ ULONG __RPC_FAR *pulPosition,
    /* [out] */ ULONG __RPC_FAR *pcRows);

void __RPC_STUB IRowsetExactScroll_GetExactPosition_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IRowsetExactScroll_INTERFACE_DEFINED__ */

/****************************************
 * Generated header for interface: __MIDL_itf_oledb_0088
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [local] */

#endif // OLEDBVER >= 0x0200
//@@@- V2.0

extern RPC_IF_HANDLE __MIDL_itf_oledb_0088_v0_0_c_ifspec;
extern RPC_IF_HANDLE __MIDL_itf_oledb_0088_v0_0_s_ifspec;

#ifndef __IRowsetChange_INTERFACE_DEFINED__
#define __IRowsetChange_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IRowsetChange
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object][local] */

EXTERN_C const IID IID_IRowsetChange;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a05-2a1c-11ce-ade5-00aa0044773d")
    IRowsetChange : public IUnknown
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE DeleteRows(
            /* [in] */ HCHAPTER hReserved,
            /* [in] */ ULONG cRows,
            /* [size_is][in] */ const HROW __RPC_FAR rghRows[  ],
            /* [size_is][out] */ DBROWSTATUS __RPC_FAR rgRowStatus[  ]) = 0;

        virtual HRESULT STDMETHODCALLTYPE SetData(
            /* [in] */ HROW hRow,
            /* [in] */ HACCESSOR hAccessor,
            /* [in] */ void __RPC_FAR *pData) = 0;

        virtual HRESULT STDMETHODCALLTYPE InsertRow(
            /* [in] */ HCHAPTER hReserved,
            /* [in] */ HACCESSOR hAccessor,
            /* [in] */ void __RPC_FAR *pData,
            /* [out] */ HROW __RPC_FAR *phRow) = 0;

    };

#else /* C style interface */

    typedef struct IRowsetChangeVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IRowsetChange __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IRowsetChange __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IRowsetChange __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *DeleteRows )(
            IRowsetChange __RPC_FAR * This,
            /* [in] */ HCHAPTER hReserved,
            /* [in] */ ULONG cRows,
            /* [size_is][in] */ const HROW __RPC_FAR rghRows[  ],
            /* [size_is][out] */ DBROWSTATUS __RPC_FAR rgRowStatus[  ]);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetData )(
            IRowsetChange __RPC_FAR * This,
            /* [in] */ HROW hRow,
            /* [in] */ HACCESSOR hAccessor,
            /* [in] */ void __RPC_FAR *pData);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *InsertRow )(
            IRowsetChange __RPC_FAR * This,
            /* [in] */ HCHAPTER hReserved,
            /* [in] */ HACCESSOR hAccessor,
            /* [in] */ void __RPC_FAR *pData,
            /* [out] */ HROW __RPC_FAR *phRow);

        END_INTERFACE
    } IRowsetChangeVtbl;

    interface IRowsetChange
    {
        CONST_VTBL struct IRowsetChangeVtbl __RPC_FAR *lpVtbl;

};

#ifdef COBJMACROS

#define IRowsetChange_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IRowsetChange_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IRowsetChange_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IRowsetChange_DeleteRows(This,hReserved,cRows,rghRows,rgRowStatus)\
    (This)->lpVtbl -> DeleteRows(This,hReserved,cRows,rghRows,rgRowStatus)

#define IRowsetChange_SetData(This,hRow,hAccessor,pData)\
    (This)->lpVtbl -> SetData(This,hRow,hAccessor,pData)

#define IRowsetChange_InsertRow(This,hReserved,hAccessor,pData,phRow)\
    (This)->lpVtbl -> InsertRow(This,hReserved,hAccessor,pData,phRow)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE IRowsetChange_DeleteRows_Proxy(
    IRowsetChange __RPC_FAR * This,
    /* [in] */ HCHAPTER hReserved,
    /* [in] */ ULONG cRows,
    /* [size_is][in] */ const HROW __RPC_FAR rghRows[  ],
    /* [size_is][out] */ DBROWSTATUS __RPC_FAR rgRowStatus[  ]);

void __RPC_STUB IRowsetChange_DeleteRows_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IRowsetChange_SetData_Proxy(
    IRowsetChange __RPC_FAR * This,
    /* [in] */ HROW hRow,
    /* [in] */ HACCESSOR hAccessor,
    /* [in] */ void __RPC_FAR *pData);

void __RPC_STUB IRowsetChange_SetData_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IRowsetChange_InsertRow_Proxy(
    IRowsetChange __RPC_FAR * This,
    /* [in] */ HCHAPTER hReserved,
    /* [in] */ HACCESSOR hAccessor,
    /* [in] */ void __RPC_FAR *pData,
    /* [out] */ HROW __RPC_FAR *phRow);

void __RPC_STUB IRowsetChange_InsertRow_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IRowsetChange_INTERFACE_DEFINED__ */

#ifndef __IRowsetUpdate_INTERFACE_DEFINED__
#define __IRowsetUpdate_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IRowsetUpdate
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object][local] */

typedef DWORD DBPENDINGSTATUS;

enum DBPENDINGSTATUSENUM
    {DBPENDINGSTATUS_NEW= 0x1,
DBPENDINGSTATUS_CHANGED= 0x2,
DBPENDINGSTATUS_DELETED= 0x4,
DBPENDINGSTATUS_UNCHANGED= 0x8,
DBPENDINGSTATUS_INVALIDROW= 0x10
    };

EXTERN_C const IID IID_IRowsetUpdate;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a6d-2a1c-11ce-ade5-00aa0044773d")
    IRowsetUpdate : public IRowsetChange
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE GetOriginalData(
            /* [in] */ HROW hRow,
            /* [in] */ HACCESSOR hAccessor,
            /* [out] */ void __RPC_FAR *pData) = 0;

        virtual HRESULT STDMETHODCALLTYPE GetPendingRows(
            /* [in] */ HCHAPTER hReserved,
            /* [in] */ DBPENDINGSTATUS dwRowStatus,
            /* [out][in] */ ULONG __RPC_FAR *pcPendingRows,
            /* [size_is][size_is][out] */ HROW __RPC_FAR *__RPC_FAR *prgPendingRows,
            /* [size_is][size_is][out] */ DBPENDINGSTATUS __RPC_FAR *__RPC_FAR *prgPendingStatus) = 0;

        virtual HRESULT STDMETHODCALLTYPE GetRowStatus(
            /* [in] */ HCHAPTER hReserved,
            /* [in] */ ULONG cRows,
            /* [size_is][in] */ const HROW __RPC_FAR rghRows[  ],
            /* [size_is][out] */ DBPENDINGSTATUS __RPC_FAR rgPendingStatus[  ]) = 0;

        virtual HRESULT STDMETHODCALLTYPE Undo(
            /* [in] */ HCHAPTER hReserved,
            /* [in] */ ULONG cRows,
            /* [size_is][in] */ const HROW __RPC_FAR rghRows[  ],
            /* [out][in] */ ULONG __RPC_FAR *pcRowsUndone,
            /* [size_is][size_is][out] */ HROW __RPC_FAR *__RPC_FAR *prgRowsUndone,
            /* [size_is][size_is][out] */ DBROWSTATUS __RPC_FAR *__RPC_FAR *prgRowStatus) = 0;

        virtual HRESULT STDMETHODCALLTYPE Update(
            /* [in] */ HCHAPTER hReserved,
            /* [in] */ ULONG cRows,
            /* [size_is][in] */ const HROW __RPC_FAR rghRows[  ],
            /* [out][in] */ ULONG __RPC_FAR *pcRows,
            /* [size_is][size_is][out] */ HROW __RPC_FAR *__RPC_FAR *prgRows,
            /* [size_is][size_is][out] */ DBROWSTATUS __RPC_FAR *__RPC_FAR *prgRowStatus) = 0;

    };

#else /* C style interface */

    typedef struct IRowsetUpdateVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IRowsetUpdate __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IRowsetUpdate __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IRowsetUpdate __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *DeleteRows )(
            IRowsetUpdate __RPC_FAR * This,
            /* [in] */ HCHAPTER hReserved,
            /* [in] */ ULONG cRows,
            /* [size_is][in] */ const HROW __RPC_FAR rghRows[  ],
            /* [size_is][out] */ DBROWSTATUS __RPC_FAR rgRowStatus[  ]);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetData )(
            IRowsetUpdate __RPC_FAR * This,
            /* [in] */ HROW hRow,
            /* [in] */ HACCESSOR hAccessor,
            /* [in] */ void __RPC_FAR *pData);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *InsertRow )(
            IRowsetUpdate __RPC_FAR * This,
            /* [in] */ HCHAPTER hReserved,
            /* [in] */ HACCESSOR hAccessor,
            /* [in] */ void __RPC_FAR *pData,
            /* [out] */ HROW __RPC_FAR *phRow);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetOriginalData )(
            IRowsetUpdate __RPC_FAR * This,
            /* [in] */ HROW hRow,
            /* [in] */ HACCESSOR hAccessor,
            /* [out] */ void __RPC_FAR *pData);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetPendingRows )(
            IRowsetUpdate __RPC_FAR * This,
            /* [in] */ HCHAPTER hReserved,
            /* [in] */ DBPENDINGSTATUS dwRowStatus,
            /* [out][in] */ ULONG __RPC_FAR *pcPendingRows,
            /* [size_is][size_is][out] */ HROW __RPC_FAR *__RPC_FAR *prgPendingRows,
            /* [size_is][size_is][out] */ DBPENDINGSTATUS __RPC_FAR *__RPC_FAR *prgPendingStatus);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetRowStatus )(
            IRowsetUpdate __RPC_FAR * This,
            /* [in] */ HCHAPTER hReserved,
            /* [in] */ ULONG cRows,
            /* [size_is][in] */ const HROW __RPC_FAR rghRows[  ],
            /* [size_is][out] */ DBPENDINGSTATUS __RPC_FAR rgPendingStatus[  ]);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Undo )(
            IRowsetUpdate __RPC_FAR * This,
            /* [in] */ HCHAPTER hReserved,
            /* [in] */ ULONG cRows,
            /* [size_is][in] */ const HROW __RPC_FAR rghRows[  ],
            /* [out][in] */ ULONG __RPC_FAR *pcRowsUndone,
            /* [size_is][size_is][out] */ HROW __RPC_FAR *__RPC_FAR *prgRowsUndone,
            /* [size_is][size_is][out] */ DBROWSTATUS __RPC_FAR *__RPC_FAR *prgRowStatus);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Update )(
            IRowsetUpdate __RPC_FAR * This,
            /* [in] */ HCHAPTER hReserved,
            /* [in] */ ULONG cRows,
            /* [size_is][in] */ const HROW __RPC_FAR rghRows[  ],
            /* [out][in] */ ULONG __RPC_FAR *pcRows,
            /* [size_is][size_is][out] */ HROW __RPC_FAR *__RPC_FAR *prgRows,
            /* [size_is][size_is][out] */ DBROWSTATUS __RPC_FAR *__RPC_FAR *prgRowStatus);

        END_INTERFACE
    } IRowsetUpdateVtbl;

    interface IRowsetUpdate
    {
        CONST_VTBL struct IRowsetUpdateVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IRowsetUpdate_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IRowsetUpdate_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IRowsetUpdate_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IRowsetUpdate_DeleteRows(This,hReserved,cRows,rghRows,rgRowStatus)\
    (This)->lpVtbl -> DeleteRows(This,hReserved,cRows,rghRows,rgRowStatus)

#define IRowsetUpdate_SetData(This,hRow,hAccessor,pData)\
    (This)->lpVtbl -> SetData(This,hRow,hAccessor,pData)

#define IRowsetUpdate_InsertRow(This,hReserved,hAccessor,pData,phRow)\
    (This)->lpVtbl -> InsertRow(This,hReserved,hAccessor,pData,phRow)

#define IRowsetUpdate_GetOriginalData(This,hRow,hAccessor,pData)\
    (This)->lpVtbl -> GetOriginalData(This,hRow,hAccessor,pData)

#define IRowsetUpdate_GetPendingRows(This,hReserved,dwRowStatus,pcPendingRows,prgPendingRows,prgPendingStatus)\
    (This)->lpVtbl -> GetPendingRows(This,hReserved,dwRowStatus,pcPendingRows,prgPendingRows,prgPendingStatus)

#define IRowsetUpdate_GetRowStatus(This,hReserved,cRows,rghRows,rgPendingStatus)\
    (This)->lpVtbl -> GetRowStatus(This,hReserved,cRows,rghRows,rgPendingStatus)

#define IRowsetUpdate_Undo(This,hReserved,cRows,rghRows,pcRowsUndone,prgRowsUndone,prgRowStatus)\
    (This)->lpVtbl -> Undo(This,hReserved,cRows,rghRows,pcRowsUndone,prgRowsUndone,prgRowStatus)

#define IRowsetUpdate_Update(This,hReserved,cRows,rghRows,pcRows,prgRows,prgRowStatus)\
    (This)->lpVtbl -> Update(This,hReserved,cRows,rghRows,pcRows,prgRows,prgRowStatus)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE IRowsetUpdate_GetOriginalData_Proxy(
    IRowsetUpdate __RPC_FAR * This,
    /* [in] */ HROW hRow,
    /* [in] */ HACCESSOR hAccessor,
    /* [out] */ void __RPC_FAR *pData);

void __RPC_STUB IRowsetUpdate_GetOriginalData_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IRowsetUpdate_GetPendingRows_Proxy(
    IRowsetUpdate __RPC_FAR * This,
    /* [in] */ HCHAPTER hReserved,
    /* [in] */ DBPENDINGSTATUS dwRowStatus,
    /* [out][in] */ ULONG __RPC_FAR *pcPendingRows,
    /* [size_is][size_is][out] */ HROW __RPC_FAR *__RPC_FAR *prgPendingRows,
    /* [size_is][size_is][out] */ DBPENDINGSTATUS __RPC_FAR *__RPC_FAR *prgPendingStatus);

void __RPC_STUB IRowsetUpdate_GetPendingRows_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IRowsetUpdate_GetRowStatus_Proxy(
    IRowsetUpdate __RPC_FAR * This,
    /* [in] */ HCHAPTER hReserved,
    /* [in] */ ULONG cRows,
    /* [size_is][in] */ const HROW __RPC_FAR rghRows[  ],
    /* [size_is][out] */ DBPENDINGSTATUS __RPC_FAR rgPendingStatus[  ]);

void __RPC_STUB IRowsetUpdate_GetRowStatus_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IRowsetUpdate_Undo_Proxy(
    IRowsetUpdate __RPC_FAR * This,
    /* [in] */ HCHAPTER hReserved,
    /* [in] */ ULONG cRows,
    /* [size_is][in] */ const HROW __RPC_FAR rghRows[  ],
    /* [out][in] */ ULONG __RPC_FAR *pcRowsUndone,
    /* [size_is][size_is][out] */ HROW __RPC_FAR *__RPC_FAR *prgRowsUndone,
    /* [size_is][size_is][out] */ DBROWSTATUS __RPC_FAR *__RPC_FAR *prgRowStatus);

void __RPC_STUB IRowsetUpdate_Undo_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IRowsetUpdate_Update_Proxy(
    IRowsetUpdate __RPC_FAR * This,
    /* [in] */ HCHAPTER hReserved,
    /* [in] */ ULONG cRows,
    /* [size_is][in] */ const HROW __RPC_FAR rghRows[  ],
    /* [out][in] */ ULONG __RPC_FAR *pcRows,
    /* [size_is][size_is][out] */ HROW __RPC_FAR *__RPC_FAR *prgRows,
    /* [size_is][size_is][out] */ DBROWSTATUS __RPC_FAR *__RPC_FAR *prgRowStatus);

void __RPC_STUB IRowsetUpdate_Update_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IRowsetUpdate_INTERFACE_DEFINED__ */

/****************************************
 * Generated header for interface: __MIDL_itf_oledb_0090
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [local] */

//@@@+ V2.0
#if( OLEDBVER >= 0x0200 )

extern RPC_IF_HANDLE __MIDL_itf_oledb_0090_v0_0_c_ifspec;
extern RPC_IF_HANDLE __MIDL_itf_oledb_0090_v0_0_s_ifspec;

#ifndef __IRowsetNextRowset_INTERFACE_DEFINED__
#define __IRowsetNextRowset_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IRowsetNextRowset
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object][local] */

EXTERN_C const IID IID_IRowsetNextRowset;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a72-2a1c-11ce-ade5-00aa0044773d")
    IRowsetNextRowset : public IUnknown
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE GetNextRowset(
            /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppNextRowset) = 0;

    };

#else /* C style interface */

    typedef struct IRowsetNextRowsetVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IRowsetNextRowset __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IRowsetNextRowset __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IRowsetNextRowset __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetNextRowset )(
            IRowsetNextRowset __RPC_FAR * This,
            /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppNextRowset);

        END_INTERFACE
    } IRowsetNextRowsetVtbl;

    interface IRowsetNextRowset
    {
        CONST_VTBL struct IRowsetNextRowsetVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IRowsetNextRowset_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IRowsetNextRowset_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IRowsetNextRowset_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IRowsetNextRowset_GetNextRowset(This,pUnkOuter,riid,ppNextRowset)\
    (This)->lpVtbl -> GetNextRowset(This,pUnkOuter,riid,ppNextRowset)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE IRowsetNextRowset_GetNextRowset_Proxy(
    IRowsetNextRowset __RPC_FAR * This,
    /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
    /* [in] */ REFIID riid,
    /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppNextRowset);

void __RPC_STUB IRowsetNextRowset_GetNextRowset_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IRowsetNextRowset_INTERFACE_DEFINED__ */

/****************************************
 * Generated header for interface: __MIDL_itf_oledb_0091
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [local] */

#endif // OLEDBVER >= 0x0200
//@@@- V2.0

extern RPC_IF_HANDLE __MIDL_itf_oledb_0091_v0_0_c_ifspec;
extern RPC_IF_HANDLE __MIDL_itf_oledb_0091_v0_0_s_ifspec;

#ifndef __IRowsetIdentity_INTERFACE_DEFINED__
#define __IRowsetIdentity_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IRowsetIdentity
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object] */

EXTERN_C const IID IID_IRowsetIdentity;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a09-2a1c-11ce-ade5-00aa0044773d")
    IRowsetIdentity : public IUnknown
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE IsSameRow(
            /* [in] */ HROW hThisRow,
            /* [in] */ HROW hThatRow) = 0;

    };

#else /* C style interface */

    typedef struct IRowsetIdentityVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IRowsetIdentity __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IRowsetIdentity __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IRowsetIdentity __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *IsSameRow )(
            IRowsetIdentity __RPC_FAR * This,
            /* [in] */ HROW hThisRow,
            /* [in] */ HROW hThatRow);

        END_INTERFACE
    } IRowsetIdentityVtbl;

    interface IRowsetIdentity
    {
        CONST_VTBL struct IRowsetIdentityVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IRowsetIdentity_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IRowsetIdentity_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IRowsetIdentity_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IRowsetIdentity_IsSameRow(This,hThisRow,hThatRow)\
    (This)->lpVtbl -> IsSameRow(This,hThisRow,hThatRow)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE IRowsetIdentity_IsSameRow_Proxy(
    IRowsetIdentity __RPC_FAR * This,
    /* [in] */ HROW hThisRow,
    /* [in] */ HROW hThatRow);

void __RPC_STUB IRowsetIdentity_IsSameRow_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IRowsetIdentity_INTERFACE_DEFINED__ */

/****************************************
 * Generated header for interface: __MIDL_itf_oledb_0092
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [local] */

//@@@+ V2.0
#if( OLEDBVER >= 0x0200 )

extern RPC_IF_HANDLE __MIDL_itf_oledb_0092_v0_0_c_ifspec;
extern RPC_IF_HANDLE __MIDL_itf_oledb_0092_v0_0_s_ifspec;

#ifndef __IRowsetNewRowAfter_INTERFACE_DEFINED__
#define __IRowsetNewRowAfter_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IRowsetNewRowAfter
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object][local] */

EXTERN_C const IID IID_IRowsetNewRowAfter;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a71-2a1c-11ce-ade5-00aa0044773d")
    IRowsetNewRowAfter : public IUnknown
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE SetNewDataAfter(
            /* [in] */ HCHAPTER hChapter,
            /* [in] */ ULONG cbbmPrevious,
            /* [size_is][in] */ const BYTE __RPC_FAR *pbmPrevious,
            /* [in] */ HACCESSOR hAccessor,
            /* [in] */ BYTE __RPC_FAR *pData,
            /* [out] */ HROW __RPC_FAR *phRow) = 0;

    };

#else /* C style interface */

    typedef struct IRowsetNewRowAfterVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IRowsetNewRowAfter __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IRowsetNewRowAfter __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IRowsetNewRowAfter __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetNewDataAfter )(
            IRowsetNewRowAfter __RPC_FAR * This,
            /* [in] */ HCHAPTER hChapter,
            /* [in] */ ULONG cbbmPrevious,
            /* [size_is][in] */ const BYTE __RPC_FAR *pbmPrevious,
            /* [in] */ HACCESSOR hAccessor,
            /* [in] */ BYTE __RPC_FAR *pData,
            /* [out] */ HROW __RPC_FAR *phRow);

        END_INTERFACE
    } IRowsetNewRowAfterVtbl;

    interface IRowsetNewRowAfter
    {
        CONST_VTBL struct IRowsetNewRowAfterVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IRowsetNewRowAfter_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IRowsetNewRowAfter_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IRowsetNewRowAfter_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IRowsetNewRowAfter_SetNewDataAfter(This,hChapter,cbbmPrevious,pbmPrevious,hAccessor,pData,phRow)\
    (This)->lpVtbl -> SetNewDataAfter(This,hChapter,cbbmPrevious,pbmPrevious,hAccessor,pData,phRow)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE IRowsetNewRowAfter_SetNewDataAfter_Proxy(
    IRowsetNewRowAfter __RPC_FAR * This,
    /* [in] */ HCHAPTER hChapter,
    /* [in] */ ULONG cbbmPrevious,
    /* [size_is][in] */ const BYTE __RPC_FAR *pbmPrevious,
    /* [in] */ HACCESSOR hAccessor,
    /* [in] */ BYTE __RPC_FAR *pData,
    /* [out] */ HROW __RPC_FAR *phRow);

void __RPC_STUB IRowsetNewRowAfter_SetNewDataAfter_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IRowsetNewRowAfter_INTERFACE_DEFINED__ */

#ifndef __IRowsetWithParameters_INTERFACE_DEFINED__
#define __IRowsetWithParameters_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IRowsetWithParameters
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object][local] */

EXTERN_C const IID IID_IRowsetWithParameters;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a6e-2a1c-11ce-ade5-00aa0044773d")
    IRowsetWithParameters : public IUnknown
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE GetParameterInfo(
            /* [out][in] */ ULONG __RPC_FAR *pcParams,
            /* [size_is][size_is][out] */ DBPARAMINFO __RPC_FAR *__RPC_FAR *prgParamInfo,
            /* [out] */ OLECHAR __RPC_FAR *__RPC_FAR *ppNamesBuffer) = 0;

        virtual HRESULT STDMETHODCALLTYPE Requery(
            /* [in] */ DBPARAMS __RPC_FAR *pParams,
            /* [out] */ ULONG __RPC_FAR *pulErrorParam,
            /* [out] */ HCHAPTER __RPC_FAR *phReserved) = 0;

    };

#else /* C style interface */

    typedef struct IRowsetWithParametersVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IRowsetWithParameters __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IRowsetWithParameters __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IRowsetWithParameters __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetParameterInfo )(
            IRowsetWithParameters __RPC_FAR * This,
            /* [out][in] */ ULONG __RPC_FAR *pcParams,
            /* [size_is][size_is][out] */ DBPARAMINFO __RPC_FAR *__RPC_FAR *prgParamInfo,
            /* [out] */ OLECHAR __RPC_FAR *__RPC_FAR *ppNamesBuffer);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Requery )(
            IRowsetWithParameters __RPC_FAR * This,
            /* [in] */ DBPARAMS __RPC_FAR *pParams,
            /* [out] */ ULONG __RPC_FAR *pulErrorParam,
            /* [out] */ HCHAPTER __RPC_FAR *phReserved);

        END_INTERFACE
    } IRowsetWithParametersVtbl;

    interface IRowsetWithParameters
    {
        CONST_VTBL struct IRowsetWithParametersVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IRowsetWithParameters_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IRowsetWithParameters_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IRowsetWithParameters_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IRowsetWithParameters_GetParameterInfo(This,pcParams,prgParamInfo,ppNamesBuffer)\
    (This)->lpVtbl -> GetParameterInfo(This,pcParams,prgParamInfo,ppNamesBuffer)

#define IRowsetWithParameters_Requery(This,pParams,pulErrorParam,phReserved)\
    (This)->lpVtbl -> Requery(This,pParams,pulErrorParam,phReserved)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE IRowsetWithParameters_GetParameterInfo_Proxy(
    IRowsetWithParameters __RPC_FAR * This,
    /* [out][in] */ ULONG __RPC_FAR *pcParams,
    /* [size_is][size_is][out] */ DBPARAMINFO __RPC_FAR *__RPC_FAR *prgParamInfo,
    /* [out] */ OLECHAR __RPC_FAR *__RPC_FAR *ppNamesBuffer);

void __RPC_STUB IRowsetWithParameters_GetParameterInfo_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IRowsetWithParameters_Requery_Proxy(
    IRowsetWithParameters __RPC_FAR * This,
    /* [in] */ DBPARAMS __RPC_FAR *pParams,
    /* [out] */ ULONG __RPC_FAR *pulErrorParam,
    /* [out] */ HCHAPTER __RPC_FAR *phReserved);

void __RPC_STUB IRowsetWithParameters_Requery_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IRowsetWithParameters_INTERFACE_DEFINED__ */

#ifndef __IRowsetAsynch_INTERFACE_DEFINED__
#define __IRowsetAsynch_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IRowsetAsynch
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object][local] */

EXTERN_C const IID IID_IRowsetAsynch;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a0f-2a1c-11ce-ade5-00aa0044773d")
    IRowsetAsynch : public IUnknown
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE RatioFinished(
            /* [out] */ ULONG __RPC_FAR *pulDenominator,
            /* [out] */ ULONG __RPC_FAR *pulNumerator,
            /* [out] */ ULONG __RPC_FAR *pcRows,
            /* [out] */ BOOL __RPC_FAR *pfNewRows) = 0;

        virtual HRESULT STDMETHODCALLTYPE Stop( void) = 0;

    };

#else /* C style interface */

    typedef struct IRowsetAsynchVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IRowsetAsynch __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IRowsetAsynch __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IRowsetAsynch __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RatioFinished )(
            IRowsetAsynch __RPC_FAR * This,
            /* [out] */ ULONG __RPC_FAR *pulDenominator,
            /* [out] */ ULONG __RPC_FAR *pulNumerator,
            /* [out] */ ULONG __RPC_FAR *pcRows,
            /* [out] */ BOOL __RPC_FAR *pfNewRows);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Stop )(
            IRowsetAsynch __RPC_FAR * This);

        END_INTERFACE

} IRowsetAsynchVtbl;

    interface IRowsetAsynch
    {
        CONST_VTBL struct IRowsetAsynchVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IRowsetAsynch_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IRowsetAsynch_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IRowsetAsynch_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IRowsetAsynch_RatioFinished(This,pulDenominator,pulNumerator,pcRows,pfNewRows)\
    (This)->lpVtbl -> RatioFinished(This,pulDenominator,pulNumerator,pcRows,pfNewRows)

#define IRowsetAsynch_Stop(This)\
    (This)->lpVtbl -> Stop(This)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE IRowsetAsynch_RatioFinished_Proxy(
    IRowsetAsynch __RPC_FAR * This,
    /* [out] */ ULONG __RPC_FAR *pulDenominator,
    /* [out] */ ULONG __RPC_FAR *pulNumerator,
    /* [out] */ ULONG __RPC_FAR *pcRows,
    /* [out] */ BOOL __RPC_FAR *pfNewRows);

void __RPC_STUB IRowsetAsynch_RatioFinished_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IRowsetAsynch_Stop_Proxy(
    IRowsetAsynch __RPC_FAR * This);

void __RPC_STUB IRowsetAsynch_Stop_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IRowsetAsynch_INTERFACE_DEFINED__ */

#ifndef __IRowsetKeys_INTERFACE_DEFINED__
#define __IRowsetKeys_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IRowsetKeys
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object][local] */

EXTERN_C const IID IID_IRowsetKeys;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a12-2a1c-11ce-ade5-00aa0044773d")
    IRowsetKeys : public IUnknown
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE ListKeys(
            /* [out][in] */ ULONG __RPC_FAR *pcColumns,
            /* [size_is][size_is][out] */ ULONG __RPC_FAR *__RPC_FAR *prgColumns) = 0;

    };

#else /* C style interface */

    typedef struct IRowsetKeysVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IRowsetKeys __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IRowsetKeys __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IRowsetKeys __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *ListKeys )(
            IRowsetKeys __RPC_FAR * This,
            /* [out][in] */ ULONG __RPC_FAR *pcColumns,
            /* [size_is][size_is][out] */ ULONG __RPC_FAR *__RPC_FAR *prgColumns);

        END_INTERFACE
    } IRowsetKeysVtbl;

    interface IRowsetKeys
    {
        CONST_VTBL struct IRowsetKeysVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IRowsetKeys_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IRowsetKeys_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IRowsetKeys_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IRowsetKeys_ListKeys(This,pcColumns,prgColumns)\
    (This)->lpVtbl -> ListKeys(This,pcColumns,prgColumns)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE IRowsetKeys_ListKeys_Proxy(
    IRowsetKeys __RPC_FAR * This,
    /* [out][in] */ ULONG __RPC_FAR *pcColumns,
    /* [size_is][size_is][out] */ ULONG __RPC_FAR *__RPC_FAR *prgColumns);

void __RPC_STUB IRowsetKeys_ListKeys_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IRowsetKeys_INTERFACE_DEFINED__ */

/****************************************
 * Generated header for interface: __MIDL_itf_oledb_0096
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [local] */

#endif // OLEDBVER >= 0x0200
//@@@- V2.0

extern RPC_IF_HANDLE __MIDL_itf_oledb_0096_v0_0_c_ifspec;
extern RPC_IF_HANDLE __MIDL_itf_oledb_0096_v0_0_s_ifspec;

#ifndef __IRowsetNotify_INTERFACE_DEFINED__
#define __IRowsetNotify_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IRowsetNotify
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object] */

EXTERN_C const IID IID_IRowsetNotify;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a83-2a1c-11ce-ade5-00aa0044773d")
    IRowsetNotify : public IUnknown
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE OnFieldChange(
            /* [in] */ IRowset __RPC_FAR *pRowset,
            /* [in] */ HROW hRow,
            /* [in] */ ULONG cColumns,
            /* [size_is][in] */ ULONG __RPC_FAR rgColumns[  ],
            /* [in] */ DBREASON eReason,
            /* [in] */ DBEVENTPHASE ePhase,
            /* [in] */ BOOL fCantDeny) = 0;

        virtual HRESULT STDMETHODCALLTYPE OnRowChange(
            /* [in] */ IRowset __RPC_FAR *pRowset,
            /* [in] */ ULONG cRows,
            /* [size_is][in] */ const HROW __RPC_FAR rghRows[  ],
            /* [in] */ DBREASON eReason,
            /* [in] */ DBEVENTPHASE ePhase,
            /* [in] */ BOOL fCantDeny) = 0;

        virtual HRESULT STDMETHODCALLTYPE OnRowsetChange(
            /* [in] */ IRowset __RPC_FAR *pRowset,
            /* [in] */ DBREASON eReason,
            /* [in] */ DBEVENTPHASE ePhase,
            /* [in] */ BOOL fCantDeny) = 0;

    };

#else /* C style interface */

    typedef struct IRowsetNotifyVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IRowsetNotify __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IRowsetNotify __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IRowsetNotify __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnFieldChange )(
            IRowsetNotify __RPC_FAR * This,
            /* [in] */ IRowset __RPC_FAR *pRowset,
            /* [in] */ HROW hRow,
            /* [in] */ ULONG cColumns,
            /* [size_is][in] */ ULONG __RPC_FAR rgColumns[  ],
            /* [in] */ DBREASON eReason,
            /* [in] */ DBEVENTPHASE ePhase,
            /* [in] */ BOOL fCantDeny);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnRowChange )(
            IRowsetNotify __RPC_FAR * This,
            /* [in] */ IRowset __RPC_FAR *pRowset,
            /* [in] */ ULONG cRows,
            /* [size_is][in] */ const HROW __RPC_FAR rghRows[  ],
            /* [in] */ DBREASON eReason,
            /* [in] */ DBEVENTPHASE ePhase,
            /* [in] */ BOOL fCantDeny);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnRowsetChange )(
            IRowsetNotify __RPC_FAR * This,
            /* [in] */ IRowset __RPC_FAR *pRowset,
            /* [in] */ DBREASON eReason,
            /* [in] */ DBEVENTPHASE ePhase,
            /* [in] */ BOOL fCantDeny);

        END_INTERFACE
    } IRowsetNotifyVtbl;

    interface IRowsetNotify
    {
        CONST_VTBL struct IRowsetNotifyVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IRowsetNotify_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IRowsetNotify_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IRowsetNotify_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IRowsetNotify_OnFieldChange(This,pRowset,hRow,cColumns,rgColumns,eReason,ePhase,fCantDeny)\
    (This)->lpVtbl -> OnFieldChange(This,pRowset,hRow,cColumns,rgColumns,eReason,ePhase,fCantDeny)

#define IRowsetNotify_OnRowChange(This,pRowset,cRows,rghRows,eReason,ePhase,fCantDeny)\
    (This)->lpVtbl -> OnRowChange(This,pRowset,cRows,rghRows,eReason,ePhase,fCantDeny)

#define IRowsetNotify_OnRowsetChange(This,pRowset,eReason,ePhase,fCantDeny)\
    (This)->lpVtbl -> OnRowsetChange(This,pRowset,eReason,ePhase,fCantDeny)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE IRowsetNotify_OnFieldChange_Proxy(
    IRowsetNotify __RPC_FAR * This,
    /* [in] */ IRowset __RPC_FAR *pRowset,
    /* [in] */ HROW hRow,
    /* [in] */ ULONG cColumns,
    /* [size_is][in] */ ULONG __RPC_FAR rgColumns[  ],
    /* [in] */ DBREASON eReason,
    /* [in] */ DBEVENTPHASE ePhase,
    /* [in] */ BOOL fCantDeny);

void __RPC_STUB IRowsetNotify_OnFieldChange_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IRowsetNotify_OnRowChange_Proxy(
    IRowsetNotify __RPC_FAR * This,
    /* [in] */ IRowset __RPC_FAR *pRowset,
    /* [in] */ ULONG cRows,
    /* [size_is][in] */ const HROW __RPC_FAR rghRows[  ],
    /* [in] */ DBREASON eReason,
    /* [in] */ DBEVENTPHASE ePhase,
    /* [in] */ BOOL fCantDeny);

void __RPC_STUB IRowsetNotify_OnRowChange_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IRowsetNotify_OnRowsetChange_Proxy(
    IRowsetNotify __RPC_FAR * This,
    /* [in] */ IRowset __RPC_FAR *pRowset,
    /* [in] */ DBREASON eReason,
    /* [in] */ DBEVENTPHASE ePhase,
    /* [in] */ BOOL fCantDeny);

void __RPC_STUB IRowsetNotify_OnRowsetChange_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IRowsetNotify_INTERFACE_DEFINED__ */

#ifndef __IRowsetIndex_INTERFACE_DEFINED__
#define __IRowsetIndex_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IRowsetIndex
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object][local] */

typedef DWORD DBSEEK;

enum DBSEEKENUM
    {DBSEEK_INVALID= 0,
DBSEEK_FIRSTEQ= 0x1,
DBSEEK_LASTEQ= 0x2,
DBSEEK_GE= 0x4,
DBSEEK_GT= 0x8,
DBSEEK_LE= 0x10,
DBSEEK_LT= 0x20
    };
typedef DWORD DBRANGE;

enum DBRANGEENUM
    {DBRANGE_INCLUSIVESTART= 0,
DBRANGE_INCLUSIVEEND= 0,
DBRANGE_EXCLUSIVESTART= 0x1,
DBRANGE_EXCLUSIVEEND= 0x2,
DBRANGE_EXCLUDENULLS= 0x4,
DBRANGE_PREFIX= 0x8,
DBRANGE_MATCH= 0x10
    };

EXTERN_C const IID IID_IRowsetIndex;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a82-2a1c-11ce-ade5-00aa0044773d")
    IRowsetIndex : public IUnknown
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE GetIndexInfo(
            /* [out][in] */ ULONG __RPC_FAR *pcKeyColumns,
            /* [size_is][size_is][out] */ DBINDEXCOLUMNDESC __RPC_FAR *__RPC_FAR *prgIndexColumnDesc,
            /* [out][in] */ ULONG __RPC_FAR *pcIndexProperties,
            /* [size_is][size_is][out] */ DBPROPSET __RPC_FAR *__RPC_FAR *prgIndexProperties) = 0;

        virtual HRESULT STDMETHODCALLTYPE Seek(
            /* [in] */ HACCESSOR hAccessor,
            /* [in] */ ULONG cKeyValues,
            /* [in] */ void __RPC_FAR *pData,
            /* [in] */ DBSEEK dwSeekOptions) = 0;

        virtual HRESULT STDMETHODCALLTYPE SetRange(
            /* [in] */ HACCESSOR hAccessor,
            /* [in] */ ULONG cStartKeyColumns,
            /* [in] */ void __RPC_FAR *pStartData,
            /* [in] */ ULONG cEndKeyColumns,
            /* [in] */ void __RPC_FAR *pEndData,
            /* [in] */ DBRANGE dwRangeOptions) = 0;

    };

#else /* C style interface */

    typedef struct IRowsetIndexVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IRowsetIndex __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IRowsetIndex __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IRowsetIndex __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetIndexInfo )(
            IRowsetIndex __RPC_FAR * This,
            /* [out][in] */ ULONG __RPC_FAR *pcKeyColumns,
            /* [size_is][size_is][out] */ DBINDEXCOLUMNDESC __RPC_FAR *__RPC_FAR *prgIndexColumnDesc,
            /* [out][in] */ ULONG __RPC_FAR *pcIndexProperties,
            /* [size_is][size_is][out] */ DBPROPSET __RPC_FAR *__RPC_FAR *prgIndexProperties);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Seek )(
            IRowsetIndex __RPC_FAR * This,
            /* [in] */ HACCESSOR hAccessor,
            /* [in] */ ULONG cKeyValues,
            /* [in] */ void __RPC_FAR *pData,
            /* [in] */ DBSEEK dwSeekOptions);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetRange )(
            IRowsetIndex __RPC_FAR * This,
            /* [in] */ HACCESSOR hAccessor,
            /* [in] */ ULONG cStartKeyColumns,
            /* [in] */ void __RPC_FAR *pStartData,
            /* [in] */ ULONG cEndKeyColumns,
            /* [in] */ void __RPC_FAR *pEndData,
            /* [in] */ DBRANGE dwRangeOptions);

        END_INTERFACE
    } IRowsetIndexVtbl;

    interface IRowsetIndex
    {
        CONST_VTBL struct IRowsetIndexVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IRowsetIndex_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IRowsetIndex_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IRowsetIndex_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IRowsetIndex_GetIndexInfo(This,pcKeyColumns,prgIndexColumnDesc,pcIndexProperties,prgIndexProperties)\
    (This)->lpVtbl -> GetIndexInfo(This,pcKeyColumns,prgIndexColumnDesc,pcIndexProperties,prgIndexProperties)

#define IRowsetIndex_Seek(This,hAccessor,cKeyValues,pData,dwSeekOptions)\
    (This)->lpVtbl -> Seek(This,hAccessor,cKeyValues,pData,dwSeekOptions)

#define IRowsetIndex_SetRange(This,hAccessor,cStartKeyColumns,pStartData,cEndKeyColumns,pEndData,dwRangeOptions)\
    (This)->lpVtbl -> SetRange(This,hAccessor,cStartKeyColumns,pStartData,cEndKeyColumns,pEndData,dwRangeOptions)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE IRowsetIndex_GetIndexInfo_Proxy(
    IRowsetIndex __RPC_FAR * This,
    /* [out][in] */ ULONG __RPC_FAR *pcKeyColumns,
    /* [size_is][size_is][out] */ DBINDEXCOLUMNDESC __RPC_FAR *__RPC_FAR *prgIndexColumnDesc,
    /* [out][in] */ ULONG __RPC_FAR *pcIndexProperties,
    /* [size_is][size_is][out] */ DBPROPSET __RPC_FAR *__RPC_FAR *prgIndexProperties);

void __RPC_STUB IRowsetIndex_GetIndexInfo_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IRowsetIndex_Seek_Proxy(
    IRowsetIndex __RPC_FAR * This,
    /* [in] */ HACCESSOR hAccessor,
    /* [in] */ ULONG cKeyValues,
    /* [in] */ void __RPC_FAR *pData,
    /* [in] */ DBSEEK dwSeekOptions);

void __RPC_STUB IRowsetIndex_Seek_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IRowsetIndex_SetRange_Proxy(
    IRowsetIndex __RPC_FAR * This,
    /* [in] */ HACCESSOR hAccessor,
    /* [in] */ ULONG cStartKeyColumns,
    /* [in] */ void __RPC_FAR *pStartData,
    /* [in] */ ULONG cEndKeyColumns,
    /* [in] */ void __RPC_FAR *pEndData,
    /* [in] */ DBRANGE dwRangeOptions);

void __RPC_STUB IRowsetIndex_SetRange_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IRowsetIndex_INTERFACE_DEFINED__ */

/****************************************
 * Generated header for interface: __MIDL_itf_oledb_0098
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [local] */

//@@@+ V2.0
#if( OLEDBVER >= 0x0200 )

extern RPC_IF_HANDLE __MIDL_itf_oledb_0098_v0_0_c_ifspec;
extern RPC_IF_HANDLE __MIDL_itf_oledb_0098_v0_0_s_ifspec;

#ifndef __IRowsetWatchAll_INTERFACE_DEFINED__
#define __IRowsetWatchAll_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IRowsetWatchAll
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object][local] */

EXTERN_C const IID IID_IRowsetWatchAll;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a73-2a1c-11ce-ade5-00aa0044773d")
    IRowsetWatchAll : public IUnknown
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE Acknowledge( void) = 0;

        virtual HRESULT STDMETHODCALLTYPE Start( void) = 0;

        virtual HRESULT STDMETHODCALLTYPE StopWatching( void) = 0;

    };

#else /* C style interface */

    typedef struct IRowsetWatchAllVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IRowsetWatchAll __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IRowsetWatchAll __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IRowsetWatchAll __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Acknowledge )(
            IRowsetWatchAll __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Start )(
            IRowsetWatchAll __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *StopWatching )(
            IRowsetWatchAll __RPC_FAR * This);

        END_INTERFACE
    } IRowsetWatchAllVtbl;

    interface IRowsetWatchAll
    {
        CONST_VTBL struct IRowsetWatchAllVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IRowsetWatchAll_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IRowsetWatchAll_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IRowsetWatchAll_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IRowsetWatchAll_Acknowledge(This)\
    (This)->lpVtbl -> Acknowledge(This)

#define IRowsetWatchAll_Start(This)\
    (This)->lpVtbl -> Start(This)

#define IRowsetWatchAll_StopWatching(This)\
    (This)->lpVtbl -> StopWatching(This)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE IRowsetWatchAll_Acknowledge_Proxy(
    IRowsetWatchAll __RPC_FAR * This);

void __RPC_STUB IRowsetWatchAll_Acknowledge_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IRowsetWatchAll_Start_Proxy(
    IRowsetWatchAll __RPC_FAR * This);

void __RPC_STUB IRowsetWatchAll_Start_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IRowsetWatchAll_StopWatching_Proxy(
    IRowsetWatchAll __RPC_FAR * This);

void __RPC_STUB IRowsetWatchAll_StopWatching_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IRowsetWatchAll_INTERFACE_DEFINED__ */

#ifndef __IRowsetWatchNotify_INTERFACE_DEFINED__
#define __IRowsetWatchNotify_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IRowsetWatchNotify
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object][local] */

typedef DWORD DBWATCHNOTIFY;

enum DBWATCHNOTIFYENUM
    {DBWATCHNOTIFY_ROWSCHANGED= 1,
DBWATCHNOTIFY_QUERYDONE= 2,
DBWATCHNOTIFY_QUERYREEXECUTED= 3
    };

EXTERN_C const IID IID_IRowsetWatchNotify;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a44-2a1c-11ce-ade5-00aa0044773d")
    IRowsetWatchNotify : public IUnknown
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE OnChange(
            /* [in] */ IRowset __RPC_FAR *pRowset,
            /* [in] */ DBWATCHNOTIFY eChangeReason) = 0;

    };

#else /* C style interface */

    typedef struct IRowsetWatchNotifyVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IRowsetWatchNotify __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IRowsetWatchNotify __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IRowsetWatchNotify __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnChange )(
            IRowsetWatchNotify __RPC_FAR * This,
            /* [in] */ IRowset __RPC_FAR *pRowset,
            /* [in] */ DBWATCHNOTIFY eChangeReason);

        END_INTERFACE
    } IRowsetWatchNotifyVtbl;

    interface IRowsetWatchNotify
    {
        CONST_VTBL struct IRowsetWatchNotifyVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IRowsetWatchNotify_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IRowsetWatchNotify_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IRowsetWatchNotify_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IRowsetWatchNotify_OnChange(This,pRowset,eChangeReason)\
    (This)->lpVtbl -> OnChange(This,pRowset,eChangeReason)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE IRowsetWatchNotify_OnChange_Proxy(
    IRowsetWatchNotify __RPC_FAR * This,
    /* [in] */ IRowset __RPC_FAR *pRowset,
    /* [in] */ DBWATCHNOTIFY eChangeReason);

void __RPC_STUB IRowsetWatchNotify_OnChange_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IRowsetWatchNotify_INTERFACE_DEFINED__ */

#ifndef __IRowsetWatchRegion_INTERFACE_DEFINED__
#define __IRowsetWatchRegion_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IRowsetWatchRegion
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object][local] */

typedef DWORD DBWATCHMODE;

enum DBWATCHMODEENUM
    {DBWATCHMODE_ALL= 0x1,
DBWATCHMODE_EXTEND= 0x2,
DBWATCHMODE_MOVE= 0x4,
DBWATCHMODE_COUNT= 0x8
    };
typedef DWORD DBROWCHANGEKIND;

enum DBROWCHANGEKINDENUM
    {DBROWCHANGEKIND_INSERT= 0,
DBROWCHANGEKIND_DELETE= DBROWCHANGEKIND_INSERT + 1,
DBROWCHANGEKIND_UPDATE= DBROWCHANGEKIND_DELETE + 1,
DBROWCHANGEKIND_COUNT= DBROWCHANGEKIND_UPDATE + 1
    };
typedef struct  tagDBROWWATCHRANGE
    {
    HWATCHREGION hRegion;
    DBROWCHANGEKIND eChangeKind;
    HROW hRow;
    ULONG iRow;
    }DBROWWATCHCHANGE;

EXTERN_C const IID IID_IRowsetWatchRegion;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a45-2a1c-11ce-ade5-00aa0044773d")
    IRowsetWatchRegion : public IRowsetWatchAll
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE CreateWatchRegion(
            /* [in] */ DBWATCHMODE dwWatchMode,
            /* [out] */ HWATCHREGION __RPC_FAR *phRegion) = 0;

        virtual HRESULT STDMETHODCALLTYPE ChangeWatchMode(
            /* [in] */ HWATCHREGION hRegion,
            /* [in] */ DBWATCHMODE dwWatchMode) = 0;

        virtual HRESULT STDMETHODCALLTYPE DeleteWatchRegion(
            /* [in] */ HWATCHREGION hRegion) = 0;

        virtual HRESULT STDMETHODCALLTYPE GetWatchRegionInfo(
            /* [in] */ HWATCHREGION hRegion,
            /* [out] */ DBWATCHMODE __RPC_FAR *pdwWatchMode,
            /* [out] */ HCHAPTER __RPC_FAR *phChapter,
            /* [out][in] */ ULONG __RPC_FAR *pcbBookmark,
            /* [size_is][size_is][out] */ BYTE __RPC_FAR *__RPC_FAR *ppBookmark,
            /* [out] */ LONG __RPC_FAR *pcRows) = 0;

        virtual HRESULT STDMETHODCALLTYPE Refresh(
            /* [out][in] */ ULONG __RPC_FAR *pcChangesObtained,
            /* [size_is][size_is][out] */ DBROWWATCHCHANGE __RPC_FAR *__RPC_FAR *prgChanges) = 0;

        virtual HRESULT STDMETHODCALLTYPE ShrinkWatchRegion(
            /* [in] */ HWATCHREGION hRegion,
            /* [in] */ HCHAPTER hChapter,
            /* [in] */ ULONG cbBookmark,
            /* [size_is][in] */ BYTE __RPC_FAR *pBookmark,
            /* [in] */ LONG cRows) = 0;

    };

#else /* C style interface */

    typedef struct IRowsetWatchRegionVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IRowsetWatchRegion __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IRowsetWatchRegion __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IRowsetWatchRegion __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Acknowledge )(
            IRowsetWatchRegion __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Start )(
            IRowsetWatchRegion __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *StopWatching )(
            IRowsetWatchRegion __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *CreateWatchRegion )(
            IRowsetWatchRegion __RPC_FAR * This,
            /* [in] */ DBWATCHMODE dwWatchMode,
            /* [out] */ HWATCHREGION __RPC_FAR *phRegion);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *ChangeWatchMode )(
            IRowsetWatchRegion __RPC_FAR * This,
            /* [in] */ HWATCHREGION hRegion,
            /* [in] */ DBWATCHMODE dwWatchMode);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *DeleteWatchRegion )(
            IRowsetWatchRegion __RPC_FAR * This,
            /* [in] */ HWATCHREGION hRegion);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetWatchRegionInfo )(
            IRowsetWatchRegion __RPC_FAR * This,
            /* [in] */ HWATCHREGION hRegion,
            /* [out] */ DBWATCHMODE __RPC_FAR *pdwWatchMode,
            /* [out] */ HCHAPTER __RPC_FAR *phChapter,
            /* [out][in] */ ULONG __RPC_FAR *pcbBookmark,
            /* [size_is][size_is][out] */ BYTE __RPC_FAR *__RPC_FAR *ppBookmark,
            /* [out] */ LONG __RPC_FAR *pcRows);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Refresh )(
            IRowsetWatchRegion __RPC_FAR * This,
            /* [out][in] */ ULONG __RPC_FAR *pcChangesObtained,
            /* [size_is][size_is][out] */ DBROWWATCHCHANGE __RPC_FAR *__RPC_FAR *prgChanges);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *ShrinkWatchRegion )(
            IRowsetWatchRegion __RPC_FAR * This,
            /* [in] */ HWATCHREGION hRegion,
            /* [in] */ HCHAPTER hChapter,
            /* [in] */ ULONG cbBookmark,
            /* [size_is][in] */ BYTE __RPC_FAR *pBookmark,

/* [in] */ LONG cRows);

        END_INTERFACE
    } IRowsetWatchRegionVtbl;

    interface IRowsetWatchRegion
    {
        CONST_VTBL struct IRowsetWatchRegionVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IRowsetWatchRegion_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IRowsetWatchRegion_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IRowsetWatchRegion_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IRowsetWatchRegion_Acknowledge(This)\
    (This)->lpVtbl -> Acknowledge(This)

#define IRowsetWatchRegion_Start(This)\
    (This)->lpVtbl -> Start(This)

#define IRowsetWatchRegion_StopWatching(This)\
    (This)->lpVtbl -> StopWatching(This)

#define IRowsetWatchRegion_CreateWatchRegion(This,dwWatchMode,phRegion)\
    (This)->lpVtbl -> CreateWatchRegion(This,dwWatchMode,phRegion)

#define IRowsetWatchRegion_ChangeWatchMode(This,hRegion,dwWatchMode)\
    (This)->lpVtbl -> ChangeWatchMode(This,hRegion,dwWatchMode)

#define IRowsetWatchRegion_DeleteWatchRegion(This,hRegion)\
    (This)->lpVtbl -> DeleteWatchRegion(This,hRegion)

#define IRowsetWatchRegion_GetWatchRegionInfo(This,hRegion,pdwWatchMode,phChapter,pcbBookmark,ppBookmark,pcRows)\
    (This)->lpVtbl -> GetWatchRegionInfo(This,hRegion,pdwWatchMode,phChapter,pcbBookmark,ppBookmark,pcRows)

#define IRowsetWatchRegion_Refresh(This,pcChangesObtained,prgChanges)\
    (This)->lpVtbl -> Refresh(This,pcChangesObtained,prgChanges)

#define IRowsetWatchRegion_ShrinkWatchRegion(This,hRegion,hChapter,cbBookmark,pBookmark,cRows)\
    (This)->lpVtbl -> ShrinkWatchRegion(This,hRegion,hChapter,cbBookmark,pBookmark,cRows)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE IRowsetWatchRegion_CreateWatchRegion_Proxy(
    IRowsetWatchRegion __RPC_FAR * This,
    /* [in] */ DBWATCHMODE dwWatchMode,
    /* [out] */ HWATCHREGION __RPC_FAR *phRegion);

void __RPC_STUB IRowsetWatchRegion_CreateWatchRegion_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IRowsetWatchRegion_ChangeWatchMode_Proxy(
    IRowsetWatchRegion __RPC_FAR * This,
    /* [in] */ HWATCHREGION hRegion,
    /* [in] */ DBWATCHMODE dwWatchMode);

void __RPC_STUB IRowsetWatchRegion_ChangeWatchMode_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IRowsetWatchRegion_DeleteWatchRegion_Proxy(
    IRowsetWatchRegion __RPC_FAR * This,
    /* [in] */ HWATCHREGION hRegion);

void __RPC_STUB IRowsetWatchRegion_DeleteWatchRegion_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IRowsetWatchRegion_GetWatchRegionInfo_Proxy(
    IRowsetWatchRegion __RPC_FAR * This,
    /* [in] */ HWATCHREGION hRegion,
    /* [out] */ DBWATCHMODE __RPC_FAR *pdwWatchMode,
    /* [out] */ HCHAPTER __RPC_FAR *phChapter,
    /* [out][in] */ ULONG __RPC_FAR *pcbBookmark,
    /* [size_is][size_is][out] */ BYTE __RPC_FAR *__RPC_FAR *ppBookmark,
    /* [out] */ LONG __RPC_FAR *pcRows);

void __RPC_STUB IRowsetWatchRegion_GetWatchRegionInfo_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IRowsetWatchRegion_Refresh_Proxy(
    IRowsetWatchRegion __RPC_FAR * This,
    /* [out][in] */ ULONG __RPC_FAR *pcChangesObtained,
    /* [size_is][size_is][out] */ DBROWWATCHCHANGE __RPC_FAR *__RPC_FAR *prgChanges);

void __RPC_STUB IRowsetWatchRegion_Refresh_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IRowsetWatchRegion_ShrinkWatchRegion_Proxy(
    IRowsetWatchRegion __RPC_FAR * This,
    /* [in] */ HWATCHREGION hRegion,
    /* [in] */ HCHAPTER hChapter,
    /* [in] */ ULONG cbBookmark,
    /* [size_is][in] */ BYTE __RPC_FAR *pBookmark,
    /* [in] */ LONG cRows);

void __RPC_STUB IRowsetWatchRegion_ShrinkWatchRegion_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IRowsetWatchRegion_INTERFACE_DEFINED__ */

#ifndef __IRowsetCopyRows_INTERFACE_DEFINED__
#define __IRowsetCopyRows_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IRowsetCopyRows
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object][local] */

typedef WORD HSOURCE;

EXTERN_C const IID IID_IRowsetCopyRows;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a6b-2a1c-11ce-ade5-00aa0044773d")
    IRowsetCopyRows : public IUnknown
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE CloseSource(
            /* [in] */ HSOURCE hSourceID) = 0;

        virtual HRESULT STDMETHODCALLTYPE CopyByHROWS(
            /* [in] */ HSOURCE hSourceID,
            /* [in] */ HCHAPTER hReserved,
            /* [in] */ LONG cRows,
            /* [size_is][in] */ const HROW __RPC_FAR rghRows[  ],
            /* [in] */ ULONG bFlags) = 0;

        virtual HRESULT STDMETHODCALLTYPE CopyRows(
            /* [in] */ HSOURCE hSourceID,
            /* [in] */ HCHAPTER hReserved,
            /* [in] */ LONG cRows,
            /* [in] */ ULONG bFlags,
            /* [out] */ ULONG __RPC_FAR *pcRowsCopied) = 0;

        virtual HRESULT STDMETHODCALLTYPE DefineSource(
            /* [in] */ const IRowset __RPC_FAR *pRowsetSource,
            /* [in] */ const ULONG cColIds,
            /* [size_is][in] */ const LONG __RPC_FAR rgSourceColumns[  ],
            /* [size_is][in] */ const LONG __RPC_FAR rgTargetColumns[  ],
            /* [out] */ HSOURCE __RPC_FAR *phSourceID) = 0;

    };

#else /* C style interface */

    typedef struct IRowsetCopyRowsVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IRowsetCopyRows __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IRowsetCopyRows __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IRowsetCopyRows __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *CloseSource )(
            IRowsetCopyRows __RPC_FAR * This,
            /* [in] */ HSOURCE hSourceID);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *CopyByHROWS )(
            IRowsetCopyRows __RPC_FAR * This,
            /* [in] */ HSOURCE hSourceID,
            /* [in] */ HCHAPTER hReserved,
            /* [in] */ LONG cRows,
            /* [size_is][in] */ const HROW __RPC_FAR rghRows[  ],
            /* [in] */ ULONG bFlags);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *CopyRows )(
            IRowsetCopyRows __RPC_FAR * This,
            /* [in] */ HSOURCE hSourceID,
            /* [in] */ HCHAPTER hReserved,
            /* [in] */ LONG cRows,
            /* [in] */ ULONG bFlags,
            /* [out] */ ULONG __RPC_FAR *pcRowsCopied);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *DefineSource )(
            IRowsetCopyRows __RPC_FAR * This,
            /* [in] */ const IRowset __RPC_FAR *pRowsetSource,
            /* [in] */ const ULONG cColIds,
            /* [size_is][in] */ const LONG __RPC_FAR rgSourceColumns[  ],
            /* [size_is][in] */ const LONG __RPC_FAR rgTargetColumns[  ],
            /* [out] */ HSOURCE __RPC_FAR *phSourceID);

        END_INTERFACE
    } IRowsetCopyRowsVtbl;

    interface IRowsetCopyRows
    {
        CONST_VTBL struct IRowsetCopyRowsVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IRowsetCopyRows_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IRowsetCopyRows_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IRowsetCopyRows_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IRowsetCopyRows_CloseSource(This,hSourceID)\
    (This)->lpVtbl -> CloseSource(This,hSourceID)

#define IRowsetCopyRows_CopyByHROWS(This,hSourceID,hReserved,cRows,rghRows,bFlags)\
    (This)->lpVtbl -> CopyByHROWS(This,hSourceID,hReserved,cRows,rghRows,bFlags)

#define IRowsetCopyRows_CopyRows(This,hSourceID,hReserved,cRows,bFlags,pcRowsCopied)\
    (This)->lpVtbl -> CopyRows(This,hSourceID,hReserved,cRows,bFlags,pcRowsCopied)

#define IRowsetCopyRows_DefineSource(This,pRowsetSource,cColIds,rgSourceColumns,rgTargetColumns,phSourceID)\
    (This)->lpVtbl -> DefineSource(This,pRowsetSource,cColIds,rgSourceColumns,rgTargetColumns,phSourceID)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE IRowsetCopyRows_CloseSource_Proxy(
    IRowsetCopyRows __RPC_FAR * This,
    /* [in] */ HSOURCE hSourceID);

void __RPC_STUB IRowsetCopyRows_CloseSource_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IRowsetCopyRows_CopyByHROWS_Proxy(
    IRowsetCopyRows __RPC_FAR * This,
    /* [in] */ HSOURCE hSourceID,
    /* [in] */ HCHAPTER hReserved,
    /* [in] */ LONG cRows,
    /* [size_is][in] */ const HROW __RPC_FAR rghRows[  ],
    /* [in] */ ULONG bFlags);

void __RPC_STUB IRowsetCopyRows_CopyByHROWS_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IRowsetCopyRows_CopyRows_Proxy(
    IRowsetCopyRows __RPC_FAR * This,
    /* [in] */ HSOURCE hSourceID,
    /* [in] */ HCHAPTER hReserved,
    /* [in] */ LONG cRows,
    /* [in] */ ULONG bFlags,
    /* [out] */ ULONG __RPC_FAR *pcRowsCopied);

void __RPC_STUB IRowsetCopyRows_CopyRows_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IRowsetCopyRows_DefineSource_Proxy(
    IRowsetCopyRows __RPC_FAR * This,
    /* [in] */ const IRowset __RPC_FAR *pRowsetSource,
    /* [in] */ const ULONG cColIds,
    /* [size_is][in] */ const LONG __RPC_FAR rgSourceColumns[  ],
    /* [size_is][in] */ const LONG __RPC_FAR rgTargetColumns[  ],
    /* [out] */ HSOURCE __RPC_FAR *phSourceID);

void __RPC_STUB IRowsetCopyRows_DefineSource_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IRowsetCopyRows_INTERFACE_DEFINED__ */

#ifndef __IReadData_INTERFACE_DEFINED__
#define __IReadData_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IReadData
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object][local] */

EXTERN_C const IID IID_IReadData;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a6a-2a1c-11ce-ade5-00aa0044773d")
    IReadData : public IUnknown
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE ReadData(
            /* [in] */ HCHAPTER hChapter,
            /* [in] */ ULONG cbBookmark,
            /* [size_is][in] */ const BYTE __RPC_FAR *pBookmark,
            /* [in] */ LONG lRowsOffset,
            /* [in] */ HACCESSOR hAccessor,
            /* [in] */ LONG cRows,
            /* [out] */ ULONG __RPC_FAR *pcRowsObtained,
            /* [out][in] */ BYTE __RPC_FAR *__RPC_FAR *ppFixedData,
            /* [out][in] */ ULONG __RPC_FAR *pcbVariableTotal,
            /* [out][in] */ BYTE __RPC_FAR *__RPC_FAR *ppVariableData) = 0;

        virtual HRESULT STDMETHODCALLTYPE ReleaseChapter(
            /* [in] */ HCHAPTER hChapter) = 0;

    };

#else /* C style interface */

    typedef struct IReadDataVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IReadData __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IReadData __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IReadData __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *ReadData )(
            IReadData __RPC_FAR * This,
            /* [in] */ HCHAPTER hChapter,
            /* [in] */ ULONG cbBookmark,
            /* [size_is][in] */ const BYTE __RPC_FAR *pBookmark,
            /* [in] */ LONG lRowsOffset,
            /* [in] */ HACCESSOR hAccessor,
            /* [in] */ LONG cRows,
            /* [out] */ ULONG __RPC_FAR *pcRowsObtained,
            /* [out][in] */ BYTE __RPC_FAR *__RPC_FAR *ppFixedData,
            /* [out][in] */ ULONG __RPC_FAR *pcbVariableTotal,
            /* [out][in] */ BYTE __RPC_FAR *__RPC_FAR *ppVariableData);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *ReleaseChapter )(
            IReadData __RPC_FAR * This,
            /* [in] */ HCHAPTER hChapter);

        END_INTERFACE
    } IReadDataVtbl;

    interface IReadData
    {
        CONST_VTBL struct IReadDataVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IReadData_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IReadData_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IReadData_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IReadData_ReadData(This,hChapter,cbBookmark,pBookmark,lRowsOffset,hAccessor,cRows,pcRowsObtained,ppFixedData,pcbVariableTotal,ppVariableData)\
    (This)->lpVtbl -> ReadData(This,hChapter,cbBookmark,pBookmark,lRowsOffset,hAccessor,cRows,pcRowsObtained,ppFixedData,pcbVariableTotal,ppVariableData)

#define IReadData_ReleaseChapter(This,hChapter)\
    (This)->lpVtbl -> ReleaseChapter(This,hChapter)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE IReadData_ReadData_Proxy(
    IReadData __RPC_FAR * This,
    /* [in] */ HCHAPTER hChapter,
    /* [in] */ ULONG cbBookmark,
    /* [size_is][in] */ const BYTE __RPC_FAR *pBookmark,
    /* [in] */ LONG lRowsOffset,
    /* [in] */ HACCESSOR hAccessor,
    /* [in] */ LONG cRows,
    /* [out] */ ULONG __RPC_FAR *pcRowsObtained,
    /* [out][in] */ BYTE __RPC_FAR *__RPC_FAR *ppFixedData,
    /* [out][in] */ ULONG __RPC_FAR *pcbVariableTotal,
    /* [out][in] */ BYTE __RPC_FAR *__RPC_FAR *ppVariableData);

void __RPC_STUB IReadData_ReadData_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IReadData_ReleaseChapter_Proxy(
    IReadData __RPC_FAR * This,
    /* [in] */ HCHAPTER hChapter);

void __RPC_STUB IReadData_ReleaseChapter_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IReadData_INTERFACE_DEFINED__ */

/****************************************
 * Generated header for interface: __MIDL_itf_oledb_0103
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [local] */

#endif // OLEDBVER >= 0x0200
//@@@- V2.0

extern RPC_IF_HANDLE __MIDL_itf_oledb_0103_v0_0_c_ifspec;
extern RPC_IF_HANDLE __MIDL_itf_oledb_0103_v0_0_s_ifspec;

#ifndef __ICommand_INTERFACE_DEFINED__
#define __ICommand_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: ICommand
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object] */

EXTERN_C const IID IID_ICommand;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a63-2a1c-11ce-ade5-00aa0044773d")
    ICommand : public IUnknown
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE Cancel( void) = 0;

        virtual /* [local] */ HRESULT STDMETHODCALLTYPE Execute(
            /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
            /* [in] */ REFIID riid,
            /* [out][in] */ DBPARAMS __RPC_FAR *pParams,
            /* [out] */ LONG __RPC_FAR *pcRowsAffected,
            /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppRowset) = 0;

        virtual /* [local] */ HRESULT STDMETHODCALLTYPE GetDBSession(
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppSession) = 0;

    };

#else /* C style interface */

    typedef struct ICommandVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            ICommand __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            ICommand __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            ICommand __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Cancel )(
            ICommand __RPC_FAR * This);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Execute )(
            ICommand __RPC_FAR * This,
            /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
            /* [in] */ REFIID riid,
            /* [out][in] */ DBPARAMS __RPC_FAR *pParams,
            /* [out] */ LONG __RPC_FAR *pcRowsAffected,
            /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppRowset);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetDBSession )(
            ICommand __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppSession);

        END_INTERFACE
    } ICommandVtbl;

    interface ICommand
    {
        CONST_VTBL struct ICommandVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define ICommand_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define ICommand_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define ICommand_Release(This)\
    (This)->lpVtbl -> Release(This)

#define ICommand_Cancel(This)\
    (This)->lpVtbl -> Cancel(This)

#define ICommand_Execute(This,pUnkOuter,riid,pParams,pcRowsAffected,ppRowset)\
    (This)->lpVtbl -> Execute(This,pUnkOuter,riid,pParams,pcRowsAffected,ppRowset)

#define ICommand_GetDBSession(This,riid,ppSession)\
    (This)->lpVtbl -> GetDBSession(This,riid,ppSession)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE ICommand_Cancel_Proxy(
    ICommand __RPC_FAR * This);

void __RPC_STUB ICommand_Cancel_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

/* [call_as] */ HRESULT STDMETHODCALLTYPE ICommand_RemoteExecute_Proxy(
    ICommand __RPC_FAR * This,
    /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
    /* [in] */ REFIID riid,
    /* [in] */ HACCESSOR hAccessor,
    /* [in] */ ULONG cParamSets,
    /* [in] */ ULONG cbData,
    /* [size_is][unique][out][in] */ BYTE __RPC_FAR *pbData,
    /* [unique][out][in] */ LONG __RPC_FAR *pcRowsAffected,
    /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppRowset);

void __RPC_STUB ICommand_RemoteExecute_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

/* [call_as] */ HRESULT STDMETHODCALLTYPE ICommand_RemoteGetDBSession_Proxy(
    ICommand __RPC_FAR * This,
    /* [in] */ REFIID riid,
    /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppSession);

void __RPC_STUB ICommand_RemoteGetDBSession_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __ICommand_INTERFACE_DEFINED__ */

#ifndef __IMultipleResults_INTERFACE_DEFINED__
#define __IMultipleResults_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IMultipleResults
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object] */

EXTERN_C const IID IID_IMultipleResults;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a90-2a1c-11ce-ade5-00aa0044773d")
    IMultipleResults : public IUnknown
    {
    public:
        virtual /* [local] */ HRESULT STDMETHODCALLTYPE GetResult(
            /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
            /* [in] */ LONG reserved,
            /* [in] */ REFIID riid,
            /* [out] */ LONG __RPC_FAR *pcRowsAffected,
            /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppRowset) = 0;

    };

#else /* C style interface */

    typedef struct IMultipleResultsVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IMultipleResults __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IMultipleResults __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IMultipleResults __RPC_FAR * This);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetResult )(
            IMultipleResults __RPC_FAR * This,
            /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
            /* [in] */ LONG reserved,
            /* [in] */ REFIID riid,
            /* [out] */ LONG __RPC_FAR *pcRowsAffected,
            /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppRowset);

        END_INTERFACE
    } IMultipleResultsVtbl;

    interface IMultipleResults
    {
        CONST_VTBL struct IMultipleResultsVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IMultipleResults_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IMultipleResults_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IMultipleResults_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IMultipleResults_GetResult(This,pUnkOuter,reserved,riid,pcRowsAffected,ppRowset)\
    (This)->lpVtbl -> GetResult(This,pUnkOuter,reserved,riid,pcRowsAffected,ppRowset)

#endif /* COBJMACROS */

#endif /* C style interface */

/* [call_as] */ HRESULT STDMETHODCALLTYPE IMultipleResults_RemoteGetResult_Proxy(
    IMultipleResults __RPC_FAR * This,
    /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
    /* [in] */ LONG reserved,
    /* [in] */ REFIID riid,
    /* [unique][out][in] */ LONG __RPC_FAR *pcRowsAffected,
    /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppRowset);

void __RPC_STUB IMultipleResults_RemoteGetResult_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IMultipleResults_INTERFACE_DEFINED__ */

#ifndef __IConvertType_INTERFACE_DEFINED__
#define __IConvertType_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IConvertType
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object] */

typedef DWORD DBCONVERTFLAGS;

enum DBCONVERTFLAGSENUM
    {DBCONVERTFLAGS_COLUMN= 0,
DBCONVERTFLAGS_PARAMETER= 0x1
    };

EXTERN_C const IID IID_IConvertType;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a88-2a1c-11ce-ade5-00aa0044773d")
    IConvertType : public IUnknown
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE CanConvert(
            /* [in] */ DBTYPE wFromType,
            /* [in] */ DBTYPE wToType,
            /* [in] */ DBCONVERTFLAGS dwConvertFlags) = 0;

    };

#else /* C style interface */

    typedef struct IConvertTypeVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IConvertType __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IConvertType __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IConvertType __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *CanConvert )(
            IConvertType __RPC_FAR * This,
            /* [in] */ DBTYPE wFromType,
            /* [in] */ DBTYPE wToType,
            /* [in] */ DBCONVERTFLAGS dwConvertFlags);

        END_INTERFACE
    } IConvertTypeVtbl;

    interface IConvertType
    {
        CONST_VTBL struct IConvertTypeVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IConvertType_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IConvertType_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IConvertType_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IConvertType_CanConvert(This,wFromType,wToType,dwConvertFlags)\
    (This)->lpVtbl -> CanConvert(This,wFromType,wToType,dwConvertFlags)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE IConvertType_CanConvert_Proxy(
    IConvertType __RPC_FAR * This,
    /* [in] */ DBTYPE wFromType,
    /* [in] */ DBTYPE wToType,
    /* [in] */ DBCONVERTFLAGS dwConvertFlags);

void __RPC_STUB IConvertType_CanConvert_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IConvertType_INTERFACE_DEFINED__ */

/****************************************
 * Generated header for interface: __MIDL_itf_oledb_0106
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [local] */

//@@@+ V2.0
#if( OLEDBVER >= 0x0200 )

extern RPC_IF_HANDLE __MIDL_itf_oledb_0106_v0_0_c_ifspec;
extern RPC_IF_HANDLE __MIDL_itf_oledb_0106_v0_0_s_ifspec;

#ifndef __ICommandCost_INTERFACE_DEFINED__
#define __ICommandCost_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: ICommandCost
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object][local] */

typedef DWORD DBRESOURCEKIND;

enum DBRESOURCEKINDENUM
    {DBRESOURCE_INVALID= 0,
DBRESOURCE_TOTAL= 1,
DBRESOURCE_CPU= 2,
DBRESOURCE_MEMORY= 3,
DBRESOURCE_DISK= 4,
DBRESOURCE_NETWORK= 5,
DBRESOURCE_RESPONSE= 6,
DBRESOURCE_ROWS= 7,
DBRESOURCE_OTHER= 8
    };
typedef DWORD DBCOSTUNIT;

enum DBCOSTUNITENUM
    {DBUNIT_INVALID= 0,
DBUNIT_WEIGHT= 0x1,
DBUNIT_PERCENT= 0x2,
DBUNIT_MAXIMUM= 0x4,
DBUNIT_MINIMUM= 0x8,
DBUNIT_MICRO_SECOND= 0x10,
DBUNIT_MILLI_SECOND= 0x20,
DBUNIT_SECOND= 0x40,
DBUNIT_MINUTE= 0x80,
DBUNIT_HOUR= 0x100,
DBUNIT_BYTE= 0x200,
DBUNIT_KILO_BYTE= 0x400,
DBUNIT_MEGA_BYTE= 0x800,
DBUNIT_GIGA_BYTE= 0x1000,
DBUNIT_NUM_MSGS= 0x2000,
DBUNIT_NUM_LOCKS= 0x4000,
DBUNIT_NUM_ROWS= 0x8000,
DBUNIT_OTHER= 0x10000
    };
typedef struct  tagDBCOST
    {
    DBRESOURCEKIND eKind;
    DBCOSTUNIT dwUnits;
    LONG lValue;
    }DBCOST;

typedef DWORD DBEXECLIMITS;

enum DBEXECLIMITSENUM
    {DBEXECLIMITS_ABORT= 1,
DBEXECLIMITS_STOP= 2,
DBEXECLIMITS_SUSPEND= 3
    };

EXTERN_C const IID IID_ICommandCost;

#if defined(__cplusplus) && !defined(CINTERFACE)

interface DECLSPEC_UUID("0c733a4e-2a1c-11ce-ade5-00aa0044773d")
    ICommandCost : public IUnknown
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE GetAccumulatedCost(
            /* [in] */ LPCOLESTR pwszRowsetName,
            /* [out][in] */ ULONG __RPC_FAR *pcCostLimits,
            /* [size_is][size_is][out] */ DBCOST __RPC_FAR *__RPC_FAR *prgCostLimits) = 0;

        virtual HRESULT STDMETHODCALLTYPE GetCostEstimate(
            /* [in] */ LPCOLESTR pwszRowsetName,
            /* [out] */ ULONG __RPC_FAR *pcCostEstimates,
            /* [out] */ DBCOST __RPC_FAR *prgCostEstimates) = 0;

        virtual HRESULT STDMETHODCALLTYPE GetCostGoals(
            /* [in] */ LPCOLESTR pwszRowsetName,
            /* [out] */ ULONG __RPC_FAR *pcCostGoals,
            /* [out] */ DBCOST __RPC_FAR *prgCostGoals) = 0;

        virtual HRESULT STDMETHODCALLTYPE GetCostLimits(
            /* [in] */ LPCOLESTR pwszRowsetName,
            /* [out] */ ULONG __RPC_FAR *pcCostLimits,
            /* [out] */ DBCOST __RPC_FAR *prgCostLimits) = 0;

        virtual HRESULT STDMETHODCALLTYPE SetCostGoals(
            /* [in] */ LPCOLESTR pwszRowsetName,
            /* [in] */ ULONG cCostGoals,
            /* [size_is][in] */ const DBCOST __RPC_FAR rgCostGoals[  ]) = 0;

        virtual HRESULT STDMETHODCALLTYPE SetCostLimits(
            /* [in] */ LPCOLESTR pwszRowsetName,
            /* [in] */ ULONG cCostLimits,
            /* [in] */ DBCOST __RPC_FAR *prgCostLimits,
            /* [in] */ DBEXECLIMITS dwExecutionFlags) = 0;

    };

#else /* C style interface */

    typedef struct ICommandCostVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            ICommandCost __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            ICommandCost __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            ICommandCost __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetAccumulatedCost )(
            ICommandCost __RPC_FAR * This,
            /* [in] */ LPCOLESTR pwszRowsetName,
            /* [out][in] */ ULONG __RPC_FAR *pcCostLimits,
            /* [size_is][size_is][out] */ DBCOST __RPC_FAR *__RPC_FAR *prgCostLimits);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetCostEstimate )(
            ICommandCost __RPC_FAR * This,
            /* [in] */ LPCOLESTR pwszRowsetName,
            /* [out] */ ULONG __RPC_FAR *pcCostEstimates,
            /* [out] */ DBCOST __RPC_FAR *prgCostEstimates);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetCostGoals )(
            ICommandCost __RPC_FAR * This,
            /* [in] */ LPCOLESTR pwszRowsetName,
            /* [out] */ ULONG __RPC_FAR *pcCostGoals,
            /* [out] */ DBCOST __RPC_FAR *prgCostGoals);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetCostLimits )(
            ICommandCost __RPC_FAR * This,
            /* [in] */ LPCOLESTR pwszRowsetName,
            /* [out] */ ULONG __RPC_FAR *pcCostLimits,
            /* [out] */ DBCOST __RPC_FAR *prgCostLimits);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetCostGoals )(
            ICommandCost __RPC_FAR * This,
            /* [in] */ LPCOLESTR pwszRowsetName,
            /* [in] */ ULONG cCostGoals,
            /* [size_is][in] */ const DBCOST __RPC_FAR rgCostGoals[  ]);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetCostLimits )(
            ICommandCost __RPC_FAR * This,
            /* [in] */ LPCOLESTR pwszRowsetName,
            /* [in] */ ULONG cCostLimits,
            /* [in] */ DBCOST __RPC_FAR *prgCostLimits,
            /* [in] */ DBEXECLIMITS dwExecutionFlags);

        END_INTERFACE
    } ICommandCostVtbl;

    interface ICommandCost
    {
        CONST_VTBL struct ICommandCostVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define ICommandCost_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define ICommandCost_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define ICommandCost_Release(This)\
    (This)->lpVtbl -> Release(This)

#define ICommandCost_GetAccumulatedCost(This,pwszRowsetName,pcCostLimits,prgCostLimits)\
    (This)->lpVtbl -> GetAccumulatedCost(This,pwszRowsetName,pcCostLimits,prgCostLimits)

#define ICommandCost_GetCostEstimate(This,pwszRowsetName,pcCostEstimates,prgCostEstimates)\
    (This)->lpVtbl -> GetCostEstimate(This,pwszRowsetName,pcCostEstimates,prgCostEstimates)

#define ICommandCost_GetCostGoals(This,pwszRowsetName,pcCostGoals,prgCostGoals)\
    (This)->lpVtbl -> GetCostGoals(This,pwszRowsetName,pcCostGoals,prgCostGoals)

#define ICommandCost_GetCostLimits(This,pwszRowsetName,pcCostLimits,prgCostLimits)\
    (This)->lpVtbl -> GetCostLimits(This,pwszRowsetName,pcCostLimits,prgCostLimits)

#define ICommandCost_SetCostGoals(This,pwszRowsetName,cCostGoals,rgCostGoals)\
    (This)->lpVtbl -> SetCostGoals(This,pwszRowsetName,cCostGoals,rgCostGoals)

#define ICommandCost_SetCostLimits(This,pwszRowsetName,cCostLimits,prgCostLimits,dwExecutionFlags)\
    (This)->lpVtbl -> SetCostLimits(This,pwszRowsetName,cCostLimits,prgCostLimits,dwExecutionFlags)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE ICommandCost_GetAccumulatedCost_Proxy(
    ICommandCost __RPC_FAR * This,
    /* [in] */ LPCOLESTR pwszRowsetName,
    /* [out][in] */ ULONG __RPC_FAR *pcCostLimits,
    /* [size_is][size_is][out] */ DBCOST __RPC_FAR *__RPC_FAR *prgCostLimits);

void __RPC_STUB ICommandCost_GetAccumulatedCost_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE ICommandCost_GetCostEstimate_Proxy(
    ICommandCost __RPC_FAR * This,
    /* [in] */ LPCOLESTR pwszRowsetName,
    /* [out] */ ULONG __RPC_FAR *pcCostEstimates,
    /* [out] */ DBCOST __RPC_FAR *prgCostEstimates);

void __RPC_STUB ICommandCost_GetCostEstimate_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE ICommandCost_GetCostGoals_Proxy(
    ICommandCost __RPC_FAR * This,
    /* [in] */ LPCOLESTR pwszRowsetName,
    /* [out] */ ULONG __RPC_FAR *pcCostGoals,
    /* [out] */ DBCOST __RPC_FAR *prgCostGoals);

void __RPC_STUB ICommandCost_GetCostGoals_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE ICommandCost_GetCostLimits_Proxy(
    ICommandCost __RPC_FAR * This,
    /* [in] */ LPCOLESTR pwszRowsetName,
    /* [out] */ ULONG __RPC_FAR *pcCostLimits,
    /* [out] */ DBCOST __RPC_FAR *prgCostLimits);

void __RPC_STUB ICommandCost_GetCostLimits_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE ICommandCost_SetCostGoals_Proxy(
    ICommandCost __RPC_FAR * This,
    /* [in] */ LPCOLESTR pwszRowsetName,
    /* [in] */ ULONG cCostGoals,
    /* [size_is][in] */ const DBCOST __RPC_FAR rgCostGoals[  ]);

void __RPC_STUB ICommandCost_SetCostGoals_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE ICommandCost_SetCostLimits_Proxy(
    ICommandCost __RPC_FAR * This,
    /* [in] */ LPCOLESTR pwszRowsetName,
    /* [in] */ ULONG cCostLimits,
    /* [in] */ DBCOST __RPC_FAR *prgCostLimits,
    /* [in] */ DBEXECLIMITS dwExecutionFlags);

void __RPC_STUB ICommandCost_SetCostLimits_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __ICommandCost_INTERFACE_DEFINED__ */

/****************************************
 * Generated header for interface: __MIDL_itf_oledb_0107
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [local] */

#endif // OLEDBVER >= 0x0200
//@@@- V2.0

extern RPC_IF_HANDLE __MIDL_itf_oledb_0107_v0_0_c_ifspec;
extern RPC_IF_HANDLE __MIDL_itf_oledb_0107_v0_0_s_ifspec;

#ifndef __ICommandPrepare_INTERFACE_DEFINED__
#define __ICommandPrepare_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: ICommandPrepare
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object] */

EXTERN_C const IID IID_ICommandPrepare;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a26-2a1c-11ce-ade5-00aa0044773d")
    ICommandPrepare : public IUnknown
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE Prepare(
            /* [in] */ ULONG cExpectedRuns) = 0;

        virtual HRESULT STDMETHODCALLTYPE Unprepare( void) = 0;

    };

#else /* C style interface */

    typedef struct ICommandPrepareVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            ICommandPrepare __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            ICommandPrepare __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            ICommandPrepare __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Prepare )(
            ICommandPrepare __RPC_FAR * This,
            /* [in] */ ULONG cExpectedRuns);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Unprepare )(
            ICommandPrepare __RPC_FAR * This);

        END_INTERFACE
    } ICommandPrepareVtbl;

    interface ICommandPrepare
    {
        CONST_VTBL struct ICommandPrepareVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define ICommandPrepare_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define ICommandPrepare_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define ICommandPrepare_Release(This)\
    (This)->lpVtbl -> Release(This)

#define ICommandPrepare_Prepare(This,cExpectedRuns)\
    (This)->lpVtbl -> Prepare(This,cExpectedRuns)

#define ICommandPrepare_Unprepare(This)\
    (This)->lpVtbl -> Unprepare(This)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE ICommandPrepare_Prepare_Proxy(
    ICommandPrepare __RPC_FAR * This,
    /* [in] */ ULONG cExpectedRuns);

void __RPC_STUB ICommandPrepare_Prepare_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE ICommandPrepare_Unprepare_Proxy(
    ICommandPrepare __RPC_FAR * This);

void __RPC_STUB ICommandPrepare_Unprepare_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __ICommandPrepare_INTERFACE_DEFINED__ */

#ifndef __ICommandProperties_INTERFACE_DEFINED__
#define __ICommandProperties_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: ICommandProperties
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object] */

EXTERN_C const IID IID_ICommandProperties;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a79-2a1c-11ce-ade5-00aa0044773d")
    ICommandProperties : public IUnknown
    {
    public:
        virtual /* [local] */ HRESULT STDMETHODCALLTYPE GetProperties(
            /* [in] */ const ULONG cPropertyIDSets,
            /* [size_is][in] */ const DBPROPIDSET __RPC_FAR rgPropertyIDSets[  ],
            /* [out][in] */ ULONG __RPC_FAR *pcPropertySets,
            /* [size_is][size_is][out] */ DBPROPSET __RPC_FAR *__RPC_FAR *prgPropertySets) = 0;

        virtual /* [local] */ HRESULT STDMETHODCALLTYPE SetProperties(
            /* [in] */ ULONG cPropertySets,
            /* [size_is][out][in] */ DBPROPSET __RPC_FAR rgPropertySets[  ]) = 0;

    };

#else /* C style interface */

    typedef struct ICommandPropertiesVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            ICommandProperties __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            ICommandProperties __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            ICommandProperties __RPC_FAR * This);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetProperties )(
            ICommandProperties __RPC_FAR * This,
            /* [in] */ const ULONG cPropertyIDSets,
            /* [size_is][in] */ const DBPROPIDSET __RPC_FAR rgPropertyIDSets[  ],
            /* [out][in] */ ULONG __RPC_FAR *pcPropertySets,
            /* [size_is][size_is][out] */ DBPROPSET __RPC_FAR *__RPC_FAR *prgPropertySets);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetProperties )(
            ICommandProperties __RPC_FAR * This,
            /* [in] */ ULONG cPropertySets,
            /* [size_is][out][in] */ DBPROPSET __RPC_FAR rgPropertySets[  ]);

        END_INTERFACE
    } ICommandPropertiesVtbl;

    interface ICommandProperties
    {
        CONST_VTBL struct ICommandPropertiesVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define ICommandProperties_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define ICommandProperties_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define ICommandProperties_Release(This)\
    (This)->lpVtbl -> Release(This)

#define ICommandProperties_GetProperties(This,cPropertyIDSets,rgPropertyIDSets,pcPropertySets,prgPropertySets)\
    (This)->lpVtbl -> GetProperties(This,cPropertyIDSets,rgPropertyIDSets,pcPropertySets,prgPropertySets)

#define ICommandProperties_SetProperties(This,cPropertySets,rgPropertySets)\
    (This)->lpVtbl -> SetProperties(This,cPropertySets,rgPropertySets)

#endif /* COBJMACROS */

#endif /* C style interface */

/* [call_as] */ HRESULT STDMETHODCALLTYPE ICommandProperties_RemoteGetProperties_Proxy(
    ICommandProperties __RPC_FAR * This,
    /* [in] */ const ULONG cPropertyIDSets,
    /* [size_is][unique][in] */ const DBPROPIDSET __RPC_FAR *rgPropertyIDSets,
    /* [out][in] */ ULONG __RPC_FAR *pcPropertySets,
    /* [size_is][size_is][out] */ DBPROPSET __RPC_FAR *__RPC_FAR *prgPropertySets);

void __RPC_STUB ICommandProperties_RemoteGetProperties_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

/* [call_as] */ HRESULT STDMETHODCALLTYPE ICommandProperties_RemoteSetProperties_Proxy(
    ICommandProperties __RPC_FAR * This,
    /* [in] */ ULONG cPropertySets,
    /* [size_is][unique][in] */ DBPROPSET __RPC_FAR *rgPropertySets);

void __RPC_STUB ICommandProperties_RemoteSetProperties_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __ICommandProperties_INTERFACE_DEFINED__ */

#ifndef __ICommandText_INTERFACE_DEFINED__
#define __ICommandText_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: ICommandText
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object] */

EXTERN_C const IID IID_ICommandText;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a27-2a1c-11ce-ade5-00aa0044773d")
    ICommandText : public ICommand
    {
    public:
        virtual /* [local] */ HRESULT STDMETHODCALLTYPE GetCommandText(
            /* [out][in] */ GUID __RPC_FAR *pguidDialect,
            /* [out] */ LPOLESTR __RPC_FAR *ppwszCommand) = 0;

        virtual HRESULT STDMETHODCALLTYPE SetCommandText(
            /* [in] */ REFGUID rguidDialect,
            /* [unique][in] */ LPCOLESTR pwszCommand) = 0;

    };

#else /* C style interface */

    typedef struct ICommandTextVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            ICommandText __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            ICommandText __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            ICommandText __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Cancel )(
            ICommandText __RPC_FAR * This);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Execute )(
            ICommandText __RPC_FAR * This,
            /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
            /* [in] */ REFIID riid,
            /* [out][in] */ DBPARAMS __RPC_FAR *pParams,
            /* [out] */ LONG __RPC_FAR *pcRowsAffected,
            /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppRowset);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetDBSession )(
            ICommandText __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppSession);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetCommandText )(
            ICommandText __RPC_FAR * This,
            /* [out][in] */ GUID __RPC_FAR *pguidDialect,
            /* [out] */ LPOLESTR __RPC_FAR *ppwszCommand);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetCommandText )(
            ICommandText __RPC_FAR * This,
            /* [in] */ REFGUID rguidDialect,
            /* [unique][in] */ LPCOLESTR pwszCommand);

        END_INTERFACE
    } ICommandTextVtbl;

    interface ICommandText
    {
        CONST_VTBL struct ICommandTextVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define ICommandText_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define ICommandText_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define ICommandText_Release(This)\
    (This)->lpVtbl -> Release(This)

#define ICommandText_Cancel(This)\
    (This)->lpVtbl -> Cancel(This)

#define ICommandText_Execute(This,pUnkOuter,riid,pParams,pcRowsAffected,ppRowset)\
    (This)->lpVtbl -> Execute(This,pUnkOuter,riid,pParams,pcRowsAffected,ppRowset)

#define ICommandText_GetDBSession(This,riid,ppSession)\
    (This)->lpVtbl -> GetDBSession(This,riid,ppSession)

#define ICommandText_GetCommandText(This,pguidDialect,ppwszCommand)\
    (This)->lpVtbl -> GetCommandText(This,pguidDialect,ppwszCommand)

#define ICommandText_SetCommandText(This,rguidDialect,pwszCommand)\
    (This)->lpVtbl -> SetCommandText(This,rguidDialect,pwszCommand)

#endif /* COBJMACROS */

#endif /* C style interface */

/* [call_as] */ HRESULT STDMETHODCALLTYPE ICommandText_RemoteGetCommandText_Proxy(
    ICommandText __RPC_FAR * This,
    /* [unique][out][in] */ GUID __RPC_FAR *pguidDialect,
    /* [out] */ LPOLESTR __RPC_FAR *ppwszCommand);

void __RPC_STUB ICommandText_RemoteGetCommandText_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE ICommandText_SetCommandText_Proxy(
    ICommandText __RPC_FAR * This,
    /* [in] */ REFGUID rguidDialect,
    /* [unique][in] */ LPCOLESTR pwszCommand);

void __RPC_STUB ICommandText_SetCommandText_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __ICommandText_INTERFACE_DEFINED__ */

/****************************************
 * Generated header for interface: __MIDL_itf_oledb_0110
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [local] */

//@@@+ V2.0
#if( OLEDBVER >= 0x0200 )

extern RPC_IF_HANDLE __MIDL_itf_oledb_0110_v0_0_c_ifspec;
extern RPC_IF_HANDLE __MIDL_itf_oledb_0110_v0_0_s_ifspec;

#ifndef __ICommandTree_INTERFACE_DEFINED__
#define __ICommandTree_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: ICommandTree
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object][local] */

typedef DWORD DBCOMMANDREUSE;

enum DBCOMMANDREUSEENUM
    {DBCOMMANDREUSE_NONE= 0,
DBCOMMANDREUSE_PROPERTIES= 0x1,
DBCOMMANDREUSE_PARAMETERS= 0x2
    };
typedef DWORD DBVALUEKIND;

enum DBVALUEKINDENUM
    {DBVALUEKIND_BYGUID= 256,
DBVALUEKIND_COLDESC= DBVALUEKIND_BYGUID + 1,
DBVALUEKIND_ID= DBVALUEKIND_COLDESC + 1,
DBVALUEKIND_CONTENT= DBVALUEKIND_ID + 1,
DBVALUEKIND_CONTENTVECTOR= DBVALUEKIND_CONTENT + 1,
DBVALUEKIND_GROUPINFO= DBVALUEKIND_CONTENTVECTOR + 1,
DBVALUEKIND_PARAMETER= DBVALUEKIND_GROUPINFO + 1,
DBVALUEKIND_PROPERTY= DBVALUEKIND_PARAMETER + 1,
DBVALUEKIND_SETFUNC= DBVALUEKIND_PROPERTY + 1,
DBVALUEKIND_SORTINFO= DBVALUEKIND_SETFUNC + 1,
DBVALUEKIND_TEXT= DBVALUEKIND_SORTINFO + 1,
DBVALUEKIND_COMMAND= DBVALUEKIND_TEXT + 1,
DBVALUEKIND_MONIKER= DBVALUEKIND_COMMAND + 1,
DBVALUEKIND_ROWSET= DBVALUEKIND_MONIKER + 1,
DBVALUEKIND_LIKE= DBVALUEKIND_ROWSET + 1,
DBVALUEKIND_CONTENTPROXIMITY= DBVALUEKIND_LIKE + 1,
DBVALUEKIND_IDISPATCH= 9,
DBVALUEKIND_IUNKNOWN= 13,
DBVALUEKIND_EMPTY= 0,
DBVALUEKIND_NULL= 1,
DBVALUEKIND_I2= 2,
DBVALUEKIND_I4= 3,
DBVALUEKIND_R4= 4,
DBVALUEKIND_R8= 5,
DBVALUEKIND_CY= 6,
DBVALUEKIND_DATE= 7,
DBVALUEKIND_BSTR= 8,
DBVALUEKIND_ERROR= 10,
DBVALUEKIND_BOOL= 11,
DBVALUEKIND_VARIANT= 12,
DBVALUEKIND_VECTOR= 0x1000,
DBVALUEKIND_ARRAY= 0x2000,
DBVALUEKIND_BYREF= 0x4000,
DBVALUEKIND_I1= 16,
DBVALUEKIND_UI1= 17,
DBVALUEKIND_UI2= 18,
DBVALUEKIND_UI4= DBVALUEKIND_UI2 + 1,
DBVALUEKIND_I8= DBVALUEKIND_UI4 + 1,
DBVALUEKIND_UI8= DBVALUEKIND_I8 + 1,
DBVALUEKIND_GUID= 72,
DBVALUEKIND_BYTES= 128,
DBVALUEKIND_STR= 129,
DBVALUEKIND_WSTR= 130,
DBVALUEKIND_NUMERIC= 131,
DBVALUEKIND_DBDATE= 133,
DBVALUEKIND_DBTIME= 134,
DBVALUEKIND_DBTIMESTAMP= 135
    };
typedef struct  tagDBBYGUID
    {
    /* [size_is] */ BYTE __RPC_FAR *pbInfo;
    ULONG cbInfo;
    GUID guid;
    }DBBYGUID;

#define GENERATE_METHOD_EXACT( 0 )
#define GENERATE_METHOD_PREFIX( 1 )
#define GENERATE_METHOD_INFLECT( 2 )
typedef struct  tagDBCONTENT
    {
    LPOLESTR pwszPhrase;
    DWORD dwGenerateMethod;
    LONG lWeight;
    LCID lcid;
    }DBCONTENT;

#define PROPID_QUERY_RANKVECTOR( 0x2 )
#define PROPID_QUERY_RANK( 0x3 )
#define PROPID_QUERY_HITCOUNT( 0x4 )
#define PROPID_QUERY_ALL( 0x6 )
#define PROPID_STG_CONTENTS( 0x13 )
#define VECTOR_RANK_MIN( 0 )
#define VECTOR_RANK_MAX( 1 )
#define VECTOR_RANK_INNER( 2 )
#define VECTOR_RANK_DICE( 3 )
#define VECTOR_RANK_JACCARD( 4 )
typedef struct  tagDBCONTENTVECTOR
    {
    LONG lWeight;
    DWORD dwRankingMethod;
    }DBCONTENTVECTOR;

typedef struct  tagDBGROUPINFO
    {
    LCID lcid;
    }DBGROUPINFO;

typedef struct  tagDBPARAMETER
    {
    LPOLESTR pwszName;
    ITypeInfo __RPC_FAR *pTypeInfo;
    DB_NUMERIC __RPC_FAR *pNum;
    ULONG cbMaxLength;
    DBPARAMFLAGS dwFlags;
    DBTYPE wType;
    }DBPARAMETER;

#define DBSETFUNC_NONE0x0
#define DBSETFUNC_ALL0x1
#define DBSETFUNC_DISTINCT0x2
typedef struct  tagDBSETFUNC
    {
    DWORD dwSetQuantifier;
    }DBSETFUNC;

typedef struct  tagDBSORTINFO
    {
    BOOL fDesc;
    LCID lcid;
    }DBSORTINFO;

typedef struct  tagDBTEXT
    {
    LPOLESTR pwszText;
    ULONG ulErrorLocator;
    ULONG ulTokenLength;
    GUID guidDialect;
    }DBTEXT;

typedef struct  tagDBLIKE
    {
    LONG lWeight;
    GUID guidDialect;
    }DBLIKE;

#define PROXIMITY_UNIT_WORD           ( 0 )
#define PROXIMITY_UNIT_SENTENCE       ( 1 )
#define PROXIMITY_UNIT_PARAGRAPH      ( 2 )
#define PROXIMITY_UNIT_CHAPTER        ( 3 )
typedef struct  tagDBCONTENTPROXIMITY
    {
    DWORD dwProximityUnit;
    ULONG ulProximityDistance;
    LONG lWeight;
    }DBCONTENTPROXIMITY;

typedef struct  tagDBCOMMANDTREE
    {
    DBCOMMANDOP op;
    WORD wKind;
    struct tagDBCOMMANDTREE __RPC_FAR *pctFirstChild;
    struct tagDBCOMMANDTREE __RPC_FAR *pctNextSibling;
    /* [switch_is][switch_type] */ union
        {
        /* [case()] */ BOOL fValue;
        /* [case()] */ unsigned char uchValue;
        /* [case()] */ signed char schValue;
        /* [case()] */ unsigned short usValue;
        /* [case()] */ short sValue;
        /* [case()] */ LPOLESTR pwszValue;
        /* [case()] */ LONG lValue;
        /* [case()] */ ULONG ulValue;
        /* [case()] */ float flValue;
        /* [case()] */ double dblValue;
        /* [case()] */ CY cyValue;
        /* [case()] */ DATE dateValue;
        /* [case()] */ DBDATE dbdateValue;
        /* [case()] */ DBTIME dbtimeValue;
        /* [case()] */ SCODE scodeValue;
        /* [case()] */ hyper llValue;
        /* [case()] */ MIDL_uhyper ullValue;
        /* [case()] */ BSTR __RPC_FAR *pbstrValue;
        /* [case()] */ ICommand __RPC_FAR *pCommand;
        /* [case()] */ IDispatch __RPC_FAR *pDispatch;
        /* [case()] */ IMoniker __RPC_FAR *pMoniker;
        /* [case()] */ IRowset __RPC_FAR *pRowset;
        /* [case()] */ IUnknown __RPC_FAR *pUnknown;
        /* [case()] */ DBBYGUID __RPC_FAR *pdbbygdValue;
        /* [case()] */ DBCOLUMNDESC __RPC_FAR *pcoldescValue;
        /* [case()] */ DBID __RPC_FAR *pdbidValue;
        /* [case()] */ DBLIKE __RPC_FAR *pdblikeValue;
        /* [case()] */ DBCONTENT __RPC_FAR *pdbcntntValue;
        /* [case()] */ DBCONTENTVECTOR __RPC_FAR *pdbcntntvcValue;
        /* [case()] */ DBCONTENTPROXIMITY __RPC_FAR *pdbcntntproxValue;
        /* [case()] */ DBGROUPINFO __RPC_FAR *pdbgrpinfValue;
        /* [case()] */ DBPARAMETER __RPC_FAR *pdbparamValue;
        /* [case()] */ DBPROPSET __RPC_FAR *pdbpropValue;
        /* [case()] */ DBSETFUNC __RPC_FAR *pdbstfncValue;
        /* [case()] */ DBSORTINFO __RPC_FAR *pdbsrtinfValue;
        /* [case()] */ DBTEXT __RPC_FAR *pdbtxtValue;
        /* [case()] */ DBVECTOR __RPC_FAR *pdbvectorValue;
        /* [case()] */ SAFEARRAY __RPC_FAR *parrayValue;
        /* [case()] */ VARIANT __RPC_FAR *pvarValue;
        /* [case()] */ GUID __RPC_FAR *pGuid;
        /* [case()] */ BYTE __RPC_FAR *pbValue;
        /* [case()] */ unsigned char __RPC_FAR *pzValue;
        /* [case()] */ DB_NUMERIC __RPC_FAR *pdbnValue;
        /* [case()] */ DBTIMESTAMP __RPC_FAR *pdbtsValue;
        /* [case()] */ void __RPC_FAR *pvValue;
        }value;
    HRESULT hrError;
    }DBCOMMANDTREE;

EXTERN_C const IID IID_ICommandTree;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a87-2a1c-11ce-ade5-00aa0044773d")
    ICommandTree : public IUnknown
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE FindErrorNodes(
            /* [in] */ const DBCOMMANDTREE __RPC_FAR *pRoot,
            /* [out] */ ULONG __RPC_FAR *pcErrorNodes,
            /* [out] */ DBCOMMANDTREE __RPC_FAR *__RPC_FAR *__RPC_FAR *prgErrorNodes) = 0;

        virtual HRESULT STDMETHODCALLTYPE FreeCommandTree(
            /* [in] */ DBCOMMANDTREE __RPC_FAR *__RPC_FAR *ppRoot) = 0;

        virtual HRESULT STDMETHODCALLTYPE GetCommandTree(
            /* [out] */ DBCOMMANDTREE __RPC_FAR *__RPC_FAR *ppRoot) = 0;

        virtual HRESULT STDMETHODCALLTYPE SetCommandTree(
            /* [in] */ DBCOMMANDTREE __RPC_FAR *__RPC_FAR *ppRoot,
            /* [in] */ DBCOMMANDREUSE dwCommandReuse,
            /* [in] */ BOOL fCopy) = 0;

    };

#else /* C style interface */

    typedef struct ICommandTreeVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            ICommandTree __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            ICommandTree __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            ICommandTree __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *FindErrorNodes )(
            ICommandTree __RPC_FAR * This,
            /* [in] */ const DBCOMMANDTREE __RPC_FAR *pRoot,
            /* [out] */ ULONG __RPC_FAR *pcErrorNodes,
            /* [out] */ DBCOMMANDTREE __RPC_FAR *__RPC_FAR *__RPC_FAR *prgErrorNodes);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *FreeCommandTree )(
            ICommandTree __RPC_FAR * This,
            /* [in] */ DBCOMMANDTREE __RPC_FAR *__RPC_FAR *ppRoot);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetCommandTree )(
            ICommandTree __RPC_FAR * This,
            /* [out] */ DBCOMMANDTREE __RPC_FAR *__RPC_FAR *ppRoot);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetCommandTree )(
            ICommandTree __RPC_FAR * This,
            /* [in] */ DBCOMMANDTREE __RPC_FAR *__RPC_FAR *ppRoot,
            /* [in] */ DBCOMMANDREUSE dwCommandReuse,
            /* [in] */ BOOL fCopy);

        END_INTERFACE
    } ICommandTreeVtbl;

    interface ICommandTree
    {
        CONST_VTBL struct ICommandTreeVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define ICommandTree_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define ICommandTree_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define ICommandTree_Release(This)\
    (This)->lpVtbl -> Release(This)

#define ICommandTree_FindErrorNodes(This,pRoot,pcErrorNodes,prgErrorNodes)\
    (This)->lpVtbl -> FindErrorNodes(This,pRoot,pcErrorNodes,prgErrorNodes)

#define ICommandTree_FreeCommandTree(This,ppRoot)\
    (This)->lpVtbl -> FreeCommandTree(This,ppRoot)

#define ICommandTree_GetCommandTree(This,ppRoot)\
    (This)->lpVtbl -> GetCommandTree(This,ppRoot)

#define ICommandTree_SetCommandTree(This,ppRoot,dwCommandReuse,fCopy)\
    (This)->lpVtbl -> SetCommandTree(This,ppRoot,dwCommandReuse,fCopy)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE ICommandTree_FindErrorNodes_Proxy(
    ICommandTree __RPC_FAR * This,
    /* [in] */ const DBCOMMANDTREE __RPC_FAR *pRoot,
    /* [out] */ ULONG __RPC_FAR *pcErrorNodes,
    /* [out] */ DBCOMMANDTREE __RPC_FAR *__RPC_FAR *__RPC_FAR *prgErrorNodes);

void __RPC_STUB ICommandTree_FindErrorNodes_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE ICommandTree_FreeCommandTree_Proxy(
    ICommandTree __RPC_FAR * This,
    /* [in] */ DBCOMMANDTREE __RPC_FAR *__RPC_FAR *ppRoot);

void __RPC_STUB ICommandTree_FreeCommandTree_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE ICommandTree_GetCommandTree_Proxy(
    ICommandTree __RPC_FAR * This,
    /* [out] */ DBCOMMANDTREE __RPC_FAR *__RPC_FAR *ppRoot);

void __RPC_STUB ICommandTree_GetCommandTree_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE ICommandTree_SetCommandTree_Proxy(
    ICommandTree __RPC_FAR * This,
    /* [in] */ DBCOMMANDTREE __RPC_FAR *__RPC_FAR *ppRoot,
    /* [in] */ DBCOMMANDREUSE dwCommandReuse,
    /* [in] */ BOOL fCopy);

void __RPC_STUB ICommandTree_SetCommandTree_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __ICommandTree_INTERFACE_DEFINED__ */

#ifndef __ICommandValidate_INTERFACE_DEFINED__
#define __ICommandValidate_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: ICommandValidate
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object][local] */

EXTERN_C const IID IID_ICommandValidate;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a18-2a1c-11ce-ade5-00aa0044773d")
    ICommandValidate : public IUnknown
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE ValidateCompletely( void) = 0;

        virtual HRESULT STDMETHODCALLTYPE ValidateSyntax( void) = 0;

    };

#else /* C style interface */

    typedef struct ICommandValidateVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            ICommandValidate __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            ICommandValidate __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            ICommandValidate __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *ValidateCompletely )(
            ICommandValidate __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *ValidateSyntax )(
            ICommandValidate __RPC_FAR * This);

        END_INTERFACE
    } ICommandValidateVtbl;

    interface ICommandValidate
    {
        CONST_VTBL struct ICommandValidateVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define ICommandValidate_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define ICommandValidate_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define ICommandValidate_Release(This)\
    (This)->lpVtbl -> Release(This)

#define ICommandValidate_ValidateCompletely(This)\
    (This)->lpVtbl -> ValidateCompletely(This)

#define ICommandValidate_ValidateSyntax(This)\
    (This)->lpVtbl -> ValidateSyntax(This)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE ICommandValidate_ValidateCompletely_Proxy(
    ICommandValidate __RPC_FAR * This);

void __RPC_STUB ICommandValidate_ValidateCompletely_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE ICommandValidate_ValidateSyntax_Proxy(
    ICommandValidate __RPC_FAR * This);

void __RPC_STUB ICommandValidate_ValidateSyntax_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __ICommandValidate_INTERFACE_DEFINED__ */

/****************************************
 * Generated header for interface: __MIDL_itf_oledb_0112
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [local] */

#endif // OLEDBVER >= 0x0200
//@@@- V2.0

extern RPC_IF_HANDLE __MIDL_itf_oledb_0112_v0_0_c_ifspec;
extern RPC_IF_HANDLE __MIDL_itf_oledb_0112_v0_0_s_ifspec;

#ifndef __ICommandWithParameters_INTERFACE_DEFINED__
#define __ICommandWithParameters_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: ICommandWithParameters
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object] */

typedef struct  tagDBPARAMBINDINFO
    {
    LPOLESTR pwszDataSourceType;
    LPOLESTR pwszName;
    ULONG ulParamSize;
    DBPARAMFLAGS dwFlags;
    BYTE bPrecision;
    BYTE bScale;
    }DBPARAMBINDINFO;

EXTERN_C const IID IID_ICommandWithParameters;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a64-2a1c-11ce-ade5-00aa0044773d")
    ICommandWithParameters : public IUnknown
    {
    public:
        virtual /* [local] */ HRESULT STDMETHODCALLTYPE GetParameterInfo(
            /* [out][in] */ ULONG __RPC_FAR *pcParams,
            /* [size_is][size_is][out] */ DBPARAMINFO __RPC_FAR *__RPC_FAR *prgParamInfo,
            /* [out] */ OLECHAR __RPC_FAR *__RPC_FAR *ppNamesBuffer) = 0;

        virtual /* [local] */ HRESULT STDMETHODCALLTYPE MapParameterNames(
            /* [in] */ ULONG cParamNames,
            /* [size_is][in] */ const OLECHAR __RPC_FAR *__RPC_FAR rgParamNames[  ],
            /* [size_is][out] */ LONG __RPC_FAR rgParamOrdinals[  ]) = 0;

        virtual HRESULT STDMETHODCALLTYPE SetParameterInfo(
            /* [in] */ ULONG cParams,
            /* [size_is][unique][in] */ const ULONG __RPC_FAR rgParamOrdinals[  ],
            /* [size_is][unique][in] */ const DBPARAMBINDINFO __RPC_FAR rgParamBindInfo[  ]) = 0;

    };

#else /* C style interface */

    typedef struct ICommandWithParametersVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            ICommandWithParameters __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            ICommandWithParameters __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            ICommandWithParameters __RPC_FAR * This);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetParameterInfo )(
            ICommandWithParameters __RPC_FAR * This,
            /* [out][in] */ ULONG __RPC_FAR *pcParams,
            /* [size_is][size_is][out] */ DBPARAMINFO __RPC_FAR *__RPC_FAR *prgParamInfo,
            /* [out] */ OLECHAR __RPC_FAR *__RPC_FAR *ppNamesBuffer);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *MapParameterNames )(
            ICommandWithParameters __RPC_FAR * This,
            /* [in] */ ULONG cParamNames,
            /* [size_is][in] */ const OLECHAR __RPC_FAR *__RPC_FAR rgParamNames[  ],
            /* [size_is][out] */ LONG __RPC_FAR rgParamOrdinals[  ]);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetParameterInfo )(
            ICommandWithParameters __RPC_FAR * This,
            /* [in] */ ULONG cParams,
            /* [size_is][unique][in] */ const ULONG __RPC_FAR rgParamOrdinals[  ],
            /* [size_is][unique][in] */ const DBPARAMBINDINFO __RPC_FAR rgParamBindInfo[  ]);

        END_INTERFACE
    } ICommandWithParametersVtbl;

    interface ICommandWithParameters
    {
        CONST_VTBL struct ICommandWithParametersVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define ICommandWithParameters_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define ICommandWithParameters_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define ICommandWithParameters_Release(This)\
    (This)->lpVtbl -> Release(This)

#define ICommandWithParameters_GetParameterInfo(This,pcParams,prgParamInfo,ppNamesBuffer)\
    (This)->lpVtbl -> GetParameterInfo(This,pcParams,prgParamInfo,ppNamesBuffer)

#define ICommandWithParameters_MapParameterNames(This,cParamNames,rgParamNames,rgParamOrdinals)\
    (This)->lpVtbl -> MapParameterNames(This,cParamNames,rgParamNames,rgParamOrdinals)

#define ICommandWithParameters_SetParameterInfo(This,cParams,rgParamOrdinals,rgParamBindInfo)\
    (This)->lpVtbl -> SetParameterInfo(This,cParams,rgParamOrdinals,rgParamBindInfo)

#endif /* COBJMACROS */

#endif /* C style interface */

/* [call_as] */ HRESULT STDMETHODCALLTYPE ICommandWithParameters_RemoteGetParameterInfo_Proxy(
    ICommandWithParameters __RPC_FAR * This,
    /* [out][in] */ ULONG __RPC_FAR *pcParams,
    /* [size_is][size_is][out] */ DBPARAMINFO __RPC_FAR *__RPC_FAR *prgParamInfo,
    /* [size_is][size_is][out] */ ULONG __RPC_FAR *__RPC_FAR *prgNameOffsets,
    /* [out][in] */ ULONG __RPC_FAR *pcbNamesBuffer,
    /* [size_is][size_is][unique][out][in] */ OLECHAR __RPC_FAR *__RPC_FAR *ppNamesBuffer);

void __RPC_STUB ICommandWithParameters_RemoteGetParameterInfo_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

/* [call_as] */ HRESULT STDMETHODCALLTYPE ICommandWithParameters_RemoteMapParameterNames_Proxy(
    ICommandWithParameters __RPC_FAR * This,
    /* [in] */ ULONG cParamNames,
    /* [size_is][in] */ LPCOLESTR __RPC_FAR *rgParamNames,
    /* [size_is][out] */ LONG __RPC_FAR *rgParamOrdinals);

void __RPC_STUB ICommandWithParameters_RemoteMapParameterNames_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE ICommandWithParameters_SetParameterInfo_Proxy(
    ICommandWithParameters __RPC_FAR * This,
    /* [in] */ ULONG cParams,
    /* [size_is][unique][in] */ const ULONG __RPC_FAR rgParamOrdinals[  ],
    /* [size_is][unique][in] */ const DBPARAMBINDINFO __RPC_FAR rgParamBindInfo[  ]);

void __RPC_STUB ICommandWithParameters_SetParameterInfo_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __ICommandWithParameters_INTERFACE_DEFINED__ */

/****************************************
 * Generated header for interface: __MIDL_itf_oledb_0113
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [local] */

//@@@+ V2.0
#if( OLEDBVER >= 0x0200 )

extern RPC_IF_HANDLE __MIDL_itf_oledb_0113_v0_0_c_ifspec;
extern RPC_IF_HANDLE __MIDL_itf_oledb_0113_v0_0_s_ifspec;

#ifndef __IQuery_INTERFACE_DEFINED__
#define __IQuery_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IQuery
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object][local] */

EXTERN_C const IID IID_IQuery;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a51-2a1c-11ce-ade5-00aa0044773d")
    IQuery : public ICommandTree
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE AddPostProcessing(
            /* [in] */ DBCOMMANDTREE __RPC_FAR *__RPC_FAR *ppRoot,
            /* [in] */ BOOL fCopy) = 0;

        virtual HRESULT STDMETHODCALLTYPE GetCardinalityEstimate(
            /* [out] */ ULONG __RPC_FAR *pulCardinality) = 0;

    };

#else /* C style interface */

    typedef struct IQueryVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IQuery __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IQuery __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IQuery __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *FindErrorNodes )(
            IQuery __RPC_FAR * This,
            /* [in] */ const DBCOMMANDTREE __RPC_FAR *pRoot,
            /* [out] */ ULONG __RPC_FAR *pcErrorNodes,
            /* [out] */ DBCOMMANDTREE __RPC_FAR *__RPC_FAR *__RPC_FAR *prgErrorNodes);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *FreeCommandTree )(
            IQuery __RPC_FAR * This,
            /* [in] */ DBCOMMANDTREE __RPC_FAR *__RPC_FAR *ppRoot);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetCommandTree )(
            IQuery __RPC_FAR * This,
            /* [out] */ DBCOMMANDTREE __RPC_FAR *__RPC_FAR *ppRoot);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetCommandTree )(
            IQuery __RPC_FAR * This,
            /* [in] */ DBCOMMANDTREE __RPC_FAR *__RPC_FAR *ppRoot,
            /* [in] */ DBCOMMANDREUSE dwCommandReuse,
            /* [in] */ BOOL fCopy);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *AddPostProcessing )(
            IQuery __RPC_FAR * This,
            /* [in] */ DBCOMMANDTREE __RPC_FAR *__RPC_FAR *ppRoot,
            /* [in] */ BOOL fCopy);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetCardinalityEstimate )(
            IQuery __RPC_FAR * This,
            /* [out] */ ULONG __RPC_FAR *pulCardinality);

        END_INTERFACE
    } IQueryVtbl;

    interface IQuery
    {
        CONST_VTBL struct IQueryVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IQuery_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IQuery_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IQuery_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IQuery_FindErrorNodes(This,pRoot,pcErrorNodes,prgErrorNodes)\
    (This)->lpVtbl -> FindErrorNodes(This,pRoot,pcErrorNodes,prgErrorNodes)

#define IQuery_FreeCommandTree(This,ppRoot)\
    (This)->lpVtbl -> FreeCommandTree(This,ppRoot)

#define IQuery_GetCommandTree(This,ppRoot)\
    (This)->lpVtbl -> GetCommandTree(This,ppRoot)

#define IQuery_SetCommandTree(This,ppRoot,dwCommandReuse,fCopy)\
    (This)->lpVtbl -> SetCommandTree(This,ppRoot,dwCommandReuse,fCopy)

#define IQuery_AddPostProcessing(This,ppRoot,fCopy)\
    (This)->lpVtbl -> AddPostProcessing(This,ppRoot,fCopy)

#define IQuery_GetCardinalityEstimate(This,pulCardinality)\
    (This)->lpVtbl -> GetCardinalityEstimate(This,pulCardinality)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE IQuery_AddPostProcessing_Proxy(
    IQuery __RPC_FAR * This,
    /* [in] */ DBCOMMANDTREE __RPC_FAR *__RPC_FAR *ppRoot,
    /* [in] */ BOOL fCopy);

void __RPC_STUB IQuery_AddPostProcessing_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IQuery_GetCardinalityEstimate_Proxy(
    IQuery __RPC_FAR * This,
    /* [out] */ ULONG __RPC_FAR *pulCardinality);

void __RPC_STUB IQuery_GetCardinalityEstimate_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IQuery_INTERFACE_DEFINED__ */

/****************************************
 * Generated header for interface: __MIDL_itf_oledb_0114
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [local] */

#endif // OLEDBVER >= 0x0200
//@@@- V2.0

extern RPC_IF_HANDLE __MIDL_itf_oledb_0114_v0_0_c_ifspec;
extern RPC_IF_HANDLE __MIDL_itf_oledb_0114_v0_0_s_ifspec;

#ifndef __IColumnsRowset_INTERFACE_DEFINED__
#define __IColumnsRowset_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IColumnsRowset
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object] */

EXTERN_C const IID IID_IColumnsRowset;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a10-2a1c-11ce-ade5-00aa0044773d")
    IColumnsRowset : public IUnknown
    {
    public:
        virtual /* [local] */ HRESULT STDMETHODCALLTYPE GetAvailableColumns(
            /* [out][in] */ ULONG __RPC_FAR *pcOptColumns,
            /* [size_is][size_is][out] */ DBID __RPC_FAR *__RPC_FAR *prgOptColumns) = 0;

        virtual /* [local] */ HRESULT STDMETHODCALLTYPE GetColumnsRowset(
            /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
            /* [in] */ ULONG cOptColumns,
            /* [size_is][in] */ const DBID __RPC_FAR rgOptColumns[  ],
            /* [in] */ REFIID riid,
            /* [in] */ ULONG cPropertySets,
            /* [size_is][out][in] */ DBPROPSET __RPC_FAR rgPropertySets[  ],
            /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppColRowset) = 0;

    };

#else /* C style interface */

    typedef struct IColumnsRowsetVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IColumnsRowset __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IColumnsRowset __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IColumnsRowset __RPC_FAR * This);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetAvailableColumns )(
            IColumnsRowset __RPC_FAR * This,
            /* [out][in] */ ULONG __RPC_FAR *pcOptColumns,
            /* [size_is][size_is][out] */ DBID __RPC_FAR *__RPC_FAR *prgOptColumns);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetColumnsRowset )(
            IColumnsRowset __RPC_FAR * This,
            /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
            /* [in] */ ULONG cOptColumns,
            /* [size_is][in] */ const DBID __RPC_FAR rgOptColumns[  ],
            /* [in] */ REFIID riid,
            /* [in] */ ULONG cPropertySets,
            /* [size_is][out][in] */ DBPROPSET __RPC_FAR rgPropertySets[  ],
            /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppColRowset);

        END_INTERFACE
    } IColumnsRowsetVtbl;

    interface IColumnsRowset
    {
        CONST_VTBL struct IColumnsRowsetVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IColumnsRowset_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IColumnsRowset_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IColumnsRowset_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IColumnsRowset_GetAvailableColumns(This,pcOptColumns,prgOptColumns)\
    (This)->lpVtbl -> GetAvailableColumns(This,pcOptColumns,prgOptColumns)

#define IColumnsRowset_GetColumnsRowset(This,pUnkOuter,cOptColumns,rgOptColumns,riid,cPropertySets,rgPropertySets,ppColRowset)\
    (This)->lpVtbl -> GetColumnsRowset(This,pUnkOuter,cOptColumns,rgOptColumns,riid,cPropertySets,rgPropertySets,ppColRowset)

#endif /* COBJMACROS */

#endif /* C style interface */

/* [call_as] */ HRESULT STDMETHODCALLTYPE IColumnsRowset_RemoteGetAvailableColumns_Proxy(
    IColumnsRowset __RPC_FAR * This,
    /* [out][in] */ ULONG __RPC_FAR *pcOptColumns,
    /* [size_is][size_is][out] */ DBID __RPC_FAR *__RPC_FAR *prgOptColumns);

void __RPC_STUB IColumnsRowset_RemoteGetAvailableColumns_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

/* [call_as] */ HRESULT STDMETHODCALLTYPE IColumnsRowset_RemoteGetColumnsRowset_Proxy(
    IColumnsRowset __RPC_FAR * This,
    /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
    /* [in] */ ULONG cOptColumns,
    /* [size_is][unique][in] */ const DBID __RPC_FAR *rgOptColumns,
    /* [in] */ REFIID riid,
    /* [in] */ ULONG cPropertySets,
    /* [size_is][unique][out][in] */ DBPROPSET __RPC_FAR *rgPropertySets,
    /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppColRowset);

void __RPC_STUB IColumnsRowset_RemoteGetColumnsRowset_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IColumnsRowset_INTERFACE_DEFINED__ */

#ifndef __IColumnsInfo_INTERFACE_DEFINED__
#define __IColumnsInfo_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IColumnsInfo
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object] */

EXTERN_C const IID IID_IColumnsInfo;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a11-2a1c-11ce-ade5-00aa0044773d")
    IColumnsInfo : public IUnknown
    {
    public:
        virtual /* [local] */ HRESULT STDMETHODCALLTYPE GetColumnInfo(
            /* [out][in] */ ULONG __RPC_FAR *pcColumns,
            /* [size_is][size_is][out] */ DBCOLUMNINFO __RPC_FAR *__RPC_FAR *prgInfo,
            /* [out] */ OLECHAR __RPC_FAR *__RPC_FAR *ppStringsBuffer) = 0;

        virtual /* [local] */ HRESULT STDMETHODCALLTYPE MapColumnIDs(
            /* [in] */ ULONG cColumnIDs,
            /* [size_is][in] */ const DBID __RPC_FAR rgColumnIDs[  ],
            /* [size_is][out] */ ULONG __RPC_FAR rgColumns[  ]) = 0;

    };

#else /* C style interface */

    typedef struct IColumnsInfoVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IColumnsInfo __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IColumnsInfo __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IColumnsInfo __RPC_FAR * This);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetColumnInfo )(
            IColumnsInfo __RPC_FAR * This,
            /* [out][in] */ ULONG __RPC_FAR *pcColumns,
            /* [size_is][size_is][out] */ DBCOLUMNINFO __RPC_FAR *__RPC_FAR *prgInfo,
            /* [out] */ OLECHAR __RPC_FAR *__RPC_FAR *ppStringsBuffer);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *MapColumnIDs )(
            IColumnsInfo __RPC_FAR * This,
            /* [in] */ ULONG cColumnIDs,
            /* [size_is][in] */ const DBID __RPC_FAR rgColumnIDs[  ],
            /* [size_is][out] */ ULONG __RPC_FAR rgColumns[  ]);

        END_INTERFACE
    } IColumnsInfoVtbl;

    interface IColumnsInfo
    {
        CONST_VTBL struct IColumnsInfoVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IColumnsInfo_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IColumnsInfo_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IColumnsInfo_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IColumnsInfo_GetColumnInfo(This,pcColumns,prgInfo,ppStringsBuffer)\
    (This)->lpVtbl -> GetColumnInfo(This,pcColumns,prgInfo,ppStringsBuffer)

#define IColumnsInfo_MapColumnIDs(This,cColumnIDs,rgColumnIDs,rgColumns)\
    (This)->lpVtbl -> MapColumnIDs(This,cColumnIDs,rgColumnIDs,rgColumns)

#endif /* COBJMACROS */

#endif /* C style interface */

/* [call_as] */ HRESULT STDMETHODCALLTYPE IColumnsInfo_RemoteGetColumnInfo_Proxy(
    IColumnsInfo __RPC_FAR * This,
    /* [out][in] */ ULONG __RPC_FAR *pcColumns,
    /* [size_is][size_is][out] */ DBCOLUMNINFO __RPC_FAR *__RPC_FAR *prgInfo,
    /* [size_is][size_is][out] */ ULONG __RPC_FAR *__RPC_FAR *prgNameOffsets,
    /* [size_is][size_is][out] */ ULONG __RPC_FAR *__RPC_FAR *prgcolumnidOffsets,
    /* [out][in] */ ULONG __RPC_FAR *pcbStringsBuffer,
    /* [size_is][size_is][unique][out][in] */ OLECHAR __RPC_FAR *__RPC_FAR *ppStringsBuffer);

void __RPC_STUB IColumnsInfo_RemoteGetColumnInfo_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

/* [call_as] */ HRESULT STDMETHODCALLTYPE IColumnsInfo_RemoteMapColumnIDs_Proxy(
    IColumnsInfo __RPC_FAR * This,
    /* [in] */ ULONG cColumnIDs,
    /* [size_is][in] */ const DBID __RPC_FAR *rgColumnIDs,
    /* [size_is][out] */ ULONG __RPC_FAR *rgColumns);

void __RPC_STUB IColumnsInfo_RemoteMapColumnIDs_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IColumnsInfo_INTERFACE_DEFINED__ */

#ifndef __IDBCreateCommand_INTERFACE_DEFINED__
#define __IDBCreateCommand_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IDBCreateCommand
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object] */

EXTERN_C const IID IID_IDBCreateCommand;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a1d-2a1c-11ce-ade5-00aa0044773d")
    IDBCreateCommand : public IUnknown
    {
    public:
        virtual /* [local] */ HRESULT STDMETHODCALLTYPE CreateCommand(
            /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppCommand) = 0;

    };

#else /* C style interface */

    typedef struct IDBCreateCommandVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IDBCreateCommand __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IDBCreateCommand __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IDBCreateCommand __RPC_FAR * This);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *CreateCommand )(
            IDBCreateCommand __RPC_FAR * This,
            /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppCommand);

        END_INTERFACE
    } IDBCreateCommandVtbl;

    interface IDBCreateCommand
    {
        CONST_VTBL struct IDBCreateCommandVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IDBCreateCommand_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IDBCreateCommand_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IDBCreateCommand_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IDBCreateCommand_CreateCommand(This,pUnkOuter,riid,ppCommand)\
    (This)->lpVtbl -> CreateCommand(This,pUnkOuter,riid,ppCommand)

#endif /* COBJMACROS */

#endif /* C style interface */

/* [call_as] */ HRESULT STDMETHODCALLTYPE IDBCreateCommand_RemoteCreateCommand_Proxy(
    IDBCreateCommand __RPC_FAR * This,
    /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
    /* [in] */ REFIID riid,
    /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppCommand);

void __RPC_STUB IDBCreateCommand_RemoteCreateCommand_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IDBCreateCommand_INTERFACE_DEFINED__ */

#ifndef __IDBCreateSession_INTERFACE_DEFINED__
#define __IDBCreateSession_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IDBCreateSession
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object] */

EXTERN_C const IID IID_IDBCreateSession;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a5d-2a1c-11ce-ade5-00aa0044773d")
    IDBCreateSession : public IUnknown
    {
    public:
        virtual /* [local] */ HRESULT STDMETHODCALLTYPE CreateSession(
            /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppDBSession) = 0;

    };

#else /* C style interface */

    typedef struct IDBCreateSessionVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IDBCreateSession __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IDBCreateSession __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IDBCreateSession __RPC_FAR * This);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *CreateSession )(
            IDBCreateSession __RPC_FAR * This,
            /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppDBSession);

        END_INTERFACE
    } IDBCreateSessionVtbl;

    interface IDBCreateSession
    {
        CONST_VTBL struct IDBCreateSessionVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IDBCreateSession_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IDBCreateSession_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IDBCreateSession_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IDBCreateSession_CreateSession(This,pUnkOuter,riid,ppDBSession)\
    (This)->lpVtbl -> CreateSession(This,pUnkOuter,riid,ppDBSession)

#endif /* COBJMACROS */

#endif /* C style interface */

/* [call_as] */ HRESULT STDMETHODCALLTYPE IDBCreateSession_RemoteCreateSession_Proxy(
    IDBCreateSession __RPC_FAR * This,
    /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
    /* [in] */ REFIID riid,
    /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppDBSession);

void __RPC_STUB IDBCreateSession_RemoteCreateSession_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IDBCreateSession_INTERFACE_DEFINED__ */

#ifndef __ISourcesRowset_INTERFACE_DEFINED__
#define __ISourcesRowset_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: ISourcesRowset
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object] */

typedef DWORD DBSOURCETYPE;

enum DBSOURCETYPEENUM
    {DBSOURCETYPE_DATASOURCE= 1,
DBSOURCETYPE_ENUMERATOR= 2
    };
//@@@+ V2.0
#if( OLEDBVER >= 0x0200 )

enum DBSOURCETYPEENUM20
    {DBSOURCETYPE_DATASOURCE_TDP= 1,
DBSOURCETYPE_DATASOURCE_MDP= 3
    };
#endif // OLEDBVER >= 0x0200
//@@@- V2.0

EXTERN_C const IID IID_ISourcesRowset;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a1e-2a1c-11ce-ade5-00aa0044773d")
    ISourcesRowset : public IUnknown
    {
    public:
        virtual /* [local] */ HRESULT STDMETHODCALLTYPE GetSourcesRowset(
            /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
            /* [in] */ REFIID riid,
            /* [in] */ ULONG cPropertySets,
            /* [size_is][out][in] */ DBPROPSET __RPC_FAR rgProperties[  ],
            /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppSourcesRowset) = 0;

    };

#else /* C style interface */

    typedef struct ISourcesRowsetVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            ISourcesRowset __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            ISourcesRowset __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            ISourcesRowset __RPC_FAR * This);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetSourcesRowset )(
            ISourcesRowset __RPC_FAR * This,
            /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
            /* [in] */ REFIID riid,
            /* [in] */ ULONG cPropertySets,
            /* [size_is][out][in] */ DBPROPSET __RPC_FAR rgProperties[  ],
            /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppSourcesRowset);

        END_INTERFACE
    } ISourcesRowsetVtbl;

    interface ISourcesRowset
    {
        CONST_VTBL struct ISourcesRowsetVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define ISourcesRowset_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define ISourcesRowset_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define ISourcesRowset_Release(This)\
    (This)->lpVtbl -> Release(This)

#define ISourcesRowset_GetSourcesRowset(This,pUnkOuter,riid,cPropertySets,rgProperties,ppSourcesRowset)\
    (This)->lpVtbl -> GetSourcesRowset(This,pUnkOuter,riid,cPropertySets,rgProperties,ppSourcesRowset)

#endif /* COBJMACROS */

#endif /* C style interface */

/* [call_as] */ HRESULT STDMETHODCALLTYPE ISourcesRowset_RemoteGetSourcesRowset_Proxy(
    ISourcesRowset __RPC_FAR * This,
    /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
    /* [in] */ REFIID riid,
    /* [in] */ ULONG cPropertySets,
    /* [size_is][unique][out][in] */ DBPROPSET __RPC_FAR *rgProperties,
    /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppSourcesRowset);

void __RPC_STUB ISourcesRowset_RemoteGetSourcesRowset_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __ISourcesRowset_INTERFACE_DEFINED__ */

#ifndef __IDBProperties_INTERFACE_DEFINED__
#define __IDBProperties_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IDBProperties
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object] */

EXTERN_C const IID IID_IDBProperties;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a8a-2a1c-11ce-ade5-00aa0044773d")
    IDBProperties : public IUnknown
    {
    public:
        virtual /* [local] */ HRESULT STDMETHODCALLTYPE GetProperties(
            /* [in] */ ULONG cPropertyIDSets,
            /* [size_is][in] */ const DBPROPIDSET __RPC_FAR rgPropertyIDSets[  ],
            /* [out][in] */ ULONG __RPC_FAR *pcPropertySets,
            /* [size_is][size_is][out] */ DBPROPSET __RPC_FAR *__RPC_FAR *prgPropertySets) = 0;

        virtual /* [local] */ HRESULT STDMETHODCALLTYPE GetPropertyInfo(
            /* [in] */ ULONG cPropertyIDSets,
            /* [size_is][in] */ const DBPROPIDSET __RPC_FAR rgPropertyIDSets[  ],
            /* [out][in] */ ULONG __RPC_FAR *pcPropertyInfoSets,
            /* [size_is][size_is][out] */ DBPROPINFOSET __RPC_FAR *__RPC_FAR *prgPropertyInfoSets,
            /* [out] */ OLECHAR __RPC_FAR *__RPC_FAR *ppDescBuffer) = 0;

        virtual /* [local] */ HRESULT STDMETHODCALLTYPE SetProperties(
            /* [in] */ ULONG cPropertySets,
            /* [size_is][out][in] */ DBPROPSET __RPC_FAR rgPropertySets[  ]) = 0;

    };

#else /* C style interface */

    typedef struct IDBPropertiesVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IDBProperties __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IDBProperties __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IDBProperties __RPC_FAR * This);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetProperties )(
            IDBProperties __RPC_FAR * This,
            /* [in] */ ULONG cPropertyIDSets,
            /* [size_is][in] */ const DBPROPIDSET __RPC_FAR rgPropertyIDSets[  ],
            /* [out][in] */ ULONG __RPC_FAR *pcPropertySets,
            /* [size_is][size_is][out] */ DBPROPSET __RPC_FAR *__RPC_FAR *prgPropertySets);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetPropertyInfo )(
            IDBProperties __RPC_FAR * This,
            /* [in] */ ULONG cPropertyIDSets,
            /* [size_is][in] */ const DBPROPIDSET __RPC_FAR rgPropertyIDSets[  ],
            /* [out][in] */ ULONG __RPC_FAR *pcPropertyInfoSets,
            /* [size_is][size_is][out] */ DBPROPINFOSET __RPC_FAR *__RPC_FAR *prgPropertyInfoSets,
            /* [out] */ OLECHAR __RPC_FAR *__RPC_FAR *ppDescBuffer);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetProperties )(
            IDBProperties __RPC_FAR * This,
            /* [in] */ ULONG cPropertySets,
            /* [size_is][out][in] */ DBPROPSET __RPC_FAR rgPropertySets[  ]);

        END_INTERFACE
    } IDBPropertiesVtbl;

    interface IDBProperties
    {
        CONST_VTBL struct IDBPropertiesVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IDBProperties_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IDBProperties_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IDBProperties_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IDBProperties_GetProperties(This,cPropertyIDSets,rgPropertyIDSets,pcPropertySets,prgPropertySets)\
    (This)->lpVtbl -> GetProperties(This,cPropertyIDSets,rgPropertyIDSets,pcPropertySets,prgPropertySets)

#define IDBProperties_GetPropertyInfo(This,cPropertyIDSets,rgPropertyIDSets,pcPropertyInfoSets,prgPropertyInfoSets,ppDescBuffer)\
    (This)->lpVtbl -> GetPropertyInfo(This,cPropertyIDSets,rgPropertyIDSets,pcPropertyInfoSets,prgPropertyInfoSets,ppDescBuffer)

#define IDBProperties_SetProperties(This,cPropertySets,rgPropertySets)\
    (This)->lpVtbl -> SetProperties(This,cPropertySets,rgPropertySets)

#endif /* COBJMACROS */

#endif /* C style interface */

/* [call_as] */ HRESULT STDMETHODCALLTYPE IDBProperties_RemoteGetProperties_Proxy(
    IDBProperties __RPC_FAR * This,
    /* [in] */ ULONG cPropertyIDSets,
    /* [size_is][unique][in] */ const DBPROPIDSET __RPC_FAR *rgPropertyIDSets,
    /* [out][in] */ ULONG __RPC_FAR *pcPropertySets,
    /* [size_is][size_is][out] */ DBPROPSET __RPC_FAR *__RPC_FAR *prgPropertySets);

void __RPC_STUB IDBProperties_RemoteGetProperties_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

/* [call_as] */ HRESULT STDMETHODCALLTYPE IDBProperties_RemoteGetPropertyInfo_Proxy(
    IDBProperties __RPC_FAR * This,
    /* [in] */ ULONG cPropertyIDSets,
    /* [size_is][unique][in] */ const DBPROPIDSET __RPC_FAR *rgPropertyIDSets,
    /* [out][in] */ ULONG __RPC_FAR *pcPropertyInfoSets,
    /* [size_is][size_is][out] */ DBPROPINFOSET __RPC_FAR *__RPC_FAR *prgPropertyInfoSets,
    /* [out][in] */ ULONG __RPC_FAR *pcOffsets,
    /* [size_is][size_is][out] */ ULONG __RPC_FAR *__RPC_FAR *prgDescOffsets,
    /* [out][in] */ ULONG __RPC_FAR *pcbDescBuffer,
    /* [size_is][size_is][unique][out][in] */ OLECHAR __RPC_FAR *__RPC_FAR *ppDescBuffer);

void __RPC_STUB IDBProperties_RemoteGetPropertyInfo_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

/* [call_as] */ HRESULT STDMETHODCALLTYPE IDBProperties_RemoteSetProperties_Proxy(
    IDBProperties __RPC_FAR * This,
    /* [in] */ ULONG cPropertySets,
    /* [size_is][unique][in] */ DBPROPSET __RPC_FAR *rgPropertySets);

void __RPC_STUB IDBProperties_RemoteSetProperties_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IDBProperties_INTERFACE_DEFINED__ */

#ifndef __IDBInitialize_INTERFACE_DEFINED__
#define __IDBInitialize_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IDBInitialize
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object] */

EXTERN_C const IID IID_IDBInitialize;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a8b-2a1c-11ce-ade5-00aa0044773d")
    IDBInitialize : public IUnknown
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE Initialize( void) = 0;

        virtual HRESULT STDMETHODCALLTYPE Uninitialize( void) = 0;

    };

#else /* C style interface */

    typedef struct IDBInitializeVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IDBInitialize __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IDBInitialize __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IDBInitialize __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Initialize )(
            IDBInitialize __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Uninitialize )(
            IDBInitialize __RPC_FAR * This);

        END_INTERFACE
    } IDBInitializeVtbl;

    interface IDBInitialize
    {
        CONST_VTBL struct IDBInitializeVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IDBInitialize_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IDBInitialize_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IDBInitialize_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IDBInitialize_Initialize(This)\
    (This)->lpVtbl -> Initialize(This)

#define IDBInitialize_Uninitialize(This)\
    (This)->lpVtbl -> Uninitialize(This)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE IDBInitialize_Initialize_Proxy(
    IDBInitialize __RPC_FAR * This);

void __RPC_STUB IDBInitialize_Initialize_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IDBInitialize_Uninitialize_Proxy(
    IDBInitialize __RPC_FAR * This);

void __RPC_STUB IDBInitialize_Uninitialize_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IDBInitialize_INTERFACE_DEFINED__ */

#ifndef __IDBInfo_INTERFACE_DEFINED__
#define __IDBInfo_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IDBInfo
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object] */

typedef DWORD DBLITERAL;

enum DBLITERALENUM
    {DBLITERAL_INVALID= 0,
DBLITERAL_BINARY_LITERAL= DBLITERAL_INVALID + 1,
DBLITERAL_CATALOG_NAME= DBLITERAL_BINARY_LITERAL + 1,
DBLITERAL_CATALOG_SEPARATOR= DBLITERAL_CATALOG_NAME + 1,
DBLITERAL_CHAR_LITERAL= DBLITERAL_CATALOG_SEPARATOR + 1,
DBLITERAL_COLUMN_ALIAS= DBLITERAL_CHAR_LITERAL + 1,
DBLITERAL_COLUMN_NAME= DBLITERAL_COLUMN_ALIAS + 1,
DBLITERAL_CORRELATION_NAME= DBLITERAL_COLUMN_NAME + 1,
DBLITERAL_CURSOR_NAME= DBLITERAL_CORRELATION_NAME + 1,
DBLITERAL_ESCAPE_PERCENT= DBLITERAL_CURSOR_NAME + 1,
DBLITERAL_ESCAPE_UNDERSCORE= DBLITERAL_ESCAPE_PERCENT + 1,
DBLITERAL_INDEX_NAME= DBLITERAL_ESCAPE_UNDERSCORE + 1,
DBLITERAL_LIKE_PERCENT= DBLITERAL_INDEX_NAME + 1,
DBLITERAL_LIKE_UNDERSCORE= DBLITERAL_LIKE_PERCENT + 1,
DBLITERAL_PROCEDURE_NAME= DBLITERAL_LIKE_UNDERSCORE + 1,
DBLITERAL_QUOTE= DBLITERAL_PROCEDURE_NAME + 1,
DBLITERAL_SCHEMA_NAME= DBLITERAL_QUOTE + 1,
DBLITERAL_TABLE_NAME= DBLITERAL_SCHEMA_NAME + 1,
DBLITERAL_TEXT_COMMAND= DBLITERAL_TABLE_NAME + 1,
DBLITERAL_USER_NAME= DBLITERAL_TEXT_COMMAND + 1,
DBLITERAL_VIEW_NAME= DBLITERAL_USER_NAME + 1
    };
typedef struct  tagDBLITERALINFO
    {
    LPOLESTR pwszLiteralValue;
    LPOLESTR pwszInvalidChars;
    LPOLESTR pwszInvalidStartingChars;
    DBLITERAL lt;
    BOOL fSupported;
    ULONG cchMaxLen;
    }DBLITERALINFO;

EXTERN_C const IID IID_IDBInfo;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a89-2a1c-11ce-ade5-00aa0044773d")
    IDBInfo : public IUnknown
    {
    public:
        virtual /* [local] */ HRESULT STDMETHODCALLTYPE GetKeywords(
            /* [out] */ LPOLESTR __RPC_FAR *ppwszKeywords) = 0;

        virtual /* [local] */ HRESULT STDMETHODCALLTYPE GetLiteralInfo(
            /* [in] */ ULONG cLiterals,
            /* [size_is][in] */ const DBLITERAL __RPC_FAR rgLiterals[  ],
            /* [out][in] */ ULONG __RPC_FAR *pcLiteralInfo,
            /* [size_is][size_is][out] */ DBLITERALINFO __RPC_FAR *__RPC_FAR *prgLiteralInfo,
            /* [out] */ OLECHAR __RPC_FAR *__RPC_FAR *ppCharBuffer) = 0;

    };

#else /* C style interface */

    typedef struct IDBInfoVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IDBInfo __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IDBInfo __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IDBInfo __RPC_FAR * This);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetKeywords )(
            IDBInfo __RPC_FAR * This,
            /* [out] */ LPOLESTR __RPC_FAR *ppwszKeywords);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetLiteralInfo )(
            IDBInfo __RPC_FAR * This,
            /* [in] */ ULONG cLiterals,
            /* [size_is][in] */ const DBLITERAL __RPC_FAR rgLiterals[  ],
            /* [out][in] */ ULONG __RPC_FAR *pcLiteralInfo,
            /* [size_is][size_is][out] */ DBLITERALINFO __RPC_FAR *__RPC_FAR *prgLiteralInfo,
            /* [out] */ OLECHAR __RPC_FAR *__RPC_FAR *ppCharBuffer);

        END_INTERFACE
    } IDBInfoVtbl;

    interface IDBInfo
    {
        CONST_VTBL struct IDBInfoVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IDBInfo_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IDBInfo_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IDBInfo_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IDBInfo_GetKeywords(This,ppwszKeywords)\
    (This)->lpVtbl -> GetKeywords(This,ppwszKeywords)

#define IDBInfo_GetLiteralInfo(This,cLiterals,rgLiterals,pcLiteralInfo,prgLiteralInfo,ppCharBuffer)\
    (This)->lpVtbl -> GetLiteralInfo(This,cLiterals,rgLiterals,pcLiteralInfo,prgLiteralInfo,ppCharBuffer)

#endif /* COBJMACROS */

#endif /* C style interface */

/* [call_as] */ HRESULT STDMETHODCALLTYPE IDBInfo_RemoteGetKeywords_Proxy(
    IDBInfo __RPC_FAR * This,
    /* [unique][out][in] */ LPOLESTR __RPC_FAR *ppwszKeywords);

void __RPC_STUB IDBInfo_RemoteGetKeywords_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

/* [call_as] */ HRESULT STDMETHODCALLTYPE IDBInfo_RemoteGetLiteralInfo_Proxy(
    IDBInfo __RPC_FAR * This,
    /* [in] */ ULONG cLiterals,
    /* [size_is][unique][in] */ const DBLITERAL __RPC_FAR *rgLiterals,
    /* [out][in] */ ULONG __RPC_FAR *pcLiteralInfo,
    /* [size_is][size_is][out] */ DBLITERALINFO __RPC_FAR *__RPC_FAR *prgLiteralInfo,
    /* [size_is][size_is][out] */ ULONG __RPC_FAR *__RPC_FAR *prgLVOffsets,
    /* [size_is][size_is][out] */ ULONG __RPC_FAR *__RPC_FAR *prgICOffsets,
    /* [size_is][size_is][out] */ ULONG __RPC_FAR *__RPC_FAR *prgISCOffsets,
    /* [out][in] */ ULONG __RPC_FAR *pcbCharBuffer,
    /* [size_is][size_is][unique][out][in] */ OLECHAR __RPC_FAR *__RPC_FAR *ppCharBuffer);

void __RPC_STUB IDBInfo_RemoteGetLiteralInfo_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IDBInfo_INTERFACE_DEFINED__ */

#ifndef __IDBDataSourceAdmin_INTERFACE_DEFINED__
#define __IDBDataSourceAdmin_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IDBDataSourceAdmin
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object] */

EXTERN_C const IID IID_IDBDataSourceAdmin;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a7a-2a1c-11ce-ade5-00aa0044773d")
    IDBDataSourceAdmin : public IUnknown
    {
    public:
        virtual /* [local] */ HRESULT STDMETHODCALLTYPE CreateDataSource(
            /* [in] */ ULONG cPropertySets,
            /* [size_is][out][in] */ DBPROPSET __RPC_FAR rgPropertySets[  ],
            /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppDBSession) = 0;

        virtual HRESULT STDMETHODCALLTYPE DestroyDataSource( void) = 0;

        virtual /* [local] */ HRESULT STDMETHODCALLTYPE GetCreationProperties(
            /* [in] */ ULONG cPropertyIDSets,
            /* [size_is][in] */ const DBPROPIDSET __RPC_FAR rgPropertyIDSets[  ],
            /* [out][in] */ ULONG __RPC_FAR *pcPropertyInfoSets,
            /* [size_is][size_is][out] */ DBPROPINFOSET __RPC_FAR *__RPC_FAR *prgPropertyInfoSets,
            /* [out] */ OLECHAR __RPC_FAR *__RPC_FAR *ppDescBuffer) = 0;

        virtual /* [local] */ HRESULT STDMETHODCALLTYPE ModifyDataSource(
            /* [in] */ ULONG cPropertySets,
            /* [size_is][in] */ DBPROPSET __RPC_FAR rgPropertySets[  ]) = 0;

    };

#else /* C style interface */

    typedef struct IDBDataSourceAdminVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IDBDataSourceAdmin __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IDBDataSourceAdmin __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IDBDataSourceAdmin __RPC_FAR * This);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *CreateDataSource )(
            IDBDataSourceAdmin __RPC_FAR * This,
            /* [in] */ ULONG cPropertySets,
            /* [size_is][out][in] */ DBPROPSET __RPC_FAR rgPropertySets[  ],
            /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppDBSession);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *DestroyDataSource )(
            IDBDataSourceAdmin __RPC_FAR * This);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetCreationProperties )(
            IDBDataSourceAdmin __RPC_FAR * This,
            /* [in] */ ULONG cPropertyIDSets,
            /* [size_is][in] */ const DBPROPIDSET __RPC_FAR rgPropertyIDSets[  ],
            /* [out][in] */ ULONG __RPC_FAR *pcPropertyInfoSets,
            /* [size_is][size_is][out] */ DBPROPINFOSET __RPC_FAR *__RPC_FAR *prgPropertyInfoSets,
            /* [out] */ OLECHAR __RPC_FAR *__RPC_FAR *ppDescBuffer);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *ModifyDataSource )(
            IDBDataSourceAdmin __RPC_FAR * This,
            /* [in] */ ULONG cPropertySets,
            /* [size_is][in] */ DBPROPSET __RPC_FAR rgPropertySets[  ]);

        END_INTERFACE
    } IDBDataSourceAdminVtbl;

    interface IDBDataSourceAdmin
    {
        CONST_VTBL struct IDBDataSourceAdminVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IDBDataSourceAdmin_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IDBDataSourceAdmin_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IDBDataSourceAdmin_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IDBDataSourceAdmin_CreateDataSource(This,cPropertySets,rgPropertySets,pUnkOuter,riid,ppDBSession)\
    (This)->lpVtbl -> CreateDataSource(This,cPropertySets,rgPropertySets,pUnkOuter,riid,ppDBSession)

#define IDBDataSourceAdmin_DestroyDataSource(This)\
    (This)->lpVtbl -> DestroyDataSource(This)

#define IDBDataSourceAdmin_GetCreationProperties(This,cPropertyIDSets,rgPropertyIDSets,pcPropertyInfoSets,prgPropertyInfoSets,ppDescBuffer)\
    (This)->lpVtbl -> GetCreationProperties(This,cPropertyIDSets,rgPropertyIDSets,pcPropertyInfoSets,prgPropertyInfoSets,ppDescBuffer)

#define IDBDataSourceAdmin_ModifyDataSource(This,cPropertySets,rgPropertySets)\
    (This)->lpVtbl -> ModifyDataSource(This,cPropertySets,rgPropertySets)

#endif /* COBJMACROS */

#endif /* C style interface */

/* [call_as] */ HRESULT STDMETHODCALLTYPE IDBDataSourceAdmin_RemoteCreateDataSource_Proxy(
    IDBDataSourceAdmin __RPC_FAR * This,
    /* [in] */ ULONG cPropertySets,
    /* [size_is][unique][out][in] */ DBPROPSET __RPC_FAR *rgPropertySets,
    /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
    /* [in] */ REFIID riid,
    /* [iid_is][unique][out][in] */ IUnknown __RPC_FAR *__RPC_FAR *ppDBSession);

void __RPC_STUB IDBDataSourceAdmin_RemoteCreateDataSource_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IDBDataSourceAdmin_DestroyDataSource_Proxy(
    IDBDataSourceAdmin __RPC_FAR * This);

void __RPC_STUB IDBDataSourceAdmin_DestroyDataSource_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

/* [call_as] */ HRESULT STDMETHODCALLTYPE IDBDataSourceAdmin_RemoteGetCreationProperties_Proxy(
    IDBDataSourceAdmin __RPC_FAR * This,
    /* [in] */ ULONG cPropertyIDSets,
    /* [size_is][unique][in] */ const DBPROPIDSET __RPC_FAR *rgPropertyIDSets,
    /* [out][in] */ ULONG __RPC_FAR *pcPropertyInfoSets,
    /* [size_is][size_is][out] */ DBPROPINFOSET __RPC_FAR *__RPC_FAR *prgPropertyInfoSets,
    /* [out][in] */ ULONG __RPC_FAR *pcOffsets,
    /* [size_is][size_is][out] */ ULONG __RPC_FAR *__RPC_FAR *prgDescOffsets,
    /* [out][in] */ ULONG __RPC_FAR *pcbDescBuffer,
    /* [size_is][size_is][unique][out][in] */ OLECHAR __RPC_FAR *__RPC_FAR *ppDescBuffer);

void __RPC_STUB IDBDataSourceAdmin_RemoteGetCreationProperties_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

/* [call_as] */ HRESULT STDMETHODCALLTYPE IDBDataSourceAdmin_RemoteModifyDataSource_Proxy(
    IDBDataSourceAdmin __RPC_FAR * This,
    /* [in] */ ULONG cPropertySets,
    /* [size_is][in] */ DBPROPSET __RPC_FAR *rgPropertySets);

void __RPC_STUB IDBDataSourceAdmin_RemoteModifyDataSource_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IDBDataSourceAdmin_INTERFACE_DEFINED__ */

/****************************************
 * Generated header for interface: __MIDL_itf_oledb_0123
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [local] */

//@@@+ V1.5
#if( OLEDBVER >= 0x0150 )

extern RPC_IF_HANDLE __MIDL_itf_oledb_0123_v0_0_c_ifspec;
extern RPC_IF_HANDLE __MIDL_itf_oledb_0123_v0_0_s_ifspec;

#ifndef __IDBAsynchNotify_INTERFACE_DEFINED__
#define __IDBAsynchNotify_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IDBAsynchNotify
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object] */

EXTERN_C const IID IID_IDBAsynchNotify;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a96-2a1c-11ce-ade5-00aa0044773d")
    IDBAsynchNotify : public IUnknown
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE OnLowResource(
            /* [in] */ DWORD dwReserved) = 0;

        virtual HRESULT STDMETHODCALLTYPE OnProgress(
            /* [in] */ HCHAPTER hChapter,
            /* [in] */ ULONG ulOperation,
            /* [in] */ ULONG ulProgress,
            /* [in] */ ULONG ulProgressMax,
            /* [in] */ ULONG ulAsynchPhase,
            /* [in] */ LPOLESTR pwszStatusText) = 0;

        virtual HRESULT STDMETHODCALLTYPE OnStop(
            /* [in] */ HCHAPTER hChapter,
            /* [in] */ ULONG ulOperation,
            /* [in] */ HRESULT hrStatus,
            /* [in] */ LPOLESTR pwszStatusText) = 0;

    };

#else /* C style interface */

    typedef struct IDBAsynchNotifyVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IDBAsynchNotify __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IDBAsynchNotify __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IDBAsynchNotify __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnLowResource )(
            IDBAsynchNotify __RPC_FAR * This,
            /* [in] */ DWORD dwReserved);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnProgress )(
            IDBAsynchNotify __RPC_FAR * This,
            /* [in] */ HCHAPTER hChapter,
            /* [in] */ ULONG ulOperation,
            /* [in] */ ULONG ulProgress,
            /* [in] */ ULONG ulProgressMax,
            /* [in] */ ULONG ulAsynchPhase,
            /* [in] */ LPOLESTR pwszStatusText);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnStop )(
            IDBAsynchNotify __RPC_FAR * This,
            /* [in] */ HCHAPTER hChapter,
            /* [in] */ ULONG ulOperation,
            /* [in] */ HRESULT hrStatus,
            /* [in] */ LPOLESTR pwszStatusText);

        END_INTERFACE
    } IDBAsynchNotifyVtbl;

    interface IDBAsynchNotify
    {
        CONST_VTBL struct IDBAsynchNotifyVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IDBAsynchNotify_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IDBAsynchNotify_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IDBAsynchNotify_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IDBAsynchNotify_OnLowResource(This,dwReserved)\
    (This)->lpVtbl -> OnLowResource(This,dwReserved)

#define IDBAsynchNotify_OnProgress(This,hChapter,ulOperation,ulProgress,ulProgressMax,ulAsynchPhase,pwszStatusText)\
    (This)->lpVtbl -> OnProgress(This,hChapter,ulOperation,ulProgress,ulProgressMax,ulAsynchPhase,pwszStatusText)

#define IDBAsynchNotify_OnStop(This,hChapter,ulOperation,hrStatus,pwszStatusText)\
    (This)->lpVtbl -> OnStop(This,hChapter,ulOperation,hrStatus,pwszStatusText)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE IDBAsynchNotify_OnLowResource_Proxy(
    IDBAsynchNotify __RPC_FAR * This,
    /* [in] */ DWORD dwReserved);

void __RPC_STUB IDBAsynchNotify_OnLowResource_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IDBAsynchNotify_OnProgress_Proxy(
    IDBAsynchNotify __RPC_FAR * This,
    /* [in] */ HCHAPTER hChapter,
    /* [in] */ ULONG ulOperation,
    /* [in] */ ULONG ulProgress,
    /* [in] */ ULONG ulProgressMax,
    /* [in] */ ULONG ulAsynchPhase,
    /* [in] */ LPOLESTR pwszStatusText);

void __RPC_STUB IDBAsynchNotify_OnProgress_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IDBAsynchNotify_OnStop_Proxy(
    IDBAsynchNotify __RPC_FAR * This,
    /* [in] */ HCHAPTER hChapter,
    /* [in] */ ULONG ulOperation,
    /* [in] */ HRESULT hrStatus,
    /* [in] */ LPOLESTR pwszStatusText);

void __RPC_STUB IDBAsynchNotify_OnStop_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IDBAsynchNotify_INTERFACE_DEFINED__ */

#ifndef __IDBAsynchStatus_INTERFACE_DEFINED__
#define __IDBAsynchStatus_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IDBAsynchStatus
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object] */

EXTERN_C const IID IID_IDBAsynchStatus;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a95-2a1c-11ce-ade5-00aa0044773d")
    IDBAsynchStatus : public IUnknown
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE Abort(
            /* [in] */ HCHAPTER hChapter,
            /* [in] */ ULONG ulOperation) = 0;

        virtual HRESULT STDMETHODCALLTYPE GetStatus(
            /* [in] */ HCHAPTER hChapter,
            /* [in] */ ULONG ulOperation,
            /* [out] */ ULONG __RPC_FAR *pulProgress,
            /* [out] */ ULONG __RPC_FAR *pulProgressMax,
            /* [out] */ ULONG __RPC_FAR *pulAsynchPhase,
            /* [out] */ LPOLESTR __RPC_FAR *ppwszStatusText) = 0;

    };

#else /* C style interface */

    typedef struct IDBAsynchStatusVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IDBAsynchStatus __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IDBAsynchStatus __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IDBAsynchStatus __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Abort )(
            IDBAsynchStatus __RPC_FAR * This,
            /* [in] */ HCHAPTER hChapter,
            /* [in] */ ULONG ulOperation);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetStatus )(
            IDBAsynchStatus __RPC_FAR * This,
            /* [in] */ HCHAPTER hChapter,
            /* [in] */ ULONG ulOperation,
            /* [out] */ ULONG __RPC_FAR *pulProgress,
            /* [out] */ ULONG __RPC_FAR *pulProgressMax,
            /* [out] */ ULONG __RPC_FAR *pulAsynchPhase,
            /* [out] */ LPOLESTR __RPC_FAR *ppwszStatusText);

        END_INTERFACE
    } IDBAsynchStatusVtbl;

    interface IDBAsynchStatus
    {
        CONST_VTBL struct IDBAsynchStatusVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IDBAsynchStatus_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IDBAsynchStatus_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IDBAsynchStatus_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IDBAsynchStatus_Abort(This,hChapter,ulOperation)\
    (This)->lpVtbl -> Abort(This,hChapter,ulOperation)

#define IDBAsynchStatus_GetStatus(This,hChapter,ulOperation,pulProgress,pulProgressMax,pulAsynchPhase,ppwszStatusText)\
    (This)->lpVtbl -> GetStatus(This,hChapter,ulOperation,pulProgress,pulProgressMax,pulAsynchPhase,ppwszStatusText)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE IDBAsynchStatus_Abort_Proxy(
    IDBAsynchStatus __RPC_FAR * This,
    /* [in] */ HCHAPTER hChapter,
    /* [in] */ ULONG ulOperation);

void __RPC_STUB IDBAsynchStatus_Abort_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IDBAsynchStatus_GetStatus_Proxy(
    IDBAsynchStatus __RPC_FAR * This,
    /* [in] */ HCHAPTER hChapter,
    /* [in] */ ULONG ulOperation,
    /* [out] */ ULONG __RPC_FAR *pulProgress,
    /* [out] */ ULONG __RPC_FAR *pulProgressMax,
    /* [out] */ ULONG __RPC_FAR *pulAsynchPhase,
    /* [out] */ LPOLESTR __RPC_FAR *ppwszStatusText);

void __RPC_STUB IDBAsynchStatus_GetStatus_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IDBAsynchStatus_INTERFACE_DEFINED__ */

/****************************************
 * Generated header for interface: __MIDL_itf_oledb_0125
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [local] */

#endif // OLEDBVER >= 0x0150
//@@@- V1.5

extern RPC_IF_HANDLE __MIDL_itf_oledb_0125_v0_0_c_ifspec;
extern RPC_IF_HANDLE __MIDL_itf_oledb_0125_v0_0_s_ifspec;

#ifndef __ISessionProperties_INTERFACE_DEFINED__
#define __ISessionProperties_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: ISessionProperties
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object] */

EXTERN_C const IID IID_ISessionProperties;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a85-2a1c-11ce-ade5-00aa0044773d")
    ISessionProperties : public IUnknown
    {
    public:
        virtual /* [local] */ HRESULT STDMETHODCALLTYPE GetProperties(
            /* [in] */ ULONG cPropertyIDSets,
            /* [size_is][in] */ const DBPROPIDSET __RPC_FAR rgPropertyIDSets[  ],
            /* [out][in] */ ULONG __RPC_FAR *pcPropertySets,
            /* [size_is][size_is][out] */ DBPROPSET __RPC_FAR *__RPC_FAR *prgPropertySets) = 0;

        virtual /* [local] */ HRESULT STDMETHODCALLTYPE SetProperties(
            /* [in] */ ULONG cPropertySets,
            /* [size_is][unique][in] */ DBPROPSET __RPC_FAR rgPropertySets[  ]) = 0;

    };

#else /* C style interface */

    typedef struct ISessionPropertiesVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            ISessionProperties __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            ISessionProperties __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            ISessionProperties __RPC_FAR * This);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetProperties )(
            ISessionProperties __RPC_FAR * This,
            /* [in] */ ULONG cPropertyIDSets,
            /* [size_is][in] */ const DBPROPIDSET __RPC_FAR rgPropertyIDSets[  ],
            /* [out][in] */ ULONG __RPC_FAR *pcPropertySets,
            /* [size_is][size_is][out] */ DBPROPSET __RPC_FAR *__RPC_FAR *prgPropertySets);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetProperties )(
            ISessionProperties __RPC_FAR * This,
            /* [in] */ ULONG cPropertySets,
            /* [size_is][unique][in] */ DBPROPSET __RPC_FAR rgPropertySets[  ]);

        END_INTERFACE
    } ISessionPropertiesVtbl;

    interface ISessionProperties
    {
        CONST_VTBL struct ISessionPropertiesVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define ISessionProperties_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define ISessionProperties_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define ISessionProperties_Release(This)\
    (This)->lpVtbl -> Release(This)

#define ISessionProperties_GetProperties(This,cPropertyIDSets,rgPropertyIDSets,pcPropertySets,prgPropertySets)\
    (This)->lpVtbl -> GetProperties(This,cPropertyIDSets,rgPropertyIDSets,pcPropertySets,prgPropertySets)

#define ISessionProperties_SetProperties(This,cPropertySets,rgPropertySets)\
    (This)->lpVtbl -> SetProperties(This,cPropertySets,rgPropertySets)

#endif /* COBJMACROS */

#endif /* C style interface */

/* [call_as] */ HRESULT STDMETHODCALLTYPE ISessionProperties_RemoteGetProperties_Proxy(
    ISessionProperties __RPC_FAR * This,
    /* [in] */ ULONG cPropertyIDSets,
    /* [size_is][unique][in] */ const DBPROPIDSET __RPC_FAR *rgPropertyIDSets,
    /* [out][in] */ ULONG __RPC_FAR *pcPropertySets,
    /* [size_is][size_is][out] */ DBPROPSET __RPC_FAR *__RPC_FAR *prgPropertySets);

void __RPC_STUB ISessionProperties_RemoteGetProperties_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

/* [call_as] */ HRESULT STDMETHODCALLTYPE ISessionProperties_RemoteSetProperties_Proxy(
    ISessionProperties __RPC_FAR * This,
    /* [in] */ ULONG cPropertySets,
    /* [size_is][unique][in] */ DBPROPSET __RPC_FAR *rgPropertySets);

void __RPC_STUB ISessionProperties_RemoteSetProperties_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __ISessionProperties_INTERFACE_DEFINED__ */

#ifndef __IIndexDefinition_INTERFACE_DEFINED__
#define __IIndexDefinition_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IIndexDefinition
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object] */

EXTERN_C const IID IID_IIndexDefinition;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a68-2a1c-11ce-ade5-00aa0044773d")
    IIndexDefinition : public IUnknown
    {
    public:
        virtual /* [local] */ HRESULT STDMETHODCALLTYPE CreateIndex(
            /* [in] */ DBID __RPC_FAR *pTableID,
            /* [in] */ DBID __RPC_FAR *pIndexID,
            /* [in] */ ULONG cIndexColumnDescs,
            /* [size_is][in] */ const DBINDEXCOLUMNDESC __RPC_FAR rgIndexColumnDescs[  ],
            /* [in] */ ULONG cPropertySets,
            /* [size_is][out][in] */ DBPROPSET __RPC_FAR rgPropertySets[  ],
            /* [out] */ DBID __RPC_FAR *__RPC_FAR *ppIndexID) = 0;

        virtual HRESULT STDMETHODCALLTYPE DropIndex(
            /* [unique][in] */ DBID __RPC_FAR *pTableID,
            /* [unique][in] */ DBID __RPC_FAR *pIndexID) = 0;

    };

#else /* C style interface */

    typedef struct IIndexDefinitionVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IIndexDefinition __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IIndexDefinition __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IIndexDefinition __RPC_FAR * This);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *CreateIndex )(
            IIndexDefinition __RPC_FAR * This,
            /* [in] */ DBID __RPC_FAR *pTableID,
            /* [in] */ DBID __RPC_FAR *pIndexID,
            /* [in] */ ULONG cIndexColumnDescs,
            /* [size_is][in] */ const DBINDEXCOLUMNDESC __RPC_FAR rgIndexColumnDescs[  ],
            /* [in] */ ULONG cPropertySets,
            /* [size_is][out][in] */ DBPROPSET __RPC_FAR rgPropertySets[  ],
            /* [out] */ DBID __RPC_FAR *__RPC_FAR *ppIndexID);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *DropIndex )(
            IIndexDefinition __RPC_FAR * This,
            /* [unique][in] */ DBID __RPC_FAR *pTableID,
            /* [unique][in] */ DBID __RPC_FAR *pIndexID);

        END_INTERFACE
    } IIndexDefinitionVtbl;

    interface IIndexDefinition
    {
        CONST_VTBL struct IIndexDefinitionVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IIndexDefinition_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IIndexDefinition_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IIndexDefinition_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IIndexDefinition_CreateIndex(This,pTableID,pIndexID,cIndexColumnDescs,rgIndexColumnDescs,cPropertySets,rgPropertySets,ppIndexID)\
    (This)->lpVtbl -> CreateIndex(This,pTableID,pIndexID,cIndexColumnDescs,rgIndexColumnDescs,cPropertySets,rgPropertySets,ppIndexID)

#define IIndexDefinition_DropIndex(This,pTableID,pIndexID)\
    (This)->lpVtbl -> DropIndex(This,pTableID,pIndexID)

#endif /* COBJMACROS */

#endif /* C style interface */

/* [call_as] */ HRESULT STDMETHODCALLTYPE IIndexDefinition_RemoteCreateIndex_Proxy(
    IIndexDefinition __RPC_FAR * This,
    /* [in] */ DBID __RPC_FAR *pTableID,
    /* [unique][in] */ DBID __RPC_FAR *pIndexID,
    /* [in] */ ULONG cIndexColumnDescs,
    /* [size_is][in] */ const DBINDEXCOLUMNDESC __RPC_FAR *rgIndexColumnDescs,
    /* [in] */ ULONG cPropertySets,
    /* [size_is][unique][out][in] */ DBPROPSET __RPC_FAR *rgPropertySets,
    /* [unique][out][in] */ DBID __RPC_FAR *__RPC_FAR *ppIndexID);

void __RPC_STUB IIndexDefinition_RemoteCreateIndex_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IIndexDefinition_DropIndex_Proxy(
    IIndexDefinition __RPC_FAR * This,
    /* [unique][in] */ DBID __RPC_FAR *pTableID,
    /* [unique][in] */ DBID __RPC_FAR *pIndexID);

void __RPC_STUB IIndexDefinition_DropIndex_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IIndexDefinition_INTERFACE_DEFINED__ */

#ifndef __ITableDefinition_INTERFACE_DEFINED__
#define __ITableDefinition_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: ITableDefinition
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object] */

EXTERN_C const IID IID_ITableDefinition;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a86-2a1c-11ce-ade5-00aa0044773d")
    ITableDefinition : public IUnknown
    {
    public:
        virtual /* [local] */ HRESULT STDMETHODCALLTYPE CreateTable(
            /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
            /* [in] */ DBID __RPC_FAR *pTableID,
            /* [in] */ ULONG cColumnDescs,
            /* [size_is][in] */ const DBCOLUMNDESC __RPC_FAR rgColumnDescs[  ],
            /* [in] */ REFIID riid,
            /* [in] */ ULONG cPropertySets,
            /* [size_is][out][in] */ DBPROPSET __RPC_FAR rgPropertySets[  ],
            /* [out] */ DBID __RPC_FAR *__RPC_FAR *ppTableID,
            /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppRowset) = 0;

        virtual HRESULT STDMETHODCALLTYPE DropTable(
            /* [unique][in] */ DBID __RPC_FAR *pTableID) = 0;

        virtual /* [local] */ HRESULT STDMETHODCALLTYPE AddColumn(
            /* [in] */ DBID __RPC_FAR *pTableID,
            /* [in] */ DBCOLUMNDESC __RPC_FAR *pColumnDesc,
            /* [out] */ DBID __RPC_FAR *__RPC_FAR *ppColumnID) = 0;

        virtual HRESULT STDMETHODCALLTYPE DropColumn(
            /* [unique][in] */ DBID __RPC_FAR *pTableID,
            /* [unique][in] */ DBID __RPC_FAR *pColumnID) = 0;

    };

#else /* C style interface */

    typedef struct ITableDefinitionVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            ITableDefinition __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            ITableDefinition __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            ITableDefinition __RPC_FAR * This);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *CreateTable )(
            ITableDefinition __RPC_FAR * This,
            /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
            /* [in] */ DBID __RPC_FAR *pTableID,
            /* [in] */ ULONG cColumnDescs,
            /* [size_is][in] */ const DBCOLUMNDESC __RPC_FAR rgColumnDescs[  ],
            /* [in] */ REFIID riid,
            /* [in] */ ULONG cPropertySets,
            /* [size_is][out][in] */ DBPROPSET __RPC_FAR rgPropertySets[  ],
            /* [out] */ DBID __RPC_FAR *__RPC_FAR *ppTableID,
            /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppRowset);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *DropTable )(
            ITableDefinition __RPC_FAR * This,
            /* [unique][in] */ DBID __RPC_FAR *pTableID);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *AddColumn )(
            ITableDefinition __RPC_FAR * This,
            /* [in] */ DBID __RPC_FAR *pTableID,
            /* [in] */ DBCOLUMNDESC __RPC_FAR *pColumnDesc,
            /* [out] */ DBID __RPC_FAR *__RPC_FAR *ppColumnID);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *DropColumn )(
            ITableDefinition __RPC_FAR * This,
            /* [unique][in] */ DBID __RPC_FAR *pTableID,
            /* [unique][in] */ DBID __RPC_FAR *pColumnID);

        END_INTERFACE
    } ITableDefinitionVtbl;

    interface ITableDefinition
    {
        CONST_VTBL struct ITableDefinitionVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define ITableDefinition_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define ITableDefinition_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define ITableDefinition_Release(This)\
    (This)->lpVtbl -> Release(This)

#define ITableDefinition_CreateTable(This,pUnkOuter,pTableID,cColumnDescs,rgColumnDescs,riid,cPropertySets,rgPropertySets,ppTableID,ppRowset)\
    (This)->lpVtbl -> CreateTable(This,pUnkOuter,pTableID,cColumnDescs,rgColumnDescs,riid,cPropertySets,rgPropertySets,ppTableID,ppRowset)

#define ITableDefinition_DropTable(This,pTableID)\
    (This)->lpVtbl -> DropTable(This,pTableID)

#define ITableDefinition_AddColumn(This,pTableID,pColumnDesc,ppColumnID)\
    (This)->lpVtbl -> AddColumn(This,pTableID,pColumnDesc,ppColumnID)

#define ITableDefinition_DropColumn(This,pTableID,pColumnID)\
    (This)->lpVtbl -> DropColumn(This,pTableID,pColumnID)

#endif /* COBJMACROS */

#endif /* C style interface */

/* [call_as] */ HRESULT STDMETHODCALLTYPE ITableDefinition_RemoteCreateTable_Proxy(
    ITableDefinition __RPC_FAR * This,
    /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
    /* [unique][in] */ DBID __RPC_FAR *pTableID,
    /* [in] */ ULONG cColumnDescs,
    /* [size_is][in] */ const DBCOLUMNDESC __RPC_FAR *rgColumnDescs,
    /* [in] */ REFIID riid,
    /* [in] */ ULONG cPropertySets,
    /* [size_is][unique][out][in] */ DBPROPSET __RPC_FAR *rgPropertySets,
    /* [unique][out][in] */ DBID __RPC_FAR *__RPC_FAR *ppTableID,
    /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppRowset);

void __RPC_STUB ITableDefinition_RemoteCreateTable_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE ITableDefinition_DropTable_Proxy(
    ITableDefinition __RPC_FAR * This,
    /* [unique][in] */ DBID __RPC_FAR *pTableID);

void __RPC_STUB ITableDefinition_DropTable_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

/* [call_as] */ HRESULT STDMETHODCALLTYPE ITableDefinition_RemoteAddColumn_Proxy(
    ITableDefinition __RPC_FAR * This,
    /* [in] */ DBID __RPC_FAR *pTableID,
    /* [in] */ DBCOLUMNDESC __RPC_FAR *pColumnDesc,
    /* [unique][out][in] */ DBID __RPC_FAR *__RPC_FAR *ppColumnID);

void __RPC_STUB ITableDefinition_RemoteAddColumn_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE ITableDefinition_DropColumn_Proxy(
    ITableDefinition __RPC_FAR * This,
    /* [unique][in] */ DBID __RPC_FAR *pTableID,
    /* [unique][in] */ DBID __RPC_FAR *pColumnID);

void __RPC_STUB ITableDefinition_DropColumn_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __ITableDefinition_INTERFACE_DEFINED__ */

#ifndef __IOpenRowset_INTERFACE_DEFINED__
#define __IOpenRowset_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IOpenRowset
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object] */

EXTERN_C const IID IID_IOpenRowset;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a69-2a1c-11ce-ade5-00aa0044773d")
    IOpenRowset : public IUnknown
    {
    public:
        virtual /* [local] */ HRESULT STDMETHODCALLTYPE OpenRowset(

/* [in] */ IUnknown __RPC_FAR *pUnkOuter,
            /* [unique][in] */ DBID __RPC_FAR *pTableID,
            /* [unique][in] */ DBID __RPC_FAR *pIndexID,
            /* [in] */ REFIID riid,
            /* [in] */ ULONG cPropertySets,
            /* [size_is][out][in] */ DBPROPSET __RPC_FAR rgPropertySets[  ],
            /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppRowset) = 0;

    };

#else /* C style interface */

    typedef struct IOpenRowsetVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IOpenRowset __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IOpenRowset __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IOpenRowset __RPC_FAR * This);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OpenRowset )(
            IOpenRowset __RPC_FAR * This,
            /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
            /* [unique][in] */ DBID __RPC_FAR *pTableID,
            /* [unique][in] */ DBID __RPC_FAR *pIndexID,
            /* [in] */ REFIID riid,
            /* [in] */ ULONG cPropertySets,
            /* [size_is][out][in] */ DBPROPSET __RPC_FAR rgPropertySets[  ],
            /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppRowset);

        END_INTERFACE
    } IOpenRowsetVtbl;

    interface IOpenRowset
    {
        CONST_VTBL struct IOpenRowsetVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IOpenRowset_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IOpenRowset_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IOpenRowset_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IOpenRowset_OpenRowset(This,pUnkOuter,pTableID,pIndexID,riid,cPropertySets,rgPropertySets,ppRowset)\
    (This)->lpVtbl -> OpenRowset(This,pUnkOuter,pTableID,pIndexID,riid,cPropertySets,rgPropertySets,ppRowset)

#endif /* COBJMACROS */

#endif /* C style interface */

/* [call_as] */ HRESULT STDMETHODCALLTYPE IOpenRowset_RemoteOpenRowset_Proxy(
    IOpenRowset __RPC_FAR * This,
    /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
    /* [unique][in] */ DBID __RPC_FAR *pTableID,
    /* [unique][in] */ DBID __RPC_FAR *pIndexID,
    /* [in] */ REFIID riid,
    /* [in] */ ULONG cPropertySets,
    /* [size_is][unique][out][in] */ DBPROPSET __RPC_FAR *rgPropertySets,
    /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppRowset);

void __RPC_STUB IOpenRowset_RemoteOpenRowset_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IOpenRowset_INTERFACE_DEFINED__ */

/****************************************
 * Generated header for interface: __MIDL_itf_oledb_0129
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [local] */

//@@@+ V2.0
#if( OLEDBVER >= 0x0200 )

extern RPC_IF_HANDLE __MIDL_itf_oledb_0129_v0_0_c_ifspec;
extern RPC_IF_HANDLE __MIDL_itf_oledb_0129_v0_0_s_ifspec;

#ifndef __ITableRename_INTERFACE_DEFINED__
#define __ITableRename_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: ITableRename
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object][local] */

EXTERN_C const IID IID_ITableRename;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a77-2a1c-11ce-ade5-00aa0044773d")
    ITableRename : public IUnknown
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE RenameColumn(
            /* [in] */ DBID __RPC_FAR *pTableId,
            /* [in] */ DBID __RPC_FAR *pOldColumnId,
            /* [in] */ DBID __RPC_FAR *pNewColumnId) = 0;

        virtual HRESULT STDMETHODCALLTYPE RenameTable(
            /* [in] */ DBID __RPC_FAR *pOldTableId,
            /* [in] */ DBID __RPC_FAR *pOldIndexId,
            /* [in] */ DBID __RPC_FAR *pNewTableId,
            /* [in] */ DBID __RPC_FAR *pNewIndexId) = 0;

    };

#else /* C style interface */

    typedef struct ITableRenameVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            ITableRename __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            ITableRename __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            ITableRename __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RenameColumn )(
            ITableRename __RPC_FAR * This,
            /* [in] */ DBID __RPC_FAR *pTableId,
            /* [in] */ DBID __RPC_FAR *pOldColumnId,
            /* [in] */ DBID __RPC_FAR *pNewColumnId);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RenameTable )(
            ITableRename __RPC_FAR * This,
            /* [in] */ DBID __RPC_FAR *pOldTableId,
            /* [in] */ DBID __RPC_FAR *pOldIndexId,
            /* [in] */ DBID __RPC_FAR *pNewTableId,
            /* [in] */ DBID __RPC_FAR *pNewIndexId);

        END_INTERFACE
    } ITableRenameVtbl;

    interface ITableRename
    {
        CONST_VTBL struct ITableRenameVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define ITableRename_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define ITableRename_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define ITableRename_Release(This)\
    (This)->lpVtbl -> Release(This)

#define ITableRename_RenameColumn(This,pTableId,pOldColumnId,pNewColumnId)\
    (This)->lpVtbl -> RenameColumn(This,pTableId,pOldColumnId,pNewColumnId)

#define ITableRename_RenameTable(This,pOldTableId,pOldIndexId,pNewTableId,pNewIndexId)\
    (This)->lpVtbl -> RenameTable(This,pOldTableId,pOldIndexId,pNewTableId,pNewIndexId)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE ITableRename_RenameColumn_Proxy(
    ITableRename __RPC_FAR * This,
    /* [in] */ DBID __RPC_FAR *pTableId,
    /* [in] */ DBID __RPC_FAR *pOldColumnId,
    /* [in] */ DBID __RPC_FAR *pNewColumnId);

void __RPC_STUB ITableRename_RenameColumn_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE ITableRename_RenameTable_Proxy(
    ITableRename __RPC_FAR * This,
    /* [in] */ DBID __RPC_FAR *pOldTableId,
    /* [in] */ DBID __RPC_FAR *pOldIndexId,
    /* [in] */ DBID __RPC_FAR *pNewTableId,
    /* [in] */ DBID __RPC_FAR *pNewIndexId);

void __RPC_STUB ITableRename_RenameTable_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __ITableRename_INTERFACE_DEFINED__ */

#ifndef __IDBSchemaCommand_INTERFACE_DEFINED__
#define __IDBSchemaCommand_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IDBSchemaCommand
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object][local] */

EXTERN_C const IID IID_IDBSchemaCommand;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a50-2a1c-11ce-ade5-00aa0044773d")
    IDBSchemaCommand : public IUnknown
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE GetCommand(
            /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
            /* [in] */ REFGUID rguidSchema,
            /* [out] */ ICommand __RPC_FAR *__RPC_FAR *ppCommand) = 0;

        virtual HRESULT STDMETHODCALLTYPE GetSchemas(
            /* [out][in] */ ULONG __RPC_FAR *pcSchemas,
            /* [size_is][size_is][out] */ GUID __RPC_FAR *__RPC_FAR *prgSchemas) = 0;

    };

#else /* C style interface */

    typedef struct IDBSchemaCommandVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IDBSchemaCommand __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IDBSchemaCommand __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IDBSchemaCommand __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetCommand )(
            IDBSchemaCommand __RPC_FAR * This,
            /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
            /* [in] */ REFGUID rguidSchema,
            /* [out] */ ICommand __RPC_FAR *__RPC_FAR *ppCommand);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetSchemas )(
            IDBSchemaCommand __RPC_FAR * This,
            /* [out][in] */ ULONG __RPC_FAR *pcSchemas,
            /* [size_is][size_is][out] */ GUID __RPC_FAR *__RPC_FAR *prgSchemas);

        END_INTERFACE
    } IDBSchemaCommandVtbl;

    interface IDBSchemaCommand
    {
        CONST_VTBL struct IDBSchemaCommandVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IDBSchemaCommand_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IDBSchemaCommand_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IDBSchemaCommand_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IDBSchemaCommand_GetCommand(This,pUnkOuter,rguidSchema,ppCommand)\
    (This)->lpVtbl -> GetCommand(This,pUnkOuter,rguidSchema,ppCommand)

#define IDBSchemaCommand_GetSchemas(This,pcSchemas,prgSchemas)\
    (This)->lpVtbl -> GetSchemas(This,pcSchemas,prgSchemas)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE IDBSchemaCommand_GetCommand_Proxy(
    IDBSchemaCommand __RPC_FAR * This,
    /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
    /* [in] */ REFGUID rguidSchema,
    /* [out] */ ICommand __RPC_FAR *__RPC_FAR *ppCommand);

void __RPC_STUB IDBSchemaCommand_GetCommand_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IDBSchemaCommand_GetSchemas_Proxy(
    IDBSchemaCommand __RPC_FAR * This,
    /* [out][in] */ ULONG __RPC_FAR *pcSchemas,
    /* [size_is][size_is][out] */ GUID __RPC_FAR *__RPC_FAR *prgSchemas);

void __RPC_STUB IDBSchemaCommand_GetSchemas_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IDBSchemaCommand_INTERFACE_DEFINED__ */

#ifndef __IDBSecurityInfo_INTERFACE_DEFINED__
#define __IDBSecurityInfo_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IDBSecurityInfo
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object][local] */

typedef DWORD DBTRUSTEEACCTTYPE;

enum DBTRUSTEEACCTTYPEENUM
    {DBTRUSTEEACCTTYPE_UNKNOWN= 0,
DBTRUSTEEACCTTYPE_USER= DBTRUSTEEACCTTYPE_UNKNOWN + 1,
DBTRUSTEEACCTTYPE_GROUP= DBTRUSTEEACCTTYPE_USER + 1
    };
typedef DWORD DBTRUSTEEIDTYPE;

enum DBTRUSTEEIDTYPEENUM
    {DBTRUSTEEIDTYPE_SID= 0,
DBTRUSTEEIDTYPE_NAME= DBTRUSTEEIDTYPE_SID + 1
    };
typedef DWORD DBTRUSTEEMULT;

enum DBTRUSTEEMULTENUM
    {DBTRUSTEEMULT_NONE= 0,
DBTRUSTEEMULT_IMPERSONATE= DBTRUSTEEMULT_NONE + 1
    };
typedef struct  tagDBTRUSTEE
    {
    struct tagDBTRUSTEE __RPC_FAR *pMultipleTrustee;
    LPOLESTR pwszName;
    DBTRUSTEEMULT dwMultipleTrusteeOp;
    DBTRUSTEEIDTYPE dwTrusteeIDType;
    DBTRUSTEEACCTTYPE dwTrusteeAcctType;
    }DBTRUSTEE;

typedef struct tagDBTRUSTEE __RPC_FAR *PDBTRUSTEE;

typedef struct  tagDBOBJECTTYPE
    {
    LPOLESTR pwszObjectTypeName;
    GUID __RPC_FAR *pguidObjectTypeId;
    }DBOBJECTTYPE;

typedef struct  tagDBPERMTYPE
    {
    LPOLESTR pwszPermissionTypeName;
    GUID __RPC_FAR *pguidPermissionTypeId;
    }DBPERMTYPE;

typedef struct  tagDBTRUSTEETYPE
    {
    LPOLESTR pwszTrusteeTypeName;
    GUID __RPC_FAR *pguidTrusteeTypeId;
    }DBTRUSTEETYPE;

typedef struct  tagDBACCESSTRIPLE
    {
    DBPERMTYPE PermissionType;
    DBOBJECTTYPE ObjectType;
    DBTRUSTEETYPE TrusteeType;
    }DBACCESSTRIPLE;

EXTERN_C const IID IID_IDBSecurityInfo;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a62-2a1c-11ce-ade5-00aa0044773d")
    IDBSecurityInfo : public IUnknown
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE GetAccessTriples(
            /* [out][in] */ ULONG __RPC_FAR *pcAccessTriplesInfo,
            /* [size_is][size_is][out] */ DBACCESSTRIPLE __RPC_FAR *__RPC_FAR *prgAccessTripleInfo) = 0;

        virtual HRESULT STDMETHODCALLTYPE GetAuthenticatedTrusteeIdentity(
            /* [out] */ DBTRUSTEE __RPC_FAR *pTrustee) = 0;

        virtual HRESULT STDMETHODCALLTYPE GetObjectTypesInfo(
            /* [out][in] */ ULONG __RPC_FAR *pcObjectInfo,
            /* [size_is][size_is][out] */ DBOBJECTTYPE __RPC_FAR *__RPC_FAR *prgObjectInfo) = 0;

        virtual HRESULT STDMETHODCALLTYPE GetPermTypesInfo(
            /* [out][in] */ ULONG __RPC_FAR *pcPermInfo,
            /* [size_is][size_is][out] */ DBPERMTYPE __RPC_FAR *__RPC_FAR *prgPermInfo) = 0;

    };

#else /* C style interface */

    typedef struct IDBSecurityInfoVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IDBSecurityInfo __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IDBSecurityInfo __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IDBSecurityInfo __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetAccessTriples )(
            IDBSecurityInfo __RPC_FAR * This,
            /* [out][in] */ ULONG __RPC_FAR *pcAccessTriplesInfo,
            /* [size_is][size_is][out] */ DBACCESSTRIPLE __RPC_FAR *__RPC_FAR *prgAccessTripleInfo);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetAuthenticatedTrusteeIdentity )(
            IDBSecurityInfo __RPC_FAR * This,
            /* [out] */ DBTRUSTEE __RPC_FAR *pTrustee);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetObjectTypesInfo )(
            IDBSecurityInfo __RPC_FAR * This,
            /* [out][in] */ ULONG __RPC_FAR *pcObjectInfo,
            /* [size_is][size_is][out] */ DBOBJECTTYPE __RPC_FAR *__RPC_FAR *prgObjectInfo);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetPermTypesInfo )(
            IDBSecurityInfo __RPC_FAR * This,
            /* [out][in] */ ULONG __RPC_FAR *pcPermInfo,
            /* [size_is][size_is][out] */ DBPERMTYPE __RPC_FAR *__RPC_FAR *prgPermInfo);

        END_INTERFACE
    } IDBSecurityInfoVtbl;

    interface IDBSecurityInfo
    {
        CONST_VTBL struct IDBSecurityInfoVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IDBSecurityInfo_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IDBSecurityInfo_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IDBSecurityInfo_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IDBSecurityInfo_GetAccessTriples(This,pcAccessTriplesInfo,prgAccessTripleInfo)\
    (This)->lpVtbl -> GetAccessTriples(This,pcAccessTriplesInfo,prgAccessTripleInfo)

#define IDBSecurityInfo_GetAuthenticatedTrusteeIdentity(This,pTrustee)\
    (This)->lpVtbl -> GetAuthenticatedTrusteeIdentity(This,pTrustee)

#define IDBSecurityInfo_GetObjectTypesInfo(This,pcObjectInfo,prgObjectInfo)\
    (This)->lpVtbl -> GetObjectTypesInfo(This,pcObjectInfo,prgObjectInfo)

#define IDBSecurityInfo_GetPermTypesInfo(This,pcPermInfo,prgPermInfo)\
    (This)->lpVtbl -> GetPermTypesInfo(This,pcPermInfo,prgPermInfo)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE IDBSecurityInfo_GetAccessTriples_Proxy(
    IDBSecurityInfo __RPC_FAR * This,
    /* [out][in] */ ULONG __RPC_FAR *pcAccessTriplesInfo,
    /* [size_is][size_is][out] */ DBACCESSTRIPLE __RPC_FAR *__RPC_FAR *prgAccessTripleInfo);

void __RPC_STUB IDBSecurityInfo_GetAccessTriples_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IDBSecurityInfo_GetAuthenticatedTrusteeIdentity_Proxy(
    IDBSecurityInfo __RPC_FAR * This,
    /* [out] */ DBTRUSTEE __RPC_FAR *pTrustee);

void __RPC_STUB IDBSecurityInfo_GetAuthenticatedTrusteeIdentity_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IDBSecurityInfo_GetObjectTypesInfo_Proxy(
    IDBSecurityInfo __RPC_FAR * This,
    /* [out][in] */ ULONG __RPC_FAR *pcObjectInfo,
    /* [size_is][size_is][out] */ DBOBJECTTYPE __RPC_FAR *__RPC_FAR *prgObjectInfo);

void __RPC_STUB IDBSecurityInfo_GetObjectTypesInfo_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IDBSecurityInfo_GetPermTypesInfo_Proxy(
    IDBSecurityInfo __RPC_FAR * This,
    /* [out][in] */ ULONG __RPC_FAR *pcPermInfo,
    /* [size_is][size_is][out] */ DBPERMTYPE __RPC_FAR *__RPC_FAR *prgPermInfo);

void __RPC_STUB IDBSecurityInfo_GetPermTypesInfo_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IDBSecurityInfo_INTERFACE_DEFINED__ */

/****************************************
 * Generated header for interface: __MIDL_itf_oledb_0132
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [local] */

#endif // OLEDBVER >= 0x0200
//@@@- V2.0

extern RPC_IF_HANDLE __MIDL_itf_oledb_0132_v0_0_c_ifspec;
extern RPC_IF_HANDLE __MIDL_itf_oledb_0132_v0_0_s_ifspec;

#ifndef __IDBSchemaRowset_INTERFACE_DEFINED__
#define __IDBSchemaRowset_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IDBSchemaRowset
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object] */

#define CRESTRICTIONS_DBSCHEMA_ASSERTIONS                      3
#define CRESTRICTIONS_DBSCHEMA_CATALOGS                        1
#define CRESTRICTIONS_DBSCHEMA_CHARACTER_SETS                  3
#define CRESTRICTIONS_DBSCHEMA_COLLATIONS                      3
#define CRESTRICTIONS_DBSCHEMA_COLUMNS                         4
#define CRESTRICTIONS_DBSCHEMA_CHECK_CONSTRAINTS               3
#define CRESTRICTIONS_DBSCHEMA_CONSTRAINT_COLUMN_USAGE         4
#define CRESTRICTIONS_DBSCHEMA_CONSTRAINT_TABLE_USAGE          3
#define CRESTRICTIONS_DBSCHEMA_KEY_COLUMN_USAGE                7
#define CRESTRICTIONS_DBSCHEMA_REFERENTIAL_CONSTRAINTS         3
#define CRESTRICTIONS_DBSCHEMA_TABLE_CONSTRAINTS               7
#define CRESTRICTIONS_DBSCHEMA_COLUMN_DOMAIN_USAGE             4
#define CRESTRICTIONS_DBSCHEMA_INDEXES                         5
#define CRESTRICTIONS_DBSCHEMA_OBJECT_ACTIONS                  1
#define CRESTRICTIONS_DBSCHEMA_OBJECTS                         1
#define CRESTRICTIONS_DBSCHEMA_COLUMN_PRIVILEGES               6
#define CRESTRICTIONS_DBSCHEMA_TABLE_PRIVILEGES                5
#define CRESTRICTIONS_DBSCHEMA_USAGE_PRIVILEGES                6
#define CRESTRICTIONS_DBSCHEMA_PROCEDURES                      4
#define CRESTRICTIONS_DBSCHEMA_SCHEMATA                        3
#define CRESTRICTIONS_DBSCHEMA_SQL_LANGUAGES                   0
#define CRESTRICTIONS_DBSCHEMA_STATISTICS                      3
#define CRESTRICTIONS_DBSCHEMA_TABLES                          4
#define CRESTRICTIONS_DBSCHEMA_TRANSLATIONS                    3
#define CRESTRICTIONS_DBSCHEMA_PROVIDER_TYPES                  2
#define CRESTRICTIONS_DBSCHEMA_VIEWS                           3
#define CRESTRICTIONS_DBSCHEMA_VIEW_COLUMN_USAGE               3
#define CRESTRICTIONS_DBSCHEMA_VIEW_TABLE_USAGE                3
#define CRESTRICTIONS_DBSCHEMA_PROCEDURE_PARAMETERS            4
#define CRESTRICTIONS_DBSCHEMA_FOREIGN_KEYS                    6
#define CRESTRICTIONS_DBSCHEMA_PRIMARY_KEYS                    3
#define CRESTRICTIONS_DBSCHEMA_PROCEDURE_COLUMNS               4
//@@@+ V2.0
#if( OLEDBVER >= 0x0200 )
#define CRESTRICTIONS_MDSCHEMA_CUBES                           3
#define CRESTRICTIONS_MDSCHEMA_DIMENSIONS                      5
#define CRESTRICTIONS_MDSCHEMA_HIERARCHIES                     6
#define CRESTRICTIONS_MDSCHEMA_LEVELS                          7
#define CRESTRICTIONS_MDSCHEMA_MEASURES                        5
#define CRESTRICTIONS_MDSCHEMA_PROPERTIES                      9
#define CRESTRICTIONS_MDSCHEMA_MEMBERS                         11
#endif // OLEDBVER >= 0x0200
//@@@- V2.0

EXTERN_C const IID IID_IDBSchemaRowset;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a7b-2a1c-11ce-ade5-00aa0044773d")
    IDBSchemaRowset : public IUnknown
    {
    public:
        virtual /* [local] */ HRESULT STDMETHODCALLTYPE GetRowset(
            /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
            /* [in] */ REFGUID rguidSchema,
            /* [in] */ ULONG cRestrictions,
            /* [size_is][in] */ const VARIANT __RPC_FAR rgRestrictions[  ],
            /* [in] */ REFIID riid,
            /* [in] */ ULONG cPropertySets,
            /* [size_is][out][in] */ DBPROPSET __RPC_FAR rgPropertySets[  ],
            /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppRowset) = 0;

        virtual /* [local] */ HRESULT STDMETHODCALLTYPE GetSchemas(
            /* [out][in] */ ULONG __RPC_FAR *pcSchemas,
            /* [size_is][size_is][out] */ GUID __RPC_FAR *__RPC_FAR *prgSchemas,
            /* [size_is][size_is][out] */ ULONG __RPC_FAR *__RPC_FAR *prgRestrictionSupport) = 0;

    };

#else /* C style interface */

    typedef struct IDBSchemaRowsetVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IDBSchemaRowset __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IDBSchemaRowset __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IDBSchemaRowset __RPC_FAR * This);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetRowset )(
            IDBSchemaRowset __RPC_FAR * This,
            /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
            /* [in] */ REFGUID rguidSchema,
            /* [in] */ ULONG cRestrictions,
            /* [size_is][in] */ const VARIANT __RPC_FAR rgRestrictions[  ],
            /* [in] */ REFIID riid,
            /* [in] */ ULONG cPropertySets,
            /* [size_is][out][in] */ DBPROPSET __RPC_FAR rgPropertySets[  ],
            /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppRowset);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetSchemas )(
            IDBSchemaRowset __RPC_FAR * This,
            /* [out][in] */ ULONG __RPC_FAR *pcSchemas,
            /* [size_is][size_is][out] */ GUID __RPC_FAR *__RPC_FAR *prgSchemas,
            /* [size_is][size_is][out] */ ULONG __RPC_FAR *__RPC_FAR *prgRestrictionSupport);

        END_INTERFACE
    } IDBSchemaRowsetVtbl;

    interface IDBSchemaRowset
    {
        CONST_VTBL struct IDBSchemaRowsetVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IDBSchemaRowset_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IDBSchemaRowset_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IDBSchemaRowset_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IDBSchemaRowset_GetRowset(This,pUnkOuter,rguidSchema,cRestrictions,rgRestrictions,riid,cPropertySets,rgPropertySets,ppRowset)\
    (This)->lpVtbl -> GetRowset(This,pUnkOuter,rguidSchema,cRestrictions,rgRestrictions,riid,cPropertySets,rgPropertySets,ppRowset)

#define IDBSchemaRowset_GetSchemas(This,pcSchemas,prgSchemas,prgRestrictionSupport)\
    (This)->lpVtbl -> GetSchemas(This,pcSchemas,prgSchemas,prgRestrictionSupport)

#endif /* COBJMACROS */

#endif /* C style interface */

/* [call_as] */ HRESULT STDMETHODCALLTYPE IDBSchemaRowset_RemoteGetRowset_Proxy(
    IDBSchemaRowset __RPC_FAR * This,
    /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
    /* [in] */ REFGUID rguidSchema,
    /* [in] */ ULONG cRestrictions,
    /* [size_is][unique][in] */ const VARIANT __RPC_FAR *rgRestrictions,
    /* [in] */ REFIID riid,
    /* [in] */ ULONG cPropertySets,
    /* [size_is][unique][out][in] */ DBPROPSET __RPC_FAR *rgPropertySets,
    /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppRowset);

void __RPC_STUB IDBSchemaRowset_RemoteGetRowset_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

/* [call_as] */ HRESULT STDMETHODCALLTYPE IDBSchemaRowset_RemoteGetSchemas_Proxy(
    IDBSchemaRowset __RPC_FAR * This,
    /* [out][in] */ ULONG __RPC_FAR *pcSchemas,
    /* [size_is][size_is][out] */ GUID __RPC_FAR *__RPC_FAR *prgSchemas,
    /* [size_is][size_is][out] */ ULONG __RPC_FAR *__RPC_FAR *prgRestrictionSupport);

void __RPC_STUB IDBSchemaRowset_RemoteGetSchemas_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IDBSchemaRowset_INTERFACE_DEFINED__ */

/****************************************
 * Generated header for interface: __MIDL_itf_oledb_0133
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [local] */

//@@@+ V2.0
#if( OLEDBVER >= 0x0200 )

extern RPC_IF_HANDLE __MIDL_itf_oledb_0133_v0_0_c_ifspec;
extern RPC_IF_HANDLE __MIDL_itf_oledb_0133_v0_0_s_ifspec;

#ifndef __IProvideMoniker_INTERFACE_DEFINED__
#define __IProvideMoniker_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IProvideMoniker
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object][local] */

EXTERN_C const IID IID_IProvideMoniker;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a4d-2a1c-11ce-ade5-00aa0044773d")
    IProvideMoniker : public IUnknown
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE GetMoniker(
            /* [out] */ IMoniker __RPC_FAR *__RPC_FAR *ppIMoniker) = 0;

    };

#else /* C style interface */

    typedef struct IProvideMonikerVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IProvideMoniker __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IProvideMoniker __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IProvideMoniker __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetMoniker )(
            IProvideMoniker __RPC_FAR * This,
            /* [out] */ IMoniker __RPC_FAR *__RPC_FAR *ppIMoniker);

        END_INTERFACE
    } IProvideMonikerVtbl;

    interface IProvideMoniker
    {
        CONST_VTBL struct IProvideMonikerVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IProvideMoniker_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IProvideMoniker_AddRef(This)\

(This)->lpVtbl -> AddRef(This)

#define IProvideMoniker_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IProvideMoniker_GetMoniker(This,ppIMoniker)\
    (This)->lpVtbl -> GetMoniker(This,ppIMoniker)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE IProvideMoniker_GetMoniker_Proxy(
    IProvideMoniker __RPC_FAR * This,
    /* [out] */ IMoniker __RPC_FAR *__RPC_FAR *ppIMoniker);

void __RPC_STUB IProvideMoniker_GetMoniker_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IProvideMoniker_INTERFACE_DEFINED__ */

#ifndef __IMDDataset_INTERFACE_DEFINED__
#define __IMDDataset_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IMDDataset
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object][local] */

EXTERN_C const IID IID_IMDDataset;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a9e-2a1c-11ce-ade5-00aa0044773d")
    IMDDataset : public IUnknown
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE FreeAxisInfo(
            /* [in] */ ULONG cAxes,
            /* [size_is][in] */ MDAXISINFO __RPC_FAR *rgAxisInfo) = 0;

        virtual HRESULT STDMETHODCALLTYPE GetAxisInfo(
            /* [out][in] */ ULONG __RPC_FAR *pcAxes,
            /* [size_is][size_is][out] */ MDAXISINFO __RPC_FAR *__RPC_FAR *prgAxisInfo) = 0;

        virtual HRESULT STDMETHODCALLTYPE GetAxisRowset(
            /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
            /* [in] */ ULONG iAxis,
            /* [in] */ REFIID riid,
            /* [in] */ ULONG cPropertySets,
            /* [size_is][out][in] */ DBPROPSET __RPC_FAR rgPropertySets[  ],
            /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppRowset) = 0;

        virtual HRESULT STDMETHODCALLTYPE GetCellData(
            /* [in] */ HACCESSOR hAccessor,
            /* [in] */ ULONG ulStartCell,
            /* [in] */ ULONG ulEndCell,
            /* [out] */ void __RPC_FAR *pData) = 0;

        virtual HRESULT STDMETHODCALLTYPE GetSpecification(
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppSpecification) = 0;

    };

#else /* C style interface */

    typedef struct IMDDatasetVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IMDDataset __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IMDDataset __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IMDDataset __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *FreeAxisInfo )(
            IMDDataset __RPC_FAR * This,
            /* [in] */ ULONG cAxes,
            /* [size_is][in] */ MDAXISINFO __RPC_FAR *rgAxisInfo);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetAxisInfo )(
            IMDDataset __RPC_FAR * This,
            /* [out][in] */ ULONG __RPC_FAR *pcAxes,
            /* [size_is][size_is][out] */ MDAXISINFO __RPC_FAR *__RPC_FAR *prgAxisInfo);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetAxisRowset )(
            IMDDataset __RPC_FAR * This,
            /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
            /* [in] */ ULONG iAxis,
            /* [in] */ REFIID riid,
            /* [in] */ ULONG cPropertySets,
            /* [size_is][out][in] */ DBPROPSET __RPC_FAR rgPropertySets[  ],
            /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppRowset);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetCellData )(
            IMDDataset __RPC_FAR * This,
            /* [in] */ HACCESSOR hAccessor,
            /* [in] */ ULONG ulStartCell,
            /* [in] */ ULONG ulEndCell,
            /* [out] */ void __RPC_FAR *pData);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetSpecification )(
            IMDDataset __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppSpecification);

        END_INTERFACE
    } IMDDatasetVtbl;

    interface IMDDataset
    {
        CONST_VTBL struct IMDDatasetVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IMDDataset_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IMDDataset_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IMDDataset_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IMDDataset_FreeAxisInfo(This,cAxes,rgAxisInfo)\
    (This)->lpVtbl -> FreeAxisInfo(This,cAxes,rgAxisInfo)

#define IMDDataset_GetAxisInfo(This,pcAxes,prgAxisInfo)\
    (This)->lpVtbl -> GetAxisInfo(This,pcAxes,prgAxisInfo)

#define IMDDataset_GetAxisRowset(This,pUnkOuter,iAxis,riid,cPropertySets,rgPropertySets,ppRowset)\
    (This)->lpVtbl -> GetAxisRowset(This,pUnkOuter,iAxis,riid,cPropertySets,rgPropertySets,ppRowset)

#define IMDDataset_GetCellData(This,hAccessor,ulStartCell,ulEndCell,pData)\
    (This)->lpVtbl -> GetCellData(This,hAccessor,ulStartCell,ulEndCell,pData)

#define IMDDataset_GetSpecification(This,riid,ppSpecification)\
    (This)->lpVtbl -> GetSpecification(This,riid,ppSpecification)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE IMDDataset_FreeAxisInfo_Proxy(
    IMDDataset __RPC_FAR * This,
    /* [in] */ ULONG cAxes,
    /* [size_is][in] */ MDAXISINFO __RPC_FAR *rgAxisInfo);

void __RPC_STUB IMDDataset_FreeAxisInfo_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IMDDataset_GetAxisInfo_Proxy(
    IMDDataset __RPC_FAR * This,
    /* [out][in] */ ULONG __RPC_FAR *pcAxes,
    /* [size_is][size_is][out] */ MDAXISINFO __RPC_FAR *__RPC_FAR *prgAxisInfo);

void __RPC_STUB IMDDataset_GetAxisInfo_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IMDDataset_GetAxisRowset_Proxy(
    IMDDataset __RPC_FAR * This,
    /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
    /* [in] */ ULONG iAxis,
    /* [in] */ REFIID riid,
    /* [in] */ ULONG cPropertySets,
    /* [size_is][out][in] */ DBPROPSET __RPC_FAR rgPropertySets[  ],
    /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppRowset);

void __RPC_STUB IMDDataset_GetAxisRowset_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IMDDataset_GetCellData_Proxy(
    IMDDataset __RPC_FAR * This,
    /* [in] */ HACCESSOR hAccessor,
    /* [in] */ ULONG ulStartCell,
    /* [in] */ ULONG ulEndCell,
    /* [out] */ void __RPC_FAR *pData);

void __RPC_STUB IMDDataset_GetCellData_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IMDDataset_GetSpecification_Proxy(
    IMDDataset __RPC_FAR * This,
    /* [in] */ REFIID riid,
    /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppSpecification);

void __RPC_STUB IMDDataset_GetSpecification_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IMDDataset_INTERFACE_DEFINED__ */

#ifndef __IMDFind_INTERFACE_DEFINED__
#define __IMDFind_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IMDFind
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object][local] */

EXTERN_C const IID IID_IMDFind;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a9f-2a1c-11ce-ade5-00aa0044773d")
    IMDFind : public IUnknown
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE FindCell(
            /* [in] */ ULONG ulStartingOrdinal,
            /* [in] */ ULONG cMembers,
            /* [size_is][in] */ LPCOLESTR __RPC_FAR *rgpwszMember,
            /* [out] */ ULONG __RPC_FAR *pulCellOrdinal) = 0;

        virtual HRESULT STDMETHODCALLTYPE FindTuple(
            /* [in] */ ULONG ulAxisIdentifier,
            /* [in] */ ULONG ulStartingOrdinal,
            /* [in] */ ULONG cMembers,
            /* [size_is][in] */ LPCOLESTR __RPC_FAR *rgpwszMember,
            /* [out] */ ULONG __RPC_FAR *pulTupleOrdinal) = 0;

    };

#else /* C style interface */

    typedef struct IMDFindVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IMDFind __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IMDFind __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IMDFind __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *FindCell )(
            IMDFind __RPC_FAR * This,
            /* [in] */ ULONG ulStartingOrdinal,
            /* [in] */ ULONG cMembers,
            /* [size_is][in] */ LPCOLESTR __RPC_FAR *rgpwszMember,
            /* [out] */ ULONG __RPC_FAR *pulCellOrdinal);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *FindTuple )(
            IMDFind __RPC_FAR * This,
            /* [in] */ ULONG ulAxisIdentifier,
            /* [in] */ ULONG ulStartingOrdinal,
            /* [in] */ ULONG cMembers,
            /* [size_is][in] */ LPCOLESTR __RPC_FAR *rgpwszMember,
            /* [out] */ ULONG __RPC_FAR *pulTupleOrdinal);

        END_INTERFACE
    } IMDFindVtbl;

    interface IMDFind
    {
        CONST_VTBL struct IMDFindVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IMDFind_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IMDFind_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IMDFind_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IMDFind_FindCell(This,ulStartingOrdinal,cMembers,rgpwszMember,pulCellOrdinal)\
    (This)->lpVtbl -> FindCell(This,ulStartingOrdinal,cMembers,rgpwszMember,pulCellOrdinal)

#define IMDFind_FindTuple(This,ulAxisIdentifier,ulStartingOrdinal,cMembers,rgpwszMember,pulTupleOrdinal)\
    (This)->lpVtbl -> FindTuple(This,ulAxisIdentifier,ulStartingOrdinal,cMembers,rgpwszMember,pulTupleOrdinal)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE IMDFind_FindCell_Proxy(
    IMDFind __RPC_FAR * This,
    /* [in] */ ULONG ulStartingOrdinal,
    /* [in] */ ULONG cMembers,
    /* [size_is][in] */ LPCOLESTR __RPC_FAR *rgpwszMember,
    /* [out] */ ULONG __RPC_FAR *pulCellOrdinal);

void __RPC_STUB IMDFind_FindCell_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IMDFind_FindTuple_Proxy(
    IMDFind __RPC_FAR * This,
    /* [in] */ ULONG ulAxisIdentifier,
    /* [in] */ ULONG ulStartingOrdinal,
    /* [in] */ ULONG cMembers,
    /* [size_is][in] */ LPCOLESTR __RPC_FAR *rgpwszMember,
    /* [out] */ ULONG __RPC_FAR *pulTupleOrdinal);

void __RPC_STUB IMDFind_FindTuple_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IMDFind_INTERFACE_DEFINED__ */

#ifndef __IMDRangeRowset_INTERFACE_DEFINED__
#define __IMDRangeRowset_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IMDRangeRowset
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object][local] */

EXTERN_C const IID IID_IMDRangeRowset;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733aa0-2a1c-11ce-ade5-00aa0044773d")
    IMDRangeRowset : public IUnknown
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE GetRangeRowset(
            /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
            /* [in] */ ULONG ulStartCell,
            /* [in] */ ULONG ulEndCell,
            /* [in] */ REFIID riid,
            /* [in] */ ULONG cPropertySets,
            /* [size_is][out][in] */ DBPROPSET __RPC_FAR rgPropertySets[  ],
            /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppRowset) = 0;

    };

#else /* C style interface */

    typedef struct IMDRangeRowsetVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IMDRangeRowset __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IMDRangeRowset __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IMDRangeRowset __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetRangeRowset )(
            IMDRangeRowset __RPC_FAR * This,
            /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
            /* [in] */ ULONG ulStartCell,
            /* [in] */ ULONG ulEndCell,
            /* [in] */ REFIID riid,
            /* [in] */ ULONG cPropertySets,
            /* [size_is][out][in] */ DBPROPSET __RPC_FAR rgPropertySets[  ],
            /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppRowset);

        END_INTERFACE
    } IMDRangeRowsetVtbl;

    interface IMDRangeRowset
    {
        CONST_VTBL struct IMDRangeRowsetVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IMDRangeRowset_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IMDRangeRowset_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IMDRangeRowset_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IMDRangeRowset_GetRangeRowset(This,pUnkOuter,ulStartCell,ulEndCell,riid,cPropertySets,rgPropertySets,ppRowset)\
    (This)->lpVtbl -> GetRangeRowset(This,pUnkOuter,ulStartCell,ulEndCell,riid,cPropertySets,rgPropertySets,ppRowset)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE IMDRangeRowset_GetRangeRowset_Proxy(
    IMDRangeRowset __RPC_FAR * This,
    /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
    /* [in] */ ULONG ulStartCell,
    /* [in] */ ULONG ulEndCell,
    /* [in] */ REFIID riid,
    /* [in] */ ULONG cPropertySets,
    /* [size_is][out][in] */ DBPROPSET __RPC_FAR rgPropertySets[  ],
    /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppRowset);

void __RPC_STUB IMDRangeRowset_GetRangeRowset_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IMDRangeRowset_INTERFACE_DEFINED__ */

/****************************************
 * Generated header for interface: __MIDL_itf_oledb_0137
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [local] */

#endif // OLEDBVER >= 0x0200
//@@@- V2.0

extern RPC_IF_HANDLE __MIDL_itf_oledb_0137_v0_0_c_ifspec;
extern RPC_IF_HANDLE __MIDL_itf_oledb_0137_v0_0_s_ifspec;

#ifndef __IErrorRecords_INTERFACE_DEFINED__
#define __IErrorRecords_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IErrorRecords
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object] */

#define IDENTIFIER_SDK_MASK0xF0000000
#define IDENTIFIER_SDK_ERROR0x10000000
typedef struct  tagERRORINFO
    {
    HRESULT hrError;
    DWORD dwMinor;
    CLSID clsid;
    IID iid;
    DISPID dispid;
    }ERRORINFO;

EXTERN_C const IID IID_IErrorRecords;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a67-2a1c-11ce-ade5-00aa0044773d")
    IErrorRecords : public IUnknown
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE AddErrorRecord(
            /* [in] */ ERRORINFO __RPC_FAR *pErrorInfo,
            /* [in] */ DWORD dwLookupID,
            /* [in] */ DISPPARAMS __RPC_FAR *pdispparams,
            /* [in] */ IUnknown __RPC_FAR *punkCustomError,
            /* [in] */ DWORD dwDynamicErrorID) = 0;

        virtual HRESULT STDMETHODCALLTYPE GetBasicErrorInfo(
            /* [in] */ ULONG ulRecordNum,
            /* [out] */ ERRORINFO __RPC_FAR *pErrorInfo) = 0;

        virtual HRESULT STDMETHODCALLTYPE GetCustomErrorObject(
            /* [in] */ ULONG ulRecordNum,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppObject) = 0;

        virtual HRESULT STDMETHODCALLTYPE GetErrorInfo(
            /* [in] */ ULONG ulRecordNum,
            /* [in] */ LCID lcid,
            /* [out] */ IErrorInfo __RPC_FAR *__RPC_FAR *ppErrorInfo) = 0;

        virtual HRESULT STDMETHODCALLTYPE GetErrorParameters(
            /* [in] */ ULONG ulRecordNum,
            /* [out] */ DISPPARAMS __RPC_FAR *pdispparams) = 0;

        virtual HRESULT STDMETHODCALLTYPE GetRecordCount(
            /* [out] */ ULONG __RPC_FAR *pcRecords) = 0;

    };

#else /* C style interface */

    typedef struct IErrorRecordsVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IErrorRecords __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IErrorRecords __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IErrorRecords __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *AddErrorRecord )(
            IErrorRecords __RPC_FAR * This,
            /* [in] */ ERRORINFO __RPC_FAR *pErrorInfo,
            /* [in] */ DWORD dwLookupID,
            /* [in] */ DISPPARAMS __RPC_FAR *pdispparams,
            /* [in] */ IUnknown __RPC_FAR *punkCustomError,
            /* [in] */ DWORD dwDynamicErrorID);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetBasicErrorInfo )(
            IErrorRecords __RPC_FAR * This,
            /* [in] */ ULONG ulRecordNum,
            /* [out] */ ERRORINFO __RPC_FAR *pErrorInfo);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetCustomErrorObject )(
            IErrorRecords __RPC_FAR * This,
            /* [in] */ ULONG ulRecordNum,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppObject);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetErrorInfo )(
            IErrorRecords __RPC_FAR * This,
            /* [in] */ ULONG ulRecordNum,
            /* [in] */ LCID lcid,
            /* [out] */ IErrorInfo __RPC_FAR *__RPC_FAR *ppErrorInfo);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetErrorParameters )(
            IErrorRecords __RPC_FAR * This,
            /* [in] */ ULONG ulRecordNum,
            /* [out] */ DISPPARAMS __RPC_FAR *pdispparams);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetRecordCount )(
            IErrorRecords __RPC_FAR * This,
            /* [out] */ ULONG __RPC_FAR *pcRecords);

        END_INTERFACE
    } IErrorRecordsVtbl;

    interface IErrorRecords
    {
        CONST_VTBL struct IErrorRecordsVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IErrorRecords_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IErrorRecords_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IErrorRecords_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IErrorRecords_AddErrorRecord(This,pErrorInfo,dwLookupID,pdispparams,punkCustomError,dwDynamicErrorID)\
    (This)->lpVtbl -> AddErrorRecord(This,pErrorInfo,dwLookupID,pdispparams,punkCustomError,dwDynamicErrorID)

#define IErrorRecords_GetBasicErrorInfo(This,ulRecordNum,pErrorInfo)\
    (This)->lpVtbl -> GetBasicErrorInfo(This,ulRecordNum,pErrorInfo)

#define IErrorRecords_GetCustomErrorObject(This,ulRecordNum,riid,ppObject)\
    (This)->lpVtbl -> GetCustomErrorObject(This,ulRecordNum,riid,ppObject)

#define IErrorRecords_GetErrorInfo(This,ulRecordNum,lcid,ppErrorInfo)\
    (This)->lpVtbl -> GetErrorInfo(This,ulRecordNum,lcid,ppErrorInfo)

#define IErrorRecords_GetErrorParameters(This,ulRecordNum,pdispparams)\
    (This)->lpVtbl -> GetErrorParameters(This,ulRecordNum,pdispparams)

#define IErrorRecords_GetRecordCount(This,pcRecords)\
    (This)->lpVtbl -> GetRecordCount(This,pcRecords)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE IErrorRecords_AddErrorRecord_Proxy(
    IErrorRecords __RPC_FAR * This,
    /* [in] */ ERRORINFO __RPC_FAR *pErrorInfo,
    /* [in] */ DWORD dwLookupID,
    /* [in] */ DISPPARAMS __RPC_FAR *pdispparams,
    /* [in] */ IUnknown __RPC_FAR *punkCustomError,
    /* [in] */ DWORD dwDynamicErrorID);

void __RPC_STUB IErrorRecords_AddErrorRecord_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IErrorRecords_GetBasicErrorInfo_Proxy(
    IErrorRecords __RPC_FAR * This,
    /* [in] */ ULONG ulRecordNum,
    /* [out] */ ERRORINFO __RPC_FAR *pErrorInfo);

void __RPC_STUB IErrorRecords_GetBasicErrorInfo_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IErrorRecords_GetCustomErrorObject_Proxy(
    IErrorRecords __RPC_FAR * This,
    /* [in] */ ULONG ulRecordNum,
    /* [in] */ REFIID riid,
    /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppObject);

void __RPC_STUB IErrorRecords_GetCustomErrorObject_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IErrorRecords_GetErrorInfo_Proxy(
    IErrorRecords __RPC_FAR * This,
    /* [in] */ ULONG ulRecordNum,
    /* [in] */ LCID lcid,
    /* [out] */ IErrorInfo __RPC_FAR *__RPC_FAR *ppErrorInfo);

void __RPC_STUB IErrorRecords_GetErrorInfo_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IErrorRecords_GetErrorParameters_Proxy(
    IErrorRecords __RPC_FAR * This,
    /* [in] */ ULONG ulRecordNum,
    /* [out] */ DISPPARAMS __RPC_FAR *pdispparams);

void __RPC_STUB IErrorRecords_GetErrorParameters_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IErrorRecords_GetRecordCount_Proxy(
    IErrorRecords __RPC_FAR * This,
    /* [out] */ ULONG __RPC_FAR *pcRecords);

void __RPC_STUB IErrorRecords_GetRecordCount_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IErrorRecords_INTERFACE_DEFINED__ */

#ifndef __IErrorLookup_INTERFACE_DEFINED__
#define __IErrorLookup_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IErrorLookup
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object] */

EXTERN_C const IID IID_IErrorLookup;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a66-2a1c-11ce-ade5-00aa0044773d")
    IErrorLookup : public IUnknown
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE GetErrorDescription(
            /* [in] */ HRESULT hrError,
            /* [in] */ DWORD dwLookupID,
            /* [in] */ DISPPARAMS __RPC_FAR *pdispparams,
            /* [in] */ LCID lcid,
            /* [out] */ BSTR __RPC_FAR *pbstrSource,
            /* [out] */ BSTR __RPC_FAR *pbstrDescription) = 0;

        virtual HRESULT STDMETHODCALLTYPE GetHelpInfo(
            /* [in] */ HRESULT hrError,
            /* [in] */ DWORD dwLookupID,
            /* [in] */ LCID lcid,
            /* [out] */ BSTR __RPC_FAR *pbstrHelpFile,
            /* [out] */ DWORD __RPC_FAR *pdwHelpContext) = 0;

        virtual HRESULT STDMETHODCALLTYPE ReleaseErrors(
            /* [in] */ const DWORD dwDynamicErrorID) = 0;

    };

#else /* C style interface */

    typedef struct IErrorLookupVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IErrorLookup __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IErrorLookup __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IErrorLookup __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetErrorDescription )(
            IErrorLookup __RPC_FAR * This,
            /* [in] */ HRESULT hrError,
            /* [in] */ DWORD dwLookupID,
            /* [in] */ DISPPARAMS __RPC_FAR *pdispparams,
            /* [in] */ LCID lcid,
            /* [out] */ BSTR __RPC_FAR *pbstrSource,
            /* [out] */ BSTR __RPC_FAR *pbstrDescription);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetHelpInfo )(
            IErrorLookup __RPC_FAR * This,
            /* [in] */ HRESULT hrError,
            /* [in] */ DWORD dwLookupID,
            /* [in] */ LCID lcid,
            /* [out] */ BSTR __RPC_FAR *pbstrHelpFile,
            /* [out] */ DWORD __RPC_FAR *pdwHelpContext);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *ReleaseErrors )(
            IErrorLookup __RPC_FAR * This,
            /* [in] */ const DWORD dwDynamicErrorID);

        END_INTERFACE
    } IErrorLookupVtbl;

    interface IErrorLookup
    {
        CONST_VTBL struct IErrorLookupVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IErrorLookup_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IErrorLookup_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IErrorLookup_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IErrorLookup_GetErrorDescription(This,hrError,dwLookupID,pdispparams,lcid,pbstrSource,pbstrDescription)\
    (This)->lpVtbl -> GetErrorDescription(This,hrError,dwLookupID,pdispparams,lcid,pbstrSource,pbstrDescription)

#define IErrorLookup_GetHelpInfo(This,hrError,dwLookupID,lcid,pbstrHelpFile,pdwHelpContext)\
    (This)->lpVtbl -> GetHelpInfo(This,hrError,dwLookupID,lcid,pbstrHelpFile,pdwHelpContext)

#define IErrorLookup_ReleaseErrors(This,dwDynamicErrorID)\
    (This)->lpVtbl -> ReleaseErrors(This,dwDynamicErrorID)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE IErrorLookup_GetErrorDescription_Proxy(
    IErrorLookup __RPC_FAR * This,
    /* [in] */ HRESULT hrError,
    /* [in] */ DWORD dwLookupID,
    /* [in] */ DISPPARAMS __RPC_FAR *pdispparams,
    /* [in] */ LCID lcid,
    /* [out] */ BSTR __RPC_FAR *pbstrSource,
    /* [out] */ BSTR __RPC_FAR *pbstrDescription);

void __RPC_STUB IErrorLookup_GetErrorDescription_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IErrorLookup_GetHelpInfo_Proxy(
    IErrorLookup __RPC_FAR * This,
    /* [in] */ HRESULT hrError,
    /* [in] */ DWORD dwLookupID,
    /* [in] */ LCID lcid,
    /* [out] */ BSTR __RPC_FAR *pbstrHelpFile,
    /* [out] */ DWORD __RPC_FAR *pdwHelpContext);

void __RPC_STUB IErrorLookup_GetHelpInfo_Stub(

IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE IErrorLookup_ReleaseErrors_Proxy(
    IErrorLookup __RPC_FAR * This,
    /* [in] */ const DWORD dwDynamicErrorID);

void __RPC_STUB IErrorLookup_ReleaseErrors_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IErrorLookup_INTERFACE_DEFINED__ */

#ifndef __ISQLErrorInfo_INTERFACE_DEFINED__
#define __ISQLErrorInfo_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: ISQLErrorInfo
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object] */

EXTERN_C const IID IID_ISQLErrorInfo;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a74-2a1c-11ce-ade5-00aa0044773d")
    ISQLErrorInfo : public IUnknown
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE GetSQLInfo(
            /* [out] */ BSTR __RPC_FAR *pbstrSQLState,
            /* [out] */ LONG __RPC_FAR *plNativeError) = 0;

    };

#else /* C style interface */

    typedef struct ISQLErrorInfoVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            ISQLErrorInfo __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            ISQLErrorInfo __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            ISQLErrorInfo __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetSQLInfo )(
            ISQLErrorInfo __RPC_FAR * This,
            /* [out] */ BSTR __RPC_FAR *pbstrSQLState,
            /* [out] */ LONG __RPC_FAR *plNativeError);

        END_INTERFACE
    } ISQLErrorInfoVtbl;

    interface ISQLErrorInfo
    {
        CONST_VTBL struct ISQLErrorInfoVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define ISQLErrorInfo_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define ISQLErrorInfo_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define ISQLErrorInfo_Release(This)\
    (This)->lpVtbl -> Release(This)

#define ISQLErrorInfo_GetSQLInfo(This,pbstrSQLState,plNativeError)\
    (This)->lpVtbl -> GetSQLInfo(This,pbstrSQLState,plNativeError)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE ISQLErrorInfo_GetSQLInfo_Proxy(
    ISQLErrorInfo __RPC_FAR * This,
    /* [out] */ BSTR __RPC_FAR *pbstrSQLState,
    /* [out] */ LONG __RPC_FAR *plNativeError);

void __RPC_STUB ISQLErrorInfo_GetSQLInfo_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __ISQLErrorInfo_INTERFACE_DEFINED__ */

#ifndef __IGetDataSource_INTERFACE_DEFINED__
#define __IGetDataSource_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IGetDataSource
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object] */

EXTERN_C const IID IID_IGetDataSource;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a75-2a1c-11ce-ade5-00aa0044773d")
    IGetDataSource : public IUnknown
    {
    public:
        virtual /* [local] */ HRESULT STDMETHODCALLTYPE GetDataSource(
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppDataSource) = 0;

    };

#else /* C style interface */

    typedef struct IGetDataSourceVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IGetDataSource __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IGetDataSource __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IGetDataSource __RPC_FAR * This);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetDataSource )(
            IGetDataSource __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppDataSource);

        END_INTERFACE
    } IGetDataSourceVtbl;

    interface IGetDataSource
    {
        CONST_VTBL struct IGetDataSourceVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IGetDataSource_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IGetDataSource_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IGetDataSource_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IGetDataSource_GetDataSource(This,riid,ppDataSource)\
    (This)->lpVtbl -> GetDataSource(This,riid,ppDataSource)

#endif /* COBJMACROS */

#endif /* C style interface */

/* [call_as] */ HRESULT STDMETHODCALLTYPE IGetDataSource_RemoteGetDataSource_Proxy(
    IGetDataSource __RPC_FAR * This,
    /* [in] */ REFIID riid,
    /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppDataSource);

void __RPC_STUB IGetDataSource_RemoteGetDataSource_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IGetDataSource_INTERFACE_DEFINED__ */

#ifndef __ITransactionLocal_INTERFACE_DEFINED__
#define __ITransactionLocal_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: ITransactionLocal
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object][local] */

EXTERN_C const IID IID_ITransactionLocal;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a5f-2a1c-11ce-ade5-00aa0044773d")
    ITransactionLocal : public ITransaction
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE GetOptionsObject(
            /* [out] */ ITransactionOptions __RPC_FAR *__RPC_FAR *ppOptions) = 0;

        virtual HRESULT STDMETHODCALLTYPE StartTransaction(
            /* [in] */ ISOLEVEL isoLevel,
            /* [in] */ ULONG isoFlags,
            /* [in] */ ITransactionOptions __RPC_FAR *pOtherOptions,
            /* [out] */ ULONG __RPC_FAR *pulTransactionLevel) = 0;

    };

#else /* C style interface */

    typedef struct ITransactionLocalVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            ITransactionLocal __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            ITransactionLocal __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            ITransactionLocal __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Commit )(
            ITransactionLocal __RPC_FAR * This,
            /* [in] */ BOOL fRetaining,
            /* [in] */ DWORD grfTC,
            /* [in] */ DWORD grfRM);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Abort )(
            ITransactionLocal __RPC_FAR * This,
            /* [in] */ BOID __RPC_FAR *pboidReason,
            /* [in] */ BOOL fRetaining,
            /* [in] */ BOOL fAsync);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTransactionInfo )(
            ITransactionLocal __RPC_FAR * This,
            /* [out] */ XACTTRANSINFO __RPC_FAR *pinfo);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetOptionsObject )(
            ITransactionLocal __RPC_FAR * This,
            /* [out] */ ITransactionOptions __RPC_FAR *__RPC_FAR *ppOptions);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *StartTransaction )(
            ITransactionLocal __RPC_FAR * This,
            /* [in] */ ISOLEVEL isoLevel,
            /* [in] */ ULONG isoFlags,
            /* [in] */ ITransactionOptions __RPC_FAR *pOtherOptions,
            /* [out] */ ULONG __RPC_FAR *pulTransactionLevel);

        END_INTERFACE
    } ITransactionLocalVtbl;

    interface ITransactionLocal
    {
        CONST_VTBL struct ITransactionLocalVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define ITransactionLocal_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define ITransactionLocal_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define ITransactionLocal_Release(This)\
    (This)->lpVtbl -> Release(This)

#define ITransactionLocal_Commit(This,fRetaining,grfTC,grfRM)\
    (This)->lpVtbl -> Commit(This,fRetaining,grfTC,grfRM)

#define ITransactionLocal_Abort(This,pboidReason,fRetaining,fAsync)\
    (This)->lpVtbl -> Abort(This,pboidReason,fRetaining,fAsync)

#define ITransactionLocal_GetTransactionInfo(This,pinfo)\
    (This)->lpVtbl -> GetTransactionInfo(This,pinfo)

#define ITransactionLocal_GetOptionsObject(This,ppOptions)\
    (This)->lpVtbl -> GetOptionsObject(This,ppOptions)

#define ITransactionLocal_StartTransaction(This,isoLevel,isoFlags,pOtherOptions,pulTransactionLevel)\
    (This)->lpVtbl -> StartTransaction(This,isoLevel,isoFlags,pOtherOptions,pulTransactionLevel)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE ITransactionLocal_GetOptionsObject_Proxy(
    ITransactionLocal __RPC_FAR * This,
    /* [out] */ ITransactionOptions __RPC_FAR *__RPC_FAR *ppOptions);

void __RPC_STUB ITransactionLocal_GetOptionsObject_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE ITransactionLocal_StartTransaction_Proxy(
    ITransactionLocal __RPC_FAR * This,
    /* [in] */ ISOLEVEL isoLevel,
    /* [in] */ ULONG isoFlags,
    /* [in] */ ITransactionOptions __RPC_FAR *pOtherOptions,
    /* [out] */ ULONG __RPC_FAR *pulTransactionLevel);

void __RPC_STUB ITransactionLocal_StartTransaction_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __ITransactionLocal_INTERFACE_DEFINED__ */

#ifndef __ITransactionJoin_INTERFACE_DEFINED__
#define __ITransactionJoin_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: ITransactionJoin
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object] */

EXTERN_C const IID IID_ITransactionJoin;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a5e-2a1c-11ce-ade5-00aa0044773d")
    ITransactionJoin : public IUnknown
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE GetOptionsObject(
            /* [out] */ ITransactionOptions __RPC_FAR *__RPC_FAR *ppOptions) = 0;

        virtual HRESULT STDMETHODCALLTYPE JoinTransaction(
            /* [in] */ IUnknown __RPC_FAR *punkTransactionCoord,
            /* [in] */ ISOLEVEL isoLevel,
            /* [in] */ ULONG isoFlags,
            /* [in] */ ITransactionOptions __RPC_FAR *pOtherOptions) = 0;

    };

#else /* C style interface */

    typedef struct ITransactionJoinVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            ITransactionJoin __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            ITransactionJoin __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            ITransactionJoin __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetOptionsObject )(
            ITransactionJoin __RPC_FAR * This,
            /* [out] */ ITransactionOptions __RPC_FAR *__RPC_FAR *ppOptions);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *JoinTransaction )(
            ITransactionJoin __RPC_FAR * This,
            /* [in] */ IUnknown __RPC_FAR *punkTransactionCoord,
            /* [in] */ ISOLEVEL isoLevel,
            /* [in] */ ULONG isoFlags,
            /* [in] */ ITransactionOptions __RPC_FAR *pOtherOptions);

        END_INTERFACE
    } ITransactionJoinVtbl;

    interface ITransactionJoin
    {
        CONST_VTBL struct ITransactionJoinVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define ITransactionJoin_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define ITransactionJoin_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define ITransactionJoin_Release(This)\
    (This)->lpVtbl -> Release(This)

#define ITransactionJoin_GetOptionsObject(This,ppOptions)\
    (This)->lpVtbl -> GetOptionsObject(This,ppOptions)

#define ITransactionJoin_JoinTransaction(This,punkTransactionCoord,isoLevel,isoFlags,pOtherOptions)\
    (This)->lpVtbl -> JoinTransaction(This,punkTransactionCoord,isoLevel,isoFlags,pOtherOptions)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE ITransactionJoin_GetOptionsObject_Proxy(
    ITransactionJoin __RPC_FAR * This,
    /* [out] */ ITransactionOptions __RPC_FAR *__RPC_FAR *ppOptions);

void __RPC_STUB ITransactionJoin_GetOptionsObject_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT STDMETHODCALLTYPE ITransactionJoin_JoinTransaction_Proxy(
    ITransactionJoin __RPC_FAR * This,
    /* [in] */ IUnknown __RPC_FAR *punkTransactionCoord,
    /* [in] */ ISOLEVEL isoLevel,
    /* [in] */ ULONG isoFlags,
    /* [in] */ ITransactionOptions __RPC_FAR *pOtherOptions);

void __RPC_STUB ITransactionJoin_JoinTransaction_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __ITransactionJoin_INTERFACE_DEFINED__ */

#ifndef __ITransactionObject_INTERFACE_DEFINED__
#define __ITransactionObject_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: ITransactionObject
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][uuid][object] */

EXTERN_C const IID IID_ITransactionObject;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("0c733a60-2a1c-11ce-ade5-00aa0044773d")
    ITransactionObject : public IUnknown
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE GetTransactionObject(
            /* [in] */ ULONG ulTransactionLevel,
            /* [out] */ ITransaction __RPC_FAR *__RPC_FAR *ppTransactionObject) = 0;

    };

#else /* C style interface */

    typedef struct ITransactionObjectVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            ITransactionObject __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            ITransactionObject __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            ITransactionObject __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTransactionObject )(
            ITransactionObject __RPC_FAR * This,
            /* [in] */ ULONG ulTransactionLevel,
            /* [out] */ ITransaction __RPC_FAR *__RPC_FAR *ppTransactionObject);

        END_INTERFACE
    } ITransactionObjectVtbl;

    interface ITransactionObject
    {
        CONST_VTBL struct ITransactionObjectVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define ITransactionObject_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define ITransactionObject_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define ITransactionObject_Release(This)\
    (This)->lpVtbl -> Release(This)

#define ITransactionObject_GetTransactionObject(This,ulTransactionLevel,ppTransactionObject)\
    (This)->lpVtbl -> GetTransactionObject(This,ulTransactionLevel,ppTransactionObject)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT STDMETHODCALLTYPE ITransactionObject_GetTransactionObject_Proxy(
    ITransactionObject __RPC_FAR * This,
    /* [in] */ ULONG ulTransactionLevel,
    /* [out] */ ITransaction __RPC_FAR *__RPC_FAR *ppTransactionObject);

void __RPC_STUB ITransactionObject_GetTransactionObject_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __ITransactionObject_INTERFACE_DEFINED__ */

/****************************************
 * Generated header for interface: __MIDL_itf_oledb_0152
 * at Mon Nov 03 18:14:46 1997
 * using MIDL 3.01.75
 ****************************************/
/* [local] */

//
// IID values
//

// IID_IAccessor= {0x0c733a8c,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_IRowset= {0x0c733a7c,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_IRowsetInfo= {0x0c733a55,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_IRowsetLocate= {0x0c733a7d,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_IRowsetResynch= {0x0c733a84,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_IRowsetScroll= {0x0c733a7e,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_IRowsetChange= {0x0c733a05,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_IRowsetUpdate= {0x0c733a6d,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_IRowsetIdentity= {0x0c733a09,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_IRowsetNotify= {0x0c733a83,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_IRowsetIndex= {0x0c733a82,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_ICommand= {0x0c733a63,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_IMultipleResults= {0x0c733a90,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_IConvertType= {0x0c733a88,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_ICommandPrepare= {0x0c733a26,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_ICommandProperties= {0x0c733a79,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_ICommandText= {0x0c733a27,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_ICommandWithParameters= {0x0c733a64,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_IColumnsRowset= {0x0c733a10,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_IColumnsInfo= {0x0c733a11,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_IDBCreateCommand= {0x0c733a1d,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_IDBCreateSession= {0x0c733a5d,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_ISourcesRowset= {0x0c733a1e,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_IDBProperties= {0x0c733a8a,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_IDBInitialize= {0x0c733a8b,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_IDBInfo= {0x0c733a89,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_IDBDataSourceAdmin= {0x0c733a7a,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_ISessionProperties= {0x0c733a85,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_IIndexDefinition= {0x0c733a68,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_ITableDefinition= {0x0c733a86,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_IOpenRowset= {0x0c733a69,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_IDBSchemaRowset= {0x0c733a7b,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_IErrorRecords= {0x0c733a67,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_IErrorLookup= {0x0c733a66,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_ISQLErrorInfo= {0x0c733a74,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_IGetDataSource= {0x0c733a75,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_ITransactionLocal= {0x0c733a5f,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_ITransactionJoin= {0x0c733a5e,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_ITransactionObject= {0x0c733a60,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
//@@@+ V1.5
#if( OLEDBVER >= 0x0150 )
//IID_IChapteredRowset= {0x0c733a93,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
//IID_IDBAsynchNotify= {0x0c733a96,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
//IID_IDBAsynchStatus= {0x0c733a95,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
//IID_IRowsetFind= {0x0c733a9d,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
//IID_IRowPosition= {0x0c733a94,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
//IID_IRowPositionChange= {0x0997a571,0x126e,0x11d0,{0x9f,0x8a,0x00,0xa0,0xc9,0xa0,0x63,0x1e}}
//IID_IViewRowset= {0x0c733a97,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
//IID_IViewChapter= {0x0c733a98,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
//IID_IViewSort= {0x0c733a9a,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
//IID_IViewFilter= {0x0c733a9b,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
//IID_IRowsetView= {0x0c733a99,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
#endif // OLEDBVER >= 0x0150
//@@@- V1.5
//@@@+ V2.0
#if( OLEDBVER >= 0x0200 )
// IID_IRowsetExactScroll= {0x0c733a7f,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_IRowsetNextRowset= {0x0c733a72,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_IRowsetNewRowAfter= {0x0c733a71,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_IRowsetWithParameters= {0x0c733a6e,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_IRowsetFind= {0x0c733a0d,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_IRowsetAsynch= {0x0c733a0f,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_IRowsetKeys= {0x0c733a12,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_IRowsetWatchAll= {0x0c733a73,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_IRowsetWatchNotify= {0x0c733a44,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_IRowsetWatchRegion= {0x0c733a45,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_IRowsetCopyRows= {0x0c733a6b,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_IReadData= {0x0c733a6a,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_ICommandCost= {0x0c733a4e,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_ICommandTree= {0x0c733a87,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_ICommandValidate= {0x0c733a18,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_IQuery= {0x0c733a51,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_ITableRename= {0x0c733a77,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_IDBSchemaCommand= {0x0c733a50,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_IDBSecurityInfo= {0x0c733a62,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_IProvideMoniker= {0x0c733a4d,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_IMDDataset= {0x0c733a9e,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_IMDFind= {0x0c733a9f,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
// IID_IMDRangeRowset= {0x0c733aa0,0x2a1c,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
#endif // OLEDBVER >= 0x0200
//@@@- V2.0
#include <poppack.h>// restore original structure packing

extern RPC_IF_HANDLE __MIDL_itf_oledb_0152_v0_0_c_ifspec;
extern RPC_IF_HANDLE __MIDL_itf_oledb_0152_v0_0_s_ifspec;

/* Additional Prototypes for ALL interfaces */

unsigned long             __RPC_USER  BSTR_UserSize(     unsigned long __RPC_FAR *, unsigned long            , BSTR __RPC_FAR * );
unsigned char __RPC_FAR * __RPC_USER  BSTR_UserMarshal(  unsigned long __RPC_FAR *, unsigned char __RPC_FAR *, BSTR __RPC_FAR * );
unsigned char __RPC_FAR * __RPC_USER  BSTR_UserUnmarshal(unsigned long __RPC_FAR *, unsigned char __RPC_FAR *, BSTR __RPC_FAR * );
void                      __RPC_USER  BSTR_UserFree(     unsigned long __RPC_FAR *, BSTR __RPC_FAR * );

unsigned long             __RPC_USER  VARIANT_UserSize(     unsigned long __RPC_FAR *, unsigned long            , VARIANT __RPC_FAR * );
unsigned char __RPC_FAR * __RPC_USER  VARIANT_UserMarshal(  unsigned long __RPC_FAR *, unsigned char __RPC_FAR *, VARIANT __RPC_FAR * );
unsigned char __RPC_FAR * __RPC_USER  VARIANT_UserUnmarshal(unsigned long __RPC_FAR *, unsigned char __RPC_FAR *, VARIANT __RPC_FAR * );
void                      __RPC_USER  VARIANT_UserFree(     unsigned long __RPC_FAR *, VARIANT __RPC_FAR * );

/* [local] */ HRESULT STDMETHODCALLTYPE IAccessor_CreateAccessor_Proxy(
    IAccessor __RPC_FAR * This,
    /* [in] */ DBACCESSORFLAGS dwAccessorFlags,
    /* [in] */ ULONG cBindings,
    /* [size_is][in] */ const DBBINDING __RPC_FAR rgBindings[  ],
    /* [in] */ ULONG cbRowSize,
    /* [out] */ HACCESSOR __RPC_FAR *phAccessor,
    /* [size_is][out] */ DBBINDSTATUS __RPC_FAR rgStatus[  ]);

/* [call_as] */ HRESULT STDMETHODCALLTYPE IAccessor_CreateAccessor_Stub(
    IAccessor __RPC_FAR * This,
    /* [in] */ DBACCESSORFLAGS dwAccessorFlags,
    /* [in] */ ULONG cBindings,
    /* [size_is][unique][in] */ DBBINDING __RPC_FAR *rgBindings,
    /* [in] */ ULONG cbRowSize,
    /* [out] */ HACCESSOR __RPC_FAR *phAccessor,
    /* [size_is][unique][out][in] */ DBBINDSTATUS __RPC_FAR *rgStatus);

/* [local] */ HRESULT STDMETHODCALLTYPE IAccessor_GetBindings_Proxy(
    IAccessor __RPC_FAR * This,
    /* [in] */ HACCESSOR hAccessor,
    /* [out] */ DBACCESSORFLAGS __RPC_FAR *pdwAccessorFlags,
    /* [out][in] */ ULONG __RPC_FAR *pcBindings,
    /* [size_is][size_is][out] */ DBBINDING __RPC_FAR *__RPC_FAR *prgBindings);

/* [call_as] */ HRESULT STDMETHODCALLTYPE IAccessor_GetBindings_Stub(
    IAccessor __RPC_FAR * This,
    /* [in] */ HACCESSOR hAccessor,
    /* [out] */ DBACCESSORFLAGS __RPC_FAR *pdwAccessorFlags,
    /* [out][in] */ ULONG __RPC_FAR *pcBindings,
    /* [size_is][size_is][out] */ DBBINDING __RPC_FAR *__RPC_FAR *prgBindings);

/* [local] */ HRESULT STDMETHODCALLTYPE IRowsetInfo_GetProperties_Proxy(
    IRowsetInfo __RPC_FAR * This,
    /* [in] */ const ULONG cPropertyIDSets,
    /* [size_is][in] */ const DBPROPIDSET __RPC_FAR rgPropertyIDSets[  ],
    /* [out][in] */ ULONG __RPC_FAR *pcPropertySets,
    /* [size_is][size_is][out] */ DBPROPSET __RPC_FAR *__RPC_FAR *prgPropertySets);

/* [call_as] */ HRESULT STDMETHODCALLTYPE IRowsetInfo_GetProperties_Stub(
    IRowsetInfo __RPC_FAR * This,
    /* [in] */ ULONG cPropertyIDSets,
    /* [size_is][unique][in] */ const DBPROPIDSET __RPC_FAR *rgPropertyIDSets,
    /* [out][in] */ ULONG __RPC_FAR *pcPropertySets,
    /* [size_is][size_is][out] */ DBPROPSET __RPC_FAR *__RPC_FAR *prgPropertySets);

/* [local] */ HRESULT STDMETHODCALLTYPE IRowsetInfo_GetReferencedRowset_Proxy(
    IRowsetInfo __RPC_FAR * This,
    /* [in] */ ULONG iOrdinal,
    /* [in] */ REFIID riid,
    /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppReferencedRowset);

/* [call_as] */ HRESULT STDMETHODCALLTYPE IRowsetInfo_GetReferencedRowset_Stub(
    IRowsetInfo __RPC_FAR * This,
    /* [in] */ ULONG iOrdinal,
    /* [in] */ REFIID riid,
    /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppReferencedRowset);

/* [local] */ HRESULT STDMETHODCALLTYPE IRowsetInfo_GetSpecification_Proxy(

IRowsetInfo __RPC_FAR * This,
    /* [in] */ REFIID riid,
    /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppSpecification);

/* [call_as] */ HRESULT STDMETHODCALLTYPE IRowsetInfo_GetSpecification_Stub(
    IRowsetInfo __RPC_FAR * This,
    /* [in] */ REFIID riid,
    /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppSpecification);

/* [local] */ HRESULT STDMETHODCALLTYPE IViewSort_GetSortOrder_Proxy(
    IViewSort __RPC_FAR * This,
    /* [out] */ ULONG __RPC_FAR *pcValues,
    /* [out] */ ULONG __RPC_FAR *__RPC_FAR prgColumns[  ],
    /* [out] */ DBSORT __RPC_FAR *__RPC_FAR prgOrders[  ]);

/* [call_as] */ HRESULT STDMETHODCALLTYPE IViewSort_GetSortOrder_Stub(
    IViewSort __RPC_FAR * This,
    /* [out][in] */ ULONG __RPC_FAR *pcValues,
    /* [size_is][size_is][out] */ ULONG __RPC_FAR *__RPC_FAR *prgColumns,
    /* [size_is][size_is][out] */ ULONG __RPC_FAR *__RPC_FAR *prgOrders);

/* [local] */ HRESULT STDMETHODCALLTYPE IViewFilter_GetFilterBindings_Proxy(
    IViewFilter __RPC_FAR * This,
    /* [out] */ ULONG __RPC_FAR *pcBindings,
    /* [out] */ DBBINDING __RPC_FAR *__RPC_FAR *prgBindings);

/* [call_as] */ HRESULT STDMETHODCALLTYPE IViewFilter_GetFilterBindings_Stub(
    IViewFilter __RPC_FAR * This,
    /* [out][in] */ ULONG __RPC_FAR *pcBindings,
    /* [size_is][size_is][out] */ DBBINDING __RPC_FAR *__RPC_FAR *prgBindings);

/* [local] */ HRESULT STDMETHODCALLTYPE ICommand_Execute_Proxy(
    ICommand __RPC_FAR * This,
    /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
    /* [in] */ REFIID riid,
    /* [out][in] */ DBPARAMS __RPC_FAR *pParams,
    /* [out] */ LONG __RPC_FAR *pcRowsAffected,
    /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppRowset);

/* [call_as] */ HRESULT STDMETHODCALLTYPE ICommand_Execute_Stub(
    ICommand __RPC_FAR * This,
    /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
    /* [in] */ REFIID riid,
    /* [in] */ HACCESSOR hAccessor,
    /* [in] */ ULONG cParamSets,
    /* [in] */ ULONG cbData,
    /* [size_is][unique][out][in] */ BYTE __RPC_FAR *pbData,
    /* [unique][out][in] */ LONG __RPC_FAR *pcRowsAffected,
    /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppRowset);

/* [local] */ HRESULT STDMETHODCALLTYPE ICommand_GetDBSession_Proxy(
    ICommand __RPC_FAR * This,
    /* [in] */ REFIID riid,
    /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppSession);

/* [call_as] */ HRESULT STDMETHODCALLTYPE ICommand_GetDBSession_Stub(
    ICommand __RPC_FAR * This,
    /* [in] */ REFIID riid,
    /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppSession);

/* [local] */ HRESULT STDMETHODCALLTYPE IMultipleResults_GetResult_Proxy(
    IMultipleResults __RPC_FAR * This,
    /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
    /* [in] */ LONG reserved,
    /* [in] */ REFIID riid,
    /* [out] */ LONG __RPC_FAR *pcRowsAffected,
    /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppRowset);

/* [call_as] */ HRESULT STDMETHODCALLTYPE IMultipleResults_GetResult_Stub(
    IMultipleResults __RPC_FAR * This,
    /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
    /* [in] */ LONG reserved,
    /* [in] */ REFIID riid,
    /* [unique][out][in] */ LONG __RPC_FAR *pcRowsAffected,
    /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppRowset);

/* [local] */ HRESULT STDMETHODCALLTYPE ICommandProperties_GetProperties_Proxy(
    ICommandProperties __RPC_FAR * This,
    /* [in] */ const ULONG cPropertyIDSets,
    /* [size_is][in] */ const DBPROPIDSET __RPC_FAR rgPropertyIDSets[  ],
    /* [out][in] */ ULONG __RPC_FAR *pcPropertySets,
    /* [size_is][size_is][out] */ DBPROPSET __RPC_FAR *__RPC_FAR *prgPropertySets);

/* [call_as] */ HRESULT STDMETHODCALLTYPE ICommandProperties_GetProperties_Stub(
    ICommandProperties __RPC_FAR * This,
    /* [in] */ const ULONG cPropertyIDSets,
    /* [size_is][unique][in] */ const DBPROPIDSET __RPC_FAR *rgPropertyIDSets,
    /* [out][in] */ ULONG __RPC_FAR *pcPropertySets,
    /* [size_is][size_is][out] */ DBPROPSET __RPC_FAR *__RPC_FAR *prgPropertySets);

/* [local] */ HRESULT STDMETHODCALLTYPE ICommandProperties_SetProperties_Proxy(
    ICommandProperties __RPC_FAR * This,
    /* [in] */ ULONG cPropertySets,
    /* [size_is][out][in] */ DBPROPSET __RPC_FAR rgPropertySets[  ]);

/* [call_as] */ HRESULT STDMETHODCALLTYPE ICommandProperties_SetProperties_Stub(
    ICommandProperties __RPC_FAR * This,
    /* [in] */ ULONG cPropertySets,
    /* [size_is][unique][in] */ DBPROPSET __RPC_FAR *rgPropertySets);

/* [local] */ HRESULT STDMETHODCALLTYPE ICommandText_GetCommandText_Proxy(
    ICommandText __RPC_FAR * This,
    /* [out][in] */ GUID __RPC_FAR *pguidDialect,
    /* [out] */ LPOLESTR __RPC_FAR *ppwszCommand);

/* [call_as] */ HRESULT STDMETHODCALLTYPE ICommandText_GetCommandText_Stub(
    ICommandText __RPC_FAR * This,
    /* [unique][out][in] */ GUID __RPC_FAR *pguidDialect,
    /* [out] */ LPOLESTR __RPC_FAR *ppwszCommand);

/* [local] */ HRESULT STDMETHODCALLTYPE ICommandWithParameters_GetParameterInfo_Proxy(
    ICommandWithParameters __RPC_FAR * This,
    /* [out][in] */ ULONG __RPC_FAR *pcParams,
    /* [size_is][size_is][out] */ DBPARAMINFO __RPC_FAR *__RPC_FAR *prgParamInfo,
    /* [out] */ OLECHAR __RPC_FAR *__RPC_FAR *ppNamesBuffer);

/* [call_as] */ HRESULT STDMETHODCALLTYPE ICommandWithParameters_GetParameterInfo_Stub(
    ICommandWithParameters __RPC_FAR * This,
    /* [out][in] */ ULONG __RPC_FAR *pcParams,
    /* [size_is][size_is][out] */ DBPARAMINFO __RPC_FAR *__RPC_FAR *prgParamInfo,
    /* [size_is][size_is][out] */ ULONG __RPC_FAR *__RPC_FAR *prgNameOffsets,
    /* [out][in] */ ULONG __RPC_FAR *pcbNamesBuffer,
    /* [size_is][size_is][unique][out][in] */ OLECHAR __RPC_FAR *__RPC_FAR *ppNamesBuffer);

/* [local] */ HRESULT STDMETHODCALLTYPE ICommandWithParameters_MapParameterNames_Proxy(
    ICommandWithParameters __RPC_FAR * This,
    /* [in] */ ULONG cParamNames,
    /* [size_is][in] */ const OLECHAR __RPC_FAR *__RPC_FAR rgParamNames[  ],
    /* [size_is][out] */ LONG __RPC_FAR rgParamOrdinals[  ]);

/* [call_as] */ HRESULT STDMETHODCALLTYPE ICommandWithParameters_MapParameterNames_Stub(
    ICommandWithParameters __RPC_FAR * This,
    /* [in] */ ULONG cParamNames,
    /* [size_is][in] */ LPCOLESTR __RPC_FAR *rgParamNames,
    /* [size_is][out] */ LONG __RPC_FAR *rgParamOrdinals);

/* [local] */ HRESULT STDMETHODCALLTYPE IColumnsRowset_GetAvailableColumns_Proxy(
    IColumnsRowset __RPC_FAR * This,
    /* [out][in] */ ULONG __RPC_FAR *pcOptColumns,
    /* [size_is][size_is][out] */ DBID __RPC_FAR *__RPC_FAR *prgOptColumns);

/* [call_as] */ HRESULT STDMETHODCALLTYPE IColumnsRowset_GetAvailableColumns_Stub(
    IColumnsRowset __RPC_FAR * This,
    /* [out][in] */ ULONG __RPC_FAR *pcOptColumns,
    /* [size_is][size_is][out] */ DBID __RPC_FAR *__RPC_FAR *prgOptColumns);

/* [local] */ HRESULT STDMETHODCALLTYPE IColumnsRowset_GetColumnsRowset_Proxy(
    IColumnsRowset __RPC_FAR * This,
    /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
    /* [in] */ ULONG cOptColumns,
    /* [size_is][in] */ const DBID __RPC_FAR rgOptColumns[  ],
    /* [in] */ REFIID riid,
    /* [in] */ ULONG cPropertySets,
    /* [size_is][out][in] */ DBPROPSET __RPC_FAR rgPropertySets[  ],
    /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppColRowset);

/* [call_as] */ HRESULT STDMETHODCALLTYPE IColumnsRowset_GetColumnsRowset_Stub(
    IColumnsRowset __RPC_FAR * This,
    /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
    /* [in] */ ULONG cOptColumns,
    /* [size_is][unique][in] */ const DBID __RPC_FAR *rgOptColumns,
    /* [in] */ REFIID riid,
    /* [in] */ ULONG cPropertySets,
    /* [size_is][unique][out][in] */ DBPROPSET __RPC_FAR *rgPropertySets,
    /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppColRowset);

/* [local] */ HRESULT STDMETHODCALLTYPE IColumnsInfo_GetColumnInfo_Proxy(
    IColumnsInfo __RPC_FAR * This,
    /* [out][in] */ ULONG __RPC_FAR *pcColumns,
    /* [size_is][size_is][out] */ DBCOLUMNINFO __RPC_FAR *__RPC_FAR *prgInfo,
    /* [out] */ OLECHAR __RPC_FAR *__RPC_FAR *ppStringsBuffer);

/* [call_as] */ HRESULT STDMETHODCALLTYPE IColumnsInfo_GetColumnInfo_Stub(
    IColumnsInfo __RPC_FAR * This,
    /* [out][in] */ ULONG __RPC_FAR *pcColumns,
    /* [size_is][size_is][out] */ DBCOLUMNINFO __RPC_FAR *__RPC_FAR *prgInfo,
    /* [size_is][size_is][out] */ ULONG __RPC_FAR *__RPC_FAR *prgNameOffsets,
    /* [size_is][size_is][out] */ ULONG __RPC_FAR *__RPC_FAR *prgcolumnidOffsets,
    /* [out][in] */ ULONG __RPC_FAR *pcbStringsBuffer,
    /* [size_is][size_is][unique][out][in] */ OLECHAR __RPC_FAR *__RPC_FAR *ppStringsBuffer);

/* [local] */ HRESULT STDMETHODCALLTYPE IColumnsInfo_MapColumnIDs_Proxy(
    IColumnsInfo __RPC_FAR * This,
    /* [in] */ ULONG cColumnIDs,
    /* [size_is][in] */ const DBID __RPC_FAR rgColumnIDs[  ],
    /* [size_is][out] */ ULONG __RPC_FAR rgColumns[  ]);

/* [call_as] */ HRESULT STDMETHODCALLTYPE IColumnsInfo_MapColumnIDs_Stub(
    IColumnsInfo __RPC_FAR * This,
    /* [in] */ ULONG cColumnIDs,
    /* [size_is][in] */ const DBID __RPC_FAR *rgColumnIDs,
    /* [size_is][out] */ ULONG __RPC_FAR *rgColumns);

/* [local] */ HRESULT STDMETHODCALLTYPE IDBCreateCommand_CreateCommand_Proxy(
    IDBCreateCommand __RPC_FAR * This,
    /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
    /* [in] */ REFIID riid,
    /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppCommand);

/* [call_as] */ HRESULT STDMETHODCALLTYPE IDBCreateCommand_CreateCommand_Stub(
    IDBCreateCommand __RPC_FAR * This,
    /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
    /* [in] */ REFIID riid,
    /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppCommand);

/* [local] */ HRESULT STDMETHODCALLTYPE IDBCreateSession_CreateSession_Proxy(
    IDBCreateSession __RPC_FAR * This,
    /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
    /* [in] */ REFIID riid,
    /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppDBSession);

/* [call_as] */ HRESULT STDMETHODCALLTYPE IDBCreateSession_CreateSession_Stub(
    IDBCreateSession __RPC_FAR * This,
    /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
    /* [in] */ REFIID riid,
    /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppDBSession);

/* [local] */ HRESULT STDMETHODCALLTYPE ISourcesRowset_GetSourcesRowset_Proxy(
    ISourcesRowset __RPC_FAR * This,
    /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
    /* [in] */ REFIID riid,
    /* [in] */ ULONG cPropertySets,
    /* [size_is][out][in] */ DBPROPSET __RPC_FAR rgProperties[  ],
    /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppSourcesRowset);

/* [call_as] */ HRESULT STDMETHODCALLTYPE ISourcesRowset_GetSourcesRowset_Stub(
    ISourcesRowset __RPC_FAR * This,
    /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
    /* [in] */ REFIID riid,
    /* [in] */ ULONG cPropertySets,
    /* [size_is][unique][out][in] */ DBPROPSET __RPC_FAR *rgProperties,
    /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppSourcesRowset);

/* [local] */ HRESULT STDMETHODCALLTYPE IDBProperties_GetProperties_Proxy(
    IDBProperties __RPC_FAR * This,
    /* [in] */ ULONG cPropertyIDSets,
    /* [size_is][in] */ const DBPROPIDSET __RPC_FAR rgPropertyIDSets[  ],
    /* [out][in] */ ULONG __RPC_FAR *pcPropertySets,
    /* [size_is][size_is][out] */ DBPROPSET __RPC_FAR *__RPC_FAR *prgPropertySets);

/* [call_as] */ HRESULT STDMETHODCALLTYPE IDBProperties_GetProperties_Stub(
    IDBProperties __RPC_FAR * This,
    /* [in] */ ULONG cPropertyIDSets,
    /* [size_is][unique][in] */ const DBPROPIDSET __RPC_FAR *rgPropertyIDSets,
    /* [out][in] */ ULONG __RPC_FAR *pcPropertySets,
    /* [size_is][size_is][out] */ DBPROPSET __RPC_FAR *__RPC_FAR *prgPropertySets);

/* [local] */ HRESULT STDMETHODCALLTYPE IDBProperties_GetPropertyInfo_Proxy(
    IDBProperties __RPC_FAR * This,
    /* [in] */ ULONG cPropertyIDSets,
    /* [size_is][in] */ const DBPROPIDSET __RPC_FAR rgPropertyIDSets[  ],
    /* [out][in] */ ULONG __RPC_FAR *pcPropertyInfoSets,
    /* [size_is][size_is][out] */ DBPROPINFOSET __RPC_FAR *__RPC_FAR *prgPropertyInfoSets,
    /* [out] */ OLECHAR __RPC_FAR *__RPC_FAR *ppDescBuffer);

/* [call_as] */ HRESULT STDMETHODCALLTYPE IDBProperties_GetPropertyInfo_Stub(
    IDBProperties __RPC_FAR * This,
    /* [in] */ ULONG cPropertyIDSets,
    /* [size_is][unique][in] */ const DBPROPIDSET __RPC_FAR *rgPropertyIDSets,
    /* [out][in] */ ULONG __RPC_FAR *pcPropertyInfoSets,
    /* [size_is][size_is][out] */ DBPROPINFOSET __RPC_FAR *__RPC_FAR *prgPropertyInfoSets,
    /* [out][in] */ ULONG __RPC_FAR *pcOffsets,
    /* [size_is][size_is][out] */ ULONG __RPC_FAR *__RPC_FAR *prgDescOffsets,
    /* [out][in] */ ULONG __RPC_FAR *pcbDescBuffer,
    /* [size_is][size_is][unique][out][in] */ OLECHAR __RPC_FAR *__RPC_FAR *ppDescBuffer);

/* [local] */ HRESULT STDMETHODCALLTYPE IDBProperties_SetProperties_Proxy(
    IDBProperties __RPC_FAR * This,
    /* [in] */ ULONG cPropertySets,
    /* [size_is][out][in] */ DBPROPSET __RPC_FAR rgPropertySets[  ]);

/* [call_as] */ HRESULT STDMETHODCALLTYPE IDBProperties_SetProperties_Stub(
    IDBProperties __RPC_FAR * This,
    /* [in] */ ULONG cPropertySets,
    /* [size_is][unique][in] */ DBPROPSET __RPC_FAR *rgPropertySets);

/* [local] */ HRESULT STDMETHODCALLTYPE IDBInfo_GetKeywords_Proxy(
    IDBInfo __RPC_FAR * This,
    /* [out] */ LPOLESTR __RPC_FAR *ppwszKeywords);

/* [call_as] */ HRESULT STDMETHODCALLTYPE IDBInfo_GetKeywords_Stub(
    IDBInfo __RPC_FAR * This,
    /* [unique][out][in] */ LPOLESTR __RPC_FAR *ppwszKeywords);

/* [local] */ HRESULT STDMETHODCALLTYPE IDBInfo_GetLiteralInfo_Proxy(
    IDBInfo __RPC_FAR * This,
    /* [in] */ ULONG cLiterals,
    /* [size_is][in] */ const DBLITERAL __RPC_FAR rgLiterals[  ],
    /* [out][in] */ ULONG __RPC_FAR *pcLiteralInfo,
    /* [size_is][size_is][out] */ DBLITERALINFO __RPC_FAR *__RPC_FAR *prgLiteralInfo,
    /* [out] */ OLECHAR __RPC_FAR *__RPC_FAR *ppCharBuffer);

/* [call_as] */ HRESULT STDMETHODCALLTYPE IDBInfo_GetLiteralInfo_Stub(
    IDBInfo __RPC_FAR * This,
    /* [in] */ ULONG cLiterals,
    /* [size_is][unique][in] */ const DBLITERAL __RPC_FAR *rgLiterals,
    /* [out][in] */ ULONG __RPC_FAR *pcLiteralInfo,
    /* [size_is][size_is][out] */ DBLITERALINFO __RPC_FAR *__RPC_FAR *prgLiteralInfo,
    /* [size_is][size_is][out] */ ULONG __RPC_FAR *__RPC_FAR *prgLVOffsets,
    /* [size_is][size_is][out] */ ULONG __RPC_FAR *__RPC_FAR *prgICOffsets,
    /* [size_is][size_is][out] */ ULONG __RPC_FAR *__RPC_FAR *prgISCOffsets,
    /* [out][in] */ ULONG __RPC_FAR *pcbCharBuffer,
    /* [size_is][size_is][unique][out][in] */ OLECHAR __RPC_FAR *__RPC_FAR *ppCharBuffer);

/* [local] */ HRESULT STDMETHODCALLTYPE IDBDataSourceAdmin_CreateDataSource_Proxy(
    IDBDataSourceAdmin __RPC_FAR * This,
    /* [in] */ ULONG cPropertySets,
    /* [size_is][out][in] */ DBPROPSET __RPC_FAR rgPropertySets[  ],
    /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
    /* [in] */ REFIID riid,
    /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppDBSession);

/* [call_as] */ HRESULT STDMETHODCALLTYPE IDBDataSourceAdmin_CreateDataSource_Stub(
    IDBDataSourceAdmin __RPC_FAR * This,
    /* [in] */ ULONG cPropertySets,
    /* [size_is][unique][out][in] */ DBPROPSET __RPC_FAR *rgPropertySets,
    /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
    /* [in] */ REFIID riid,
    /* [iid_is][unique][out][in] */ IUnknown __RPC_FAR *__RPC_FAR *ppDBSession);

/* [local] */ HRESULT STDMETHODCALLTYPE IDBDataSourceAdmin_GetCreationProperties_Proxy(
    IDBDataSourceAdmin __RPC_FAR * This,
    /* [in] */ ULONG cPropertyIDSets,
    /* [size_is][in] */ const DBPROPIDSET __RPC_FAR rgPropertyIDSets[  ],
    /* [out][in] */ ULONG __RPC_FAR *pcPropertyInfoSets,
    /* [size_is][size_is][out] */ DBPROPINFOSET __RPC_FAR *__RPC_FAR *prgPropertyInfoSets,
    /* [out] */ OLECHAR __RPC_FAR *__RPC_FAR *ppDescBuffer);

/* [call_as] */ HRESULT STDMETHODCALLTYPE IDBDataSourceAdmin_GetCreationProperties_Stub(
    IDBDataSourceAdmin __RPC_FAR * This,
    /* [in] */ ULONG cPropertyIDSets,
    /* [size_is][unique][in] */ const DBPROPIDSET __RPC_FAR *rgPropertyIDSets,
    /* [out][in] */ ULONG __RPC_FAR *pcPropertyInfoSets,
    /* [size_is][size_is][out] */ DBPROPINFOSET __RPC_FAR *__RPC_FAR *prgPropertyInfoSets,
    /* [out][in] */ ULONG __RPC_FAR *pcOffsets,
    /* [size_is][size_is][out] */ ULONG __RPC_FAR *__RPC_FAR *prgDescOffsets,
    /* [out][in] */ ULONG __RPC_FAR *pcbDescBuffer,
    /* [size_is][size_is][unique][out][in] */ OLECHAR __RPC_FAR *__RPC_FAR *ppDescBuffer);

/* [local] */ HRESULT STDMETHODCALLTYPE IDBDataSourceAdmin_ModifyDataSource_Proxy(
    IDBDataSourceAdmin __RPC_FAR * This,
    /* [in] */ ULONG cPropertySets,
    /* [size_is][in] */ DBPROPSET __RPC_FAR rgPropertySets[  ]);

/* [call_as] */ HRESULT STDMETHODCALLTYPE IDBDataSourceAdmin_ModifyDataSource_Stub(
    IDBDataSourceAdmin __RPC_FAR * This,
    /* [in] */ ULONG cPropertySets,
    /* [size_is][in] */ DBPROPSET __RPC_FAR *rgPropertySets);

/* [local] */ HRESULT STDMETHODCALLTYPE ISessionProperties_GetProperties_Proxy(
    ISessionProperties __RPC_FAR * This,
    /* [in] */ ULONG cPropertyIDSets,
    /* [size_is][in] */ const DBPROPIDSET __RPC_FAR rgPropertyIDSets[  ],
    /* [out][in] */ ULONG __RPC_FAR *pcPropertySets,
    /* [size_is][size_is][out] */ DBPROPSET __RPC_FAR *__RPC_FAR *prgPropertySets);

/* [call_as] */ HRESULT STDMETHODCALLTYPE ISessionProperties_GetProperties_Stub(
    ISessionProperties __RPC_FAR * This,
    /* [in] */ ULONG cPropertyIDSets,
    /* [size_is][unique][in] */ const DBPROPIDSET __RPC_FAR *rgPropertyIDSets,
    /* [out][in] */ ULONG __RPC_FAR *pcPropertySets,
    /* [size_is][size_is][out] */ DBPROPSET __RPC_FAR *__RPC_FAR *prgPropertySets);

/* [local] */ HRESULT STDMETHODCALLTYPE ISessionProperties_SetProperties_Proxy(
    ISessionProperties __RPC_FAR * This,
    /* [in] */ ULONG cPropertySets,
    /* [size_is][unique][in] */ DBPROPSET __RPC_FAR rgPropertySets[  ]);

/* [call_as] */ HRESULT STDMETHODCALLTYPE ISessionProperties_SetProperties_Stub(
    ISessionProperties __RPC_FAR * This,
    /* [in] */ ULONG cPropertySets,
    /* [size_is][unique][in] */ DBPROPSET __RPC_FAR *rgPropertySets);

/* [local] */ HRESULT STDMETHODCALLTYPE IIndexDefinition_CreateIndex_Proxy(
    IIndexDefinition __RPC_FAR * This,
    /* [in] */ DBID __RPC_FAR *pTableID,
    /* [in] */ DBID __RPC_FAR *pIndexID,
    /* [in] */ ULONG cIndexColumnDescs,
    /* [size_is][in] */ const DBINDEXCOLUMNDESC __RPC_FAR rgIndexColumnDescs[  ],
    /* [in] */ ULONG cPropertySets,
    /* [size_is][out][in] */ DBPROPSET __RPC_FAR rgPropertySets[  ],
    /* [out] */ DBID __RPC_FAR *__RPC_FAR *ppIndexID);

/* [call_as] */ HRESULT STDMETHODCALLTYPE IIndexDefinition_CreateIndex_Stub(
    IIndexDefinition __RPC_FAR * This,
    /* [in] */ DBID __RPC_FAR *pTableID,
    /* [unique][in] */ DBID __RPC_FAR *pIndexID,
    /* [in] */ ULONG cIndexColumnDescs,
    /* [size_is][in] */ const DBINDEXCOLUMNDESC __RPC_FAR *rgIndexColumnDescs,
    /* [in] */ ULONG cPropertySets,
    /* [size_is][unique][out][in] */ DBPROPSET __RPC_FAR *rgPropertySets,
    /* [unique][out][in] */ DBID __RPC_FAR *__RPC_FAR *ppIndexID);

/* [local] */ HRESULT STDMETHODCALLTYPE ITableDefinition_CreateTable_Proxy(
    ITableDefinition __RPC_FAR * This,
    /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
    /* [in] */ DBID __RPC_FAR *pTableID,
    /* [in] */ ULONG cColumnDescs,
    /* [size_is][in] */ const DBCOLUMNDESC __RPC_FAR rgColumnDescs[  ],
    /* [in] */ REFIID riid,
    /* [in] */ ULONG cPropertySets,
    /* [size_is][out][in] */ DBPROPSET __RPC_FAR rgPropertySets[  ],
    /* [out] */ DBID __RPC_FAR *__RPC_FAR *ppTableID,
    /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppRowset);

/* [call_as] */ HRESULT STDMETHODCALLTYPE ITableDefinition_CreateTable_Stub(
    ITableDefinition __RPC_FAR * This,
    /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
    /* [unique][in] */ DBID __RPC_FAR *pTableID,
    /* [in] */ ULONG cColumnDescs,
    /* [size_is][in] */ const DBCOLUMNDESC __RPC_FAR *rgColumnDescs,
    /* [in] */ REFIID riid,
    /* [in] */ ULONG cPropertySets,
    /* [size_is][unique][out][in] */ DBPROPSET __RPC_FAR *rgPropertySets,
    /* [unique][out][in] */ DBID __RPC_FAR *__RPC_FAR *ppTableID,
    /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppRowset);

/* [local] */ HRESULT STDMETHODCALLTYPE ITableDefinition_AddColumn_Proxy(
    ITableDefinition __RPC_FAR * This,
    /* [in] */ DBID __RPC_FAR *pTableID,
    /* [in] */ DBCOLUMNDESC __RPC_FAR *pColumnDesc,
    /* [out] */ DBID __RPC_FAR *__RPC_FAR *ppColumnID);

/* [call_as] */ HRESULT STDMETHODCALLTYPE ITableDefinition_AddColumn_Stub(
    ITableDefinition __RPC_FAR * This,
    /* [in] */ DBID __RPC_FAR *pTableID,
    /* [in] */ DBCOLUMNDESC __RPC_FAR *pColumnDesc,
    /* [unique][out][in] */ DBID __RPC_FAR *__RPC_FAR *ppColumnID);

/* [local] */ HRESULT STDMETHODCALLTYPE IOpenRowset_OpenRowset_Proxy(
    IOpenRowset __RPC_FAR * This,
    /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
    /* [unique][in] */ DBID __RPC_FAR *pTableID,
    /* [unique][in] */ DBID __RPC_FAR *pIndexID,
    /* [in] */ REFIID riid,
    /* [in] */ ULONG cPropertySets,
    /* [size_is][out][in] */ DBPROPSET __RPC_FAR rgPropertySets[  ],
    /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppRowset);

/* [call_as] */ HRESULT STDMETHODCALLTYPE IOpenRowset_OpenRowset_Stub(
    IOpenRowset __RPC_FAR * This,
    /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
    /* [unique][in] */ DBID __RPC_FAR *pTableID,
    /* [unique][in] */ DBID __RPC_FAR *pIndexID,
    /* [in] */ REFIID riid,
    /* [in] */ ULONG cPropertySets,
    /* [size_is][unique][out][in] */ DBPROPSET __RPC_FAR *rgPropertySets,
    /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppRowset);

/* [local] */ HRESULT STDMETHODCALLTYPE IDBSchemaRowset_GetRowset_Proxy(
    IDBSchemaRowset __RPC_FAR * This,
    /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
    /* [in] */ REFGUID rguidSchema,
    /* [in] */ ULONG cRestrictions,
    /* [size_is][in] */ const VARIANT __RPC_FAR rgRestrictions[  ],
    /* [in] */ REFIID riid,
    /* [in] */ ULONG cPropertySets,
    /* [size_is][out][in] */ DBPROPSET __RPC_FAR rgPropertySets[  ],
    /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppRowset);

/* [call_as] */ HRESULT STDMETHODCALLTYPE IDBSchemaRowset_GetRowset_Stub(
    IDBSchemaRowset __RPC_FAR * This,
    /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
    /* [in] */ REFGUID rguidSchema,
    /* [in] */ ULONG cRestrictions,
    /* [size_is][unique][in] */ const VARIANT __RPC_FAR *rgRestrictions,
    /* [in] */ REFIID riid,
    /* [in] */ ULONG cPropertySets,
    /* [size_is][unique][out][in] */ DBPROPSET __RPC_FAR *rgPropertySets,
    /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppRowset);

/* [local] */ HRESULT STDMETHODCALLTYPE IDBSchemaRowset_GetSchemas_Proxy(
    IDBSchemaRowset __RPC_FAR * This,
    /* [out][in] */ ULONG __RPC_FAR *pcSchemas,
    /* [size_is][size_is][out] */ GUID __RPC_FAR *__RPC_FAR *prgSchemas,
    /* [size_is][size_is][out] */ ULONG __RPC_FAR *__RPC_FAR *prgRestrictionSupport);

/* [call_as] */ HRESULT STDMETHODCALLTYPE IDBSchemaRowset_GetSchemas_Stub(
    IDBSchemaRowset __RPC_FAR * This,
    /* [out][in] */ ULONG __RPC_FAR *pcSchemas,
    /* [size_is][size_is][out] */ GUID __RPC_FAR *__RPC_FAR *prgSchemas,
    /* [size_is][size_is][out] */ ULONG __RPC_FAR *__RPC_FAR *prgRestrictionSupport);

/* [local] */ HRESULT STDMETHODCALLTYPE IGetDataSource_GetDataSource_Proxy(
    IGetDataSource __RPC_FAR * This,
    /* [in] */ REFIID riid,
    /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppDataSource);

/* [call_as] */ HRESULT STDMETHODCALLTYPE IGetDataSource_GetDataSource_Stub(
    IGetDataSource __RPC_FAR * This,
    /* [in] */ REFIID riid,
    /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppDataSource);

/* end of Additional Prototypes */

#ifdef __cplusplus
}
#endif

#endif


OLEDBERR.H

#ifndef _MSADERR_H_
#define _MSADERR_H_
#ifndef FACILITY_WINDOWS
//+---------------------------------------------------------------------------
//
//  Microsoft OLE DB
//  Copyright (C) Microsoft Corporation, 1994 - 1997.
//
//----------------------------------------------------------------------------

//
//  Values are 32 bit values layed out as follows:
//
//   3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
//   1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
//  +-+-+-+-+-+---------------------+-------------------------------+
//  |S|R|C|N|r|    Facility         |               Code            |
//  +-+-+-+-+-+---------------------+-------------------------------+
//
//  where
//
//      S - Severity - indicates success/fail
//
//          0 - Success
//          1 - Fail (COERROR)
//
//      R - reserved portion of the facility code, corresponds to NT's
//              second severity bit.
//
//      C - reserved portion of the facility code, corresponds to NT's
//              C field.
//
//      N - reserved portion of the facility code. Used to indicate a
//              mapped NT status value.
//
//      r - reserved portion of the facility code. Reserved for internal
//              use. Used to indicate HRESULT values that are not status
//              values, but are instead message ids for display strings.
//
//      Facility - is the facility code
//
//      Code - is the facility's status code
//
//
// Define the facility codes
//
#define FACILITY_WINDOWS                 0x8
#define FACILITY_ITF                     0x4

//
// Define the severity codes
//
#define STATUS_SEVERITY_SUCCESS          0x0
#define STATUS_SEVERITY_COERROR          0x2

//
// MessageId: DB_E_BOGUS
//
// MessageText:
//
//  Dummy error - need this error so that mc puts the above defines
//  inside the FACILITY_WINDOWS guard, instead of leaving it empty
//
#define DB_E_BOGUS                       ((HRESULT)0x80040EFFL)

#endif // FACILITY_WINDOWS

//
// Codes 0x0e00-0x0eff are reserved for the OLE DB group of
// interfaces.
//
// Free codes are:
//
//Error:
//-none-
//
//Success:
//0x0ed7
//

//
// OLEDBVER
//OLE DB version number (0x0100); to use version 2.0 features,
//#define OLEDBVER 0x0200 before including this file.
//

// If OLEDBVER is not defined, assume version 1.0
#ifndef OLEDBVER
#define OLEDBVER 0x0100
#endif

//
// MessageId: DB_E_BADACCESSORHANDLE
//
// MessageText:
//
//  Invalid accessor
//
#define DB_E_BADACCESSORHANDLE           ((HRESULT)0x80040E00L)

//
// MessageId: DB_E_ROWLIMITEXCEEDED
//
// MessageText:
//
//  Creating another row would have exceeded the total number of active
//  rows supported by the rowset
//
#define DB_E_ROWLIMITEXCEEDED            ((HRESULT)0x80040E01L)

//
// MessageId: DB_E_READONLYACCESSOR
//
// MessageText:
//
//  Unable to write with a read-only accessor
//
#define DB_E_READONLYACCESSOR            ((HRESULT)0x80040E02L)

//
// MessageId: DB_E_SCHEMAVIOLATION
//
// MessageText:
//
//  Given values violate the database schema
//
#define DB_E_SCHEMAVIOLATION             ((HRESULT)0x80040E03L)

//
// MessageId: DB_E_BADROWHANDLE
//
// MessageText:
//
//  Invalid row handle
//
#define DB_E_BADROWHANDLE                ((HRESULT)0x80040E04L)

//
// MessageId: DB_E_OBJECTOPEN
//
// MessageText:
//
//  An object was open
//
#define DB_E_OBJECTOPEN                  ((HRESULT)0x80040E05L)

//@@@+ V1.5
#if( OLEDBVER >= 0x0150 )
//
// MessageId: DB_E_BADCHAPTER
//
// MessageText:
//
//  Invalid chapter
//
#define DB_E_BADCHAPTER                  ((HRESULT)0x80040E06L)

#endif // OLEDBVER >= 0x0150
//@@@- V1.5

//
// MessageId: DB_E_CANTCONVERTVALUE
//
// MessageText:
//
//  A literal value in the command could not be converted to the
//  correct type due to a reason other than data overflow
//
#define DB_E_CANTCONVERTVALUE            ((HRESULT)0x80040E07L)

//
// MessageId: DB_E_BADBINDINFO
//
// MessageText:
//
//  Invalid binding info
//
#define DB_E_BADBINDINFO                 ((HRESULT)0x80040E08L)

//
// MessageId: DB_SEC_E_PERMISSIONDENIED
//
// MessageText:
//
//  Permission denied
//
#define DB_SEC_E_PERMISSIONDENIED        ((HRESULT)0x80040E09L)

//
// MessageId: DB_E_NOTAREFERENCECOLUMN
//
// MessageText:
//
//  Specified column does not contain bookmarks or chapters
//
#define DB_E_NOTAREFERENCECOLUMN         ((HRESULT)0x80040E0AL)

//@@@+ V2.0
#if( OLEDBVER >= 0x0200 )
//
// MessageId: DB_E_LIMITREJECTED
//
// MessageText:
//
//  Some cost limits were rejected
//
#define DB_E_LIMITREJECTED               ((HRESULT)0x80040E0BL)

#endif // OLEDBVER >= 0x0200
//@@@- V2.0

//
// MessageId: DB_E_NOCOMMAND
//
// MessageText:
//
//  No command has been set for the command object
//
#define DB_E_NOCOMMAND                   ((HRESULT)0x80040E0CL)

//@@@+ V2.0
#if( OLEDBVER >= 0x0200 )
//
// MessageId: DB_E_COSTLIMIT
//
// MessageText:
//
//  Unable to find a query plan within the given cost limit
//
#define DB_E_COSTLIMIT                   ((HRESULT)0x80040E0DL)

#endif // OLEDBVER >= 0x0200
//@@@- V2.0

//
// MessageId: DB_E_BADBOOKMARK
//
// MessageText:
//
//  Invalid bookmark
//
#define DB_E_BADBOOKMARK                 ((HRESULT)0x80040E0EL)

//
// MessageId: DB_E_BADLOCKMODE
//
// MessageText:
//
//  Invalid lock mode
//
#define DB_E_BADLOCKMODE                 ((HRESULT)0x80040E0FL)

//
// MessageId: DB_E_PARAMNOTOPTIONAL
//
// MessageText:
//
//  No value given for one or more required parameters
//
#define DB_E_PARAMNOTOPTIONAL            ((HRESULT)0x80040E10L)

//
// MessageId: DB_E_BADCOLUMNID
//
// MessageText:
//
//  Invalid column ID
//
#define DB_E_BADCOLUMNID                 ((HRESULT)0x80040E11L)

//
// MessageId: DB_E_BADRATIO
//
// MessageText:
//
//  Invalid ratio
//
#define DB_E_BADRATIO                    ((HRESULT)0x80040E12L)

//@@@+ V2.0
#if( OLEDBVER >= 0x0200 )
//
// MessageId: DB_E_BADVALUES
//
// MessageText:
//
//  Invalid value
//
#define DB_E_BADVALUES                   ((HRESULT)0x80040E13L)

#endif // OLEDBVER >= 0x0200
//@@@- V2.0

//
// MessageId: DB_E_ERRORSINCOMMAND
//
// MessageText:
//
//  The command contained one or more errors
//
#define DB_E_ERRORSINCOMMAND             ((HRESULT)0x80040E14L)

//
// MessageId: DB_E_CANTCANCEL
//
// MessageText:
//
//  The executing command cannot be canceled
//
#define DB_E_CANTCANCEL                  ((HRESULT)0x80040E15L)

//
// MessageId: DB_E_DIALECTNOTSUPPORTED
//
// MessageText:
//
//  The provider does not support the specified dialect
//
#define DB_E_DIALECTNOTSUPPORTED         ((HRESULT)0x80040E16L)

//
// MessageId: DB_E_DUPLICATEDATASOURCE
//
// MessageText:
//
//  A data source with the specified name already exists
//
#define DB_E_DUPLICATEDATASOURCE         ((HRESULT)0x80040E17L)

//
// MessageId: DB_E_CANNOTRESTART
//
// MessageText:
//
//  The rowset was built over a live data feed and cannot be restarted
//
#define DB_E_CANNOTRESTART               ((HRESULT)0x80040E18L)

//
// MessageId: DB_E_NOTFOUND
//
// MessageText:
//
//  No key matching the described characteristics could be found within
//  the current range
//
#define DB_E_NOTFOUND                    ((HRESULT)0x80040E19L)

//@@@+ V2.0
#if( OLEDBVER >= 0x0200 )
//
// MessageId: DB_E_CANNOTFREE
//
// MessageText:
//
//  Ownership of this tree has been given to the provider
//
#define DB_E_CANNOTFREE                  ((HRESULT)0x80040E1AL)

#endif // OLEDBVER >= 0x0200
//@@@- V2.0

//
// MessageId: DB_E_NEWLYINSERTED
//
// MessageText:
//
//  The provider is unable to determine identity for newly inserted
//  rows
//
#define DB_E_NEWLYINSERTED               ((HRESULT)0x80040E1BL)

//@@@+ V2.0
#if( OLEDBVER >= 0x0200 )
//
// MessageId: DB_E_GOALREJECTED
//
// MessageText:
//
//  No nonzero weights specified for any goals supported, so goal was
//  rejected; current goal was not changed
//
#define DB_E_GOALREJECTED                ((HRESULT)0x80040E1CL)

#endif // OLEDBVER >= 0x0200
//@@@- V2.0

//
// MessageId: DB_E_UNSUPPORTEDCONVERSION
//
// MessageText:
//
//  Requested conversion is not supported
//
#define DB_E_UNSUPPORTEDCONVERSION       ((HRESULT)0x80040E1DL)

//
// MessageId: DB_E_BADSTARTPOSITION
//
// MessageText:
//
//  lRowsOffset would position you past either end of the rowset,
//  regardless of the cRows value specified; cRowsObtained is 0
//
#define DB_E_BADSTARTPOSITION            ((HRESULT)0x80040E1EL)

//@@@+ V2.0
#if( OLEDBVER >= 0x0200 )
//
// MessageId: DB_E_NOQUERY
//
// MessageText:
//
//  Information was requested for a query, and the query was not set
//
#define DB_E_NOQUERY                     ((HRESULT)0x80040E1FL)

#endif // OLEDBVER >= 0x0200
//@@@- V2.0

//
// MessageId: DB_E_NOTREENTRANT
//
// MessageText:
//
//  Provider called a method from IRowsetNotify in the consumer andthe
//  method has not yet returned
//
#define DB_E_NOTREENTRANT                ((HRESULT)0x80040E20L)

//
// MessageId: DB_E_ERRORSOCCURRED
//
// MessageText:
//
//  Errors occurred
//
#define DB_E_ERRORSOCCURRED              ((HRESULT)0x80040E21L)

//
// MessageId: DB_E_NOAGGREGATION
//
// MessageText:
//
//  A non-NULL controlling IUnknown was specified and the object being
//  created does not support aggregation
//
#define DB_E_NOAGGREGATION               ((HRESULT)0x80040E22L)

//
// MessageId: DB_E_DELETEDROW
//
// MessageText:
//
//  A given HROW referred to a hard- or soft-deleted row
//
#define DB_E_DELETEDROW                  ((HRESULT)0x80040E23L)

//
// MessageId: DB_E_CANTFETCHBACKWARDS
//
// MessageText:
//
//  The rowset does not support fetching backwards
//
#define DB_E_CANTFETCHBACKWARDS          ((HRESULT)0x80040E24L)

//
// MessageId: DB_E_ROWSNOTRELEASED
//
// MessageText:
//
//  All HROWs must be released before new ones can be obtained
//
#define DB_E_ROWSNOTRELEASED             ((HRESULT)0x80040E25L)

//
// MessageId: DB_E_BADSTORAGEFLAG
//
// MessageText:
//
//  One of the specified storage flags was not supported
//
#define DB_E_BADSTORAGEFLAG              ((HRESULT)0x80040E26L)

//@@@+ V1.5
#if( OLEDBVER >= 0x0150 )
//
// MessageId: DB_E_BADCOMPAREOP
//
// MessageText:
//
//  The comparison operator was invalid
//
#define DB_E_BADCOMPAREOP                ((HRESULT)0x80040E27L)

#endif // OLEDBVER >= 0x0150
//@@@- V1.5

//
// MessageId: DB_E_BADSTATUSVALUE
//
// MessageText:
//
//  The specified status flag was neither DBCOLUMNSTATUS_OK nor
//  DBCOLUMNSTATUS_ISNULL
//
#define DB_E_BADSTATUSVALUE              ((HRESULT)0x80040E28L)

//
// MessageId: DB_E_CANTSCROLLBACKWARDS
//
// MessageText:
//
//  The rowset cannot scroll backwards
//
#define DB_E_CANTSCROLLBACKWARDS         ((HRESULT)0x80040E29L)

//@@@+ V2.0
#if( OLEDBVER >= 0x0200 )
//
// MessageId: DB_E_BADREGIONHANDLE
//
// MessageText:
//
//  Invalid region handle
//
#define DB_E_BADREGIONHANDLE             ((HRESULT)0x80040E2AL)

//
// MessageId: DB_E_NONCONTIGUOUSRANGE
//
// MessageText:
//
//  The specified set of rows was not contiguous to or overlapping the
//  rows in the specified watch region
//
#define DB_E_NONCONTIGUOUSRANGE          ((HRESULT)0x80040E2BL)

//
// MessageId: DB_E_INVALIDTRANSITION
//
// MessageText:
//
//  A transition from ALL* to MOVE* or EXTEND* was specified
//
#define DB_E_INVALIDTRANSITION           ((HRESULT)0x80040E2CL)

//
// MessageId: DB_E_NOTASUBREGION
//
// MessageText:
//
//  The specified region is not a proper subregion of the region
//  identified by the given watch region handle
//
#define DB_E_NOTASUBREGION               ((HRESULT)0x80040E2DL)

#endif // OLEDBVER >= 0x0200
//@@@- V2.0

//
// MessageId: DB_E_MULTIPLESTATEMENTS
//
// MessageText:
//
//  The provider does not support multi-statement commands
//
#define DB_E_MULTIPLESTATEMENTS          ((HRESULT)0x80040E2EL)

//
// MessageId: DB_E_INTEGRITYVIOLATION
//
// MessageText:
//
//  A specified value violated the integrity constraints for a column or
//  table
//
#define DB_E_INTEGRITYVIOLATION          ((HRESULT)0x80040E2FL)

//
// MessageId: DB_E_BADTYPENAME
//
// MessageText:
//
//  The given type name was unrecognized
//
#define DB_E_BADTYPENAME                 ((HRESULT)0x80040E30L)

//
// MessageId: DB_E_ABORTLIMITREACHED
//
// MessageText:
//
//  Execution aborted because a resource limit has been reached; no
//  results have been returned
//
#define DB_E_ABORTLIMITREACHED           ((HRESULT)0x80040E31L)

//@@@+ V2.0
#if( OLEDBVER >= 0x0200 )
//
// MessageId: DB_E_ROWSETINCOMMAND
//
// MessageText:
//
//  Cannot clone a command object whose command tree contains a rowset
//  or rowsets
//
#define DB_E_ROWSETINCOMMAND             ((HRESULT)0x80040E32L)

//
// MessageId: DB_E_CANTTRANSLATE
//
// MessageText:
//
//  Cannot represent the current tree as text
//
#define DB_E_CANTTRANSLATE               ((HRESULT)0x80040E33L)

#endif // OLEDBVER >= 0x0200
//@@@- V2.0

//
// MessageId: DB_E_DUPLICATEINDEXID
//
// MessageText:
//
//  The specified index already exists
//
#define DB_E_DUPLICATEINDEXID            ((HRESULT)0x80040E34L)

//
// MessageId: DB_E_NOINDEX
//
// MessageText:
//
//  The specified index does not exist
//
#define DB_E_NOINDEX                     ((HRESULT)0x80040E35L)

//
// MessageId: DB_E_INDEXINUSE
//
// MessageText:
//
//  The specified index was in use
//
#define DB_E_INDEXINUSE                  ((HRESULT)0x80040E36L)

//
// MessageId: DB_E_NOTABLE
//
// MessageText:
//
//  The specified table does not exist
//
#define DB_E_NOTABLE                     ((HRESULT)0x80040E37L)

//
// MessageId: DB_E_CONCURRENCYVIOLATION
//
// MessageText:
//
//  The rowset was using optimistic concurrency and the value of a
//  column has been changed since it was last read
//
#define DB_E_CONCURRENCYVIOLATION        ((HRESULT)0x80040E38L)

//
// MessageId: DB_E_BADCOPY
//
// MessageText:
//
//  Errors were detected during the copy
//
#define DB_E_BADCOPY                     ((HRESULT)0x80040E39L)

//
// MessageId: DB_E_BADPRECISION
//
// MessageText:
//
//  A specified precision was invalid
//
#define DB_E_BADPRECISION                ((HRESULT)0x80040E3AL)

//
// MessageId: DB_E_BADSCALE
//
// MessageText:
//
//  A specified scale was invalid
//
#define DB_E_BADSCALE                    ((HRESULT)0x80040E3BL)

//
// MessageId: DB_E_BADTABLEID
//
// MessageText:
//
//  Invalid table ID
//
#define DB_E_BADTABLEID                  ((HRESULT)0x80040E3CL)

// DB_E_BADID is deprecated; use DB_E_BADTABLEID instead
#define DB_E_BADID DB_E_BADTABLEID

//
// MessageId: DB_E_BADTYPE
//
// MessageText:
//
//  A specified type was invalid
//
#define DB_E_BADTYPE                     ((HRESULT)0x80040E3DL)

//
// MessageId: DB_E_DUPLICATECOLUMNID
//
// MessageText:
//
//  A column ID was occurred more than once in the specification
//
#define DB_E_DUPLICATECOLUMNID           ((HRESULT)0x80040E3EL)

//
// MessageId: DB_E_DUPLICATETABLEID
//
// MessageText:
//
//  The specified table already exists
//
#define DB_E_DUPLICATETABLEID            ((HRESULT)0x80040E3FL)

//
// MessageId: DB_E_TABLEINUSE
//
// MessageText:
//
//  The specified table was in use
//
#define DB_E_TABLEINUSE                  ((HRESULT)0x80040E40L)

//
// MessageId: DB_E_NOLOCALE
//
// MessageText:
//
//  The specified locale ID was not supported
//
#define DB_E_NOLOCALE                    ((HRESULT)0x80040E41L)

//
// MessageId: DB_E_BADRECORDNUM
//
// MessageText:
//
//  The specified record number is invalid
//
#define DB_E_BADRECORDNUM                ((HRESULT)0x80040E42L)

//
// MessageId: DB_E_BOOKMARKSKIPPED
//
// MessageText:
//
//  Although the bookmark was validly formed, no row could be found to
//  match it
//
#define DB_E_BOOKMARKSKIPPED             ((HRESULT)0x80040E43L)

//
// MessageId: DB_E_BADPROPERTYVALUE
//
// MessageText:
//
//  The value of a property was invalid
//
#define DB_E_BADPROPERTYVALUE            ((HRESULT)0x80040E44L)

//
// MessageId: DB_E_INVALID
//
// MessageText:
//
//  The rowset was not chaptered
//
#define DB_E_INVALID                     ((HRESULT)0x80040E45L)

//
// MessageId: DB_E_BADACCESSORFLAGS
//
// MessageText:
//
//  Invalid accessor
//
#define DB_E_BADACCESSORFLAGS            ((HRESULT)0x80040E46L)

//
// MessageId: DB_E_BADSTORAGEFLAGS
//
// MessageText:
//
//  Invalid storage flags
//
#define DB_E_BADSTORAGEFLAGS             ((HRESULT)0x80040E47L)

//
// MessageId: DB_E_BYREFACCESSORNOTSUPPORTED
//
// MessageText:
//
//  By-ref accessors are not supported by this provider
//
#define DB_E_BYREFACCESSORNOTSUPPORTED   ((HRESULT)0x80040E48L)

//
// MessageId: DB_E_NULLACCESSORNOTSUPPORTED
//
// MessageText:
//
//  Null accessors are not supported by this provider
//
#define DB_E_NULLACCESSORNOTSUPPORTED    ((HRESULT)0x80040E49L)

//
// MessageId: DB_E_NOTPREPARED
//
// MessageText:
//
//  The command was not prepared
//
#define DB_E_NOTPREPARED                 ((HRESULT)0x80040E4AL)

//
// MessageId: DB_E_BADACCESSORTYPE
//
// MessageText:
//
//  The specified accessor was not a parameter accessor
//
#define DB_E_BADACCESSORTYPE             ((HRESULT)0x80040E4BL)

//
// MessageId: DB_E_WRITEONLYACCESSOR
//
// MessageText:
//
//  The given accessor was write-only
//
#define DB_E_WRITEONLYACCESSOR           ((HRESULT)0x80040E4CL)

//
// MessageId: DB_SEC_E_AUTH_FAILED
//
// MessageText:
//
//  Authentication failed
//
#define DB_SEC_E_AUTH_FAILED             ((HRESULT)0x80040E4DL)

//
// MessageId: DB_E_CANCELED
//
// MessageText:
//
//  The change was canceled during notification; no columns are changed
//
#define DB_E_CANCELED                    ((HRESULT)0x80040E4EL)

//@@@+ V2.0
#if( OLEDBVER >= 0x0200 )
//
// MessageId: DB_E_CHAPTERNOTRELEASED
//
// MessageText:
//
//  The rowset was single-chaptered and the chapter was not released
//
#define DB_E_CHAPTERNOTRELEASED          ((HRESULT)0x80040E4FL)

#endif // OLEDBVER >= 0x0200
//@@@- V2.0

//
// MessageId: DB_E_BADSOURCEHANDLE
//
// MessageText:
//
//  Invalid source handle
//
#define DB_E_BADSOURCEHANDLE             ((HRESULT)0x80040E50L)

//
// MessageId: DB_E_PARAMUNAVAILABLE
//
// MessageText:
//
//  The provider cannot derive parameter info and SetParameterInfo has
//  not been called
//
#define DB_E_PARAMUNAVAILABLE            ((HRESULT)0x80040E51L)

//
// MessageId: DB_E_ALREADYINITIALIZED
//
// MessageText:
//
//  The data source object is already initialized
//
#define DB_E_ALREADYINITIALIZED          ((HRESULT)0x80040E52L)

//
// MessageId: DB_E_NOTSUPPORTED
//
// MessageText:
//
//  The provider does not support this method
//
#define DB_E_NOTSUPPORTED                ((HRESULT)0x80040E53L)

//
// MessageId: DB_E_MAXPENDCHANGESEXCEEDED
//
// MessageText:
//
//  The number of rows with pending changes has exceeded the set limit
//
#define DB_E_MAXPENDCHANGESEXCEEDED      ((HRESULT)0x80040E54L)

//
// MessageId: DB_E_BADORDINAL
//
// MessageText:
//
//  The specified column did not exist
//
#define DB_E_BADORDINAL                  ((HRESULT)0x80040E55L)

//
// MessageId: DB_E_PENDINGCHANGES
//
// MessageText:
//
//  There are pending changes on a row with a reference count of zero
//
#define DB_E_PENDINGCHANGES              ((HRESULT)0x80040E56L)

//
// MessageId: DB_E_DATAOVERFLOW
//
// MessageText:
//
//  A literal value in the command overflowed the range of the type of
//  the associated column
//
#define DB_E_DATAOVERFLOW                ((HRESULT)0x80040E57L)

//
// MessageId: DB_E_BADHRESULT
//
// MessageText:
//
//  The supplied HRESULT was invalid
//
#define DB_E_BADHRESULT                  ((HRESULT)0x80040E58L)

//
// MessageId: DB_E_BADLOOKUPID
//
// MessageText:
//
//  The supplied LookupID was invalid
//
#define DB_E_BADLOOKUPID                 ((HRESULT)0x80040E59L)

//
// MessageId: DB_E_BADDYNAMICERRORID
//
// MessageText:
//
//  The supplied DynamicErrorID was invalid
//
#define DB_E_BADDYNAMICERRORID           ((HRESULT)0x80040E5AL)

//
// MessageId: DB_E_PENDINGINSERT
//
// MessageText:
//
//  Unable to get visible data for a newly-inserted row that has not
//  yet been updated
//
#define DB_E_PENDINGINSERT               ((HRESULT)0x80040E5BL)

//
// MessageId: DB_E_BADCONVERTFLAG
//
// MessageText:
//
//  Invalid conversion flag
//
#define DB_E_BADCONVERTFLAG              ((HRESULT)0x80040E5CL)

//
// MessageId: DB_E_BADPARAMETERNAME
//
// MessageText:
//
//  The given parameter name was unrecognized
//
#define DB_E_BADPARAMETERNAME            ((HRESULT)0x80040E5DL)

//
// MessageId: DB_E_MULTIPLESTORAGE
//
// MessageText:
//
//  Multiple storage objects can not be open simultaneously
//
#define DB_E_MULTIPLESTORAGE             ((HRESULT)0x80040E5EL)

//
// MessageId: DB_E_CANTFILTER
//
// MessageText:
//
//  The requested filter could not be opened
//
#define DB_E_CANTFILTER                  ((HRESULT)0x80040E5FL)

//
// MessageId: DB_E_CANTORDER
//
// MessageText:
//
//  The requested order could not be opened
//
#define DB_E_CANTORDER                   ((HRESULT)0x80040E60L)

//@@@+ V2.0
#if( OLEDBVER >= 0x0200 )
//
// MessageId: MD_E_BADTUPLE
//
// MessageText:
//
//  Bad tuple
//
#define MD_E_BADTUPLE                    ((HRESULT)0x80040E61L)

//
// MessageId: MD_E_BADCOORDINATE
//
// MessageText:
//
//  Bad coordinate
//
#define MD_E_BADCOORDINATE               ((HRESULT)0x80040E62L)

//
// MessageId: MD_E_INVALIDAXIS
//
// MessageText:
//
//  The given aixs was not valid for this Dataset
//
#define MD_E_INVALIDAXIS                 ((HRESULT)0x80040E63L)

//
// MessageId: MD_E_INVALIDCELLRANGE
//
// MessageText:
//
//  One or more of the given cell ordinals was invalid
//
#define MD_E_INVALIDCELLRANGE            ((HRESULT)0x80040E64L)

#endif // OLEDBVER >= 0x0200
//@@@- V2.0

//
// MessageId: DB_S_ROWLIMITEXCEEDED
//
// MessageText:
//
//  Fetching requested number of rows would have exceeded total number
//  of active rows supported by the rowset
//
#define DB_S_ROWLIMITEXCEEDED            ((HRESULT)0x00040EC0L)

//
// MessageId: DB_S_COLUMNTYPEMISMATCH
//
// MessageText:
//
//  One or more column types are incompatible; conversion errors will
//  occur during copying
//
#define DB_S_COLUMNTYPEMISMATCH          ((HRESULT)0x00040EC1L)

//
// MessageId: DB_S_TYPEINFOOVERRIDDEN
//
// MessageText:
//
//  Parameter type information has been overridden by caller
//
#define DB_S_TYPEINFOOVERRIDDEN          ((HRESULT)0x00040EC2L)

//
// MessageId: DB_S_BOOKMARKSKIPPED
//
// MessageText:
//
//  Skipped bookmark for deleted or non-member row
//
#define DB_S_BOOKMARKSKIPPED             ((HRESULT)0x00040EC3L)

//@@@+ V2.0
#if( OLEDBVER >= 0x0200 )
//
// MessageId: DB_S_ERRORSINTREE
//
// MessageText:
//
//  Errors found in validating tree
//
#define DB_S_ERRORSINTREE                ((HRESULT)0x00040EC4L)

//
// MessageId: DB_S_NONEXTROWSET
//
// MessageText:
//
//  There are no more rowsets
//
#define DB_S_NONEXTROWSET                ((HRESULT)0x00040EC5L)

#endif // OLEDBVER >= 0x0200
//@@@- V2.0

//
// MessageId: DB_S_ENDOFROWSET
//
// MessageText:
//
//  Reached start or end of rowset or chapter
//
#define DB_S_ENDOFROWSET                 ((HRESULT)0x00040EC6L)

//
// MessageId: DB_S_COMMANDREEXECUTED
//
// MessageText:
//
//  The provider re-executed the command
//
#define DB_S_COMMANDREEXECUTED           ((HRESULT)0x00040EC7L)

//
// MessageId: DB_S_BUFFERFULL
//
// MessageText:
//
//  Variable data buffer full
//
#define DB_S_BUFFERFULL                  ((HRESULT)0x00040EC8L)

//
// MessageId: DB_S_NORESULT
//
// MessageText:
//
//  There are no more results
//
#define DB_S_NORESULT                    ((HRESULT)0x00040EC9L)

//
// MessageId: DB_S_CANTRELEASE
//
// MessageText:
//
//  Server cannot release or downgrade a lock until the end of the
//  transaction
//
#define DB_S_CANTRELEASE                 ((HRESULT)0x00040ECAL)

//@@@+ V2.0
#if( OLEDBVER >= 0x0200 )
//
// MessageId: DB_S_GOALCHANGED
//
// MessageText:
//
//  Specified weight was not supported or exceeded the supported limit
//  and was set to 0 or the supported limit
//
#define DB_S_GOALCHANGED                 ((HRESULT)0x00040ECBL)

#endif // OLEDBVER >= 0x0200
//@@@- V2.0

//@@@+ V1.5
#if( OLEDBVER >= 0x0150 )
//
// MessageId: DB_S_UNWANTEDOPERATION
//
// MessageText:
//
//  Consumer is uninterested in receiving further notification calls for
//  this reason
//
#define DB_S_UNWANTEDOPERATION           ((HRESULT)0x00040ECCL)

#endif // OLEDBVER >= 0x0150
//@@@- V1.5

//
// MessageId: DB_S_DIALECTIGNORED
//
// MessageText:
//
//  Input dialect was ignored and text was returned in different
//  dialect
//
#define DB_S_DIALECTIGNORED              ((HRESULT)0x00040ECDL)

//
// MessageId: DB_S_UNWANTEDPHASE
//
// MessageText:
//
//  Consumer is uninterested in receiving further notification calls for
//  this phase
//
#define DB_S_UNWANTEDPHASE               ((HRESULT)0x00040ECEL)

//
// MessageId: DB_S_UNWANTEDREASON
//
// MessageText:
//
//  Consumer is uninterested in receiving further notification calls for
//  this reason
//
#define DB_S_UNWANTEDREASON              ((HRESULT)0x00040ECFL)

//@@@+ V1.5
#if( OLEDBVER >= 0x0150 )
//
// MessageId: DB_S_ASYNCHRONOUS
//
// MessageText:
//
//  The operation is being processed asynchronously
//
#define DB_S_ASYNCHRONOUS                ((HRESULT)0x00040ED0L)

#endif // OLEDBVER >= 0x0150
//@@@- V1.5

//
// MessageId: DB_S_COLUMNSCHANGED
//
// MessageText:
//
//  In order to reposition to the start of the rowset, the provider had
//  to reexecute the query; either the order of the columns changed or
//  columns were added to or removed from the rowset
//
#define DB_S_COLUMNSCHANGED              ((HRESULT)0x00040ED1L)

//
// MessageId: DB_S_ERRORSRETURNED
//
// MessageText:
//
//  The method had some errors; errors have been returned in the error
//  array
//
#define DB_S_ERRORSRETURNED              ((HRESULT)0x00040ED2L)

//
// MessageId: DB_S_BADROWHANDLE
//
// MessageText:
//
//  Invalid row handle
//
#define DB_S_BADROWHANDLE                ((HRESULT)0x00040ED3L)

//
// MessageId: DB_S_DELETEDROW
//
// MessageText:
//
//  A given HROW referred to a hard-deleted row
//
#define DB_S_DELETEDROW                  ((HRESULT)0x00040ED4L)

//@@@+ V2.0
#if( OLEDBVER >= 0x0200 )
//
// MessageId: DB_S_TOOMANYCHANGES
//
// MessageText:
//
//  The provider was unable to keep track of all the changes; the client
//  must refetch the data associated with the watch region using another
//  method
//
#define DB_S_TOOMANYCHANGES              ((HRESULT)0x00040ED5L)

#endif // OLEDBVER >= 0x0200
//@@@- V2.0

//
// MessageId: DB_S_STOPLIMITREACHED
//
// MessageText:
//
//  Execution stopped because a resource limit has been reached; results
//  obtained so far have been returned but execution cannot be resumed
//
#define DB_S_STOPLIMITREACHED            ((HRESULT)0x00040ED6L)

//
// MessageId: DB_S_LOCKUPGRADED

//
// MessageText:
//
//  A lock was upgraded from the value specified
//
#define DB_S_LOCKUPGRADED                ((HRESULT)0x00040ED8L)

//
// MessageId: DB_S_PROPERTIESCHANGED
//
// MessageText:
//
//  One or more properties were changed as allowed by provider
//
#define DB_S_PROPERTIESCHANGED           ((HRESULT)0x00040ED9L)

//
// MessageId: DB_S_ERRORSOCCURRED
//
// MessageText:
//
//  Errors occurred
//
#define DB_S_ERRORSOCCURRED              ((HRESULT)0x00040EDAL)

//
// MessageId: DB_S_PARAMUNAVAILABLE
//
// MessageText:
//
//  A specified parameter was invalid
//
#define DB_S_PARAMUNAVAILABLE            ((HRESULT)0x00040EDBL)

//
// MessageId: DB_S_MULTIPLECHANGES
//
// MessageText:
//
//  Updating this row caused more than one row to be updated in the
//  data source
//
#define DB_S_MULTIPLECHANGES             ((HRESULT)0x00040EDCL)

#endif // _OLEDBERR_H_


TRANSACT.H

//.-------------------------------------------------------------------------
//.
//.  Microsoft Windows
//.  Copyright (C) Microsoft Corporation, 1995.
//.
//.  File: transact.idl
//.
//.  Contents: The basic transaction interfaces and types.
//.
//.--------------------------------------------------------------------------

/* File created by MIDL compiler version 2.00.0102 */
/* at Tue Nov 21 16:54:32 1995
 */
//@@MIDL_FILE_HEADING(  )
#include "rpc.h"
#include "rpcndr.h"
#ifndef COM_NO_WINDOWS_H
#include "windows.h"
#include "ole2.h"
#endif /*COM_NO_WINDOWS_H*/

#ifndef __transact_h__
#define __transact_h__

#ifdef __cplusplus
extern "C"{
#endif

/* Forward Declarations */

#ifndef __ITransaction_FWD_DEFINED__
#define __ITransaction_FWD_DEFINED__
typedef interface ITransaction ITransaction;
#endif /* __ITransaction_FWD_DEFINED__ */

#ifndef __ITransactionDispenser_FWD_DEFINED__
#define __ITransactionDispenser_FWD_DEFINED__
typedef interface ITransactionDispenser ITransactionDispenser;
#endif /* __ITransactionDispenser_FWD_DEFINED__ */

#ifndef __ITransactionOptions_FWD_DEFINED__
#define __ITransactionOptions_FWD_DEFINED__
typedef interface ITransactionOptions ITransactionOptions;
#endif /* __ITransactionOptions_FWD_DEFINED__ */

#ifndef __ITransactionOutcomeEvents_FWD_DEFINED__
#define __ITransactionOutcomeEvents_FWD_DEFINED__
typedef interface ITransactionOutcomeEvents ITransactionOutcomeEvents;
#endif /* __ITransactionOutcomeEvents_FWD_DEFINED__ */

#ifndef __ITransactionCompletionEvents_FWD_DEFINED__
#define __ITransactionCompletionEvents_FWD_DEFINED__
typedef interface ITransactionCompletionEvents ITransactionCompletionEvents;
#endif /* __ITransactionCompletionEvents_FWD_DEFINED__ */

/* header files for imported files */

void __RPC_FAR * __RPC_USER MIDL_user_allocate(size_t);
void __RPC_USER MIDL_user_free( void __RPC_FAR * );

/****************************************
 * Generated header for interface: __MIDL__intf_0000
 * at Tue Nov 21 16:54:32 1995
 * using MIDL 2.00.0102
 ****************************************/
/* [local] */

/* size is 0 */

/* size is 0 */

/* size is 0 */

/* size is 0 */

/* size is 0 */

extern RPC_IF_HANDLE __MIDL__intf_0000_v0_0_c_ifspec;
extern RPC_IF_HANDLE __MIDL__intf_0000_v0_0_s_ifspec;

#ifndef __BasicTransactionTypes_INTERFACE_DEFINED__
#define __BasicTransactionTypes_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: BasicTransactionTypes
 * at Tue Nov 21 16:54:32 1995
 * using MIDL 2.00.0102
 ****************************************/
/* [unique][local] */

/* size is 16 */
typedef struct  BOID
    {
    BYTE rgb[ 16 ];
    }BOID;

#define BOID_NULL (*((BOID*)(&IID_NULL)))

#define MAX_TRAN_DESC 40// used by XACTOPT via midl - see transact.idl
/* size is 16 */
typedef BOID XACTUOW;

/* size is 4 */
typedef LONG ISOLEVEL;

#if defined(_WIN32)
/* size is 2 */
typedef
enum ISOLATIONLEVEL
    {ISOLATIONLEVEL_UNSPECIFIED= 0xffffffff,
ISOLATIONLEVEL_CHAOS= 0x10,
ISOLATIONLEVEL_READUNCOMMITTED= 0x100,
ISOLATIONLEVEL_BROWSE= 0x100,
ISOLATIONLEVEL_CURSORSTABILITY= 0x1000,
ISOLATIONLEVEL_READCOMMITTED= 0x1000,
ISOLATIONLEVEL_REPEATABLEREAD= 0x10000,
ISOLATIONLEVEL_SERIALIZABLE= 0x100000,
ISOLATIONLEVEL_ISOLATED= 0x100000
    }ISOLATIONLEVEL;

#else
#define ISOLATIONLEVEL_UNSPECIFIED      0xFFFFFFFF
#define ISOLATIONLEVEL_CHAOS            0x00000010
#define ISOLATIONLEVEL_READUNCOMMITTED  0x00000100
#define ISOLATIONLEVEL_BROWSE           0x00000100
#define ISOLATIONLEVEL_CURSORSTABILITY  0x00001000
#define ISOLATIONLEVEL_READCOMMITTED    0x00001000
#define ISOLATIONLEVEL_REPEATABLEREAD   0x00010000
#define ISOLATIONLEVEL_SERIALIZABLE     0x00100000
#define ISOLATIONLEVEL_ISOLATED         0x00100000
#endif
/* size is 40 */
typedef struct  XACTTRANSINFO
    {
    XACTUOW uow;
    ISOLEVEL isoLevel;
    ULONG isoFlags;
    DWORD grfTCSupported;
    DWORD grfRMSupported;
    DWORD grfTCSupportedRetaining;
    DWORD grfRMSupportedRetaining;
    }XACTTRANSINFO;

/* size is 36 */
typedef struct  XACTSTATS
    {
    ULONG cOpen;
    ULONG cCommitting;
    ULONG cCommitted;
    ULONG cAborting;
    ULONG cAborted;
    ULONG cInDoubt;
    ULONG cHeuristicDecision;
    FILETIME timeTransactionsUp;
    }XACTSTATS;

/* size is 2 */
typedef
enum ISOFLAG
    {ISOFLAG_RETAIN_COMMIT_DC= 1,
ISOFLAG_RETAIN_COMMIT= 2,
ISOFLAG_RETAIN_COMMIT_NO= 3,
ISOFLAG_RETAIN_ABORT_DC= 4,
ISOFLAG_RETAIN_ABORT= 8,
ISOFLAG_RETAIN_ABORT_NO= 12,
ISOFLAG_RETAIN_DONTCARE= ISOFLAG_RETAIN_COMMIT_DC | ISOFLAG_RETAIN_ABORT_DC,
ISOFLAG_RETAIN_BOTH= ISOFLAG_RETAIN_COMMIT | ISOFLAG_RETAIN_ABORT,
ISOFLAG_RETAIN_NONE= ISOFLAG_RETAIN_COMMIT_NO | ISOFLAG_RETAIN_ABORT_NO,
ISOFLAG_OPTIMISTIC= 16
    }ISOFLAG;

/* size is 2 */
typedef
enum XACTTC
    {XACTTC_SYNC_PHASEONE= 1,
XACTTC_SYNC_PHASETWO= 2,
XACTTC_SYNC= 2,
XACTTC_ASYNC_PHASEONE= 4
    }XACTTC;

/* size is 2 */
typedef
enum XACTRM
    {XACTRM_OPTIMISTICLASTWINS= 1,
XACTRM_NOREADONLYPREPARES= 2
    }XACTRM;

/* size is 2 */
typedef
enum XACTCONST
    {XACTCONST_TIMEOUTINFINITE= 0
    }XACTCONST;

/* size is 2 */
typedef
enum XACTHEURISTIC
    {XACTHEURISTIC_ABORT= 1,
XACTHEURISTIC_COMMIT= 2,
XACTHEURISTIC_DAMAGE= 3,
XACTHEURISTIC_DANGER= 4
    }XACTHEURISTIC;

#if defined(_WIN32)
/* size is 2 */
typedef
enum XACTSTAT
    {XACTSTAT_NONE= 0,
XACTSTAT_OPENNORMAL= 0x1,
XACTSTAT_OPENREFUSED= 0x2,
XACTSTAT_PREPARING= 0x4,
XACTSTAT_PREPARED= 0x8,
XACTSTAT_PREPARERETAINING= 0x10,
XACTSTAT_PREPARERETAINED= 0x20,
XACTSTAT_COMMITTING= 0x40,
XACTSTAT_COMMITRETAINING= 0x80,
XACTSTAT_ABORTING= 0x100,
XACTSTAT_ABORTED= 0x200,
XACTSTAT_COMMITTED= 0x400,
XACTSTAT_HEURISTIC_ABORT= 0x800,
XACTSTAT_HEURISTIC_COMMIT= 0x1000,
XACTSTAT_HEURISTIC_DAMAGE= 0x2000,
XACTSTAT_HEURISTIC_DANGER= 0x4000,
XACTSTAT_FORCED_ABORT= 0x8000,
XACTSTAT_FORCED_COMMIT= 0x10000,
XACTSTAT_INDOUBT= 0x20000,
XACTSTAT_CLOSED= 0x40000,
XACTSTAT_OPEN= 0x3,
XACTSTAT_NOTPREPARED= 0x7ffc3,
XACTSTAT_ALL= 0x7ffff
    }XACTSTAT;

#else
#define XACTSTAT_NONE               0x00000000
#define XACTSTAT_OPENNORMAL         0x00000001
#define XACTSTAT_OPENREFUSED        0x00000002
#define XACTSTAT_PREPARING          0x00000004
#define XACTSTAT_PREPARED           0x00000008
#define XACTSTAT_PREPARERETAINING   0x00000010
#define XACTSTAT_PREPARERETAINED    0x00000020
#define XACTSTAT_COMMITTING         0x00000040
#define XACTSTAT_COMMITRETAINING    0x00000080
#define XACTSTAT_ABORTING           0x00000100
#define XACTSTAT_ABORTED            0x00000200
#define XACTSTAT_COMMITTED          0x00000400
#define XACTSTAT_HEURISTIC_ABORT    0x00000800
#define XACTSTAT_HEURISTIC_COMMIT   0x00001000
#define XACTSTAT_HEURISTIC_DAMAGE   0x00002000
#define XACTSTAT_HEURISTIC_DANGER   0x00004000
#define XACTSTAT_FORCED_ABORT       0x00008000
#define XACTSTAT_FORCED_COMMIT      0x00010000
#define XACTSTAT_INDOUBT            0x00020000
#define XACTSTAT_CLOSED             0x00040000
#define XACTSTAT_OPEN               0x00000003
#define XACTSTAT_NOTPREPARED        0x0007FFC3
#define XACTSTAT_ALL                0x0007FFFF
#endif
/* size is 44 */
typedef struct  XACTOPT
    {
    ULONG ulTimeout;
    unsigned char szDescription[ 40 ];
    }XACTOPT;

extern RPC_IF_HANDLE BasicTransactionTypes_v0_0_c_ifspec;
extern RPC_IF_HANDLE BasicTransactionTypes_v0_0_s_ifspec;
#endif /* __BasicTransactionTypes_INTERFACE_DEFINED__ */

#ifndef __ITransaction_INTERFACE_DEFINED__
#define __ITransaction_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: ITransaction
 * at Tue Nov 21 16:54:32 1995
 * using MIDL 2.00.0102
 ****************************************/
/* [unique][uuid][object] */

EXTERN_C const IID IID_ITransaction;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface ITransaction : public IUnknown
    {
    public:
        virtual HRESULT __stdcall Commit(
            /* [in] */ BOOL fRetaining,
            /* [in] */ DWORD grfTC,
            /* [in] */ DWORD grfRM) = 0;

        virtual HRESULT __stdcall Abort(
            /* [in] */ BOID __RPC_FAR *pboidReason,
            /* [in] */ BOOL fRetaining,
            /* [in] */ BOOL fAsync) = 0;

        virtual HRESULT __stdcall GetTransactionInfo(
            /* [out] */ XACTTRANSINFO __RPC_FAR *pinfo) = 0;

    };

#else /* C style interface */

    typedef struct ITransactionVtbl
    {

        HRESULT ( __stdcall __RPC_FAR *QueryInterface )(
            ITransaction __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( __stdcall __RPC_FAR *AddRef )(
            ITransaction __RPC_FAR * This);

        ULONG ( __stdcall __RPC_FAR *Release )(
            ITransaction __RPC_FAR * This);

        HRESULT ( __stdcall __RPC_FAR *Commit )(
            ITransaction __RPC_FAR * This,
            /* [in] */ BOOL fRetaining,
            /* [in] */ DWORD grfTC,
            /* [in] */ DWORD grfRM);

        HRESULT ( __stdcall __RPC_FAR *Abort )(
            ITransaction __RPC_FAR * This,
            /* [in] */ BOID __RPC_FAR *pboidReason,
            /* [in] */ BOOL fRetaining,
            /* [in] */ BOOL fAsync);

        HRESULT ( __stdcall __RPC_FAR *GetTransactionInfo )(
            ITransaction __RPC_FAR * This,
            /* [out] */ XACTTRANSINFO __RPC_FAR *pinfo);

    } ITransactionVtbl;

    interface ITransaction
    {
        CONST_VTBL struct ITransactionVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define ITransaction_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define ITransaction_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define ITransaction_Release(This)\
    (This)->lpVtbl -> Release(This)

#define ITransaction_Commit(This,fRetaining,grfTC,grfRM)\
    (This)->lpVtbl -> Commit(This,fRetaining,grfTC,grfRM)

#define ITransaction_Abort(This,pboidReason,fRetaining,fAsync)\
    (This)->lpVtbl -> Abort(This,pboidReason,fRetaining,fAsync)

#define ITransaction_GetTransactionInfo(This,pinfo)\
    (This)->lpVtbl -> GetTransactionInfo(This,pinfo)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT __stdcall ITransaction_Commit_Proxy(
    ITransaction __RPC_FAR * This,
    /* [in] */ BOOL fRetaining,
    /* [in] */ DWORD grfTC,
    /* [in] */ DWORD grfRM);

void __RPC_STUB ITransaction_Commit_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT __stdcall ITransaction_Abort_Proxy(
    ITransaction __RPC_FAR * This,
    /* [in] */ BOID __RPC_FAR *pboidReason,
    /* [in] */ BOOL fRetaining,
    /* [in] */ BOOL fAsync);

void __RPC_STUB ITransaction_Abort_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT __stdcall ITransaction_GetTransactionInfo_Proxy(
    ITransaction __RPC_FAR * This,
    /* [out] */ XACTTRANSINFO __RPC_FAR *pinfo);

void __RPC_STUB ITransaction_GetTransactionInfo_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __ITransaction_INTERFACE_DEFINED__ */

#ifndef __ITransactionDispenser_INTERFACE_DEFINED__
#define __ITransactionDispenser_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: ITransactionDispenser
 * at Tue Nov 21 16:54:32 1995
 * using MIDL 2.00.0102
 ****************************************/
/* [unique][uuid][object] */

EXTERN_C const IID IID_ITransactionDispenser;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface ITransactionDispenser : public IUnknown
    {
    public:
        virtual HRESULT __stdcall GetOptionsObject(
            /* [out] */ ITransactionOptions __RPC_FAR *__RPC_FAR *ppOptions) = 0;

        virtual HRESULT __stdcall BeginTransaction(
            /* [in] */ IUnknown __RPC_FAR *punkOuter,
            /* [in] */ ISOLEVEL isoLevel,
            /* [in] */ ULONG isoFlags,
            /* [in] */ ITransactionOptions __RPC_FAR *pOptions,
            /* [out] */ ITransaction __RPC_FAR *__RPC_FAR *ppTransaction) = 0;

    };

#else /* C style interface */

    typedef struct ITransactionDispenserVtbl
    {

        HRESULT ( __stdcall __RPC_FAR *QueryInterface )(
            ITransactionDispenser __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( __stdcall __RPC_FAR *AddRef )(
            ITransactionDispenser __RPC_FAR * This);

        ULONG ( __stdcall __RPC_FAR *Release )(
            ITransactionDispenser __RPC_FAR * This);

        HRESULT ( __stdcall __RPC_FAR *GetOptionsObject )(
            ITransactionDispenser __RPC_FAR * This,
            /* [out] */ ITransactionOptions __RPC_FAR *__RPC_FAR *ppOptions);

        HRESULT ( __stdcall __RPC_FAR *BeginTransaction )(
            ITransactionDispenser __RPC_FAR * This,
            /* [in] */ IUnknown __RPC_FAR *punkOuter,
            /* [in] */ ISOLEVEL isoLevel,
            /* [in] */ ULONG isoFlags,
            /* [in] */ ITransactionOptions __RPC_FAR *pOptions,
            /* [out] */ ITransaction __RPC_FAR *__RPC_FAR *ppTransaction);

    } ITransactionDispenserVtbl;

    interface ITransactionDispenser
    {
        CONST_VTBL struct ITransactionDispenserVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define ITransactionDispenser_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define ITransactionDispenser_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define ITransactionDispenser_Release(This)\
    (This)->lpVtbl -> Release(This)

#define ITransactionDispenser_GetOptionsObject(This,ppOptions)\
    (This)->lpVtbl -> GetOptionsObject(This,ppOptions)

#define ITransactionDispenser_BeginTransaction(This,punkOuter,isoLevel,isoFlags,pOptions,ppTransaction)\
    (This)->lpVtbl -> BeginTransaction(This,punkOuter,isoLevel,isoFlags,pOptions,ppTransaction)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT __stdcall ITransactionDispenser_GetOptionsObject_Proxy(
    ITransactionDispenser __RPC_FAR * This,
    /* [out] */ ITransactionOptions __RPC_FAR *__RPC_FAR *ppOptions);

void __RPC_STUB ITransactionDispenser_GetOptionsObject_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT __stdcall ITransactionDispenser_BeginTransaction_Proxy(
    ITransactionDispenser __RPC_FAR * This,
    /* [in] */ IUnknown __RPC_FAR *punkOuter,
    /* [in] */ ISOLEVEL isoLevel,
    /* [in] */ ULONG isoFlags,
    /* [in] */ ITransactionOptions __RPC_FAR *pOptions,
    /* [out] */ ITransaction __RPC_FAR *__RPC_FAR *ppTransaction);

void __RPC_STUB ITransactionDispenser_BeginTransaction_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __ITransactionDispenser_INTERFACE_DEFINED__ */

#ifndef __ITransactionOptions_INTERFACE_DEFINED__
#define __ITransactionOptions_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: ITransactionOptions
 * at Tue Nov 21 16:54:32 1995
 * using MIDL 2.00.0102
 ****************************************/
/* [unique][uuid][object] */

EXTERN_C const IID IID_ITransactionOptions;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface ITransactionOptions : public IUnknown
    {
    public:
        virtual HRESULT __stdcall SetOptions(
            /* [in] */ XACTOPT __RPC_FAR *pOptions) = 0;

        virtual HRESULT __stdcall GetOptions(
            /* [out][in] */ XACTOPT __RPC_FAR *pOptions) = 0;

    };

#else /* C style interface */

    typedef struct ITransactionOptionsVtbl
    {

        HRESULT ( __stdcall __RPC_FAR *QueryInterface )(
            ITransactionOptions __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( __stdcall __RPC_FAR *AddRef )(
            ITransactionOptions __RPC_FAR * This);

        ULONG ( __stdcall __RPC_FAR *Release )(
            ITransactionOptions __RPC_FAR * This);

        HRESULT ( __stdcall __RPC_FAR *SetOptions )(
            ITransactionOptions __RPC_FAR * This,
            /* [in] */ XACTOPT __RPC_FAR *pOptions);

        HRESULT ( __stdcall __RPC_FAR *GetOptions )(
            ITransactionOptions __RPC_FAR * This,
            /* [out][in] */ XACTOPT __RPC_FAR *pOptions);

    } ITransactionOptionsVtbl;

    interface ITransactionOptions
    {
        CONST_VTBL struct ITransactionOptionsVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define ITransactionOptions_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define ITransactionOptions_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define ITransactionOptions_Release(This)\
    (This)->lpVtbl -> Release(This)

#define ITransactionOptions_SetOptions(This,pOptions)\
    (This)->lpVtbl -> SetOptions(This,pOptions)

#define ITransactionOptions_GetOptions(This,pOptions)\
    (This)->lpVtbl -> GetOptions(This,pOptions)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT __stdcall ITransactionOptions_SetOptions_Proxy(
    ITransactionOptions __RPC_FAR * This,
    /* [in] */ XACTOPT __RPC_FAR *pOptions);

void __RPC_STUB ITransactionOptions_SetOptions_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT __stdcall ITransactionOptions_GetOptions_Proxy(
    ITransactionOptions __RPC_FAR * This,
    /* [out][in] */ XACTOPT __RPC_FAR *pOptions);

void __RPC_STUB ITransactionOptions_GetOptions_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __ITransactionOptions_INTERFACE_DEFINED__ */

#ifndef __ITransactionOutcomeEvents_INTERFACE_DEFINED__
#define __ITransactionOutcomeEvents_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: ITransactionOutcomeEvents
 * at Tue Nov 21 16:54:32 1995
 * using MIDL 2.00.0102
 ****************************************/
/* [unique][uuid][object] */

EXTERN_C const IID IID_ITransactionOutcomeEvents;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface ITransactionOutcomeEvents : public IUnknown
    {
    public:
        virtual HRESULT __stdcall Committed(
            /* [in] */ BOOL fRetaining,
            /* [in] */ XACTUOW __RPC_FAR *pNewUOW,
            /* [in] */ HRESULT hr) = 0;

        virtual HRESULT __stdcall Aborted(
            /* [in] */ BOID __RPC_FAR *pboidReason,
            /* [in] */ BOOL fRetaining,
            /* [in] */ XACTUOW __RPC_FAR *pNewUOW,
            /* [in] */ HRESULT hr) = 0;

        virtual HRESULT __stdcall HeuristicDecision(
            /* [in] */ DWORD dwDecision,
            /* [in] */ BOID __RPC_FAR *pboidReason,
            /* [in] */ HRESULT hr) = 0;

        virtual HRESULT __stdcall Indoubt( void) = 0;

    };

#else /* C style interface */

    typedef struct ITransactionOutcomeEventsVtbl
    {

        HRESULT ( __stdcall __RPC_FAR *QueryInterface )(
            ITransactionOutcomeEvents __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( __stdcall __RPC_FAR *AddRef )(
            ITransactionOutcomeEvents __RPC_FAR * This);

        ULONG ( __stdcall __RPC_FAR *Release )(
            ITransactionOutcomeEvents __RPC_FAR * This);

        HRESULT ( __stdcall __RPC_FAR *Committed )(
            ITransactionOutcomeEvents __RPC_FAR * This,
            /* [in] */ BOOL fRetaining,
            /* [in] */ XACTUOW __RPC_FAR *pNewUOW,
            /* [in] */ HRESULT hr);

        HRESULT ( __stdcall __RPC_FAR *Aborted )(
            ITransactionOutcomeEvents __RPC_FAR * This,
            /* [in] */ BOID __RPC_FAR *pboidReason,
            /* [in] */ BOOL fRetaining,
            /* [in] */ XACTUOW __RPC_FAR *pNewUOW,
            /* [in] */ HRESULT hr);

        HRESULT ( __stdcall __RPC_FAR *HeuristicDecision )(
            ITransactionOutcomeEvents __RPC_FAR * This,
            /* [in] */ DWORD dwDecision,
            /* [in] */ BOID __RPC_FAR *pboidReason,
            /* [in] */ HRESULT hr);

        HRESULT ( __stdcall __RPC_FAR *Indoubt )(
            ITransactionOutcomeEvents __RPC_FAR * This);

    } ITransactionOutcomeEventsVtbl;

    interface ITransactionOutcomeEvents
    {
        CONST_VTBL struct ITransactionOutcomeEventsVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define ITransactionOutcomeEvents_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define ITransactionOutcomeEvents_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define ITransactionOutcomeEvents_Release(This)\
    (This)->lpVtbl -> Release(This)

#define ITransactionOutcomeEvents_Committed(This,fRetaining,pNewUOW,hr)\
    (This)->lpVtbl -> Committed(This,fRetaining,pNewUOW,hr)

#define ITransactionOutcomeEvents_Aborted(This,pboidReason,fRetaining,pNewUOW,hr)\
    (This)->lpVtbl -> Aborted(This,pboidReason,fRetaining,pNewUOW,hr)

#define ITransactionOutcomeEvents_HeuristicDecision(This,dwDecision,pboidReason,hr)\
    (This)->lpVtbl -> HeuristicDecision(This,dwDecision,pboidReason,hr)

#define ITransactionOutcomeEvents_Indoubt(This)\
    (This)->lpVtbl -> Indoubt(This)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT __stdcall ITransactionOutcomeEvents_Committed_Proxy(
    ITransactionOutcomeEvents __RPC_FAR * This,
    /* [in] */ BOOL fRetaining,
    /* [in] */ XACTUOW __RPC_FAR *pNewUOW,
    /* [in] */ HRESULT hr);

void __RPC_STUB ITransactionOutcomeEvents_Committed_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT __stdcall ITransactionOutcomeEvents_Aborted_Proxy(
    ITransactionOutcomeEvents __RPC_FAR * This,
    /* [in] */ BOID __RPC_FAR *pboidReason,
    /* [in] */ BOOL fRetaining,
    /* [in] */ XACTUOW __RPC_FAR *pNewUOW,
    /* [in] */ HRESULT hr);

void __RPC_STUB ITransactionOutcomeEvents_Aborted_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT __stdcall ITransactionOutcomeEvents_HeuristicDecision_Proxy(
    ITransactionOutcomeEvents __RPC_FAR * This,
    /* [in] */ DWORD dwDecision,
    /* [in] */ BOID __RPC_FAR *pboidReason,
    /* [in] */ HRESULT hr);

void __RPC_STUB ITransactionOutcomeEvents_HeuristicDecision_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT __stdcall ITransactionOutcomeEvents_Indoubt_Proxy(
    ITransactionOutcomeEvents __RPC_FAR * This);

void __RPC_STUB ITransactionOutcomeEvents_Indoubt_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __ITransactionOutcomeEvents_INTERFACE_DEFINED__ */

#ifndef __ITransactionCompletionEvents_INTERFACE_DEFINED__
#define __ITransactionCompletionEvents_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: ITransactionCompletionEvents
 * at Tue Nov 21 16:54:32 1995
 * using MIDL 2.00.0102
 ****************************************/
/* [unique][uuid][object] */

EXTERN_C const IID IID_ITransactionCompletionEvents;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface ITransactionCompletionEvents : public IUnknown
    {
    public:
        virtual HRESULT __stdcall Committed(
            /* [in] */ BOOL fRetaining,
            /* [in] */ XACTUOW __RPC_FAR *pNewUOW,
            /* [in] */ HRESULT hr) = 0;

        virtual HRESULT __stdcall Aborted(
            /* [in] */ BOID __RPC_FAR *pboidReason,
            /* [in] */ BOOL fRetaining,
            /* [in] */ XACTUOW __RPC_FAR *pNewUOW,
            /* [in] */ HRESULT hr) = 0;

        virtual HRESULT __stdcall HeuristicDecision(
            /* [in] */ DWORD dwDecision,
            /* [in] */ BOID __RPC_FAR *pboidReason,
            /* [in] */ HRESULT hr) = 0;

        virtual HRESULT __stdcall Indoubt( void) = 0;

    };

#else /* C style interface */

    typedef struct ITransactionCompletionEventsVtbl
    {

        HRESULT ( __stdcall __RPC_FAR *QueryInterface )(
            ITransactionCompletionEvents __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( __stdcall __RPC_FAR *AddRef )(
            ITransactionCompletionEvents __RPC_FAR * This);

        ULONG ( __stdcall __RPC_FAR *Release )(
            ITransactionCompletionEvents __RPC_FAR * This);

        HRESULT ( __stdcall __RPC_FAR *Committed )(
            ITransactionCompletionEvents __RPC_FAR * This,
            /* [in] */ BOOL fRetaining,
            /* [in] */ XACTUOW __RPC_FAR *pNewUOW,
            /* [in] */ HRESULT hr);

        HRESULT ( __stdcall __RPC_FAR *Aborted )(
            ITransactionCompletionEvents __RPC_FAR * This,
            /* [in] */ BOID __RPC_FAR *pboidReason,
            /* [in] */ BOOL fRetaining,
            /* [in] */ XACTUOW __RPC_FAR *pNewUOW,
            /* [in] */ HRESULT hr);

        HRESULT ( __stdcall __RPC_FAR *HeuristicDecision )(
            ITransactionCompletionEvents __RPC_FAR * This,
            /* [in] */ DWORD dwDecision,
            /* [in] */ BOID __RPC_FAR *pboidReason,
            /* [in] */ HRESULT hr);

        HRESULT ( __stdcall __RPC_FAR *Indoubt )(
            ITransactionCompletionEvents __RPC_FAR * This);

    } ITransactionCompletionEventsVtbl;

    interface ITransactionCompletionEvents
    {
        CONST_VTBL struct ITransactionCompletionEventsVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define ITransactionCompletionEvents_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define ITransactionCompletionEvents_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define ITransactionCompletionEvents_Release(This)\
    (This)->lpVtbl -> Release(This)

#define ITransactionCompletionEvents_Committed(This,fRetaining,pNewUOW,hr)\
    (This)->lpVtbl -> Committed(This,fRetaining,pNewUOW,hr)

#define ITransactionCompletionEvents_Aborted(This,pboidReason,fRetaining,pNewUOW,hr)\
    (This)->lpVtbl -> Aborted(This,pboidReason,fRetaining,pNewUOW,hr)

#define ITransactionCompletionEvents_HeuristicDecision(This,dwDecision,pboidReason,hr)\
    (This)->lpVtbl -> HeuristicDecision(This,dwDecision,pboidReason,hr)

#define ITransactionCompletionEvents_Indoubt(This)\
    (This)->lpVtbl -> Indoubt(This)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT __stdcall ITransactionCompletionEvents_Committed_Proxy(
    ITransactionCompletionEvents __RPC_FAR * This,
    /* [in] */ BOOL fRetaining,
    /* [in] */ XACTUOW __RPC_FAR *pNewUOW,
    /* [in] */ HRESULT hr);

void __RPC_STUB ITransactionCompletionEvents_Committed_Stub(

IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT __stdcall ITransactionCompletionEvents_Aborted_Proxy(
    ITransactionCompletionEvents __RPC_FAR * This,
    /* [in] */ BOID __RPC_FAR *pboidReason,
    /* [in] */ BOOL fRetaining,
    /* [in] */ XACTUOW __RPC_FAR *pNewUOW,
    /* [in] */ HRESULT hr);

void __RPC_STUB ITransactionCompletionEvents_Aborted_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT __stdcall ITransactionCompletionEvents_HeuristicDecision_Proxy(
    ITransactionCompletionEvents __RPC_FAR * This,
    /* [in] */ DWORD dwDecision,
    /* [in] */ BOID __RPC_FAR *pboidReason,
    /* [in] */ HRESULT hr);

void __RPC_STUB ITransactionCompletionEvents_HeuristicDecision_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT __stdcall ITransactionCompletionEvents_Indoubt_Proxy(
    ITransactionCompletionEvents __RPC_FAR * This);

void __RPC_STUB ITransactionCompletionEvents_Indoubt_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __ITransactionCompletionEvents_INTERFACE_DEFINED__ */

/****************************************
 * Generated header for interface: __MIDL__intf_0011
 * at Tue Nov 21 16:54:32 1995
 * using MIDL 2.00.0102
 ****************************************/
/* [local] */

#define XACT_E_FIRST                    0x8004D000
#define XACT_E_LAST                     0x8004D01E
#define XACT_S_FIRST                    0x0004D000
#define XACT_S_LAST                     0x0004D009

#define XACT_E_ABORTED                  0x8004D019
#define XACT_E_ALREADYOTHERSINGLEPHASE  0x8004D000
#define XACT_E_ALREADYINPROGRESS        0x8004D018
#define XACT_E_CANTRETAIN               0x8004D001
#define XACT_E_COMMITFAILED             0x8004D002
#define XACT_E_COMMITPREVENTED          0x8004D003
#define XACT_E_CONNECTION_DENIED        0x8004D01D
#define XACT_E_CONNECTION_DOWN          0x8004D01C
#define XACT_E_HEURISTICABORT           0x8004D004
#define XACT_E_HEURISTICCOMMIT          0x8004D005
#define XACT_E_HEURISTICDAMAGE          0x8004D006
#define XACT_E_HEURISTICDANGER          0x8004D007
#define XACT_E_INDOUBT                  0x8004D016
#define XACT_E_INVALIDCOOKIE            0x8004D015
#define XACT_E_ISOLATIONLEVEL           0x8004D008
#define XACT_E_LOGFULL                  0x8004D01A
#define XACT_E_NOASYNC                  0x8004D009
#define XACT_E_NOENLIST                 0x8004D00A
#define XACT_E_NOIMPORTOBJECT           0x8004D014
#define XACT_E_NOISORETAIN              0x8004D00B
#define XACT_E_NORESOURCE               0x8004D00C
#define XACT_E_NOTCURRENT               0x8004D00D
#define XACT_E_NOTIMEOUT                0x8004D017
#define XACT_E_NOTRANSACTION            0x8004D00E
#define XACT_E_NOTSUPPORTED             0x8004D00F
#define XACT_E_REENLISTTIMEOUT          0x8004D01E
#define XACT_E_TMNOTAVAILABLE           0x8004D01B
#define XACT_E_UNKNOWNRMGRID            0x8004D010
#define XACT_E_WRONGSTATE               0x8004D011
#define XACT_E_WRONGUOW                 0x8004D012
#define XACT_E_XTIONEXISTS              0x8004D013

#define XACT_S_ABORTING                 0x0004D008
#define XACT_S_ALLNORETAIN              0x0004D007
#define XACT_S_ASYNC                    0x0004D000
#define XACT_S_DEFECT                   0x0004D001
#define XACT_S_OKINFORM                 0x0004D004
#define XACT_S_MADECHANGESCONTENT       0x0004D005
#define XACT_S_MADECHANGESINFORM        0x0004D006
#define XACT_S_READONLY                 0x0004D002
#define XACT_S_SINGLEPHASE              0x0004D009
#define XACT_S_SOMENORETAIN             0x0004D003

extern RPC_IF_HANDLE __MIDL__intf_0011_v0_0_c_ifspec;
extern RPC_IF_HANDLE __MIDL__intf_0011_v0_0_s_ifspec;

/* Additional Prototypes for ALL interfaces */

/* end of Additional Prototypes */

#ifdef __cplusplus
}
#endif

#endif

///////////////////////////////////////////////////////////////////////
//
// IID definitions for interfaces defined in this header file
//

#if !defined(_transact_iid_) && defined(INITGUID)
#define      _transact_iid_
const IID IID_ITransaction = {0x0fb15084,0xaf41,0x11ce,{0xbd,0x2b,0x20,0x4c,0x4f,0x4f,0x50,0x20}};
const IID IID_ITransactionDispenser = {0x3A6AD9E1,0x23B9,0x11cf,{0xAD,0x60,0x00,0xAA,0x00,0xA7,0x4C,0xCD}};
const IID IID_ITransactionOptions = {0x3A6AD9E0,0x23B9,0x11cf,{0xAD,0x60,0x00,0xAA,0x00,0xA7,0x4C,0xCD}};
const IID IID_ITransactionOutcomeEvents = {0x3A6AD9E2,0x23B9,0x11cf,{0xAD,0x60,0x00,0xAA,0x00,0xA7,0x4C,0xCD}};
const IID IID_ITransactionCompletionEvents = {0xB38D5220,0x23CE,0x11cf,{0xAD,0x60,0x00,0xAA,0x00,0xA7,0x4C,0xCD}};
#endif

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


NTFILTER.C

#include "NtFilter.h"

int SessId =156789;
void *  Uid = (void *)&SessId ;
FILE *f;

BOOL APIENTRY DllMain( HANDLE hModule,DWORD ul_reason_for_call,LPVOID lpReserved )
{
    switch( ul_reason_for_call )
{
case DLL_PROCESS_ATTACH:
trace("Initialization - Process Attach");
break;
case DLL_THREAD_ATTACH:
trace("Thread Attach");
   break;
case DLL_THREAD_DETACH:
trace("Thread Detach");
    break;
case DLL_PROCESS_DETACH:
trace("Process Detach");
    break;
    }
return TRUE;
}

__declspec( dllexport ) void * WINAPI PrtFilterAlloc(DWORD BufLen)
{
/* MSFT - 3 lines for Debug OutPut - START */
void * pBuf ;
char TrBuf[50] ;
trace("FilterAlloc");
/* MSFT - 3 lines for Debug OutPut - END */

pBuf = malloc(BufLen) ;

/* MSFT - 2 lines for Debug OutPut - START */
sprintf (TrBuf,"%x ",pBuf);
trace (TrBuf) ;
/* MSFT - 2 lines for Debug OutPut - END */

return(pBuf);
}

__declspec( dllexport ) void WINAPI PrtFilterFree(void *pBuf)
{
/* MSFT - 4 lines for Debug OutPut - START */
char TrBuf[50] ;
trace("FilterFree");
sprintf (TrBuf,"%x ",pBuf);
trace (TrBuf) ;
/* MSFT - 4 lines for Debug OutPut - END */

free(pBuf);

/* MSFT - 1 line for Debug OutPut - START */
trace("  Buffer library");
/* MSFT - 1 line for Debug OutPut - END */

return;
}

__declspec( dllexport ) void   WINAPI PrtFilterJobData(void *UniqueID, char **pBufPtr,DWORD *pBufLen)
{
/* MSFT - 4 lines for Debug OutPut - START */
char TrBuf[50] ;
trace("FilterJobData");
sprintf (TrBuf,"%d ",*pBufLen);
trace (TrBuf) ;
/* MSFT - 4 lines for Debug OutPut - END */

if (*pBufLen > 49)
{
strcpy(*pBufPtr,"- FILTER - FilterJobData --- FilterJobData --- Fil");
*pBufLen=strlen(*pBufPtr);
}
else
/* MSFT - 1 line for Debug OutPut - START */
trace("Buffer too small - No modifications to the original data");
/* MSFT - 1 line for Debug OutPut - END */
return;
}

__declspec( dllexport ) void *WINAPI PrtFilterJobStart(char *SessionName,DWORD LUType,char** pBufPtr,DWORD *pBuflen)
{
/* MSFT - 1 line for Debug OutPut - START */
trace("FilterJobStart");
/* MSFT - 1 line for Debug OutPut - END */

return((void *)(Uid));
}

__declspec( dllexport ) void *WINAPI PrtFilterJobEnd(char *SessionName,char** pBufPtr,DWORD *pBufLen)
{
/* MSFT - 1 line for Debug OutPut - START */
trace("FilterJobEnd");
/* MSFT - 1 line for Debug OutPut - END */

return((void *)(Uid));
}

void trace(char *text)
{
f= fopen("c:\\trfilter.txt","a+t");
fprintf(f,"NTFILTER : %s \n",text);
fclose(f);
}


NTFILTER.H

#include <windows.h>
#include <stdio.h>

#define SIZEBUF     4096

__declspec( dllexport ) void   WINAPI PrtFilterJobData(void *UniqueID, char **pBufPtr,DWORD *pBufLen);
__declspec( dllexport ) void * WINAPI PrtFilterAlloc(DWORD Buflen);
__declspec( dllexport ) void WINAPI PrtFilterFree(void *pBuf);
__declspec( dllexport ) void *WINAPI PrtFilterJobStart(char *SessionName,DWORD LUType,char** pBufPtr,DWORD *pBuflen);
__declspec( dllexport ) void *WINAPI PrtFilterJobEnd(char *SessionName,char** pBufPtr,DWORD *pBuflen);

BOOL APIENTRY DllMain( HANDLE hModule,DWORD ul_reason_for_call,LPVOID lpReserved );

void trace(char *text);


RUI3270.C

/*****************************************************************************/
/* RUI Sample Program                                                        */
/*                                                                           */
/* (C) Copyright 1990 - 1993 Data Connection Ltd.                            */
/*                                                                           */
/* This program is a crude 3270 emulator.  It uses the RUI API to access     */
/* both the SSCP and LU sessions.  Outbound data from the host is            */
/* displayed on the screen unformatted.  Both SSCP and LU data are shown.    */
/* If the outbound data is an RQD request an automatic positive response     */
/* is sent.  Inbound data can be entered at the keyboard and is sent on      */
/* the current session.  This current session can be toggled between the     */
/* SSCP and LU sessions by hitting the [ (left square bracket) key           */
/* followed by <cr> (carriage return), although it switches                  */
/* automatically on receipt of BIND or UNBIND.                               */
/*                                                                           */
/* The program is invoked with a single parameter - the name of the LUA LU   */
/* to use.  This is converted to upper case and must match an LUA LU in      */
/* the configuration file.  This LU should be configured for a 327? on the   */
/* host.                                                                     */
/*                                                                           */
/* It works reasonably well with TSO provided that complex formatted         */
/* logon screens are not used.                                               */
/*                                                                           */
/* To exit the program type ] (right square bracket)                         */
/*                                                                           */
/* The CSV CONVERT function is used to convert data between ASCII and        */
/* EBCDIC.  This uses the type G conversion table, so the environment        */
/* variable COMTBLG must be set to point to a suitable file.                 */
/*                                                                           */
/* Keys that work are:                                                       */
/*                                                                           */
/*     Alt-1 to Alt-0 are PF1 to PF0                                         */
/*     F2 sends a CLEAR to the host                                          */
/*     backspace                                                             */
/*     Letters, numbers and symbols                                          */
/*                                                                           */
/*****************************************************************************/

#ifdef WIN32
  #include <windows.h>
#else
#ifdef DOS5
  #define INCL_DOS
  #define INCL_DOSERRORS
  #include <os2.h>
#else
#define FAR far
typedef unsigned int   BOOL;
typedef int            INT;
typedef unsigned int   UINT;
typedef unsigned long  ULONG;
typedef long           LONG;
typedef unsigned char  UCHAR;
typedef unsigned short USHORT;
#define TRUE    1
#define FALSE   0
#define kbhit   _kbhit
#define getch   _getche
#endif
#endif

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <conio.h>
#include <lua_c.h>
#include <acssvcc.h>

/*****************************************************************************/
/* Program constants                                                         */
/*****************************************************************************/
#define DATASIZE 4096                  /* Max size of RU                     */
#define SWITKEY  0x5B                  /* '['   to switch sessions           */
#define EXITKEY  0x5D                  /* ']'   to exit program              */

#define BETB     1                     /* Between brackets                   */
#define SEND     2                     /* In bracket and can send            */
#define RECV     3                     /* In bracket, but cannot send        */

/*****************************************************************************/
/* State variables                                                           */
/*                                                                           */
/* We can only have one read or write verb outstanding per flow.  These      */
/* state variables keep track of these verbs and what we need to do with     */
/* them.                                                                     */
/*****************************************************************************/

UINT  write_state;
#define NO_WRITE              1
#define WRITE_OUTSTANDING     2        /* waiting for write to complete      */
#define WRITE_QUEUED          3        /* have data to write                 */

UINT  read_state;
#define NO_READ               1
#define READ_OUTSTANDING      2        /* read verb outstanding              */
#define RSP_QUEUED            3        /* have a response to write           */
#define RSP_OUTSTANDING       4        /* waiting for a response to complete */

UINT  rpq_state;
#define NO_RPQ                1
#define RPQ_OUTSTANDING       2        /* waiting for an RPQ to complete     */
#define RPQ_QUEUED            3        /* have RPQ to send                   */

/***************************************************************************/
/* Global variables                                                        */
/*                                                                         */
/* Inbound data is sent as typed on the SSCP session.  On the LU session   */
/* it is prefixed with the <enter> key AID plus a two byte cursor address. */
/***************************************************************************/
LUA_VERB_RECORD read_verb;             /* RUI read verb                    */
LUA_VERB_RECORD other_verb;            /* RUI init, write or term verb     */
LUA_VERB_RECORD rpq_verb;              /* RUI_WRITE for RPQs               */

UCHAR rpq_data[] = {

  0x88,
  0x00, 0x0C, 0x81, 0x80, 0x80, 0x81, 0x85, 0x86, 0x87, 0x88, 0x95, 0xA6,

  0x00, 0x17, 0x81, 0x81, 0x21, 0x00, 0x00, 0x50, 0x00, 0x18, 0x00, 0x00,
  0x0A, 0x02, 0xE5, 0x00, 0x02, 0x00, 0x6F, 0x09, 0x10, 0x07, 0x80,

  0x00, 0x14, 0x81, 0x85, 0x02, 0x00, 0x09, 0x0E, 0x00, 0x00, 0x00, 0x00,
  0x07, 0x00, 0x00, 0x00, 0x02, 0xB9, 0x01, 0x1D,

  0x00, 0x16, 0x81, 0x86, 0x00, 0x08, 0x00, 0xF4, 0xF1, 0x00, 0xF2, 0x00,
  0xF3, 0x00, 0xF4, 0x00, 0xF5, 0x00, 0xF6, 0x00, 0xF7, 0x00,

  0x00, 0x0D, 0x81, 0x87, 0x04, 0x00, 0xF0, 0xF1, 0x00, 0xF2, 0x00, 0xF4,
  0x00,

  0x00, 0x07, 0x81, 0x88, 0x00, 0x01, 0x02,

  0x00, 0x0C, 0x81, 0x95, 0x00, 0x00, 0x7F, 0xFF, 0x7F, 0xFF, 0x01, 0x01,

  0x00, 0x11, 0x81, 0xA6, 0x00, 0x00, 0x0B, 0x01, 0x00, 0x00, 0x50, 0x00,
  0x18, 0x00, 0x50, 0x00, 0x18};

#define RPQ_LENGTH (sizeof(rpq_data))

unsigned char read_data[DATASIZE];     /* Outbound RU                       */
#define WRITE_EXTRA 6
unsigned char write_array[DATASIZE + WRITE_EXTRA] =
                                          {0x7d, 0x40, 0x40, 0x11, 0x40, 0x40};
                                       /* Inbound RU                         */
unsigned char *write_data = write_array + WRITE_EXTRA;
                                       /* Pointer to inbound RU              */
UINT data_offset = 0;                  /* offset into read_data              */
UINT write_len   = 0;                  /* length of write data               */

struct convert convert_to_asc;         /* Outbound convert verb              */
struct convert convert_to_ebc;         /* Inbound convert verb               */

#ifdef WIN32
  HANDLE write_event = 0l;             /* write arm verb semaphore           */
  HANDLE read_event  = 0l;             /* read arm verb semaphore            */
  HANDLE init_event  = 0l;             /* RUI_INIT semaphore                 */
  HANDLE term_event  = 0l;             /* RUI_TERM semaphore                 */
  HANDLE rpq_event   = 0l;
  HANDLE status_event = 0l;            /* RUI status event                   */
  HANDLE std_input;                    /* standard input handle              */
  HANDLE std_output;                   /* standard output handle             */
#else
  ULONG write_sema4 = 0l;              /* write arm verb semaphore           */
  ULONG read_sema4  = 0l;              /* read arm verb semaphore            */
  ULONG init_sema4  = 0l;              /* RUI_INIT semaphore                 */
  ULONG term_sema4  = 0l;              /* RUI_TERM semaphore                 */
  ULONG rpq_sema4   = 0l;
#endif

BOOL terminating = FALSE;              /* are we already in closedown() ?    */
BOOL lu_session  = FALSE;              /* LU session inbound flag            */

unsigned long sense = 0l;              /* sense code                         */

unsigned char lu_name[9] = "        "; /* LU name                            */
unsigned long sid;                     /* RUI session ID                     */
int send_state = BETB;                 /* LU session send state              */

#ifdef WIN32
  #define INITPOST         (ULONG) init_event
  #define TERMPOST         (ULONG) term_event
  #define READPOST         (ULONG) read_event
  #define WRITEPOST        (ULONG) write_event
  #define RPQPOST          (ULONG) rpq_event
#else
  #define INITPOST         (unsigned long) ((UCHAR FAR *) &init_sema4)
  #define TERMPOST         (unsigned long) ((UCHAR FAR *) &term_sema4)
  #define READPOST         (unsigned long) ((UCHAR FAR *) &read_sema4)
  #define WRITEPOST        (unsigned long) ((UCHAR FAR *) &write_sema4)
  #define RPQPOST          (unsigned long) ((UCHAR FAR *) &rpq_sema4)
#endif

/*****************************************************************************/
/* Function declarations.                                                    */
/*****************************************************************************/

#ifdef WIN32
  #define LOADDS
#else
  #define LOADDS _loadds
#endif

void pascal far LOADDS RUI (LUA_VERB_RECORD FAR *);

void            csv_init            (void);
int             issue_verb          (unsigned int  type);
void            issue_read          (void);
void            other_done          (LUA_VERB_RECORD *verb);
void            read_done           (LUA_VERB_RECORD *verb);
void            issue_rsp           (unsigned long  sense);
void            rsp_done            (LUA_VERB_RECORD *verb);
void            closedown           (void);
BOOL            do_write            (void);
void            wait_verb_complete  (LUA_VERB_RECORD *verb);
void            wait_active_status  (LUA_VERB_RECORD *verb);
void            set_semaphore       (LUA_VERB_RECORD *verb);
BOOL            do_keyboard_stuff   (void);
BOOL            check_verb_complete (LUA_VERB_RECORD *verb);
void            parse_data          (UCHAR FAR *, USHORT);
BOOL            issue_rpq           (void);
#ifdef WIN32
extern int      WINAPI GetFmiReturnCode(unsigned short, unsigned short, unsigned short, unsigned char FAR *);
#endif

/**PROC+**********************************************************************/
/* Name:      main                                                           */
/*                                                                           */
/* Purpose:   set up CSV convert verbs                                       */
/*                                                                           */
/* Returns:   none                                                           */
/*                                                                           */
/* Params:    argv, argc                                                     */
/*                                                                           */
/* Operation: Get LU name from command line                                  */
/*            do some initialisation                                         */
/*            Issue RUI_INIT to get LU-SSCP session                          */
/*            Loop reading data from keyboard and host, and send keyboard    */
/*            data to host                                                   */
/**PROC-**********************************************************************/
#ifdef DOS5
int main(int argc, char *argv[])
#else
#ifdef PDOS
int _cdecl main(int argc, char *argv[])
#else
int _CRTAPI1 main(int argc, char *argv[])
#endif
#endif
{
  BOOL whole_data;       /* we have a whole data string to write             */
  BOOL verb_complete;    /* Has a verb completed                             */
  BOOL write_ok;         /* Has write worked?                                */
  BOOL rpq_ok;           /* Has rpq worked                                   */
  int ii;
#ifdef WIN32
  HANDLE handle_list [6];
#endif
#ifdef DOS5
  DEFINEMUXSEMLIST(sem_list,5)
  USHORT           sem_index;
#endif

  printf("RUI simple 3270 application\n");

  /***************************************************************************/
  /* Validate parameter usage and get LU name.                               */
  /***************************************************************************/
  if (argc != 2)
  {
    printf("Usage: RUI3270 luname\n");
    exit(1);
  }
  if (strlen(argv[1]) > 8)
  {
    printf("LU name too long\n");
    exit(1);
  }

  /***************************************************************************/
  /* Convert to upper case                                                   */
  /***************************************************************************/
  ii=0;
  while (argv[1][ii])
  {
    if (argv[1][ii] >= 'a' && argv[1][ii] <= 'z')
    {
      argv[1][ii] = argv[1][ii] - 'a' + 'A';
    }
    ii++;
  }
  memcpy(lu_name, argv[1], strlen(argv[1]));
  printf("lu_name %s\n", lu_name);

  /***************************************************************************/
  /* Set up CSV convert verbs                                                */
  /***************************************************************************/
  csv_init();

#ifdef WIN32
  /***************************************************************************/
  /* Create the necessary events                                             */
  /***************************************************************************/
  write_event  = CreateEvent (NULL, TRUE, FALSE, NULL);
  read_event   = CreateEvent (NULL, TRUE, FALSE, NULL);
  init_event   = CreateEvent (NULL, TRUE, FALSE, NULL);
  term_event   = CreateEvent (NULL, TRUE, FALSE, NULL);
  rpq_event    = CreateEvent (NULL, TRUE, FALSE, NULL);
  status_event = CreateEvent (NULL,FALSE, FALSE, NULL);

  /***************************************************************************/
  /* Get standard input and output handles.                                  */
  /***************************************************************************/
  std_input  = GetStdHandle (STD_INPUT_HANDLE );
  std_output = GetStdHandle (STD_OUTPUT_HANDLE);
  if ((std_input  == INVALID_HANDLE_VALUE) ||
      (std_output == INVALID_HANDLE_VALUE))
  {
    printf("GetStdHandle Failed, %d", GetLastError());
    exit (1);
  }

  /***************************************************************************/
  /* Set up list of handles to wait for                                      */
  /***************************************************************************/
  handle_list[0] = write_event;
  handle_list[1] = read_event;
  handle_list[2] = init_event;
  handle_list[3] = term_event;
  handle_list[4] = rpq_event;
  handle_list[5] = std_input;
#endif
#ifdef DOS5
  /***************************************************************************/
  /* Set semaphores to begin with                                            */
  /***************************************************************************/
  DosSemSet (&init_sema4);
  DosSemSet (&term_sema4);
  DosSemSet (&read_sema4);
  DosSemSet (&write_sema4);
  DosSemSet (&rpq_sema4);

  sem_list.amxs[0].hsem = &init_sema4;
  sem_list.amxs[1].hsem = &term_sema4;
  sem_list.amxs[2].hsem = &read_sema4;
  sem_list.amxs[3].hsem = &write_sema4;
  sem_list.amxs[4].hsem = &rpq_sema4;
#endif

  /***************************************************************************/
  /* Initialize RUI and wait for ACTLU fom host.                             */
  /***************************************************************************/
  if (issue_verb((unsigned int) LUA_OPCODE_RUI_INIT))
  {
    printf("Init failed\n");
    exit(1);
  }

  /***************************************************************************/
  /* Set initial states                                                      */
  /***************************************************************************/
  write_state = NO_WRITE;
  read_state  = NO_READ;
  rpq_state   = NO_RPQ;

  /***************************************************************************/
  /* Loop reading inbound data from the keyboard                             */
  /***************************************************************************/
  while (!terminating)
  {
#ifndef PDOS
    /*************************************************************************/
    /* Determine if we can pause                                             */
    /*************************************************************************/
    if (  (read_state != NO_READ)   &&

          ((write_state != WRITE_QUEUED)    ||
           (rpq_state   != NO_RPQ)          ||
           (read_state  == RSP_OUTSTANDING) ||
           (read_state  == RSP_QUEUED)) &&

          ((rpq_state   != RPQ_QUEUED)        ||
           (write_state == WRITE_OUTSTANDING) ||
           (read_state  == RSP_OUTSTANDING)   ||
           (read_state  == RSP_QUEUED))    &&

          ((read_state != RSP_QUEUED) ||
           (write_state == WRITE_OUTSTANDING)))
    {
#ifdef WIN32
      WaitForMultipleObjects (6, handle_list, FALSE, INFINITE);
#else
      if (!kbhit())
      {
        DosMuxSemWait (&sem_index, &sem_list, 200l);
      }
#endif
    }
#endif

    /*************************************************************************/
    /* First check on the state of write verbs                               */
    /*************************************************************************/
    if ((write_state == NO_WRITE)        &&
        (rpq_state   == NO_RPQ)          &&
        (read_state  != RSP_QUEUED)      &&
        (read_state  != RSP_OUTSTANDING))
    {
      /***********************************************************************/
      /* Don't write here if we need to respond, since we can only have one  */
      /* write outstanding on each flow.                                     */
      /* Here we want to check if we have a whole string of data (i.e.  that */
      /* the user has hit enter).  If so write the data to the HOST          */
      /***********************************************************************/
      whole_data = do_keyboard_stuff ();
      if (whole_data)
      {
        /*********************************************************************/
        /* Attempt to write the data.  May not work because of bracket state */
        /*********************************************************************/

        write_ok = do_write ();
        if (write_ok)
        {
          /*******************************************************************/
          /* If verb completes async change state to indicate this o.w.      */
          /* call the verb done processor                                    */
          /*******************************************************************/
          if (other_verb.common.lua_flag2.async)
          {
            write_state = WRITE_OUTSTANDING;
          }
          else
          {
            other_done (&other_verb);
            write_state = NO_WRITE;
          }
        }
        else
        {
          /*******************************************************************/
          /* Failed to do a write.  Queue up data                            */
          /*******************************************************************/
          write_state = WRITE_QUEUED;
        }
      }
    }
    else if ((write_state == WRITE_QUEUED)    &&
             (rpq_state   == NO_RPQ)          &&
             (read_state  != RSP_OUTSTANDING) &&
             (read_state  != RSP_QUEUED))
    {
      /***********************************************************************/
      /* Attempt to write the data.  May not work because of bracket state   */
      /***********************************************************************/
      write_ok = do_write ();
      if (write_ok)
      {
        /*********************************************************************/
        /* If verb completes async change state to indicate this o.w.  call  */
        /* the verb done processor                                           */
        /*********************************************************************/
        if (other_verb.common.lua_flag2.async)
        {
          write_state = WRITE_OUTSTANDING;
        }
        else
        {
          other_done (&other_verb);
          write_state = NO_WRITE;
        }
      }

    }
    else if (write_state == WRITE_OUTSTANDING)
    {
      /***********************************************************************/
      /* We have a RUI_WRITE outstanding so check if it is complete          */
      /***********************************************************************/
      verb_complete = check_verb_complete (&other_verb);
      if (verb_complete)
      {
        other_done (&other_verb);
        write_state = NO_WRITE;
      }
    }

    /*************************************************************************/
    /* Now check the RPQ state                                               */
    /*************************************************************************/
    if ((rpq_state   == RPQ_QUEUED)        &&
        (write_state != WRITE_OUTSTANDING) &&
        (read_state  != RSP_OUTSTANDING)   &&
        (read_state  != RSP_QUEUED))
    {
      /***********************************************************************/
      /* We have an RPQ queued up.  Try to send it                           */
      /***********************************************************************/
      rpq_ok = issue_rpq ();
      if (rpq_ok)
      {
        /*********************************************************************/
        /* If verb completes async change state to indicate this o.w.  call  */
        /* the verb done processor                                           */
        /*********************************************************************/
        if (rpq_verb.common.lua_flag2.async)
        {
          rpq_state = WRITE_OUTSTANDING;
        }
        else
        {
          other_done (&rpq_verb);
          write_state = NO_RPQ;
        }
      }
    }
    else if (rpq_state == RPQ_OUTSTANDING)
    {
      /***********************************************************************/
      /* Check for verb completion                                           */
      /***********************************************************************/
      verb_complete = check_verb_complete (&rpq_verb);
      if (verb_complete)
      {
        other_done (&rpq_verb);
        rpq_state = NO_RPQ;
      }

    }

    /*************************************************************************/
    /* Now check on read verbs                                               */
    /*************************************************************************/
    if (read_state == NO_READ)
    {
      issue_read ();
      if (read_verb.common.lua_flag2.async)
      {
        read_state = READ_OUTSTANDING;
      }
      else
      {
        /*********************************************************************/
        /* read_done will change read_state to RESPONSE_QUEUED or NO_READ    */
        /*********************************************************************/
        read_done (&read_verb);
      }
    }
    else if (read_state == READ_OUTSTANDING)
    {
      verb_complete = check_verb_complete (&read_verb);
      if (verb_complete)
      {
        /*********************************************************************/
        /* read_done will change read_state to RESPONSE_QUEUED or NO_READ    */
        /*********************************************************************/
        read_done (&read_verb);
      }
    }
    else if (read_state == RSP_QUEUED)
    {
      /***********************************************************************/
      /* We need to respond to a message read.  Do a write if one is         */
      /* availible.                                                          */
      /***********************************************************************/
      if (write_state != WRITE_OUTSTANDING)
      {
        /*********************************************************************/
        /* The response is issued using the read_verb                        */
        /*********************************************************************/
        issue_rsp (sense);
        if (read_verb.common.lua_flag2.async)
        {
          read_state = RSP_OUTSTANDING;
        }
        else
        {
          rsp_done (&read_verb);
          read_state = NO_READ;
        }
      }
    }
    else if (read_state == RSP_OUTSTANDING)
    {
      /***********************************************************************/
      /* Check to see if the response is complete                            */
      /***********************************************************************/
      verb_complete = check_verb_complete (&read_verb);
      if (verb_complete)
      {
        rsp_done (&read_verb);
        read_state = NO_READ;
      }
    }
    else
    {
      /***********************************************************************/
      /* Invalid state                                                       */
      /***********************************************************************/
      printf("invalid state");
    }

  }  /* while not terminating                                                */

  return(0);
}  /* main ()                                                                */

/**PROC+**********************************************************************/
/* Name:      csv_init                                                       */
/*                                                                           */
/* Purpose:   set up CSV convert verbs                                       */
/*                                                                           */
/* Returns:   void                                                           */
/*                                                                           */
/* Params:    none                                                           */
/*                                                                           */
/* Operation: sets uup two CSV convert verbs, one in each direction.  Only   */
/* the length needs to be set when the verb is called                        */
/**PROC-**********************************************************************/
void csv_init (void)
{
  convert_to_asc.opcode       = SV_CONVERT;
  convert_to_asc.direction    = SV_EBCDIC_TO_ASCII;
  convert_to_asc.char_set     = SV_G;
  convert_to_asc.source       = read_data;
  convert_to_asc.target       = read_data;

  convert_to_ebc.opcode       = SV_CONVERT;
  convert_to_ebc.direction    = SV_ASCII_TO_EBCDIC;
  convert_to_ebc.char_set     = SV_G;
  convert_to_ebc.source       = write_data;
  convert_to_ebc.target       = write_data;
}  /* csv_init                                                               */

/**PROC+**********************************************************************/
/* Name:      issue_verb                                                     */
/*                                                                           */
/* Purpose:   build a verb and send it                                       */
/*                                                                           */
/* Returns:   int - non-zero => verb failed                                  */
/*                                                                           */
/* Params:    IN type - type of verb to issue                                */
/*                                                                           */
/* Operation: Build an INIT or TERM verb and issue it.  Wait for the verb    */
/*            to complete                                                    */
/**PROC-**********************************************************************/
int issue_verb(type)
unsigned int  type;
{

  memset(&other_verb, 0, sizeof(other_verb));
  other_verb.common.lua_verb             = LUA_VERB_RUI;
  other_verb.common.lua_verb_length      = sizeof(other_verb);

other_verb.common.lua_opcode           = type;
  if (type == LUA_OPCODE_RUI_INIT)
  {
    memcpy(other_verb.common.lua_luname, lu_name, 8);
    other_verb.common.lua_post_handle    = INITPOST;
  }
  else
  {
    other_verb.common.lua_sid            = sid;
    other_verb.common.lua_post_handle    = TERMPOST;
  }
  RUI((LUA_VERB_RECORD FAR *) &other_verb);

#ifdef WIN32
  if (type == LUA_OPCODE_RUI_INIT)
  {
    WinRUIGetLastInitStatus(0, status_event, WLUA_NTFY_EVENT, 0);
  }
#endif

  if (other_verb.common.lua_flag2.async)
  {
#ifdef WIN32
    if (type == LUA_OPCODE_RUI_INIT)
    {
      wait_active_status (&other_verb);
      wait_verb_complete (&other_verb);
    }
    else
#endif
    {
      wait_verb_complete (&other_verb);
    }
  }
  other_done(&other_verb);

  return(other_verb.common.lua_prim_rc != LUA_OK);
}  /* issue_verb                                                             */

/**PROC+**********************************************************************/
/* Name:      issue_read                                                     */
/*                                                                           */
/* Purpose:   build a read verb and send it                                  */
/*                                                                           */
/* Returns:   void                                                           */
/*                                                                           */
/* Params:    none                                                           */
/*                                                                           */
/* Operation: Build a read verb for all flows and issue it                   */
/**PROC-**********************************************************************/
void issue_read (void)
{
  memset(&read_verb, 0, sizeof(read_verb));
  memset(read_data, 0, DATASIZE);
  read_verb.common.lua_verb             = LUA_VERB_RUI;
  read_verb.common.lua_verb_length      = sizeof(read_verb);
  read_verb.common.lua_opcode           = LUA_OPCODE_RUI_READ;
  read_verb.common.lua_sid              = sid;
  read_verb.common.lua_max_length       = DATASIZE;
  read_verb.common.lua_data_ptr         = (char *) read_data;
  read_verb.common.lua_post_handle      = READPOST;
  read_verb.common.lua_flag1.lu_norm    = 1;
  read_verb.common.lua_flag1.lu_exp     = 1;
  read_verb.common.lua_flag1.sscp_norm  = 1;
  read_verb.common.lua_flag1.sscp_exp   = 1;
  RUI((LUA_VERB_RECORD FAR *)&read_verb);

}  /* issue_read                                                             */

/**PROC+**********************************************************************/
/* Name:      other_done                                                     */
/*                                                                           */
/* Purpose:   handle completed init, write or term verb                      */
/*                                                                           */
/* Returns:   void                                                           */
/*                                                                           */
/* Params:    IN verb - completed verb                                       */
/*                                                                           */
/* Operation: write status to he screen                                      */
/**PROC-**********************************************************************/
void other_done(verb)
LUA_VERB_RECORD *verb;
{
  switch (verb->common.lua_opcode)
  {
    case LUA_OPCODE_RUI_INIT:
         if (verb->common.lua_prim_rc == LUA_OK)
         {
           printf("LU active\n");
           sid = verb->common.lua_sid;
         }
         else
         {
           printf("INIT failed, (%4.4x, %8.8lx)\n",
                  verb->common.lua_prim_rc, verb->common.lua_sec_rc);
         }
         break;

    case LUA_OPCODE_RUI_WRITE:
         if (verb->common.lua_prim_rc != LUA_OK)
         {
           printf("WRITE failed, (%4.4x, %8.8lx)\n",
                  verb->common.lua_prim_rc, verb->common.lua_sec_rc);
         }
         break;

    case LUA_OPCODE_RUI_TERM:
         printf("Terminated\n");
         break;

  }
}  /* other_done                                                             */

/**PROC+**********************************************************************/
/* Name:      read_done                                                      */
/*                                                                           */
/* Purpose:   handle completed read verb                                     */
/*                                                                           */
/* Returns:   void                                                           */
/*                                                                           */
/* Params:    IN verb - completed read verb                                  */
/*                                                                           */
/* Operation: If the completed verb contains data, first parse it and then   */
/*            display it.                                                    */
/*            If message is a BIND or an UNBIND, set the LU-LU session state */
/*            accordingly                                                    */
/*            Also check for EB, CD and change session state flags           */
/**PROC-**********************************************************************/
void read_done(verb)
LUA_VERB_RECORD *verb;
{
  int            count;         /* number of bytes of data left to display   */
  unsigned char *data;          /* pointer to next data to display           */
  BOOL           rsp_required = FALSE; /* is a resonse required              */

  sense = 0l;

  if (verb->common.lua_prim_rc == LUA_OK)
  {
    if ((verb->common.lua_message_type == LUA_MESSAGE_TYPE_LU_DATA)   ||
        (verb->common.lua_message_type == LUA_MESSAGE_TYPE_SSCP_DATA))
    {
      if (verb->common.lua_data_length > 0)
      {
        /*********************************************************************/
        /* We now have to parse the data.  For the moment we only look for   */
        /* RPQ requests.                                                     */
        /*********************************************************************/
        parse_data (read_data, (USHORT) (verb->common.lua_data_length));

        /*********************************************************************/
        /* Display outbound SSCP or LU data.                                 */
        /*********************************************************************/
        convert_to_asc.len = (unsigned short) verb->common.lua_data_length;
        ACSSVC_C((LONG)((UCHAR FAR *)&convert_to_asc));
        data  = read_data;
        count = strlen(data);
        while (count > 0)
        {
          if (count <= 80)
          {
            printf("%s\n",data);
            count = 0;
          }
          else
          {
            printf("%80.80s\n",data);
            count -= 80;
            data  += 80;
          }
        }
      }

      /***********************************************************************/
      /* Set session state.                                                  */
      /***********************************************************************/
      if (verb->common.lua_rh.ebi)
      {
        printf("EB\n");
        send_state = BETB;
      }
      else if (verb->common.lua_rh.cdi)
      {
        printf("CD\n");
        send_state = SEND;
      }
      else
      {
        send_state = RECV;
      }
    }
    /*************************************************************************/
    /* Handle BIND or UNBIND as a special case.                              */
    /*************************************************************************/
    else if (verb->common.lua_message_type == LUA_MESSAGE_TYPE_BIND)
    {
      if (((read_data[6] & 0x20) == 0x20)  &&      /* Brackets used and BETB */
          ((read_data[7] & 0xF0) == 0x80)  &&      /* HDX-FF, sec con winner */
          ( read_data[14]        == 0x02))         /* LU type 2              */
      {
        printf("BIND\n");
        send_state = BETB;
        lu_session = TRUE;
      }
      else
      {
        printf("BIND rejected\n");
        sense = LUA_INVALID_SESSION_PARAMETERS;
      }
    }
    else if (verb->common.lua_message_type == LUA_MESSAGE_TYPE_UNBIND)
    {
      printf("UNBIND\n");
      send_state = BETB;
      lu_session = FALSE;
    }

    /*************************************************************************/
    /* Respond to any RQD request.                                           */
    /*************************************************************************/
    if ((verb->common.lua_message_type != LUA_MESSAGE_TYPE_RSP)  &&
        (verb->common.lua_rh.ri == 0))                  /* definite response */
    {
      rsp_required = TRUE;
    }
  }
  else         /* primary rc not OK - read failed - stop now */
  {
    printf("READ ERROR, primary rc = %4.4x, secondary rc = %8.8lx\n",
           verb->common.lua_prim_rc, verb->common.lua_sec_rc);
    closedown();
  }

  /***************************************************************************/
  /* Continue processing with either another read or a write positive rsp.   */
  /* (The callback from issuing response will then issue another read) If    */
  /* we've already gone into closedown, stop here                            */
  /***************************************************************************/
  if (!terminating)
  {
    if (rsp_required)
    {
      read_state = RSP_QUEUED;
    }
    else
    {
      read_state = NO_READ;
    }
  }
}  /* read_done                                                              */

/**PROC+**********************************************************************/
/* Name:      issue_rsp                                                      */
/*                                                                           */
/* Purpose:   issue a response for a request from the host                   */
/*                                                                           */
/* Returns:   void                                                           */
/*                                                                           */
/* Params:    IN sense - sense code for the response                         */
/*                                                                           */
/* Operation: Build a response and write it out.  Uses the read verb control */
/*            block because it has the necessary information.                */
/*                                                                           */
/**PROC-**********************************************************************/
void issue_rsp(sense)
unsigned long sense;
{
  read_verb.common.lua_opcode           = LUA_OPCODE_RUI_WRITE;
  read_verb.common.lua_max_length       = 0;
  read_verb.common.lua_post_handle      = (ULONG) READPOST;
  read_verb.common.lua_rh.rri           = 1;       /* response             */
  read_verb.common.lua_flag1.lu_norm    = 0;
  read_verb.common.lua_flag1.lu_exp     = 0;
  read_verb.common.lua_flag1.sscp_norm  = 0;
  read_verb.common.lua_flag1.sscp_exp   = 0;

  /***************************************************************************/
  /* If we have been given a sense code this must be a negative response     */
  /***************************************************************************/
  if (sense)
  {
    read_verb.common.lua_data_length      = 4;
    memcpy(read_data, &sense, 4);
    read_verb.common.lua_rh.ri            = 1;       /* negative rsp         */
  }
  else
  {
    read_verb.common.lua_data_length      = 0;
    read_verb.common.lua_rh.ri            = 0;       /* positive rsp         */
  }
  /***************************************************************************/
  /* Send the response back on the flow from which the request came          */
  /***************************************************************************/
  if (read_verb.common.lua_flag2.lu_norm)
  {
    read_verb.common.lua_flag1.lu_norm = 1;
  }
  else if (read_verb.common.lua_flag2.lu_exp)
  {
    read_verb.common.lua_flag1.lu_exp = 1;
  }
  else if (read_verb.common.lua_flag2.sscp_norm)
  {
    read_verb.common.lua_flag1.sscp_norm = 1;
  }
  else if (read_verb.common.lua_flag2.sscp_exp)
  {
    read_verb.common.lua_flag1.sscp_exp = 1;
  }
  /***************************************************************************/
  /* Send out the verb                                                       */
  /***************************************************************************/
  RUI((LUA_VERB_RECORD FAR *)&read_verb);              /* write the response */
}  /* issue_rsp                                                              */

/**PROC+**********************************************************************/
/* Name:      rsp_done                                                       */
/*                                                                           */
/* Purpose:   handle completion of RUI_WRITE, when RUI_WRITE has written a   */
/*            response                                                       */
/*                                                                           */
/* Returns:   void                                                           */
/*                                                                           */
/* Params:    IN verb - completed verb                                       */
/*                                                                           */
/* Operation: If verb did not complete OK print a message and terminate      */
/*                                                                           */
/**PROC-**********************************************************************/
void rsp_done (verb)
LUA_VERB_RECORD *verb;
{
  if (verb->common.lua_prim_rc != LUA_OK)   /* failed to write the response */
  {                                         /* so stop here                 */
    printf("WRITE for response failed, (%4.4x, %8.8lx)\n",
            verb->common.lua_prim_rc, verb->common.lua_sec_rc);
    closedown();
  }
}  /* rsp_done                                                               */

/**PROC+**********************************************************************/
/* Name:      closedown                                                      */
/*                                                                           */
/* Purpose:   terminate the application cleanly                              */
/*                                                                           */
/* Returns:   void                                                           */
/*                                                                           */
/* Params:    none                                                           */
/*                                                                           */
/* Operation: Issue an RUI_TERM and wait for it to complete.  Then exit      */
/*                                                                           */
/**PROC-**********************************************************************/
void closedown (void)
{
  if (!terminating)                /* check we haven't already got here from */
  {                                /* another section of the code            */
    terminating = TRUE;            /* then make sure we can't get here again */
    printf("Closedown\n");
    if (issue_verb((unsigned int) LUA_OPCODE_RUI_TERM))
    {
      exit(1);
    }
    exit(0);
  }
}  /* closedown                                                              */

/**PROC+**********************************************************************/
/* Name:      check_verb_complete                                            */
/*                                                                           */
/* Purpose:   check for asynchronous verb completion                         */
/*                                                                           */
/* Returns:   BOOL - TRUE => verb has completed                              */
/*                                                                           */
/* Params:    IN - verb - pointer to verb control block                      */
/*                                                                           */
/* Operation: OS specific                                                    */
/*                                                                           */
/**PROC-**********************************************************************/
BOOL check_verb_complete (verb)

LUA_VERB_RECORD * verb;
{
#ifdef DOS5
  /***************************************************************************/
  /* OS/2 case.  The verb is complete when the semaphore is cleared          */
  /***************************************************************************/
  USHORT RetCode;                     /* Holds return code from OS calls     */
  BOOL   verb_complete;               /* Has the verb completed              */

  verb_complete = FALSE;

  RetCode = DosSemRequest ((HSEM) verb->common.lua_post_handle, 0l);

  if (RetCode != NO_ERROR)
  {
    if (RetCode != ERROR_SEM_TIMEOUT)
    {
      /***********************************************************************/
      /* If we get here we've got problems                                   */
      /***********************************************************************/
      printf("Bad DosSemRequest return code, %d", RetCode);
    }
  }
  else
  {
    verb_complete = TRUE;
    set_semaphore (verb);
  }

  return(verb_complete);
#endif

#ifdef PDOS
  /***************************************************************************/
  /* DOS case.  The verb is complete when the lua_post_handle is cleared     */
  /***************************************************************************/
  BOOL   verb_complete;               /* Has the verb completed              */

  verb_complete = FALSE;

  if (!verb->common.lua_post_handle)
  {
    verb_complete = TRUE;
    set_semaphore (verb);
  }

  return(verb_complete);
#endif

#ifdef WIN32
  /***************************************************************************/
  /* Win32 case.  The verb is complete when the semaphore is cleared         */
  /***************************************************************************/
  DWORD  RetCode;

  RetCode = WaitForSingleObject((HANDLE) verb->common.lua_post_handle, 0l);

  if (RetCode == 0)
  {
    set_semaphore (verb);
  }
  return((RetCode == 0));
#endif
}  /* check_verb_complete                                                    */

#ifdef WIN32
/**PROC+**********************************************************************/
/* Name:      wait_active_status                                             */
/*                                                                           */
/* Purpose:   waits for status to become active                              */
/*                                                                           */
/* Returns:   void                                                           */
/*                                                                           */
/* Operation:                                                                */
/*                                                                           */
/**PROC-**********************************************************************/
void wait_active_status(verb)

LUA_VERB_RECORD * verb ;
{
  DWORD RetCode;
  DWORD Status = 0;
  char status_buff[80];

  while (Status != WLUALUACTIVE)
  {
    RetCode = WaitForSingleObject(status_event,(DWORD)-1);
    Status = WinRUIGetLastInitStatus(verb->common.lua_sid, NULL, WLUA_NTFY_EVENT, 0);
    GetFmiReturnCode((unsigned short)Status,(unsigned short) 0xffff,(unsigned short) 80, status_buff);
    printf("%s\n",status_buff);
  }
  printf("LU ACT received\n");

}
#endif

/**PROC+**********************************************************************/
/* Name:      wait_verb_complete                                             */
/*                                                                           */
/* Purpose:   waits for async verb completion                                */
/*                                                                           */
/* Returns:   void                                                           */
/*                                                                           */
/* Params:    IN - verb - pointer to verb control block                      */
/*                                                                           */
/* Operation: Wait for the lua_post_handle to become clear                   */
/*                                                                           */
/**PROC-**********************************************************************/
void wait_verb_complete (verb)

LUA_VERB_RECORD * verb ;
{
#ifdef DOS5
  USHORT RetCode;                     /* Holds return code from OS calls     */

  RetCode = DosSemRequest ((HSEM)verb->common.lua_post_handle, -1l);
#endif

#ifdef PDOS
  while (verb->common.lua_post_handle);
#endif

#ifdef WIN32
  DWORD RetCode;

  RetCode = WaitForSingleObject((HANDLE)verb->common.lua_post_handle,
                                                                   (DWORD)-1l);
#endif
  set_semaphore (verb);

}  /* wait_verb_complete                                                     */

/**PROC+**********************************************************************/
/* Name:      set_semaphore                                                  */
/*                                                                           */
/* Purpose:   sets verb completion flags                                     */
/*                                                                           */
/* Returns:   void                                                           */
/*                                                                           */
/* Params:    IN - verb - pointer to vcb                                     */
/*                                                                           */
/* Operation: set the lua_post_handle to the non-signaled state              */
/*                                                                           */
/**PROC-**********************************************************************/
void set_semaphore (verb)

LUA_VERB_RECORD * verb;
{
#ifdef DOS5
  USHORT RetCode;
  RetCode = DosSemSet ((HSEM)verb->common.lua_post_handle);
#endif

#ifdef PDOS
  /***************************************************************************/
  /* Dos Case.  Set lua_post_handle to 1                                     */
  /***************************************************************************/
  verb->common.lua_post_handle = 1l;
#endif

#ifdef WIN32
  /*W32***********************************************************************/
  /*W32* Win 32 case.  Clear the event                                       */
  /*W32***********************************************************************/
  ResetEvent ((HANDLE) verb->common.lua_post_handle);
#endif
}  /* set_semaphore                                                          */

/**PROC+**********************************************************************/
/* Name:      do_keyboard_stuff                                              */
/*                                                                           */
/* Purpose:   poll keyboard for data                                         */
/*                                                                           */
/* Returns:   BOOL - TRUE => we have data to send                            */
/*                                                                           */
/* Params:    none                                                           */
/*                                                                           */
/* Operation: poll keyboard                                                  */
/*            if some data                                                   */
/*              read it in                                                   */
/*              add it to data buffer                                        */
/*              if data is <enter>                                           */
/*                return TRUE                                                */
/*              endif                                                        */
/*            endif                                                          */
/*            return false                                                   */
/*                                                                           */
/* Also keep track of which session is being used, and if user wishes to     */
/* exit.                                                                     */
/*                                                                           */
/**PROC-**********************************************************************/
BOOL do_keyboard_stuff (void)

{
  BOOL   data_to_send;             /* do we have data to send?               */
  INT    input_char;               /* an input character                     */
#ifdef WIN32
  INPUT_RECORD input_record;       /* input record                           */
  DWORD        no_read;            /* number of input records read           */
#endif

  data_to_send = FALSE;

  /***************************************************************************/
  /* Check if keyboard has been hit                                          */
  /***************************************************************************/
#ifdef WIN32
  while ((!data_to_send) &&
         (!terminating)  &&
         (PeekConsoleInput (std_input, &input_record, 1, &no_read)) &&
         (no_read > 0))
#else
  while ((!data_to_send) && (!terminating) && (kbhit()))
#endif
  {
#ifndef WIN32
    input_char = getch();
    if ((input_char == 0) || (input_char == 0xE0))
#else
    ReadConsoleInput (std_input, &input_record, 1, &no_read);

    if ((input_record.EventType == KEY_EVENT) &&
        (input_record.Event.KeyEvent.bKeyDown) &&
        ((input_record.Event.KeyEvent.dwControlKeyState == LEFT_ALT_PRESSED) ||
         ((input_record.Event.KeyEvent.wVirtualKeyCode >= VK_F1 ) &&
          (input_record.Event.KeyEvent.wVirtualKeyCode <= VK_F24))))
#endif
    {
      /***********************************************************************/
      /* Not an ascii char                                                   */
      /***********************************************************************/
#ifndef WIN32
      input_char = getch ();
#endif

      /***********************************************************************/
      /* Check for function keys and ALT-??                                  */
      /***********************************************************************/
#ifndef WIN32
      if (input_char == 60)               /* F2                              */
#else
      if (input_record.Event.KeyEvent.wVirtualKeyCode == VK_F2)
#endif
      {
        /*********************************************************************/
        /* Send a Clear.                                                     */
        /*********************************************************************/
        printf("sending clear\n");
        *write_data = 0x6d;
        write_len   = 1;
        data_to_send = TRUE;
      }
#ifndef WIN32
      else if ((input_char >= 120) && (input_char <= 129))
#else
      else if ((input_record.Event.KeyEvent.wVirtualKeyCode >= '0') &&
               (input_record.Event.KeyEvent.wVirtualKeyCode <= '9'))
#endif
      {
        /*********************************************************************/
        /* ALT-1 to ALT-0                                                    */
        /* 0xF1 is the AID code for PF1                                                              */
        /* Follow this with the cursor address                               */
        /*********************************************************************/
#ifndef WIN32
        *write_data = 0xF1 + input_char - 120;
#else
        *write_data = 0xF0 + input_record.Event.KeyEvent.wVirtualKeyCode - '0';
#endif
        *(write_data + 1) = 0x40;
        *(write_data + 2) = 0x40;
        write_len = 3;
        data_to_send = TRUE;

      }
    }
    else
#ifdef WIN32
        if ((input_record.EventType == KEY_EVENT ) &&
            (input_record.Event.KeyEvent.bKeyDown))
#endif
    {
      /***********************************************************************/
      /* An ASCII char                                                       */
      /***********************************************************************/

#ifdef WIN32
      input_char = input_record.Event.KeyEvent.uChar.AsciiChar;
#endif
      if (input_char == EXITKEY)
      {
        closedown();
      }
      else if (input_char == SWITKEY)
      {
        /*********************************************************************/
        /* Hit key to switch between sessions                                */
        /*********************************************************************/
        if (lu_session)
        {
          printf("SSCP session\n");
          lu_session = FALSE;
        }
        else
        {
          printf("LU session\n");
          lu_session = TRUE;
        }
      }
      else
      {
        /*********************************************************************/
        /* Key should be real data.  Add it to the string                    */
        /* For Windows NT echo the character to the screen                   */
        /*********************************************************************/

        if (input_char == 13)
        {
#ifdef WIN32
          WriteConsole (std_output, &input_char, 1, &no_read, NULL);
#endif
          /*******************************************************************/
          /* If user pressed return key then send the data.  Reset           */
          /* data_offset for next data.                                      */
          /*******************************************************************/
          write_data[data_offset++] = '\0';
          data_to_send = TRUE;
          data_offset  = 0;

          /*******************************************************************/
          /* Convert data to EBCDIC                                          */
          /*******************************************************************/
          convert_to_ebc.len = (unsigned short) (strlen(write_data) );
          if (convert_to_ebc.len > 0)
          {
            ACSSVC_C((LONG)((UCHAR FAR *)&convert_to_ebc));
          }
          write_len = convert_to_ebc.len;

          /*******************************************************************/
          /* Allow for ENTER AID if necessary                                */
          /*******************************************************************/
          if (lu_session)
          {
            write_data -= WRITE_EXTRA;
            write_len  += WRITE_EXTRA;
          }
        }
        else if (input_char == 8)
        {
#ifdef WIN32
          WriteConsole (std_output, &input_char, 1, &no_read, NULL);
#endif
          /*******************************************************************/
          /* Backspace.  Delete last char                                    */
          /*******************************************************************/
          if (data_offset > 0)
          {
            data_offset--;
          }
        }
        else if (input_char != 0)
        {
#ifdef WIN32
          WriteConsole (std_output, &input_char, 1, &no_read, NULL);
#endif
          write_data[data_offset++] = (UCHAR) input_char;
        }
      }
    }
  }  /* if (kbhit())                                                         */

  return(data_to_send);
}  /* do_keyboard_stuff                                                      */

/**PROC+**********************************************************************/
/* Name:      do_write                                                       */
/*                                                                           */
/* Purpose:   issue a write verb                                             */
/*                                                                           */
/* Returns:   BOOL - TRUE => data sent OK                                    */
/*                                                                           */
/* Params:    none                                                           */
/*                                                                           */
/* Operation: convert data to EBCDIC and send it on the right session        */
/*                                                                           */
/**PROC-**********************************************************************/
BOOL do_write (void)

{
  BOOL ok;

  ok = TRUE;

  /***************************************************************************/
  /* Set up the vcb                                                          */
  /***************************************************************************/
  memset(&other_verb, 0, sizeof(other_verb));
  other_verb.common.lua_verb             = LUA_VERB_RUI;
  other_verb.common.lua_verb_length      = sizeof(read_verb);
  other_verb.common.lua_opcode           = LUA_OPCODE_RUI_WRITE;
  other_verb.common.lua_sid              = sid;
  other_verb.common.lua_data_length      = write_len;
  other_verb.common.lua_data_ptr         = (char far *) write_data;
  other_verb.common.lua_post_handle      = WRITEPOST;
  other_verb.common.lua_rh.bci           = 1;
  other_verb.common.lua_rh.eci           = 1;
  other_verb.common.lua_rh.dr1i          = 1;

  if (lu_session)
  {
    /*************************************************************************/
    /* On the LU session we must add the <enter> key prefix.  All inbound    */
    /* requests flow RQE with the BBI and CDI flags set depending on the     */
    /* current session state.                                                */
    /*************************************************************************/
    other_verb.common.lua_flag1.lu_norm  = 1;
    other_verb.common.lua_rh.ri          = 1;
    if (send_state == BETB)
    {
      /***********************************************************************/
      /* Between bracket, so open bracket and give direction.  Note that we  */
      /* can do this since we will always be contention winner.              */
      /***********************************************************************/
      other_verb.common.lua_rh.bbi         = 1;
      other_verb.common.lua_rh.cdi         = 1;
      send_state = RECV;
    }
    else if (send_state = SEND)
    {
      /***********************************************************************/
      /* In bracket and we have direction, so simply give direction.         */
      /***********************************************************************/
      other_verb.common.lua_rh.cdi         = 1;
      send_state = RECV;
    }
    else
    {
      /***********************************************************************/
      /* In bracket and we do not have direction, so do not send.            */
      /***********************************************************************/
      printf("Wait\n");
      ok = FALSE;
    }
  }
  else
  {
    /*************************************************************************/
    /* On the SSCP session things are straightforward.                       */
    /*************************************************************************/
    other_verb.common.lua_flag1.sscp_norm  = 1;
  }

  if (ok)
  {
    /*************************************************************************/
    /* Issue write verb                                                      */
    /*************************************************************************/
    RUI((LUA_VERB_RECORD FAR *) &other_verb);

    /*************************************************************************/
    /* Reset write_data to point to correct point in write_array             */
    /*************************************************************************/
    write_data = write_array + WRITE_EXTRA;
  }

  return(ok);
}  /* do_write ()                                                            */

/**PROC+**********************************************************************/
/* Name:      parse_data                                                     */
/*                                                                           */
/* Purpose:   parse data from the host                                       */
/*                                                                           */
/* Returns:   void                                                           */
/*                                                                           */
/* Params:    IN  data - pointer to data                                     */
/*                                                                           */
/* Operation: Looks through data for read partition query from host.  Could  */
/* be expanded to format data from host                                      */
/*                                                                           */
/**PROC-**********************************************************************/
void parse_data (data, length)

UCHAR FAR * data;
USHORT      length;
{
  USHORT  field_length;

  switch (*(data++))
  {
    case 0xF3:                       /* write structured field               */
      /***********************************************************************/
      /* Next byte is the WCC - ignore                                       */
      /***********************************************************************/
      data++;
      length -= 2;

      while (length > 0)
      {
        /*********************************************************************/
        /* We're just looking for a Read Partion query                       */
        /*********************************************************************/
        field_length = (((USHORT) *data) << 8) || ((USHORT) *(data + 1));
        if ((*(data+2) == 0x01) &&   /* Read partition                       */
            (*(data+3) == 0xFF) &&   /* Query                                */
            (*(data+4) == 0x02))     /* not a list                           */
        {
          /*******************************************************************/
          /* Build an RPQ and flag it to be sent                             */
          /*******************************************************************/
          rpq_state = RPQ_QUEUED;
        }
        data   += field_length;
        length -= field_length;
      }

      break;

    default:
      break;

  }
}  /* parse_data                                                             */

/**PROC+**********************************************************************/
/* Name:      issue_rpq                                                      */
/*                                                                           */
/* Purpose:   issue a response to a Read partition query                     */
/*                                                                           */
/* Returns:   BOOL - TRUE => sent OK                                         */
/*                                                                           */
/* Params:    none                                                           */
/*                                                                           */
/* Operation: Builds and sends an RPQ reesponse                              */
/*                                                                           */
/**PROC-**********************************************************************/
BOOL issue_rpq (void)

{
  BOOL issue_rpq;

  issue_rpq = TRUE;
  /***************************************************************************/
  /* Set up the vcb                                                          */
  /***************************************************************************/
  memset(&rpq_verb, 0, sizeof(rpq_verb));
  rpq_verb.common.lua_verb             = LUA_VERB_RUI;
  rpq_verb.common.lua_verb_length      = sizeof(rpq_verb);
  rpq_verb.common.lua_opcode           = LUA_OPCODE_RUI_WRITE;
  rpq_verb.common.lua_sid              = sid;
  rpq_verb.common.lua_data_length      = RPQ_LENGTH;
  rpq_verb.common.lua_data_ptr         = (char far *) rpq_data;
  rpq_verb.common.lua_post_handle      = RPQPOST;
  rpq_verb.common.lua_rh.bci           = 1;
  rpq_verb.common.lua_rh.eci           = 1;
  rpq_verb.common.lua_rh.dr1i          = 1;

  /***************************************************************************/
  /* On the LU session we must add the <enter> key prefix.  All inbound      */
  /* requests flow RQE with the BBI and CDI flags set depending on the       */
  /* current session state.                                                  */
  /***************************************************************************/
  rpq_verb.common.lua_flag1.lu_norm  = 1;
  rpq_verb.common.lua_rh.ri          = 1;
  if (send_state == BETB)
  {
    /*************************************************************************/
    /* Between bracket, so open bracket and give direction.  Note that we    */
    /* can do this since we will always be contention winner.                */
    /*************************************************************************/
    rpq_verb.common.lua_rh.bbi         = 1;
    rpq_verb.common.lua_rh.cdi         = 1;
    send_state = RECV;
  }
  else if (send_state = SEND)
  {
    /*************************************************************************/
    /* In bracket and we have direction, so simply give direction.           */
    /*************************************************************************/
    rpq_verb.common.lua_rh.cdi         = 1;
    send_state = RECV;
  }
  else
  {
    /*************************************************************************/
    /* In bracket and we do not have direction, so do not send.              */
    /*************************************************************************/
    printf("Wait\n");
    issue_rpq = FALSE;
  }

  if (issue_rpq)
  {
    RUI((LUA_VERB_RECORD FAR *) &rpq_verb);
  }

  return(issue_rpq);
}  /* issue_rpq                                                              */


WRUI3270.C

/**MOD+***********************************************************************/
/* Module:  wrui3270.exe                                                     */
/*                                                                           */
/* Purpose: simple 3270 emulator                                             */
/*                                                                           */
/* (C) COPYRIGHT DATA CONNECTION LIMITED 1991                                */
/*                                                                           */
/**MOD-***********************************************************************/

/*****************************************************************************/
/* RUI Sample Program                                                        */
/*                                                                           */
/* (C) Copyright 1990 - 1993 Data Connection Ltd.                            */
/*                                                                           */
/* This program is a crude 3270 emulator.  It uses the RUI API to access     */
/* both the SSCP and LU sessions.  Outbound data from the host is            */
/* displayed on the screen unformatted.  Both SSCP and LU data are shown.    */
/* If the outbound data is an RQD request an automatic positive response     */
/* is sent.  Inbound data can be entered at the keyboard and is sent on      */
/* the current session.  This current session can be toggled between the     */
/* SSCP and LU sessions by hitting the [ (left square bracket) key           */
/* followed by <cr> (carriage return), although it switches                  */
/* automatically on receipt of BIND or UNBIND.                               */
/*                                                                           */
/* The program is invoked with a single parameter - the name of the LUA LU   */
/* to use.  This is converted to upper case and must match an LUA LU in      */
/* the configuration file.  This LU should be configured for a 327? on the   */
/* host.                                                                     */
/*                                                                           */
/* It works reasonably well with TSO provided that complex formatted         */
/* logon screens are not used.                                               */
/*                                                                           */
/*                                                                           */
/* The CSV CONVERT function is used to convert data between ASCII and        */
/* EBCDIC.  This uses the type G conversion table, so the environment        */
/* variable COMTBLG must be set to point to a suitable file.                 */
/*                                                                           */
/* Keys that work are:                                                       */
/*                                                                           */
/*     Alt-1 to Alt-0 are PF1 to PF0                                         */
/*     backspace                                                             */
/*     Letters, numbers and symbols                                          */
/*                                                                           */
/*****************************************************************************/

/*****************************************************************************/
/* SYSTEM INCLUDES                                                           */
/*****************************************************************************/
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <winlua.h>
#include <wincsv.h>

typedef unsigned char  UCHAR;
typedef unsigned short USHORT;
typedef unsigned long  ULONG;

/*****************************************************************************/
/* Program constants                                                         */
/*****************************************************************************/
#define DATASIZE 4096                  /* Max size of RU                     */
#define SWITKEY  0x5B                  /* '['   to switch sessions           */

#define BETB     1                     /* Between brackets                   */
#define SEND     2                     /* In bracket and can send            */
#define RECV     3                     /* In bracket, but cannot send        */

#define APPNAME                "WRUI3270"
#define RUI_WINDOW_MESSAGE     "WinRUI"

#define DISPLAY_BUFFER_SIZE    3200    /* size of circular display buffer    */

/*****************************************************************************/
/* State variables                                                           */
/*                                                                           */
/* We can only have one read or write verb outstanding per flow.  These      */
/* state variables keep track of these verbs and what we need to do with     */
/* them.                                                                     */
/*****************************************************************************/

UINT  write_state;
#define NO_WRITE              1
#define WRITE_OUTSTANDING     2        /* waiting for write to complete      */
#define WRITE_QUEUED          3        /* have data to write                 */

UINT  read_state;
#define NO_READ               1
#define READ_OUTSTANDING      2        /* read verb outstanding              */
#define RSP_QUEUED            3        /* have a response to write           */
#define RSP_OUTSTANDING       4        /* waiting for a response to complete */

UINT  rpq_state;
#define NO_RPQ                1
#define RPQ_OUTSTANDING       2        /* waiting for an RPQ to complete     */
#define RPQ_QUEUED            3        /* have RPQ to send                   */

/***************************************************************************/
/* Global variables                                                        */
/*                                                                         */
/* Inbound data is sent as typed on the SSCP session.  On the LU session   */
/* it is prefixed with the <enter> key AID plus a two byte cursor address. */
/***************************************************************************/
LUA_VERB_RECORD read_verb;             /* RUI read verb                    */
LUA_VERB_RECORD other_verb;            /* RUI init, write or term verb     */
LUA_VERB_RECORD rpq_verb;              /* RUI_WRITE for RPQs               */

UCHAR rpq_data[] = {

  0x88,
  0x00, 0x0C, 0x81, 0x80, 0x80, 0x81, 0x85, 0x86, 0x87, 0x88, 0x95, 0xA6,

  0x00, 0x17, 0x81, 0x81, 0x21, 0x00, 0x00, 0x50, 0x00, 0x18, 0x00, 0x00,
  0x0A, 0x02, 0xE5, 0x00, 0x02, 0x00, 0x6F, 0x09, 0x10, 0x07, 0x80,

  0x00, 0x14, 0x81, 0x85, 0x02, 0x00, 0x09, 0x0E, 0x00, 0x00, 0x00, 0x00,
  0x07, 0x00, 0x00, 0x00, 0x02, 0xB9, 0x01, 0x1D,

  0x00, 0x16, 0x81, 0x86, 0x00, 0x08, 0x00, 0xF4, 0xF1, 0x00, 0xF2, 0x00,
  0xF3, 0x00, 0xF4, 0x00, 0xF5, 0x00, 0xF6, 0x00, 0xF7, 0x00,

  0x00, 0x0D, 0x81, 0x87, 0x04, 0x00, 0xF0, 0xF1, 0x00, 0xF2, 0x00, 0xF4,
  0x00,

  0x00, 0x07, 0x81, 0x88, 0x00, 0x01, 0x02,

  0x00, 0x0C, 0x81, 0x95, 0x00, 0x00, 0x7F, 0xFF, 0x7F, 0xFF, 0x01, 0x01,

  0x00, 0x11, 0x81, 0xA6, 0x00, 0x00, 0x0B, 0x01, 0x00, 0x00, 0x50, 0x00,
  0x18, 0x00, 0x50, 0x00, 0x18};

#define RPQ_LENGTH (sizeof(rpq_data))

UCHAR display_buffer[DISPLAY_BUFFER_SIZE] = {0};
UINT  buffer_end   = 0;             /* end of data in display buffer         */
BOOL  wrapped      = FALSE;         /* has buffer wrapped?                   */

/*****************************************************************************/
/* Global Variables                                                          */
/*****************************************************************************/
HANDLE hInst = 0;                   /* current instance                      */
UINT   verb_complete = 0;           /* verb completion message               */
HANDLE hWnd = 0;                    /* Handle to program window              */

BOOL   init_done = FALSE;           /* RUI_INIT completed                    */

unsigned char read_data[DATASIZE];     /* Outbound RU                       */
#define WRITE_EXTRA 6
unsigned char write_array[DATASIZE + WRITE_EXTRA] =
                                          {0x7d, 0x40, 0x40, 0x11, 0x40, 0x40};
                                       /* Inbound RU                         */
unsigned char *write_data = write_array + WRITE_EXTRA;
                                       /* Pointer to inbound RU              */
UINT data_offset = 0;                  /* offset into read_data              */
UINT write_len   = 0;                  /* length of write data               */

struct convert convert_to_asc;         /* Outbound convert verb              */
struct convert convert_to_ebc;         /* Inbound convert verb               */

BOOL terminating = FALSE;              /* are we already in closedown() ?    */
BOOL lu_session  = FALSE;              /* LU session inbound flag            */

unsigned long sense = 0l;              /* sense code                         */

unsigned char lu_name[9] = "        "; /* LU name                            */
unsigned long sid;                     /* RUI session ID                     */
int send_state = BETB;                 /* LU session send state              */
int last_status = WLUAGETLU;           /* Current status of Session     *STAT*/

UCHAR  string[64]={0};

void pascal far LOADDS RUI (LUA_VERB_RECORD FAR *);

extern int      WINAPI GetFmiReturnCode(unsigned short,                /*STAT*/
                                        unsigned short,                /*STAT*/
                                        unsigned short,                /*STAT*/
                                        unsigned char FAR *);          /*STAT*/

void            csv_init            (void);
void            issue_init          (void);
void            issue_read          (void);
void            other_done          (LUA_VERB_RECORD FAR *verb);
void            read_done           (LUA_VERB_RECORD FAR *verb);
void            issue_rsp           (unsigned long  sense);
void            rsp_done            (LUA_VERB_RECORD FAR *verb);
void            closedown           (void);
BOOL            do_write            (void);
void            wait_verb_complete  (LUA_VERB_RECORD FAR *verb);
void            set_semaphore       (LUA_VERB_RECORD FAR *verb);
BOOL            do_keyboard_stuff   (UINT, UCHAR);
BOOL            check_verb_complete (LUA_VERB_RECORD FAR *verb);
void            parse_data          (UCHAR FAR *, USHORT);
BOOL            issue_rpq           (void);
void            verb_complete_proc  (LUA_VERB_RECORD FAR * verb);
void          add_to_display_buffer (UCHAR FAR *);

/*****************************************************************************/
/* Windows function prototypes.                                              */
/*****************************************************************************/
int PASCAL WinMain(HINSTANCE, HINSTANCE, LPSTR, int);
long FAR PASCAL WndProc(HWND, UINT, UINT, LONG);

/***PROC+*********************************************************************/
/* Name:     WinMain                                                         */
/*                                                                           */
/* Purpose:  Main Windows function                                           */
/*                                                                           */
/* Params:   IN current instance handle                                      */
/*           IN previous instance handle                                     */
/*           IN pointer to command line                                      */
/*           IN show window type                                             */
/*                                                                           */
/* Returns:  int                                                             */
/*                                                                           */
/**PROC-**********************************************************************/

int PASCAL WinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow)

HINSTANCE hInstance;                         /* current instance             */
HINSTANCE hPrevInstance;                     /* previous instance            */
LPSTR     lpCmdLine;                         /* command line                 */
int       nCmdShow;                          /* show-window type (open/icon) */
{
  /***************************************************************************/
  /* LOCAL VARIABLES                                                         */
  /***************************************************************************/
  MSG   msg;                                 /* message                      */
  int   xscreen;
  int   yscreen;
  int   delta;
  WNDCLASS class;
  LUADATA  LUAData;

  if (!hPrevInstance)
  {
    class.style         = CS_HREDRAW | CS_VREDRAW;
    class.lpfnWndProc   = (WNDPROC)WndProc;
    class.cbClsExtra    = 0;
    class.cbWndExtra    = 0;
    class.hInstance     = hInstance;
    class.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
    class.hCursor       = LoadCursor(NULL, IDC_ARROW);
    class.hbrBackground = GetStockObject(WHITE_BRUSH);
    class.lpszMenuName  = NULL;
    class.lpszClassName = APPNAME;

    if (!RegisterClass(&class))
    {
      return (0);
    }
  }

  /***************************************************************************/
  /* save current instance handle                                            */
  /***************************************************************************/
  hInst = hInstance;

  /***************************************************************************/
  /* Set initial states                                                      */
  /***************************************************************************/
  write_state = NO_WRITE;
  read_state  = NO_READ;
  rpq_state   = NO_RPQ;

  /***************************************************************************/
  /* Set up CSV convert verbs                                                */
  /***************************************************************************/
  csv_init();

  /***************************************************************************/
  /* get details of display                                                  */
  /***************************************************************************/
  xscreen = GetSystemMetrics(SM_CXSCREEN);
  yscreen = GetSystemMetrics(SM_CYSCREEN);

  /***************************************************************************/
  /* invent somewhere to put window                                          */
  /***************************************************************************/
  delta = xscreen / 100;

  /***************************************************************************/
  /* create display window                                                   */
  /***************************************************************************/
  hWnd = CreateWindow(APPNAME,                    /* window class            */
        "RUI3270",                                /* window name             */
        WS_OVERLAPPEDWINDOW,                      /* window style            */
        (xscreen/2) + delta,                      /* x position              */
        yscreen / 8,                              /* y position              */
        (xscreen / 2) - (delta * 2),              /* width                   */
        3 * yscreen / 4,                          /* height                  */
        NULL,                                     /* parent handle           */
        NULL,                                     /* menu or child ID        */
        hInstance,                                /* instance                */
        NULL);                                    /* additional info         */

  if (!hWnd)
  {
    return (0);
  }

  ShowWindow(hWnd, nCmdShow);                     /* Shows the window        */
  UpdateWindow(hWnd);                             /* Sends WM_PAINT message  */

  /***************************************************************************/
  /* Register message used for async completion                              */
  /***************************************************************************/
  verb_complete = RegisterWindowMessage(RUI_WINDOW_MESSAGE);

  if (verb_complete == 0)
  {
    return (0);
  }

  WinRUIStartup (0x0001, &LUAData);

  /***************************************************************************/
  /* Issue RUI_INIT to get the ball rolling.  Setup up the LU name           */
  /***************************************************************************/
  _fmemcpy ((UCHAR FAR *)lu_name,
            lpCmdLine,
            _fstrlen(lpCmdLine));
  issue_init ();

  while(GetMessage(&msg,NULL,0,0))
  {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }

  return msg.wParam;         /* save exit parameter for return               */
}

/***PROC+*********************************************************************/
/* Name:     WndProc                                                         */
/*                                                                           */
/* Purpose:  Windows call-back routine                                       */
/*                                                                           */
/* Params:   IN window handle                                                */
/*           IN message type                                                 */
/*           IN additional information 1                                     */
/*           IN additional information 2                                     */
/*                                                                           */
/* Returns:  long                                                            */
/*                                                                           */
/* Operation:set up window characteristics and register it                   */
/*           return success or failure                                       */
/*                                                                           */
/**PROC-**********************************************************************/

long FAR PASCAL WndProc(hWnd, message, wParam, lParam)
HWND hWnd;                                /* window handle                   */
UINT message;                             /* type of message                 */
UINT wParam;                              /* additional information          */
LONG lParam;                              /* additional information          */
{
  /***************************************************************************/
  /* LOCAL VARIABLES                                                         */
  /***************************************************************************/
  HDC          hDC;              /* handle to our device context             */
  TEXTMETRIC   tm;               /* holds info on the current font           */
  PAINTSTRUCT  ps;               /*  paint structure                         */
  RECT         rc;               /* holds info on the size oof our window    */
  ULONG        width;            /* width of window in chars                 */
  ULONG        height;           /* height of window in chars                */
  UINT         buffer_offset;    /* offset withing display buffer            */
  UINT         buffer_length;    /* length of data in buffer                 */
  UINT         out_length;       /* lngth of text to output                  */
  UINT         out_length2;      /* and again                                */
  static int   xChar, yChar;     /* width & height of the average char       */
  int          position;         /* number of lines down the screen          */
  BOOL         whole_data;       /* do we have data to send?                 */
  char         status_buff[80];  /* Current status                      *STAT*/

  if ((message == verb_complete) &&
      (!terminating))
  {
    /*************************************************************************/
    /* Message is RUI verb completed.  We are not terminating, so process    */
    /* the message.                                                          */
    /*************************************************************************/
    if (wParam == 0)                                                   /*STAT*/
    {                                                                  /*STAT*/
      verb_complete_proc ((LUA_VERB_RECORD FAR *) lParam);
    }                                                                  /*STAT*/
    else                                                               /*STAT*/
    {                                                                  /*STAT*/
      /***********************************************************************/
      /* Message is Notification of LUA Session State Change            *STAT*/
      /***********************************************************************/
      last_status = WinRUIGetLastInitStatus(lParam,                    /*STAT*/
                                            NULL,                      /*STAT*/
                                            WLUA_NTFY_MSG_SID,         /*STAT*/
                                            0);                        /*STAT*/
      /******************************************************************STAT*/
      /* Cause the window to be re-painted                              *STAT*/
      /******************************************************************STAT*/
      InvalidateRect (hWnd, NULL, TRUE);                               /*STAT*/
    }                                                                  /*STAT*/
    return(0l);
  }

  switch (message)
  {
    case WM_CREATE:
      /***********************************************************************/
      /* Window creation message.  Get some infomation about our font and    */
      /* save it for later                                                   */
      /***********************************************************************/
      hDC= GetDC(hWnd);
      GetTextMetrics (hDC, &tm);
      xChar = (int)tm.tmAveCharWidth;
      yChar = (int)(tm.tmHeight + tm.tmExternalLeading);
      ReleaseDC(hWnd, hDC);
      return 0l;
      break;

    case WM_CHAR:
    case WM_SYSCHAR:
      /***********************************************************************/
      /* Character or Alt+char hit                                           */
      /***********************************************************************/
      if ((init_done)                 &&
          (!terminating)              &&
          (write_state == NO_WRITE)   &&
          (rpq_state   == NO_RPQ)     &&
          (read_state  != RSP_QUEUED) &&
          (read_state  != RSP_OUTSTANDING))
      {
        /*********************************************************************/
        /* Someone pressed a key and we are in a state to deal with it       */
        /*********************************************************************/
        whole_data = do_keyboard_stuff (message, (UCHAR)wParam);
        if (whole_data)
        {
          /*******************************************************************/
          /* We have data to send                                            */
          /* Attempt to write the data out                                   */
          /*******************************************************************/
          if (do_write())
          {
            write_state = WRITE_OUTSTANDING;
          }
          else
          {
            write_state = WRITE_QUEUED;
          }
        }
        return(0l);
      }
      break;

    case WM_PAINT:
      BeginPaint (hWnd, &ps);
      /***********************************************************************/
      /* Determine the size of the Window in characters                      */
      /***********************************************************************/
      GetClientRect (hWnd, &rc);
      height = rc.bottom / yChar;
      width  = rc.right  / xChar;
      /***********************************************************************/
      /* subtract 2 from height for the status line                          */
      /***********************************************************************/
      height -= 2;
      /***********************************************************************/
      /* Work out how early in the buffer we can start                       */
      /***********************************************************************/
      if (!wrapped)
      {
        /*********************************************************************/
        /* buffer is not wrapped                                             */
        /*********************************************************************/
        buffer_length = buffer_end;
        buffer_offset = (buffer_length > (width * height)) ?
                         buffer_end - (width * height) :
                         0;
      }
      else
      {
        /*********************************************************************/
        /* buffer is wrapped                                                 */
        /*********************************************************************/
        buffer_length = DISPLAY_BUFFER_SIZE;
        if (buffer_length > width * height)
        {
          /*******************************************************************/
          /* buffer is too big.  Work out where best to start in the buffer/ */
          /*******************************************************************/
          if (buffer_end > width * height)
          {
            buffer_offset = buffer_end - (width * height);
          }
          else
          {
            buffer_offset = DISPLAY_BUFFER_SIZE +
                            buffer_end -
                            (width * height);
          }
        }
        else
        {
          /*******************************************************************/
          /* We can fit everything in                                        */
          /*******************************************************************/
          buffer_offset = buffer_end;
        }
      }
      /***********************************************************************/
      /* We've worked out where to start printing from, so start printing    */
      /***********************************************************************/
      position = 0;
      out_length = width;
      if (buffer_offset > buffer_end)
      {
        while (buffer_offset < DISPLAY_BUFFER_SIZE)
        {
          if (DISPLAY_BUFFER_SIZE - buffer_offset < width)
          {
            out_length = DISPLAY_BUFFER_SIZE - buffer_offset;
          }
          else
          {
            out_length = width;
          }
          TextOut(ps.hdc,
                  0,
                  yChar * position,
                  display_buffer + buffer_offset,
                  out_length);
          buffer_offset += out_length;
          position++;
        }
        buffer_offset = 0;
      }
      /***********************************************************************/
      /* If last line printed was a partial line, print the rest of the line */
      /***********************************************************************/
      if (out_length < width)
      {
        out_length2 = out_length;
        if (buffer_end < (width - out_length))
        {
          out_length = buffer_end;
        }
        else
        {
          out_length = width - out_length;
        }
        TextOut(ps.hdc,
                xChar * out_length2,
                yChar * (position - 1),
                display_buffer,
                out_length);
        buffer_offset += out_length;

      }
      /***********************************************************************/
      /* Now print to the end of the buffer                                  */
      /***********************************************************************/
      while (buffer_offset < buffer_end)
      {
        if (buffer_end - buffer_offset < width)
        {
          out_length = buffer_end - buffer_offset;
        }
        else
        {
          out_length = width;
        }
        TextOut(ps.hdc,
                0,
                yChar * position,
                display_buffer + buffer_offset,
                out_length);
        buffer_offset += out_length;
        position++;
      }

      /******************************************************************STAT*/
      /* Convert current status into display string                     *STAT*/
      /******************************************************************STAT*/
      GetFmiReturnCode(last_status,                                    /*STAT*/
                       (unsigned short) 0xffff,                        /*STAT*/
                       (unsigned short) 80, status_buff);              /*STAT*/

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

/* Now fill in the status line                                         */
      /***********************************************************************/
      out_length  = wsprintf (string,
                              "%s     %s     %s",                      /*STAT*/
                   (UCHAR FAR *)((send_state == BETB) ? "BETB" : "INB"),
                   (UCHAR FAR *)((lu_session)         ? "LU"   : "SSCP"),
                   (UCHAR FAR *)status_buff);                         /*STAT*/
      TextOut(ps.hdc,
              0,
              (UINT) (yChar * (height + 2)),
              string,
              out_length);
      /***********************************************************************/
      /* Now write the input line.  Only in NO_WRITE state, o.w.  data is in */
      /* EBCDIC                                                              */
      /***********************************************************************/
      if (write_state == NO_WRITE)
      {
        TextOut(ps.hdc,
                0,
                (UINT) (yChar * (height+1)),
                write_data,
                data_offset);
      }
      EndPaint(hWnd, &ps);
      break;

    case WM_CLOSE:
      if (!terminating)
      {
        /*********************************************************************/
        /* Tell the RUI we are going                                         */
        /*********************************************************************/
        terminating = TRUE;
        WinRUICleanup();
      }
      break;

    case WM_DESTROY:
      PostQuitMessage(0);
      return 0l;
      break;

    default:
      break;
  }
  return (DefWindowProc(hWnd, message, wParam, lParam));
}

/**PROC+**********************************************************************/
/* Name:      verb_complete_proc                                             */
/*                                                                           */
/* Purpose:   deal with verb completion                                      */
/*                                                                           */
/* Returns:   void                                                           */
/*                                                                           */
/* Params:    IN verb - pointer to completed verb                            */
/*                                                                           */
/* Operation: Determine which verb has completed and perform the appropriate */
/* action                                                                    */
/**PROC-**********************************************************************/
VOID verb_complete_proc (verb)

LUA_VERB_RECORD FAR * verb;
{

  switch (verb->common.lua_opcode)
  {
    case LUA_OPCODE_RUI_INIT:
      /***********************************************************************/
      /* Received ACTLU.  Save the SID for use on other verbs, flag init     */
      /* done so that we can write data, and issue first read                */
      /***********************************************************************/
      sid = other_verb.common.lua_sid;
      init_done = TRUE;
      issue_read();
      break;

    case LUA_OPCODE_RUI_READ:
      /***********************************************************************/
      /* Read completed.  Call read dne processor to display data and        */
      /* determine if a response is required.                                */
      /* If no response is required issue a new read                         */
      /* if a response is required issue the response if we can              */
      /***********************************************************************/
      read_done (verb);
      if (terminating == TRUE)
        break;
      if (read_state == NO_READ)
      {
        /*********************************************************************/
        /* No work to do, issue a new read verb                              */
        /*********************************************************************/
        read_state = READ_OUTSTANDING;
        issue_read();
      }
      else if ((read_state  == RSP_QUEUED) &&
               (write_state != WRITE_OUTSTANDING) &&
               (rpq_state   != RPQ_OUTSTANDING))
      {
        /*********************************************************************/
        /* We need to send a response to the RU we have just received.  The  */
        /* write flow is free, so send it                                    */
        /*********************************************************************/
        read_state = RSP_OUTSTANDING;
        issue_rsp (sense);
      }

      if ((rpq_state   == RPQ_QUEUED) &&
          (write_state != WRITE_OUTSTANDING) &&
          (read_state  != RSP_OUTSTANDING))
      {
        /*********************************************************************/
        /* We need to send a read partition query response and we can, so do */
        /* it.                                                               */
        /*********************************************************************/
        if (issue_rpq())
        {
          rpq_state = RPQ_OUTSTANDING;
        }
      }
      break;

    case LUA_OPCODE_RUI_WRITE:
      /***********************************************************************/
      /* Determine what kind of RUI_WRITE completed                          */
      /* either                                                              */
      /* -  writing user data                                                */
      /* -  writing a RPQ response                                           */
      /* -  writing a RSP to host data                                       */
      /***********************************************************************/
      if (write_state == WRITE_OUTSTANDING)
      {
        write_state = NO_WRITE;
        write_data[0] = '\0';
      }
      else if (rpq_state == RPQ_OUTSTANDING)
      {
        rpq_state = NO_RPQ;
      }
      else if (read_state == RSP_OUTSTANDING)
      {
        read_state = NO_READ;
        issue_read();
      }
      /***********************************************************************/
      /* Perform the next action depending on what work their is to do.      */
      /***********************************************************************/
      if (read_state == RSP_QUEUED)
      {
        /*********************************************************************/
        /* We need to issue a RSP to an RU we have received.  Do it          */
        /*********************************************************************/
        read_state = RSP_OUTSTANDING;
        issue_rsp (sense);
      }
      else if (rpq_state == RPQ_QUEUED)
      {
        /*********************************************************************/
        /* We need to issue a response to a read partion query.  Send it if  */
        /* the bracket state allows.                                         */
        /*********************************************************************/
        if (issue_rpq())
        {
          rpq_state = RPQ_OUTSTANDING;
        }
      }
      else if (write_state == WRITE_QUEUED)
      {
        /*********************************************************************/
        /* We have user data to send to the Host.  Send it if the bracket    */
        /* state allows                                                      */
        /*********************************************************************/
        if (do_write())
        {
          write_state = WRITE_OUTSTANDING;
        }
      }
      break;

    default:
      break;
  }
}  /* verb_complete_proc                                                     */

/**PROC+**********************************************************************/
/* Name:      csv_init                                                       */
/*                                                                           */
/* Purpose:   set up CSV convert verbs                                       */
/*                                                                           */
/* Returns:   void                                                           */
/*                                                                           */
/* Params:    none                                                           */
/*                                                                           */
/* Operation: sets uup two CSV convert verbs, one in each direction.  Only   */
/* the length needs to be set when the verb is called                        */
/**PROC-**********************************************************************/
void csv_init (void)
{
  convert_to_asc.opcode       = SV_CONVERT;
  convert_to_asc.direction    = SV_EBCDIC_TO_ASCII;
  convert_to_asc.char_set     = SV_G;
  convert_to_asc.source       = read_data;
  convert_to_asc.target       = read_data;

  convert_to_ebc.opcode       = SV_CONVERT;
  convert_to_ebc.direction    = SV_ASCII_TO_EBCDIC;
  convert_to_ebc.char_set     = SV_G;
  convert_to_ebc.source       = write_data;
  convert_to_ebc.target       = write_data;
}  /* csv_init                                                               */

/**PROC+**********************************************************************/
/* Name:      issue_init                                                     */
/*                                                                           */
/* Purpose:   build a verb and send it                                       */
/*                                                                           */
/* Returns:   void                                                           */
/*                                                                           */
/* Params:    None                                                           */
/*                                                                           */
/* Operation: Build an INIT verb and issue it.                               */
/**PROC-**********************************************************************/
void issue_init(void)
{
  memset(&other_verb, 0, sizeof(other_verb));
  other_verb.common.lua_verb             = LUA_VERB_RUI;
  other_verb.common.lua_verb_length      = sizeof(other_verb);
  other_verb.common.lua_opcode           = LUA_OPCODE_RUI_INIT;
  memcpy(other_verb.common.lua_luname, lu_name, 8);

  WinRUI(hWnd, (LUA_VERB_RECORD FAR *) &other_verb);

  /***************************************************************************/
  /* The sid is valid immediately.  Save it now.                             */
  /***************************************************************************/
  sid = other_verb.common.lua_sid;

  last_status = WinRUIGetLastInitStatus(0, hWnd, WLUA_NTFY_MSG_SID, 0);/*STAT*/

  if (!other_verb.common.lua_flag2.async)
  {
    /*************************************************************************/
    /* Verb completed immediately.  Post our own completion message          */
    /*************************************************************************/
    PostMessage (hWnd, verb_complete, 0, (LPARAM) (VOID FAR *)&other_verb);
  }

}  /* issue_init                                                             */

/**PROC+**********************************************************************/
/* Name:      issue_read                                                     */
/*                                                                           */
/* Purpose:   build a read verb and send it                                  */
/*                                                                           */
/* Returns:   void                                                           */
/*                                                                           */
/* Params:    none                                                           */
/*                                                                           */
/* Operation: Build a read verb for all flows and issue it                   */
/**PROC-**********************************************************************/
void issue_read (void)
{
  memset(&read_verb, 0, sizeof(read_verb));
  memset(read_data, 0, DATASIZE);
  read_verb.common.lua_verb             = LUA_VERB_RUI;
  read_verb.common.lua_verb_length      = sizeof(read_verb);
  read_verb.common.lua_opcode           = LUA_OPCODE_RUI_READ;
  read_verb.common.lua_sid              = sid;
  read_verb.common.lua_max_length       = DATASIZE;
  read_verb.common.lua_data_ptr         = (char FAR *) read_data;
  read_verb.common.lua_flag1.lu_norm    = 1;
  read_verb.common.lua_flag1.lu_exp     = 1;
  read_verb.common.lua_flag1.sscp_norm  = 1;
  read_verb.common.lua_flag1.sscp_exp   = 1;
  WinRUI(hWnd, (LUA_VERB_RECORD FAR *)&(read_verb));
  if (!read_verb.common.lua_flag2.async)
  {
    /*************************************************************************/
    /* Verb completed immediately.  Post our own completion message          */
    /*************************************************************************/
    PostMessage (hWnd, verb_complete, 0, (LPARAM) (VOID FAR *)&read_verb);
  }

}  /* issue_read                                                             */

/**PROC+**********************************************************************/
/* Name:      read_done                                                      */
/*                                                                           */
/* Purpose:   handle completed read verb                                     */
/*                                                                           */
/* Returns:   void                                                           */
/*                                                                           */
/* Params:    IN verb - completed read verb                                  */
/*                                                                           */
/* Operation: If the completed verb contains data, first parse it and then   */
/*            display it.                                                    */
/*            If message is a BIND or an UNBIND, set the LU-LU session state */
/*            accordingly                                                    */
/*            Also check for EB, CD and change session state flags           */
/**PROC-**********************************************************************/
void read_done(verb)
LUA_VERB_RECORD FAR * verb;
{
  BOOL           rsp_required = FALSE; /* is a resonse required              */

  sense = 0l;

  if (verb->common.lua_prim_rc == LUA_OK)
  {
    if ((verb->common.lua_message_type == LUA_MESSAGE_TYPE_LU_DATA)   ||
        (verb->common.lua_message_type == LUA_MESSAGE_TYPE_SSCP_DATA))
    {
      if (verb->common.lua_data_length > 0)
      {
        /*********************************************************************/
        /* We now have to parse the data.  For the moment we only look for   */
        /* RPQ requests.                                                     */
        /*********************************************************************/
        parse_data (read_data, (USHORT) (verb->common.lua_data_length));

        /*********************************************************************/
        /* Display outbound SSCP or LU data.                                 */
        /*********************************************************************/
        convert_to_asc.len = (unsigned short) verb->common.lua_data_length;
        ACSSVC_C((LONG)((UCHAR FAR *)&convert_to_asc));
        add_to_display_buffer (read_data);
      }

      /***********************************************************************/
      /* Set session state.                                                  */
      /***********************************************************************/
      if (verb->common.lua_rh.ebi)
      {
        send_state = BETB;
      }
      else if (verb->common.lua_rh.cdi)
      {
        send_state = SEND;
      }
      else
      {
        send_state = RECV;
      }
      InvalidateRect (hWnd, NULL, TRUE);
    }
    /*************************************************************************/
    /* Handle BIND or UNBIND as a special case.                              */
    /*************************************************************************/
    else if (verb->common.lua_message_type == LUA_MESSAGE_TYPE_BIND)
    {
      if (((read_data[6] & 0x20) == 0x20)  &&      /* Brackets used and BETB */
          ((read_data[7] & 0xF0) == 0x80)  &&      /* HDX-FF, sec con winner */
          ( read_data[14]        == 0x02))         /* LU type 2              */
      {
        InvalidateRect (hWnd, NULL, TRUE);
        send_state = BETB;
        lu_session = TRUE;
      }
      else
      {
        sense = LUA_INVALID_SESSION_PARAMETERS;
      }
    }
    else if (verb->common.lua_message_type == LUA_MESSAGE_TYPE_UNBIND)
    {
      send_state = BETB;
      lu_session = FALSE;
      InvalidateRect (hWnd, NULL, TRUE);
    }

    /*************************************************************************/
    /* Respond to any RQD request.                                           */
    /*************************************************************************/
    if ((verb->common.lua_message_type != LUA_MESSAGE_TYPE_RSP)  &&
        (verb->common.lua_rh.ri == 0))                  /* definite response */
    {
      rsp_required = TRUE;
    }
  }
  else         /* primary rc not OK - read failed - stop now */
  {
    closedown();
  }

  /***************************************************************************/
  /* Continue processing with either another read or a write positive rsp.   */
  /* (The callback from issuing response will then issue another read) If    */
  /* we've already gone into closedown, stop here                            */
  /***************************************************************************/
  if (!terminating)
  {
    if (rsp_required)
    {
      read_state = RSP_QUEUED;
    }
    else
    {
      read_state = NO_READ;
    }
  }
}  /* read_done                                                              */

/**PROC+**********************************************************************/
/* Name:      issue_rsp                                                      */
/*                                                                           */
/* Purpose:   issue a response for a request from the host                   */
/*                                                                           */
/* Returns:   void                                                           */
/*                                                                           */
/* Params:    IN sense - sense code for the response                         */
/*                                                                           */
/* Operation: Build a response and write it out.  Uses the read verb control */
/*            block because it has the necessary information.                */
/*                                                                           */
/**PROC-**********************************************************************/
void issue_rsp(sense)
unsigned long sense;
{
  read_verb.common.lua_opcode           = LUA_OPCODE_RUI_WRITE;
  read_verb.common.lua_max_length       = 0;
  read_verb.common.lua_rh.rri           = 1;       /* response             */
  read_verb.common.lua_flag1.lu_norm    = 0;
  read_verb.common.lua_flag1.lu_exp     = 0;
  read_verb.common.lua_flag1.sscp_norm  = 0;
  read_verb.common.lua_flag1.sscp_exp   = 0;

  /***************************************************************************/
  /* If we have been given a sense code this must be a negative response     */
  /***************************************************************************/
  if (sense)
  {
    read_verb.common.lua_data_length      = 4;
    memcpy(read_data, &sense, 4);
    read_verb.common.lua_rh.ri            = 1;       /* negative rsp         */
  }
  else
  {
    read_verb.common.lua_data_length      = 0;
    read_verb.common.lua_rh.ri            = 0;       /* positive rsp         */
  }
  /***************************************************************************/
  /* Send the response back on the flow from which the request came          */
  /***************************************************************************/
  if (read_verb.common.lua_flag2.lu_norm)
  {
    read_verb.common.lua_flag1.lu_norm = 1;
  }
  else if (read_verb.common.lua_flag2.lu_exp)
  {
    read_verb.common.lua_flag1.lu_exp = 1;
  }
  else if (read_verb.common.lua_flag2.sscp_norm)
  {
    read_verb.common.lua_flag1.sscp_norm = 1;
  }
  else if (read_verb.common.lua_flag2.sscp_exp)
  {
    read_verb.common.lua_flag1.sscp_exp = 1;
  }
  /***************************************************************************/
  /* Send out the verb                                                       */
  /***************************************************************************/
  WinRUI(hWnd, (LUA_VERB_RECORD FAR *)&read_verb);     /* write the response */
  if (!read_verb.common.lua_flag2.async)
  {
    /*************************************************************************/
    /* Verb completed immediately.  Post our own completion message          */
    /*************************************************************************/
    PostMessage (hWnd, verb_complete, 0, (LPARAM) (VOID FAR *)&read_verb);
  }
}  /* issue_rsp                                                              */

/**PROC+**********************************************************************/
/* Name:      rsp_done                                                       */
/*                                                                           */
/* Purpose:   handle completion of RUI_WRITE, when RUI_WRITE has written a   */
/*            response                                                       */
/*                                                                           */
/* Returns:   void                                                           */
/*                                                                           */
/* Params:    IN verb - completed verb                                       */
/*                                                                           */
/* Operation: If verb did not complete OK terminate                          */
/*                                                                           */
/**PROC-**********************************************************************/
void rsp_done (verb)
LUA_VERB_RECORD FAR * verb;
{
  if (verb->common.lua_prim_rc != LUA_OK)   /* failed to write the response */
  {                                         /* so stop here                 */
    closedown();
  }
}  /* rsp_done                                                               */

/**PROC+**********************************************************************/
/* Name:      closedown                                                      */
/*                                                                           */
/* Purpose:   terminate the application cleanly                              */
/*                                                                           */
/* Returns:   void                                                           */
/*                                                                           */
/* Params:    none                                                           */
/*                                                                           */
/* Operation: Issue a WinRUICleanup() to tell RUI the we are about to go,    */
/* and then post a close message so that we do.                              */
/*                                                                           */
/**PROC-**********************************************************************/
void closedown (void)
{
  if (!terminating)                /* check we haven't already got here from */
  {                                /* another section of the code            */
    terminating = TRUE;            /* then make sure we can't get here again */
    WinRUICleanup();
    PostMessage (hWnd, WM_CLOSE, 0, 0l);
  }
}  /* closedown                                                              */

/**PROC+**********************************************************************/
/* Name:      do_keyboard_stuff                                              */
/*                                                                           */
/* Purpose:   poll keyboard for data                                         */
/*                                                                           */
/* Returns:   BOOL - TRUE => we have data to send                            */
/*                                                                           */
/* Params:    none                                                           */
/*                                                                           */
/* Operation: Look for Alt-1 to Alt-9 and ascii chars.                       */
/*                                                                           */
/**PROC-**********************************************************************/
BOOL do_keyboard_stuff (type, key)

UINT  type;
UCHAR key;

{
  BOOL   data_to_send;             /* do we have data to send?               */

  data_to_send = FALSE;

  if (type == WM_SYSCHAR)
  {
    /*************************************************************************/
    /* Alt key pressed                                                       */
    /*************************************************************************/
    if ((key >= '1') &&
        (key <= '9'))
    {
      /***********************************************************************/
      /* ALT-1 to ALT-9 0xF1 is the AID code for PF1.  Follow this with the  */
      /* cursor address                                                      */
      /***********************************************************************/
      *write_data = 0xF1 + key - '1';
      *(write_data + 1) = 0x40;
      *(write_data + 2) = 0x40;
      write_len = 3;
      data_to_send = TRUE;
    }
  }
  else
  {
    /*************************************************************************/
    /* Ordinary ASCII char                                                   */
    /*************************************************************************/
    if (key == SWITKEY)
    {
      /***********************************************************************/
      /* Hit key to switch between sessions                                  */
      /***********************************************************************/
      if (lu_session)
      {
        lu_session = FALSE;
      }
      else
      {
        lu_session = TRUE;
      }
      InvalidateRect (hWnd, NULL, TRUE);
    }
    else
    {
      /***********************************************************************/
      /* Key should be real data.  Add it to the string                      */
      /***********************************************************************/
      if (key == 13)
      {
        /*********************************************************************/
        /* If user pressed return key then send the data.  Reset data_offset */
        /* for next data.                                                    */
        /*********************************************************************/
        write_data[data_offset++] = '\0';
        data_to_send = TRUE;
        data_offset  = 0;

        /*********************************************************************/
        /* Convert data to EBCDIC                                            */
        /*********************************************************************/
        convert_to_ebc.len = (unsigned short) (strlen(write_data) );
        if (convert_to_ebc.len > 0)
        {
          ACSSVC_C((LONG)((UCHAR FAR *)&convert_to_ebc));
        }
        write_len = convert_to_ebc.len;

        /*********************************************************************/
        /* Allow for ENTER AID if necessary                                  */
        /*********************************************************************/
        if (lu_session)
        {
          write_data -= WRITE_EXTRA;
          write_len  += WRITE_EXTRA;
        }
      }
      else if (key == 8)
      {
        /*********************************************************************/
        /* Backspace.  Delete last char                                      */
        /*********************************************************************/
        if (data_offset > 0)
        {
          data_offset--;
        }
      }
      else if (key != 0)
      {
        /*********************************************************************/
        /* ASCII char.  Add it to the data buffer                            */
        /*********************************************************************/

write_data[data_offset++] = (UCHAR) key;

      }
      /***********************************************************************/
      /* Invalidate our window so that the new data is displayed             */
      /***********************************************************************/
      InvalidateRect (hWnd, NULL, TRUE);
    }
  }

  return(data_to_send);
}  /* do_keyboard_stuff                                                      */

/**PROC+**********************************************************************/
/* Name:      do_write                                                       */
/*                                                                           */
/* Purpose:   issue a write verb                                             */
/*                                                                           */
/* Returns:   BOOL - TRUE => data sent OK                                    */
/*                                                                           */
/* Params:    none                                                           */
/*                                                                           */
/* Operation: convert data to EBCDIC and send it on the right session        */
/*                                                                           */
/**PROC-**********************************************************************/
BOOL do_write (void)

{
  BOOL ok;

  ok = TRUE;

  /***************************************************************************/
  /* Set up the vcb                                                          */
  /***************************************************************************/
  memset(&other_verb, 0, sizeof(other_verb));
  other_verb.common.lua_verb             = LUA_VERB_RUI;
  other_verb.common.lua_verb_length      = sizeof(read_verb);
  other_verb.common.lua_opcode           = LUA_OPCODE_RUI_WRITE;
  other_verb.common.lua_sid              = sid;
  other_verb.common.lua_data_length      = write_len;
  other_verb.common.lua_data_ptr         = (char far *) write_data;
  other_verb.common.lua_rh.bci           = 1;
  other_verb.common.lua_rh.eci           = 1;
  other_verb.common.lua_rh.dr1i          = 1;

  if (lu_session)
  {
    /*************************************************************************/
    /* On the LU session we must add the <enter> key prefix.  All inbound    */
    /* requests flow RQE with the BBI and CDI flags set depending on the     */
    /* current session state.                                                */
    /*************************************************************************/
    other_verb.common.lua_flag1.lu_norm  = 1;
    other_verb.common.lua_rh.ri          = 1;
    if (send_state == BETB)
    {
      /***********************************************************************/
      /* Between bracket, so open bracket and give direction.  Note that we  */
      /* can do this since we will always be contention winner.              */
      /***********************************************************************/
      other_verb.common.lua_rh.bbi         = 1;
      other_verb.common.lua_rh.cdi         = 1;
      send_state = RECV;
    }
    else if (send_state = SEND)
    {
      /***********************************************************************/
      /* In bracket and we have direction, so simply give direction.         */
      /***********************************************************************/
      other_verb.common.lua_rh.cdi         = 1;
      send_state = RECV;
    }
    else
    {
      /***********************************************************************/
      /* In bracket and we do not have direction, so do not send.            */
      /***********************************************************************/
      ok = FALSE;
    }
  }
  else
  {
    /*************************************************************************/
    /* On the SSCP session things are straightforward.                       */
    /*************************************************************************/
    other_verb.common.lua_flag1.sscp_norm  = 1;
  }

  if (ok)
  {
    /*************************************************************************/
    /* Issue write verb                                                      */
    /*************************************************************************/
    WinRUI(hWnd, (LUA_VERB_RECORD FAR *) &other_verb);
    if (!other_verb.common.lua_flag2.async)
    {
      /***********************************************************************/
      /* Verb completed immediately.  Post our own completion message        */
      /***********************************************************************/
      PostMessage (hWnd, verb_complete, 0, (LPARAM) (VOID FAR *)&other_verb);
    }

    /*************************************************************************/
    /* Reset write_data to point to correct point in write_array             */
    /*************************************************************************/
    write_data = write_array + WRITE_EXTRA;
  }

  return(ok);
}  /* do_write ()                                                            */

/**PROC+**********************************************************************/
/* Name:      parse_data                                                     */
/*                                                                           */
/* Purpose:   parse data from the host                                       */
/*                                                                           */
/* Returns:   void                                                           */
/*                                                                           */
/* Params:    IN  data - pointer to data                                     */
/*                                                                           */
/* Operation: Looks through data for read partition query from host.  Could  */
/* be expanded to format data from host                                      */
/*                                                                           */
/**PROC-**********************************************************************/
void parse_data (data, length)

UCHAR FAR * data;
USHORT      length;
{
  USHORT  field_length;

  switch (*(data++))
  {
    case 0xF3:                       /* write structured field               */
      /***********************************************************************/
      /* Next byte is the WCC - ignore                                       */
      /***********************************************************************/
      data++;
      length -= 2;

      while (length > 0)
      {
        /*********************************************************************/
        /* We're just looking for a Read Partion query                       */
        /*********************************************************************/
        field_length = (((USHORT) *data) << 8) || ((USHORT) *(data + 1));
        if ((*(data+2) == 0x01) &&   /* Read partition                       */
            (*(data+3) == 0xFF) &&   /* Query                                */
            (*(data+4) == 0x02))     /* not a list                           */
        {
          /*******************************************************************/
          /* Build an RPQ and flag it to be sent                             */
          /*******************************************************************/
          rpq_state = RPQ_QUEUED;
        }
        data   += field_length;
        length -= field_length;
      }

      break;

    default:
      break;

  }
}  /* parse_data                                                             */

/**PROC+**********************************************************************/
/* Name:      issue_rpq                                                      */
/*                                                                           */
/* Purpose:   issue a response to a Read partition query                     */
/*                                                                           */
/* Returns:   BOOL - TRUE => sent OK                                         */
/*                                                                           */
/* Params:    none                                                           */
/*                                                                           */
/* Operation: Builds and sends an RPQ reesponse                              */
/*                                                                           */
/**PROC-**********************************************************************/
BOOL issue_rpq (void)

{
  BOOL issue_rpq;

  issue_rpq = TRUE;
  /***************************************************************************/
  /* Set up the vcb                                                          */
  /***************************************************************************/
  memset(&rpq_verb, 0, sizeof(rpq_verb));
  rpq_verb.common.lua_verb             = LUA_VERB_RUI;
  rpq_verb.common.lua_verb_length      = sizeof(rpq_verb);
  rpq_verb.common.lua_opcode           = LUA_OPCODE_RUI_WRITE;
  rpq_verb.common.lua_sid              = sid;
  rpq_verb.common.lua_data_length      = RPQ_LENGTH;
  rpq_verb.common.lua_data_ptr         = (char far *) rpq_data;
  rpq_verb.common.lua_rh.bci           = 1;
  rpq_verb.common.lua_rh.eci           = 1;
  rpq_verb.common.lua_rh.dr1i          = 1;

  /***************************************************************************/
  /* All inbound requests flow RQD with the BBI and CDI flags set depending  */
  /* on the current session state.                                           */
  /***************************************************************************/
  rpq_verb.common.lua_flag1.lu_norm  = 1;
  rpq_verb.common.lua_rh.ri          = 1;
  if (send_state == BETB)
  {
    /*************************************************************************/
    /* Between bracket, so open bracket and give direction.  Note that we    */
    /* can do this since we will always be contention winner.                */
    /*************************************************************************/
    rpq_verb.common.lua_rh.bbi         = 1;
    rpq_verb.common.lua_rh.cdi         = 1;
    send_state = RECV;
  }
  else if (send_state = SEND)
  {
    /*************************************************************************/
    /* In bracket and we have direction, so simply give direction.           */
    /*************************************************************************/
    rpq_verb.common.lua_rh.cdi         = 1;
    send_state = RECV;
  }
  else
  {
    /*************************************************************************/
    /* In bracket and we do not have direction, so do not send.              */
    /*************************************************************************/
    issue_rpq = FALSE;
  }

  if (issue_rpq)
  {
    WinRUI(hWnd, (LUA_VERB_RECORD FAR *) &rpq_verb);
    if (!rpq_verb.common.lua_flag2.async)
    {
      /***********************************************************************/
      /* Verb completed immediately.  Post our own completion message        */
      /***********************************************************************/
      PostMessage (hWnd, verb_complete, 0, (LPARAM) (VOID FAR *)&rpq_verb);
    }
  }

  return(issue_rpq);
}  /* issue_rpq                                                              */

/**PROC+**********************************************************************/
/* Name:      add_to_display_buffer                                          */
/*                                                                           */
/* Purpose:   add data to the display buffer                                 */
/*                                                                           */
/* Returns:   void                                                           */
/*                                                                           */
/* Params:    IN data - data to be displayed                                 */
/*                                                                           */
/* Operation: Add the zero terminated data string to the circular display    */
/* buffer.  Cope with wrapping                                               */
/*                                                                           */
/**PROC-**********************************************************************/
void add_to_display_buffer (data)

UCHAR FAR * data;
{
  UINT data_length;         /* length of new data to display                 */
  BOOL need_to_wrap;        /* do we need to wrap to the start of the buffer */
  UINT copy_length;         /* length of data to copy                        */

  data_length = _fstrlen (data);

  /***************************************************************************/
  /* Work out whether the data will go over the end of the dispaly buffer.   */
  /* If it will, wrap it back over the start of the buffer.                  */
  /***************************************************************************/
  need_to_wrap = (buffer_end + data_length > DISPLAY_BUFFER_SIZE);

  if (need_to_wrap)
  {
    copy_length = DISPLAY_BUFFER_SIZE - buffer_end;
    wrapped = TRUE;
  }
  else
  {
    copy_length = data_length;
  }

  /***************************************************************************/
  /* Copy as much data as will fit into the buffer                           */
  /***************************************************************************/
  _fmemcpy ((UCHAR FAR *) (display_buffer + buffer_end),
            data,
            copy_length);

  /***************************************************************************/
  /* If there is more data to copy, copy it to the start of the buffer       */
  /* Set up the new end point.                                               */
  /***************************************************************************/
  if (need_to_wrap)
  {
    _fmemcpy ((UCHAR FAR *)display_buffer,
              data + copy_length,
              data_length - copy_length);

    buffer_end   = data_length - copy_length;
  }
  else
  {
    buffer_end   += data_length;
  }

  /***************************************************************************/
  /* Cause the window to be re-painted                                       */
  /***************************************************************************/
  InvalidateRect (hWnd, NULL, TRUE);

}  /* add_to_display_buffer                                                  */


CPICDEFS.H

/*****************************************************************************
 *
 *  MODULE NAME: CPICDEFS.H
 *
 *  COPYRIGHTS:
 *             This module contains code made available by IBM
 *             Corporation on an AS IS basis.  Any one receiving the
 *             module is considered to be licensed under IBM copyrights
 *             to use the IBM-provided source code in any way he or she
 *             deems fit, including copying it, compiling it, modifying
 *             it, and redistributing it, with or without
 *             modifications.  No license under any IBM patents or
 *             patent applications is to be implied from this copyright
 *             license.
 *
 *             A user of the module should understand that IBM cannot
 *             provide technical support for the module and will not be
 *             responsible for any consequences of use of the program.
 *
 *             Any notices, including this one, are not to be removed
 *             from the module without the prior written consent of
 *             IBM.
 *
 *  AUTHOR:    Peter J. Schwaller
 *             VNET:     PJS at RALVM6           Tie Line: 444-4376
 *             Internet: pjs@ralvm6.vnet.ibm.com     (919) 254-4376
 *
 *  AVAILABILITY:
 *             These sample programs and source are also available on
 *             CompuServe through the APPC Information Exchange.  To get
 *             to the APPC forum just type 'GO APPC' from any CompuServe
 *             prompt.  The samples are available in the Sample Programs
 *             library section.  Just search on the keyword CPICPGMS to
 *             find all the samples in this series.
 *
 *             Updates for the sample programs and support for many more
 *             CPI-C platforms will also be made available on CompuServe.
 *
 *  RELATED FILES:
 *             Used by CPICPORT.C
 *
 *  CHANGE HISTORY:
 *  Date       Description
 *  08/01/92   This file was created.
 *  08/05/92   Version 2.31 of APING, ATELL and AREXEC released to CompuServe.
 *             This version was also distributed at the APPC/APPN Platform
 *             Developer's Conference held in Raleigh, NC.
 *  08/17/92   Added FAPI to list of SUPPORTS_SETTING_SECURITY.
 *  01/06/93   Added OS2_20 to list of SUPPORTS_SETTING_SECURITY.
 *
 *
 *****************************************************************************/

#ifndef INCL_CPICDEFS
#define INCL_CPICDEFS

/*
 * Define the operating system string.
 */

#define OPSYS_STRING    "Windows NT 3.1"

#define DOES_NOT_NEED_STRUPR

/*
 * There are a few CPI-C extensions that are not supported across all
 * platforms and a few local names that are handled differently.  Many
 * of these are treated the same on a group of platforms.  To avoid
 * testing for each platform throughout the code, I have established
 * a number of constants that indicate whether a feature is supported
 * or not.
 *
 * If you are porting this application to another platform, you can
 * add your platform to the list with an or (||) clause.
 */

/*
 * Setting security type and userid/password information is not part of
 * the SAA CPI-C base.
 *
 * This constant is referenced in the CPICINIT.C file.
 */
#if defined(OS2) || defined(DOS) || defined(VM) || defined(AIX) || \
    defined(FAPI) || defined(OS2_20)
#define SUPPORTS_SETTING_SECURITY
#endif

/*
 * On most platforms partner LU names will always be in uppercase.
 * Some platforms allow you to specify a partner LU name that can
 * contain lowercase characters (e.g., OS/2 partner LU aliases).
 * If this constant is defined, the partner LU name will only be
 * converted to uppercase if it is a fully qualified LU name.
 *
 * This constant is referenced in the CPICINIT.C file.
 */
#if defined(OS2) || defined(FAPI) || defined(OS2_20) \
    || defined (AIX)
#define PLN_COULD_REQUIRE_LOWERCASE
#endif

/*
 * Some platforms do not support the base function, Extract Conversation
 * State.
 *
 * This constant is referenced in the CPICERR.C file.
 */
#if defined(OS400) || defined(MVS)
#define  ECS_NOT_SUPPORTED
#endif

/* min and max macros */
#ifndef max
#define max(a,b)        (((a) > (b)) ? (a) : (b))
#endif

#ifndef min
#define min(a,b)        (((a) < (b)) ? (a) : (b))
#endif

#ifndef TRUE
#define  TRUE  (1)
#endif

#ifndef FALSE
#define  FALSE (0)
#endif

#endif


CPICERR.C

/*****************************************************************************
 *
 *  MODULE NAME: CPICERR.C
 *
 *  COPYRIGHTS:
 *             This module contains code made available by IBM
 *             Corporation on an AS IS basis.  Any one receiving the
 *             module is considered to be licensed under IBM copyrights
 *             to use the IBM-provided source code in any way he or she
 *             deems fit, including copying it, compiling it, modifying
 *             it, and redistributing it, with or without
 *             modifications.  No license under any IBM patents or
 *             patent applications is to be implied from this copyright
 *             license.
 *
 *             A user of the module should understand that IBM cannot
 *             provide technical support for the module and will not be
 *             responsible for any consequences of use of the program.
 *
 *             Any notices, including this one, are not to be removed
 *             from the module without the prior written consent of
 *             IBM.
 *
 *  AUTHOR:    Peter J. Schwaller
 *             VNET:     PJS at RALVM6           Tie Line: 444-4376
 *             Internet: pjs@ralvm6.vnet.ibm.com     (919) 254-4376
 *
 *             John Q. Walker
 *             VNET:     JOHNQ at RALVM6         Tie Line: 444-4414
 *             Internet: johnq@ralvm6.vnet.ibm.com   (919) 254-4414
 *
 *  FUNCTION:  Contains procedures to be called for handling
 *             unexpected CPI-C return codes.
 *             Contains procedures to be called for handling the
 *             CPI-C error information cpicerr.
 *
 *  AVAILABILITY:
 *             These sample programs and source are also available on
 *             CompuServe through the APPC Information Exchange.  To get
 *             to the APPC forum just type 'GO APPC' from any CompuServe
 *             prompt.  The samples are available in the Sample Programs
 *             library section.  Just search on the keyword CPICPGMS to
 *             find all the samples in this series.
 *
 *             Updates for the sample programs and support for many more
 *             CPI-C platforms will also be made available on CompuServe.
 *
 *  RELATED FILES:
 *             CPICERR.H
 *
 *  CHANGE HISTORY:
 *  Date       Description
 *  08/05/92   Version 2.31 of APING, ATELL and AREXEC released to CompuServe.
 *             This version was also distributed at the APPC/APPN Platform
 *             Developer's Conference held in Raleigh, NC.
 *  08/13/92   Changed all printf and fprintf calls to use a write_*() call.
 *             Changed cpicerr_handle_rc() to a macro referencing a new call,
 *             cpicerr_handle_rc_extended().  The macro adds the current
 *             source file and line number to improve source code debugging.
 *  08/15/92   Changed cpicerr_exchange_version() to receive 128 bytes
 *             instead of 3 bytes.  This change was also made to the
 *             NS/DOS ship code.
 *  08/18/92   Removed extra newline in cpicerr_show_rc().
 *  09/02/92   Fixed bug in cpicerr_log_cpicerr() when the log_file_path
 *             was a zero length string.
 *  11/15/92   Changed reply structure to include 2 byte indicator.
 *  11/17/92   Added send/receipt of operating system string.
 *             Added cpicerr_exchange_version_plus() to return the string.
 *
 *****************************************************************************/

/*****************************************************************************
 *
 * OVERVIEW OF CPICERR CALLS
 *
 * cpicerr_new()                      Creates a CPICERR object.
 *                                    This must be done before any other
 *                                    cpicerr calls can be used.
 *
 * These calls set values in the cpicerr object structure and affect how
 * cpicerr_handle_rc reacts to errors.
 *
 * cpicerr_set_conv_id()              Used to extract conversation state info
 * cpicerr_set_exit_level()           Level of error on which to exit
 * cpicerr_set_log_file_name()        What filename to use for logging
 * cpicerr_set_log_file_path()        Where the filename is
 * cpicerr_set_log_level()            Level of error on which to log errors
 * cpicerr_set_major_version()        8 bit int - see cpicerr_exchange_version
 * cpicerr_set_minor_version()        8 bit int - see cpicerr_exchange_version
 * cpicerr_set_program_name()         String - Output as part of log info
 * cpicerr_set_program_info()         String - Output as part of log info
 * cpicerr_set_show_level()           Level of error on which to show errors
 *
 * cpicerr_handle_rc()                Should be called by the program for all
 *                                    UNEXPECTED return codes.
 *                                    Functions performed are:
 *                                       Classification of the return code
 *                                       Showing partial info to end user
 *                                       Logging complete info to disk
 *                                    This is a macro that actually expands
 *                                    to cpicerr_handle_rc_extended().
 *
 * cpicerr_exchange_version()         Exchanges version numbers with the
 *                                    partner.  Very useful when supporting
 *                                    multiple versions of a program.
 *
 * cpicerr_destroy()                  Destroys the CPICERR  object.
 *
 *
 * cpicerr_classify_rc()              These are internal calls used by
 * cpicerr_show_rc()                  other cpicerr_handle_rc.
 * cpicerr_log_rc()
 * cpicerr_log_cpicerr()
 * cpicerr_get_message()
 * cpicerr_set_rc_info()
 * cpicerr_show_product_info()
 *
 *****************************************************************************/
#if defined(WIN32) || defined(WINDOWS)                                /*WIN32*/
#include <windows.h>                                                  /*WIN32*/
#define GET_OS2_SENSE_DATA                                            /*WIN32*/
#endif                                                                /*WIN32*/

#include "wincpic.h"

#ifdef GET_OS2_SENSE_DATA
#include <winappc.h>
#endif

/* Set up constant declarations */
#include "cpicdefs.h"

/* Collection of routines with special ported version for each platform */
#include "cpicport.h"

#include <stdio.h>                          /* C library includes            */
#include <stdlib.h>
#include <string.h>

#include <time.h>

#include "cpicerrs.h"                       /* CPI-C error handling vars.    */

/*
 * Each of the following is a list of messages, which are actually defined
 * in this file below.  The messages are grouped together.  For example,
 * all of the return code messages are in the cpicerr_return_codes list.
 */
extern CPICERR_MESSAGE cpicerr_verbs_short[];
extern CPICERR_MESSAGE cpicerr_verbs_long[];
extern CPICERR_MESSAGE cpicerr_return_codes[];
extern CPICERR_MESSAGE cpicerr_rc_classes[];
extern CPICERR_MESSAGE cpicerr_states_conv[];
extern CPICERR_MESSAGE cpicerr_conv_types[];
extern CPICERR_MESSAGE cpicerr_sync_levels[];

/*****************************************************************************
 * Function prototypes for internal routines.  In general, these should
 * not be called directly by an application program.
 *****************************************************************************/
void   cpicerr_classify_rc(      CM_RETURN_CODE       conv_rc,
                                 CPIC_RC_HANDLING *   classification);
void   cpicerr_show_rc(          CPICERR *            cpicerr);
void   cpicerr_log_rc(           CPICERR *            cpicerr,
                                 FILE *               log_file);
int    cpicerr_log_cpicerr(      CPICERR *            cpicerr,
                                 char *               file_name,
                                 int                  file_line);
char * cpicerr_get_message(      CPICERR_MESSAGE_TYPE message_type,
                                 CM_INT32             index);
void   cpicerr_set_rc_info(      CPICERR *            cpicerr,
                                 CPIC_VERB_INDEX      verb_index,
                                 CM_RETURN_CODE       conv_rc);
void   cpicerr_show_product_info(CPICERR *            cpicerr);

/*****************************************************************************
 * The following function prototypes and macros are used only by the
 * OS/2 specific code used to obtain the sense data after an
 * allocation failure.  They are not necessary, if GET_OS2_SENSE_DATA
 * has not been externally defined (with the -D compile flag).
 *****************************************************************************/
#ifdef GET_OS2_SENSE_DATA
int    cpicerr_os2_appc_allocate(CPICERR *            cpicerr);

int
parse_destination(char * in_string,
                  char * plu_alias,
                  char * fqplu_name);

/* Macro BLANK_STRING sets string to all blanks */
#define BLANK_STRING(str)  memset(str,(int)' ',sizeof(str))

#undef CLEAR_VCB
/* Macro CLEAR_VCB sets the APPC verb control block to zeros */
#define CLEAR_VCB(vcb)     memset((char *)&vcb,(int)'\0',sizeof(vcb))

/* convert a string to uppercase, up to length specified */
#define TOUPPER_STRING(str,length) \
                           {int i; for (i=0;i < length;i ++) \
                           {str[i] = (char)toupper(str[i]);}}

/* convert an ASCII blank padded string to an ASCIIZ string without pads */
#define SNA2STRING(outstr, instr, max_length) \
                   {int i; for (i=0;i < max_length && instr[i] != ' ';i ++) \
                   {outstr[i] = instr[i];} outstr[i] = '\0';}

#endif

/*
 * The error REPLY routines below are experimental and may change
 * at any time!
 */

void
cpicerr_show_reply(CPICERR_REPLY * reply)
{
    unsigned short length;
    unsigned short offset;
    unsigned short indicator;
    int            rc = 0;

    if (reply->length >= CPICERR_REPLY_MIN_LENGTH) {
        write_error(
            "\nAn application error was detected by the partner:\n");
        indicator =
          convert_short_from_network(
              *((unsigned short*)&reply->buffer[CPICERR_REPLY_INDICATOR]));
        if (indicator == CPICERR_REPLY_INDICATOR_VALUE) {
            write_error("Response:              %d\n",
                        (int)reply->buffer[CPICERR_REPLY_RESPONSE]);
            write_error("Message Category:      %u\n",
              convert_short_from_network(
                  *((unsigned short*)&reply->buffer[CPICERR_REPLY_CATEGORY])));
            write_error("Primary Code:          %ld\n",
              convert_long_from_network(
                  *((unsigned long *)&reply->buffer[CPICERR_REPLY_PRIMARY])));
            write_error("Secondary Code:        %ld\n",
              convert_long_from_network(
                 *((unsigned long *)&reply->buffer[CPICERR_REPLY_SECONDARY])));

            if (reply->length >=
                (unsigned int)(CPICERR_REPLY_MIN_LENGTH + 2)) {
                length = convert_short_from_network(
                *((unsigned short *)
                               &reply->buffer[CPICERR_REPLY_PRIMARY_LENGTH]));

                if (reply->length >=
                    (unsigned int)(CPICERR_REPLY_PRIMARY_TEXT+length)) {
                    reply->buffer[length + CPICERR_REPLY_PRIMARY_TEXT - 1] =
                                                                          '\0';
                    write_error("%s\n",
                                &reply->buffer[CPICERR_REPLY_PRIMARY_TEXT]);
                    offset = CPICERR_REPLY_PRIMARY_TEXT + length;
                    if (reply->length >= 2+offset) {
                        length = convert_short_from_network(
                                  *((unsigned short *)&reply->buffer[offset]));
                        if (reply->length >= (offset+ 2 +length)) {
                            reply->buffer[length + offset + 2] = '\0';
                            write_error("%s\n", &reply->buffer[offset+2]);
                        }
                        else {
                            rc = 1;
                        }
                    }
                    else {
                        rc = 1;
                    }
                }
                else {
                    rc = 1;
                }
            }
            else {
                write_error("No text strings were sent.\n");
            }
        }
        else {
            write_error("Record received was not a reply structure.\n");
        }

    }
    else {
        rc = 1;
    }
    if (rc) {
        write_error("Reply length error in cpicerr_show_reply().\n");
    }
}

CPICERR_REPLY *
cpicerr_create_reply(unsigned int primary_message_buffer_size,
                     unsigned int secondary_message_buffer_size)
{
    CPICERR_REPLY * reply;

    reply = calloc(1, sizeof(CPICERR_REPLY));
    if (reply == NULL) {
        return NULL;
    }

    reply->buffer_length = CPICERR_REPLY_MIN_LENGTH+
                           primary_message_buffer_size+1 +
                           secondary_message_buffer_size+1 +
                           4;          /* two length fields */
    reply->buffer = calloc(reply->buffer_length, 1);
    if (reply == NULL) {
        return NULL;
    }

    return reply;
}

void
cpicerr_destroy_reply(CPICERR_REPLY * reply)
{
    /*
     * Make sure we don't free any NULL pointers!
     */
    if (reply != NULL) {
        if (reply->buffer != NULL) {
            free(reply->buffer);
        }
        free(reply);
    }
}

/*
 * Requires that cpicerr_set_conv_id() has been called.
 */
int    cpicerr_recv_appl_error(CPICERR *  cpicerr,
                               CPICERR_REPLY * reply)
{
    CM_RETURN_CODE cm_rc;
    CM_INT32  rts_received;
    CM_INT32  max_receive_len;
    CM_INT32  what_received;
    CM_INT32  received_len;
    CM_INT32  status_received;

    max_receive_len = reply->buffer_length;

    cmrcv (cpicerr->conversation_id,
           reply->buffer,
           &max_receive_len,
           &what_received,
           &received_len,
           &status_received,
           &rts_received,
           &cm_rc);

    reply->length = (unsigned int)received_len;

    return reply->buffer[0];
}

void
cpicerr_set_error_reply(CPICERR_REPLY * reply,
                        REPLY_RESPONSE  response,
                        unsigned int    message_category,
                        unsigned long   primary_code,
                        char *          primary_message_text,
                        unsigned long   secondary_code,
                        char *          secondary_message_text)
{
    unsigned int length = 0;
    unsigned short string_length;

    /*
     * Check and see that there is at least enough space in the reply_buffer
     * to sent the required fields.  If there is not enough space, return
     * 0 (zero) to the caller, indicating that no bytes were copied into
     * the reply_buffer.
     */
    if (reply->buffer_length < CPICERR_REPLY_MIN_LENGTH) {
        reply->length = 0;
        return/* 0 */;
    }

    *((unsigned short *)&reply->buffer[CPICERR_REPLY_INDICATOR]) =
                       convert_short_to_network(CPICERR_REPLY_INDICATOR_VALUE);
    reply->buffer[CPICERR_REPLY_RESPONSE] = (char)response;
    reply->buffer[CPICERR_REPLY_RESERVED] = 0; /* set reserved byte to zero  */
    *((unsigned short *)&reply->buffer[CPICERR_REPLY_CATEGORY]) =
                            convert_short_to_network(message_category);
    *((unsigned long *)&reply->buffer[CPICERR_REPLY_PRIMARY]) =
                            convert_long_to_network(primary_code);
    *((unsigned long *)&reply->buffer[CPICERR_REPLY_SECONDARY]) =
                            convert_long_to_network(secondary_code);
    length = CPICERR_REPLY_MIN_LENGTH;

    /* check length of buffer!!! */

    /*
     * Copy the length and text of the primary message.
     */
    string_length = strlen(primary_message_text) + 1;
    *((unsigned short *)&reply->buffer[CPICERR_REPLY_PRIMARY_LENGTH]) =
                  convert_short_to_network(string_length);
    memcpy(&reply->buffer[CPICERR_REPLY_PRIMARY_TEXT],
           primary_message_text,
           string_length);
    convert_to_ascii(&reply->buffer[CPICERR_REPLY_PRIMARY_TEXT],
                     string_length);

    length += 2 + string_length;

    /*
     * Copy the length and text of the secondary message.
     */
    string_length = strlen(secondary_message_text) + 1;
    *((unsigned short *)&reply->buffer[length]) =
              convert_short_to_network(string_length);
    memcpy(&reply->buffer[length+2],
           secondary_message_text,
           string_length);
    convert_to_ascii(&reply->buffer[length+2],
                     string_length);

    length += 2 + string_length;

    reply->length = length;

    return /* length */;
}

/*
 * Requires that cpicerr_set_conv_id() has been called.
 */

int
cpicerr_send_appl_error( CPICERR *       cpicerr,
                         NEXT_STATE      next_state,
                         CPICERR_REPLY * reply)
{
    CM_RETURN_CODE  cm_rc;
    CM_INT32        length;
    CM_INT32        rts_received;
    CM_SEND_TYPE    send_type;

    cmserr(cpicerr->conversation_id,
           &rts_received,
           &cm_rc);

    switch (next_state) {
        case NEXT_DEALLOCATE:
        case NEXT_DEALLOCATE_AND_EXIT:
            send_type = CM_SEND_AND_DEALLOCATE;
            {
            CM_INT32 deallocate_type = CM_DEALLOCATE_FLUSH;
            cmsdt(cpicerr->conversation_id,
                  &deallocate_type,
                  &cm_rc);
            }
            break;
        case NEXT_SEND:
            send_type = CM_SEND_AND_FLUSH;
            break;
        case NEXT_RECEIVE:
            send_type = CM_SEND_AND_PREP_TO_RECEIVE;
            {
            CM_PREPARE_TO_RECEIVE_TYPE ptr_type;
            ptr_type = CM_PREP_TO_RECEIVE_FLUSH;
            cmsptr (cpicerr->conversation_id,
                    &ptr_type,
                    &cm_rc);
            }
            break;
    }

    cmsst (cpicerr->conversation_id,
           &send_type,
           &cm_rc);
#if 1
    if (cm_rc != CM_OK) {
        printf("Ouch!!!!!!!!\n");
    }
    else {
    }
#endif

    length = reply->length;

    cmsend (cpicerr->conversation_id,
            reply->buffer,
            &length,
            &rts_received,
            &cm_rc);

    if (next_state == NEXT_DEALLOCATE_AND_EXIT) {
        exit(EXIT_FAILURE);
    }

#if 1
    printf("didn't exit\n");
#endif

return0;

}

/*****************************************************************************
 *
 *  cpicerr_new()
 *
 *  Usage:
 *  This function creates a CPICERR object which should be used on all
 *  subsequent calls to cpicerr.  If an error occurs and a valid object
 *  cannot be created, NULL will be returned.
 *
 *****************************************************************************/
CPICERR *
cpicerr_new(void)
{
    CPICERR *  cpicerr;

    /*-----------------------------------------------------------------------*
     * Allocate a block for the CPICERR structure.  calloc() will initialize
     * all bytes to 0.
     *-----------------------------------------------------------------------*/
    cpicerr = (CPICERR *) calloc(1, sizeof(*cpicerr));
    if (cpicerr == NULL) {
        return NULL;
    }

    /*-----------------------------------------------------------------------*
     * Save the time of program initialization.
     *-----------------------------------------------------------------------*/
    cpicerr->program_start_time = time(NULL);

    /*-----------------------------------------------------------------------*
     * Indicate that the conversation id field has not been set.
     *-----------------------------------------------------------------------*/
    cpicerr->conv_id_set = FALSE;

    /*-----------------------------------------------------------------------*
     * Initialize version levels.  0 indicates they have not been set.
     *-----------------------------------------------------------------------*/
    cpicerr->major_version = 0;
    cpicerr->minor_version = 0;

    /*-----------------------------------------------------------------------*
     * Initialize to always exit after an error occurs.
     *-----------------------------------------------------------------------*/
    cpicerr->exit_level = ALL_ERRORS;

    /*-----------------------------------------------------------------------*
     * Initialize to always show errors.
     *-----------------------------------------------------------------------*/
    cpicerr->show_level = ALL_ERRORS;

    /*-----------------------------------------------------------------------*
     * Initialize to always log errors.
     *-----------------------------------------------------------------------*/
    cpicerr->log_level = ALL_ERRORS;

    /*-----------------------------------------------------------------------*
     * Indicate that cpicerr should process all ERROR_RECEIVED return codes
     * (indicating our partner issued Send_Error())
     *-----------------------------------------------------------------------*/
    cpicerr->handle_error = TRUE;

    /*-----------------------------------------------------------------------*
     * Initialize strings to NULL in case 0 != NULL
     *-----------------------------------------------------------------------*/
    cpicerr->program_name  = NULL;
    cpicerr->program_info  = NULL;
    cpicerr->log_file_name = NULL;
    cpicerr->log_file_path = NULL;

    /*-----------------------------------------------------------------------*
     * Set the conversation characteristics to invalid values.  If this
     * isn't done, we could display seemingly valid values for these
     * parameters even though they were never set properly.
     *-----------------------------------------------------------------------*/
    cpicerr->conversation_type  = MAX_MESSAGE;
    cpicerr->conversation_state = MAX_MESSAGE;
    cpicerr->sync_level         = MAX_MESSAGE;

    return cpicerr;
}

/*****************************************************************************
 *
 *  cpicerr_set_conv_id
 *
 *  Usage:
 *  This call should be used just before the cmallc call (for clients),
 *  or just after the cmaccp call (for servers).
 *
 *  This routine will save the conversation id, as well as partner information
 *  and conversation status which can be extracted from CPI-C.
 *
 *  This information is often helpful for debugging and in many cases cannot
 *  be obtained after an error occurs, since you no longer have a valid
 *  conversation id after the conversation has been deallocated.
 *
 *  Returns 0 if everything was processed successfully.
 *  Returns 1 if an error occurred.  In general, the only reason for an
 *     error to occur is if one of the passed parameters is invalid.  Since
 *     this is just storing information in the footprint, we will not
 *     attempt to do sophisticated error processing in this procedure.
 *
 *****************************************************************************/
int
cpicerr_set_conv_id(CPICERR *       cpicerr,
                    unsigned char * conversation_id)
{
    CM_RETURN_CODE        conv_rc;          /* Return code from CPI-C call   */
    int                   rc;               /* return value for function     */

    if (cpicerr != (CPICERR *)NULL) {
        /*-------------------------------------------------------------------*
         * Save the conversation ID.
         * This can often be used in conjunction with error logs or trace
         * utilities.
         *-------------------------------------------------------------------*/
        memcpy(cpicerr->conversation_id,
               conversation_id,
               sizeof(cpicerr->conversation_id));

        /*
         * Extract the conversation type.
         */
        cmect(conversation_id,              /* Current conversation ID       */
              &cpicerr->conversation_type,  /* Returned conversation type    */
              &conv_rc);                    /* Put the return code here      */
        if (conv_rc != CM_OK) {
            /*
             * Set the conversation type to an invalid value and set
             * the return code to indicate an error.
             */
            cpicerr->conversation_type = MAX_MESSAGE;
            rc = 1;
        }

        /*
         * Extract the mode name used for this conversation.
         */
        cmemn(conversation_id,              /* Current conversation ID       */
              cpicerr->mode_name,           /* Returned mode name            */
              &(cpicerr->mode_name_length), /* Returned mode name length     */
              &conv_rc);                    /* Put the return code here      */
        if (conv_rc != CM_OK) {
            /*
             * Make sure the mode name is reset to uninitialized and
             * set the return code to indicate an error.
             */
            cpicerr->mode_name_length = 0;
            rc = 1;
        }
        /* make sure we place the null terminator at the end of the string   */
        cpicerr->mode_name[(int)cpicerr->mode_name_length] = '\0';

        /*-------------------------------------------------------------------*
         * Extract the partner's LU name.
         *-------------------------------------------------------------------*/
        cmepln(conversation_id,             /* Current conversation ID       */
               cpicerr->partner_LU_name,    /* Returned partner LU           */
               &(cpicerr->partner_LU_name_length),/* Partner LU name length  */
               &conv_rc);                   /* Put the return code here      */
        if (conv_rc != CM_OK) {
            /*
             * Make sure the partner LU name is reset to uninitialized and
             * set the return code to indicate an error.
             */
            cpicerr->partner_LU_name_length = 0;
            rc = 1;
        }
        /* make sure we place the null terminator at the end of the string   */
        cpicerr->partner_LU_name[(int)cpicerr->partner_LU_name_length] = '\0';

        /*-------------------------------------------------------------------*
         * Extract the conversation sync level.
         *-------------------------------------------------------------------*/
        cmesl(conversation_id,              /* Current conversation ID       */
              &cpicerr->sync_level,         /* Returned sync level           */
              &conv_rc);                    /* Put the return code here      */
        if (conv_rc != CM_OK) {
            /*
             * Set the sync level to an invalid value and set
             * the return code to indicate an error.
             */
            cpicerr->sync_level = MAX_MESSAGE;
            rc = 1;
        }

        rc = 0;
    } else {
        rc = 1;
    }
    return rc;
}

/*****************************************************************************
 *
 *  cpicerr_set_exit_level
 *
 *  Usage:
 *  Specify the CPI-C error classification as the exit level.
 *  When cpicerr_handle_rc() is called, the return code will be classified.
 *  If the classification is above the exit level, the program will be
 *  terminated (after conversation cleanup).
 *
 *  A value of ALL_ERRORS can be used to force an exit on any call to
 *  cpicerr_handle_rc().  This is the DEFAULT value.
 *
 *  A value of NO_ERRORS can be used to indicate that cpicerr should never
 *  cause an exit.  Your application should handle all exit termination.
 *  This is especially useful when using more than one conversation.
 *
 *  Also see:
 *      cpicerr_set_log_level
 *      cpicerr_set_show_level
 *****************************************************************************/
int
cpicerr_set_exit_level(CPICERR *        cpicerr,
                       CPIC_RC_HANDLING exit_level)
{
    cpicerr->exit_level = exit_level;
    return 0;
}

/*****************************************************************************
 *
 *  cpicerr_set_log_file_name
 *
 *  Usage:
 *  Sets the name of the log file to be used in cpicerr_handle_rc() to
 *  log complete error information.  No attempt is made to verify that
 *  the log_file_name specified is a valid filename or whether the file
 *  can be opened.  If the log_file_name cannot be opened when logging
 *  needs to be done, all log output will be sent to stderr instead.
 *
 *  For environments that support directory structures, you should specify
 *  the filename on cpicerr_set_log_file_name and the directory on
 *  cpicerr_set_log_file_path.  This allows your program to be isolated
 *  from changes in environments that only support a single level

*  of directory (for example, VM).
 *
 *  Also see:
 *      cpicerr_set_log_file_path
 *      cpicerr_log_cpicerr (internal)
 *****************************************************************************/
int
cpicerr_set_log_file_name(CPICERR * cpicerr,
                          char *    log_file_name)
{
    /*-----------------------------------------------------------------------*
     * Initialize the name of the log file.
     *-----------------------------------------------------------------------*/
    cpicerr->log_file_name = (char *) malloc(strlen(log_file_name)+1);
    if (cpicerr->log_file_name != NULL) {
        strcpy(cpicerr->log_file_name, log_file_name);
        return 0;
    } else {
        return 1;
    }

}

/*****************************************************************************
 *
 *  cpicerr_set_log_file_path
 *
 *  Usage:
 *  Specifies the path qualifier for the log file name specified in the
 *  cpicerr_set_log_file_name call.  If cpicerr_set_log_file_path is used
 *  without setting the log file name, no error logging will occur.
 *
 *  If you specify a string whose first character is a $, the rest of the
 *  string will be looked for as an environment variable.  For example,
 *  in OS/2, you put the following in your CONFIG.SYS:
 *  SET LOGPATH=d:\logfiles
 *
 *  then specify $LOGPATH as a parameter on cpicerr_set_log_file_path.
 *
 *****************************************************************************/
int
cpicerr_set_log_file_path(CPICERR * cpicerr,
                          char *    log_file_path)
{
    char * path;
    int    rc;

    /*-----------------------------------------------------------------------*
     * Initialize the name of the log file.
     * If the first character is a dollar sign ('$'), then try to extract
     * the log file path from the environment.  If not, use the path
     * as specified.
     *-----------------------------------------------------------------------*/
    if (log_file_path[0] == '$') {
        path = getenv(&log_file_path[1]);
    } else {
        path = log_file_path;
    }

    /*-----------------------------------------------------------------------*
     * Check that we have a valid log file path string, allocate memory for
     * the string and copy it into the cpicerr structure.
     *-----------------------------------------------------------------------*/
    if (path != NULL) {
        cpicerr->log_file_path = (char *) malloc(strlen(path)+1);
        if (cpicerr->log_file_path != NULL) {
            strcpy(cpicerr->log_file_path, path);
            rc = 0;
        } else {
            rc = 1;
        }
    }
    else {
        rc = 1;
    }
    return rc;

}

/*****************************************************************************
 *
 *  cpicerr_set_log_level
 *
 *  Usage:
 *  Specify the CPI-C error classification as the log level.
 *  When cpicerr_handle_rc() is called, the return code will be classified.
 *  If the classification is above the log level, the return code and
 *  conversation information will be logged to the log file specified
 *  with the cpicerr_set_log_file_name and cpicerr_set_log_file_path.
 *
 *  A value of ALL_ERRORS can be used to force logging on any call to
 *  cpicerr_handle_rc().  This is the DEFAULT value.  This is especially
 *  useful when used on servers.
 *
 *  A value of NO_ERRORS can be used to indicate that cpicerr should never
 *  cause an error log.  Your application should handle all error logging.
 *
 *  Also see:
 *      cpicerr_set_log_file_name
 *      cpicerr_set_log_file_path
 *      cpicerr_log_cpicerr (internal)
 *      cpicerr_set_exit_level
 *      cpicerr_set_show_level
 *****************************************************************************/
int
cpicerr_set_log_level(CPICERR *        cpicerr,
                      CPIC_RC_HANDLING log_level)
{
    cpicerr->log_level = log_level;
    return 0;
}

/*****************************************************************************
 *
 *  cpicerr_set_major_version
 *
 *  Usage:
 *  Sets the major version number for the application.
 *  The version number can be 0-255.
 *
 *  The version number is used on error logging and by the
 *  cpicerr_exchange_version() call.
 *
 *  Also see:
 *      cpicerr_set_minor_version
 *****************************************************************************/
int
cpicerr_set_major_version(CPICERR *     cpicerr,
                          unsigned char major_version)
{
    cpicerr->major_version = major_version;
    return 0;
}

/*****************************************************************************
 *
 *  cpicerr_set_minor_version
 *
 *  Usage:
 *  Sets the minor version number for the application.
 *  The version number can be 0-255.
 *
 *  The version number is used on error logging and by the
 *  cpicerr_exchange_version() call.
 *
 *  Also see:
 *      cpicerr_set_major_version
 *****************************************************************************/
int
cpicerr_set_minor_version(CPICERR *     cpicerr,
                          unsigned char minor_version)
{
    cpicerr->minor_version = minor_version;
    return 0;
}

/*****************************************************************************
 *
 *  cpicerr_set_program_info
 *
 *  Usage:
 *  Sets a program information string that is included in the log information.
 *****************************************************************************/
int
cpicerr_set_program_info( CPICERR * cpicerr,
                          char * program_info)
{
    int rc;

    /*-----------------------------------------------------------------------*
     * Save the application's program information string
     *-----------------------------------------------------------------------*/
    cpicerr->program_info = (char *) malloc(strlen(program_info)+1);
    if (cpicerr->program_info != NULL) {
        strcpy(cpicerr->program_info, program_info);
        rc = 0;
    } else {
        rc = 1;
    }
    return rc;
}

/*****************************************************************************
 *
 *  cpicerr_set_program_name
 *
 *  Usage:
 *  Sets a program name string that is included in the log information.
 *****************************************************************************/
int
cpicerr_set_program_name(CPICERR * cpicerr,
                         char * program_name)
{
    int rc;
    /*-----------------------------------------------------------------------*
     * Save the local program name.
     *-----------------------------------------------------------------------*/
    cpicerr->program_name = (char *) malloc(strlen(program_name)+1);
    if (cpicerr->program_name != NULL) {
        strcpy(cpicerr->program_name, program_name);
        rc = 0;
    } else {
        rc = 1;
    }
    return rc;
}

/*****************************************************************************
 *
 *  cpicerr_set_show_level
 *
 *  Usage:
 *  Specify the CPI-C error classification as the show level.
 *  When cpicerr_handle_rc() is called, the return code will be classified.
 *  If the classification is above the show level, the return code and
 *  some conversation information will be shown to the user.
 *
 *  A value of ALL_ERRORS can be used to force showing of all calls to
 *  cpicerr_handle_rc().  This is the DEFAULT value.
 *
 *  A value of NO_ERRORS can be used to indicate that cpicerr should never
 *  cause an error to be shown to the user.
 *
 *  Also see:
 *      cpicerr_set_exit_level
 *      cpicerr_set_log_level
 *****************************************************************************/
int
cpicerr_set_show_level(CPICERR *        cpicerr,
                       CPIC_RC_HANDLING show_level)
{
    cpicerr->show_level = show_level;
    return 0;
}

/******************************************************************************
 *
 *  cpicerr_handle_rc_extended
 *
 *  Note:
 *  applications should use the cpicerr_handle_rc() macro.
 *
 *  Usage:
 *  This function should be called to handle any unexpected CPI-C return
 *  codes.  The exact function of this routine depends upon the settings
 *  that have been made to the CPICERR object.
 *
 *  Functions include:
 *      Determining the current conversation state
 *      Classifying the return code (CPIC_RC_HANDLING enum)
 *      Showing return code info to the user
 *      Logging the return code and conversation info to disk
 *      Deallocate the conversation and exit
 *
 *  Also see:
 *      cpicerr_set_exit_level
 *      cpicerr_set_log_level
 *      cpicerr_set_show_level
 *
 *****************************************************************************/
CPIC_RC_HANDLING
cpicerr_handle_rc_extended(CPICERR *       cpicerr,
                           CPIC_VERB_INDEX verb_index,
                           CM_RETURN_CODE  conv_rc,
                           char *          file_name,
                           int             file_line)

{
   /* Displays the CPI-C return code and the verb name. */

    CM_RETURN_CODE   cm_rc;                 /* Return code from CPI-C call   */
    CPIC_RC_HANDLING classification;        /* Returned RC classification    */
    char *           string;                /* temp var for output strings   */
    CM_CONVERSATION_STATE conversation_state; /* Current conv. state         */

    /*-----------------------------------------------------------------------*
     * Extract the current conversation state.
     * This will not be useful if the conversation has failed or a deallocate
     * return code has been received.  The conversation state is most useful
     * when a state check has occurred.
     *-----------------------------------------------------------------------*/
#ifndef ECS_NOT_SUPPORTED
     cmecs(cpicerr->conversation_id,
           &conversation_state,
           &cm_rc);
     if (cm_rc == CM_OK) {
         cpicerr->conversation_state = conversation_state;
     }
     else {
         cpicerr->conversation_state = CMECS_NOT_SUPPORTED;
     }
#else
     cpicerr->conversation_state = CMECS_NOT_SUPPORTED;
#endif

    /*-----------------------------------------------------------------------*
     * Store the error information in the cpicerr structure
     *-----------------------------------------------------------------------*/
     cpicerr_set_rc_info(cpicerr, verb_index, conv_rc);

    /*-----------------------------------------------------------------------*
     * Get the classification for this return code.
     *-----------------------------------------------------------------------*/
    cpicerr_classify_rc(conv_rc, &classification);

    if (classification == ERROR_RECEIVED && cpicerr->handle_error == TRUE) {
        CPICERR_REPLY * reply;
        reply = cpicerr_create_reply(1000,1000);
        cpicerr_recv_appl_error(cpicerr, reply);
        cpicerr_show_reply(reply);
        if (classification >= cpicerr->exit_level) {
            CM_INT32 deallocate_type;
            /*
             * Exit the application, rather than returning to the caller.
             */
            deallocate_type = CM_DEALLOCATE_ABEND;

            cmsdt(cpicerr -> conversation_id,
                  &deallocate_type,
                  &cm_rc);

            cmdeal(cpicerr -> conversation_id,
                   &cm_rc);

            do_exit(EXIT_FAILURE);
        }
        return classification;
    }

    /*-----------------------------------------------------------------------*
     * Show the CPI-C verb and return code.
     *-----------------------------------------------------------------------*/
    if (classification >= cpicerr->show_level) {
        cpicerr_show_rc(cpicerr);
    }

    /*-----------------------------------------------------------------------*
     * Show the classification for this return code.
     *-----------------------------------------------------------------------*/
    if (classification >= cpicerr->show_level) {
        string = cpicerr_get_message(CPIC_RC_CLASSES, classification);
        write_error("       return code class: %s\n", string);
        /*-------------------------------------------------------------------*
         * Show product specific information associated with this error.
         *-------------------------------------------------------------------*/
        cpicerr_show_product_info(cpicerr);
    }

    /*-----------------------------------------------------------------------*
     * Log this error, along with the infomation in its cpicerr.
     *-----------------------------------------------------------------------*/
    if (classification >= cpicerr->log_level) {
        cpicerr_log_cpicerr(cpicerr, file_name, file_line);
    }

    if (classification >= cpicerr->exit_level) {
        CM_INT32 deallocate_type;
        /*-------------------------------------------------------------------*
         * Exit the application, rather than returning to the caller.
         *-------------------------------------------------------------------*/
        deallocate_type = CM_DEALLOCATE_ABEND;

        cmsdt(cpicerr -> conversation_id,
              &deallocate_type,
              &cm_rc);

        cmdeal(cpicerr -> conversation_id,
               &cm_rc);

        do_exit(EXIT_FAILURE);              /* Set a failure return code     */
    }
    return classification;
}

/******************************************************************************
 *
 *  cpicerr_exchange_version
 *
 *  Usage:
 *  Calls cpicerr_exchange_version_plus()
 *
 *****************************************************************************/
int
cpicerr_exchange_version(CPICERR *       cpicerr,
                         unsigned char * cm_conv_id,
                         CM_INT32        conv_state,
                         unsigned char * partner_major_version,
                         unsigned char * partner_minor_version)
{
    return cpicerr_exchange_version_plus(cpicerr,
                                         cm_conv_id,
                                         conv_state,
                                         partner_major_version,
                                         partner_minor_version,
                                         NULL,
                                         0);

}

/******************************************************************************
 *
 *  cpicerr_exchange_version_plus
 *
 *  Usage:
 *  Send our two version number bytes to the partner and receive our
 *  partner's two version numbers.  The input parameter conv_state
 *  determines whether our version numbers are sent first, or whether
 *  we receive the partner's numbers first.
 *
 *  Also will return the remote operating system string, if sent.
 *
 *****************************************************************************/
int
cpicerr_exchange_version_plus(CPICERR *       cpicerr,
                              unsigned char * cm_conv_id,
                              CM_INT32        conv_state,
                              unsigned char * partner_major_version,
                              unsigned char * partner_minor_version,
                              unsigned char * opsys_string,
                              unsigned int    opsys_string_length)
{
    CM_SEND_TYPE send_type;                 /* CPI-C send type               */
    CM_PREPARE_TO_RECEIVE_TYPE prep_to_receive; /* CPI-C prepare to receive  */
    CM_INT32    cm_rc;                      /* CPI-C return code             */
    CM_INT32    length;                     /* generic length variable       */
    CM_INT32    rts_received;               /* request to send received      */
    CM_INT32    max_receive_len;            /* Max receive length on CMRCV   */
    CM_INT32    what_received;              /* What received parm from CMRCV */
    CM_INT32    received_len;               /* Amount of data rcvd on CMRCV  */
    CM_INT32    status_received;            /* Status from CMRCV             */
    unsigned char buffer[EXCHANGE_BUFFER_SIZE]; /* data buffer               */

    *partner_major_version = 0;
    *partner_minor_version = 0;

    prep_to_receive = CM_PREP_TO_RECEIVE_FLUSH;
    cmsptr(cm_conv_id,                      /* Set prepare to receive type   */
           &prep_to_receive,
           &cm_rc);
    /* The only expected return code is CM_OK */
    if (cm_rc != CM_OK) return cpicerr_handle_rc(cpicerr, MSG_CMSPTR, cm_rc);

    send_type = CM_BUFFER_DATA;
    cmsst(cm_conv_id,                       /* Set send type                 */
          &send_type,
          &cm_rc);
    if (cm_rc != CM_OK) return cpicerr_handle_rc(cpicerr, MSG_CMSST, cm_rc);

    switch (conv_state) {
    case CM_SEND_STATE:
        buffer[0] = CPICERR_EXCHANGE_VERSION;
        buffer[1] = cpicerr->major_version;
        buffer[2] = cpicerr->minor_version;
        length = 3;
        cmsend(cm_conv_id,
               buffer,
               &length,
               &rts_received,
               &cm_rc);
        /* The only expected return code is CM_OK */
        if (cm_rc != CM_OK)
            return cpicerr_handle_rc(cpicerr, MSG_CMSEND, cm_rc);
        /* this falls through to the receive code!!! */
    case CM_RECEIVE_STATE:
        max_receive_len = sizeof(buffer);
        cmrcv (cm_conv_id,
               buffer,
               &max_receive_len,
               &what_received,
               &received_len,
               &status_received,
               &rts_received,
               &cm_rc);
        if (cm_rc == CM_OK) {
            if (what_received != CM_NO_DATA_RECEIVED) {
                if (received_len > 2 && buffer[0]==CPICERR_EXCHANGE_VERSION) {
                    *partner_major_version = buffer[1];
                    *partner_minor_version = buffer[2];
                }
                if (opsys_string != NULL && opsys_string_length > 1) {
                    if (received_len > 4 &&
                        buffer[3]==CPICERR_EXCHANGE_OPSYS_STRING) {
                        if (received_len < sizeof(buffer)) {
                            buffer[received_len] = '\0';
                        }
                        else {
                            buffer[sizeof(buffer)] = '\0';
                        }
                        {
                        unsigned int string_length;

                        string_length = strlen(&buffer[4]);
                        convert_from_ascii(&buffer[4], string_length);

                        memcpy(opsys_string,
                               &buffer[4],
                               min(string_length, opsys_string_length-1));

                        opsys_string[min(string_length, opsys_string_length-1)]
                                                                        = '\0';

                        }

                    }
                    else {
                        opsys_string[0] = '\0';
                    }
                }
                else {
                }

            }
            switch (status_received) {
            case CM_CONFIRM_RECEIVED:
                cmcfmd(cm_conv_id,
                       &cm_rc);
                if (cm_rc != CM_OK)
                    return cpicerr_handle_rc(cpicerr, MSG_CMCFMD, cm_rc);
                break;
            case CM_SEND_RECEIVED:
                /* This is good, we don't have to do anything. */
                break;
            default:
                ;
                /* should do a reply_error here */

            }
        } else {
            return cpicerr_handle_rc(cpicerr, MSG_CMRCV, cm_rc);
        }
        break;
    default:
        /* do a reply error here */
        return UNRECOVERABLE;
    }

    if ((conv_state == CM_RECEIVE_STATE) &&
         (status_received == CM_SEND_RECEIVED)) {

        char *       local_opsys_string;
        unsigned int local_opsys_string_length;

        buffer[0] = CPICERR_EXCHANGE_VERSION;
        buffer[1] = cpicerr->major_version;
        buffer[2] = cpicerr->minor_version;
        local_opsys_string = OPSYS_STRING;
        local_opsys_string_length = strlen(local_opsys_string);
        if ((local_opsys_string_length + 1+ 3 +1) < sizeof(buffer)) {
            buffer[3] = CPICERR_EXCHANGE_OPSYS_STRING;
            memcpy(&buffer[4],
                   local_opsys_string,
                   local_opsys_string_length);
            convert_to_ascii(&buffer[4], local_opsys_string_length);
            buffer[4+local_opsys_string_length] ='\0';
            length = 4 + local_opsys_string_length + 1;
        }
        else {

            length = 3;
        }

        cmsend(cm_conv_id,
               buffer,
               &length,
               &rts_received,
               &cm_rc);
        /* The only expected return code is CM_OK */

        if (cm_rc != CM_OK)
            return cpicerr_handle_rc(cpicerr, MSG_CMSEND, cm_rc);

        cmptr(cm_conv_id,
              &cm_rc);
        if (cm_rc != CM_OK)
            return cpicerr_handle_rc(cpicerr, MSG_CMPTR, cm_rc);
    }
    return RC_OK;
}

/*****************************************************************************
 *
 *  cpicerr_destroy()
 *
 *  Destroys a CPICERR structure created with cpicerr_new().
 *  All memory assocated with the input CPICERR pointer is freed.
 *
 *****************************************************************************/
void
cpicerr_destroy(CPICERR * cpicerr)
{
    /*
     * Make sure we don't free() a NULL pointer!!!
     */
    if (cpicerr != NULL) {
        if (cpicerr->program_name != NULL) {
            free(cpicerr->program_name);
        }

        if (cpicerr->program_info != NULL) {
            free(cpicerr->program_info);
        }

        if (cpicerr->log_file_name != NULL) {
            free(cpicerr->log_file_name);
        }

        if (cpicerr->log_file_path != NULL) {
            free(cpicerr->log_file_path);
        }

        free(cpicerr);
    }
    return;
}

/*****************************************************************************
 *
 *  cpicerr_set_rc_info
 *
 *  Internal call which makes it easier to store return code information
 *  in the CPICERR structure.  Called from cpicerr_handle_rc().
 *
 *****************************************************************************/
void
cpicerr_set_rc_info(CPICERR *       cpicerr,
                    CPIC_VERB_INDEX verb_index,
                    CM_RETURN_CODE  conv_rc)
/* Store the verb return code information into the cpicerr structure */
{
    cpicerr->verb_index = verb_index;
    cpicerr->conv_rc =    conv_rc;
}

/*****************************************************************************
 *
 *  cpicerr_show_rc
 *
 *  Internal call which will display information about the unexpected
 *  return code encountered.  Called from cpicerr_handle_rc().
 *
 *****************************************************************************/
void
cpicerr_show_rc(CPICERR * cpicerr)
{
    write_error("\n  Unexpected CPI-C return code encountered...\n");

    /*-----------------------------------------------------------------------*
     * Find the CPI-C verb's name, and show it.
     *-----------------------------------------------------------------------*/
    write_error( "         CPI-C verb name: %s, %s\n",
            cpicerr_get_message(CPIC_VERBS_SHORT,
                                (CM_INT32)cpicerr->verb_index),
            cpicerr_get_message(CPIC_VERBS_LONG,
                                (CM_INT32)cpicerr->verb_index));

    /*-----------------------------------------------------------------------*
     * Find the CPI-C return code's name, and show it.
     *-----------------------------------------------------------------------*/
    write_error( "       CPI-C return code: %lu, %s\n",
            cpicerr->conv_rc,
            cpicerr_get_message(CPIC_RETURN_CODES, cpicerr->conv_rc));

    return;
}

/*****************************************************************************
 *
 *  cpicerr_log_rc
 *
 *  Internal call which will log information about the unexpected
 *  return code encountered.  Called from cpicerr_handle_rc().
 *
 *****************************************************************************/
void
cpicerr_log_rc(CPICERR * cpicerr,
               FILE *    log_file)
{
    write_log(log_file, "  Unexpected CPI-C return code encountered...\n");

    /*-----------------------------------------------------------------------*
     * Find the CPI-C verb's name, and show it.
     *-----------------------------------------------------------------------*/
    write_log(log_file,
              "         CPI-C verb name: %s, %s\n",
              cpicerr_get_message(CPIC_VERBS_SHORT,
                                  (CM_INT32)cpicerr->verb_index),
              cpicerr_get_message(CPIC_VERBS_LONG,
                                  (CM_INT32)cpicerr->verb_index));

    /*-----------------------------------------------------------------------*
     * Find the CPI-C return code's name, and show it.
     *-----------------------------------------------------------------------*/
    write_log(log_file,
              "       CPI-C return code: %lu, %s\n",
              cpicerr->conv_rc,
              cpicerr_get_message(CPIC_RETURN_CODES, cpicerr->conv_rc));

    return;
}

/*****************************************************************************
 *
 *  cpicerr_classify_rc
 *
 *  Internal call which takes a CPI-C return code and classifies it
 *  into one of the categories that we have defined.
 *  Called from cpicerr_handle_rc().
 *
 *****************************************************************************/
void
cpicerr_classify_rc (CM_RETURN_CODE conv_rc,
                     CPIC_RC_HANDLING * classification)
{
    switch (conv_rc) {

        case CM_ALLOCATE_FAILURE_NO_RETRY:
            /* configuration defect or protocol defect, local or partner */
        case CM_TP_NOT_AVAILABLE_NO_RETRY:
            /* partner program could not be sucessfully started */
        case CM_RESOURCE_FAILURE_NO_RETRY:
            /* communication failure or protocol defect, local or partner */

        case CM_DEALLOCATED_ABEND:
            /* unrecoverable error in the partner TP */
        case CM_DEALLOCATED_ABEND_SVC:
            /* unrecoverable error in the partner TP */
        case CM_DEALLOCATED_ABEND_TIMER:
            /* unrecoverable error in the partner TP */

        case CM_CONVERSATION_TYPE_MISMATCH:
        case CM_PIP_NOT_SPECIFIED_CORRECTLY:

            /* configuration defect or mismatch with the partner */

        case CM_OK:
            /* application design defect, local or partner program */
        case CM_DEALLOCATED_NORMAL:
            /* TP design defect, local or partner program */
        case CM_PARAMETER_ERROR:
            /* local program design or coding defect */
        case CM_PROGRAM_PARAMETER_CHECK:
            /* local program design or coding defect */
        case CM_PROGRAM_STATE_CHECK:
            /* local program design or coding defect */
            *classification = UNRECOVERABLE;
            break;

        case CM_ALLOCATE_FAILURE_RETRY:
            /* configuration defect or route temporarily down */
        case CM_TP_NOT_AVAILABLE_RETRY:
            /* congestion at the partner operating system */
        case CM_RESOURCE_FAILURE_RETRY:
            /* route temporarily down */
            *classification = RETRY_CONV;
            break;

        case CM_UNSUCCESSFUL:
            /* verb could not be completed now */
            *classification = RETRY_VERB;
            break;

        case CM_PROGRAM_ERROR_NO_TRUNC:
        case CM_SVC_ERROR_NO_TRUNC:
            /* partner failed while building a record to send */
        case CM_PROGRAM_ERROR_PURGING:
        case CM_SVC_ERROR_PURGING:
            /* partner failed while processing a received record */
        case CM_PROGRAM_ERROR_TRUNC:
        case CM_SVC_ERROR_TRUNC:
            /* partner failed after partially sending a record */
            *classification = ERROR_RECEIVED;
            break;

        case CM_PRODUCT_SPECIFIC_ERROR:
            /********************************/
            /* Examine each of these.       */
            /* In some cases, you may want  */
            /* to return CONTINUE.          */
            /********************************/
            *classification = UNRECOVERABLE;
            break;

        case CM_SECURITY_NOT_VALID:
            *classification = SECURITY_NOT_VALID;
            break;

        default:
            *classification = UNRECOVERABLE;
            break;
    }
    return;
}

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

*
 *  cpicerr_log_cpicerr()
 *
 *  Internal call which logs complete conversation and error information
 *  to a log file.  Called by cpicerr_handle_rc().
 *
 *****************************************************************************/
int
cpicerr_log_cpicerr(CPICERR * cpicerr,
                    char *    file_name,
                    int       file_line)
{
    int      rc;                            /* return value                  */
    FILE *   log_file;                      /* log file handle               */
    unsigned count;                         /* used to extract text strings  */
    char     filepath[256];                 /* actual file path for logging  */
    char     last_char;

    if (cpicerr != (CPICERR *)NULL) {

        /*-------------------------------------------------------------------*
         * Save the time of the call to this procedure.
         *-------------------------------------------------------------------*/
        cpicerr->program_error_time = time(NULL);

        filepath[0] = '\0';
        if (cpicerr->log_file_path != NULL &&
            cpicerr->log_file_path[0] != '\0') {
            strcpy(filepath, cpicerr->log_file_path);
            last_char =
                    cpicerr->log_file_path[strlen(cpicerr->log_file_path)-1];
            if (!(last_char == '\\' || last_char == '/')) {
                strcat(filepath, "/");
            }
        }
        if (cpicerr->log_file_name != NULL) {
            strcat(filepath, cpicerr->log_file_name);
        }

        if ((strlen(filepath) == 0) ||
                               ((log_file = fopen(filepath, "a")) == NULL  )) {
            log_file = stderr;
        }

        write_log(log_file,
                  "------------------------------------------------------\n");

        if (cpicerr->program_name != NULL) {
            write_log(log_file, "  CPI-C error in program: \"%s\"",
                                                     cpicerr->program_name);
        }

        if (cpicerr->program_info != NULL) {
            write_log(log_file, ", %s\n", cpicerr->program_info);
        }

        if (cpicerr->major_version != 0 || cpicerr->minor_version != 0) {
            write_log(log_file, "         Program version: %u.%u\n",
                                       (unsigned int)cpicerr->major_version,
                                       (unsigned int)cpicerr->minor_version);
        }

        cpicerr_log_rc(cpicerr, log_file);

        if (cpicerr->partner_LU_name[0] != '\0') {
            cpicerr->partner_LU_name[(int)cpicerr->partner_LU_name_length]
                                                                       = '\0';
        }
        if (cpicerr->mode_name[0] != '\0') {
            cpicerr->mode_name[(int)cpicerr->mode_name_length] = '\0';
        }
        write_log(log_file, "         Partner LU name: %s\n",
                                                     cpicerr->partner_LU_name);
        write_log(log_file, "               Mode name: %s\n",
                                                     cpicerr->mode_name);

        write_log(log_file, "   CPI-C conversation ID: ");

        for (count = 0; count<sizeof(cpicerr->conversation_id); count++ ) {
            write_log(log_file, "%02X",
                               (unsigned int)cpicerr->conversation_id[count]);
        }
        write_log(log_file, "\n");

        write_log(log_file, "CPI-C conversation state: %lu, %s\n",
           cpicerr->conversation_state,
           cpicerr_get_message(CPIC_STATES_CONV, cpicerr->conversation_state));

        write_log(log_file, " CPI-C conversation type: %lu, %s\n",
           cpicerr->conversation_type,
           cpicerr_get_message( CPIC_CONV_TYPES, cpicerr->conversation_type));

        write_log(log_file, " Conversation sync level: %lu, %s\n",
                cpicerr->sync_level,
                cpicerr_get_message( CPIC_SYNC_LEVELS, cpicerr->sync_level));

        write_log(log_file, "      Program start time: %s",
                                        ctime(&(cpicerr->program_start_time)));
        write_log(log_file, "      Program error time: %s",
                                        ctime(&(cpicerr->program_error_time)));
        write_log(log_file, " Called from source file: %s\n", file_name);
        write_log(log_file, "                 at line: %d\n", file_line);

        fclose(log_file);

        rc = 0;
    } else {
        rc = 1;
    }

    return rc;
}

CPICERR_MESSAGE cpicerr_verbs_short[] = {
    MSG_CMACCP,         "CMACCP",
    MSG_CMALLC,         "CMALLC",
    MSG_CMCFM,          "CMCFM",
    MSG_CMCFMD,         "CMCFMD",
    MSG_CMDEAL,         "CMDEAL",
    MSG_CMECS,          "CMECS",
    MSG_CMECT,          "CMECT",
    MSG_CMEMN,          "CMEMN",
    MSG_CMEPLN,         "CMEPLN",
    MSG_CMESL,          "CMESL",
    MSG_CMFLUS,         "CMFLUS",
    MSG_CMINIT,         "CMINIT",
    MSG_CMPTR,          "CMPTR",
    MSG_CMRCV,          "CMRCV",
    MSG_CMRTS,          "CMRTS",
    MSG_CMSCT,          "CMSCT",
    MSG_CMSDT,          "CMSDT",
    MSG_CMSED,          "CMSED",
    MSG_CMSEND,         "CMSEND",
    MSG_CMSERR,         "CMSERR",
    MSG_CMSF,           "CMSF",
    MSG_CMSLD,          "CMSLD",
    MSG_CMSMN,          "CMSMN",
    MSG_CMSPLN,         "CMSPLN",
    MSG_CMSPTR,         "CMSPTR",
    MSG_CMSRC,          "CMSRC",
    MSG_CMSRT,          "CMSRT",
    MSG_CMSSL,          "CMSSL",
    MSG_CMSST,          "CMSST",
    MSG_CMSTPN,         "CMSTPN",
    MSG_CMTRTS,         "CMTRTS",
    MSG_XCSCSU,         "XCSCSU",
    MSG_XCSCSP,         "XCSCSP",
    MSG_XCSCST,         "XCSCST",
    MSG_XCECST,         "XCECST",
    MSG_XCECSU,         "XCECSU",
    MSG_XCMSSI,         "XCMSSI",
    MSG_XCMESI,         "XCMESI",
    MSG_XCMDSI,         "XCMDSI",
    MAX_MESSAGE,        "ERROR "
};

CPICERR_MESSAGE cpicerr_verbs_long[] = {
    MSG_CMACCP,         "Accept_Conversation",
    MSG_CMALLC,         "Allocate",
    MSG_CMCFM,          "Confirm",
    MSG_CMCFMD,         "Confirmed",
    MSG_CMDEAL,         "Deallocate",
    MSG_CMECS,          "Extract_Conversation_State",
    MSG_CMECT,          "Extract_Conversation_Type",
    MSG_CMEMN,          "Extract_Mode_Name",
    MSG_CMEPLN,         "Extract_Partner_LU_Name",
    MSG_CMESL,          "Extract_Sync_Level",
    MSG_CMFLUS,         "Flush",
    MSG_CMINIT,         "Initialize_Conversation",
    MSG_CMPTR,          "Prepare_To_Receive",
    MSG_CMRCV,          "Receive",
    MSG_CMRTS,          "Request_To_Send",
    MSG_CMSCT,          "Set_Conversation_Type",
    MSG_CMSDT,          "Set_Deallocate_Type",
    MSG_CMSED,          "Set_Error_Direction",
    MSG_CMSEND,         "Send_Data",
    MSG_CMSERR,         "Send_Error",
    MSG_CMSF,           "Set_Fill",
    MSG_CMSLD,          "Set_Log_Data",
    MSG_CMSMN,          "Set_Mode_Name",
    MSG_CMSPLN,         "Set_Partner_LU_Name",
    MSG_CMSPTR,         "Set_Prepare_To_Receive_Type",
    MSG_CMSRC,          "Set_Return_Control",
    MSG_CMSRT,          "Set_Receive_Type",
    MSG_CMSSL,          "Set_Sync_Level",
    MSG_CMSST,          "Set_Send_Type",
    MSG_CMSTPN,         "Set_TP_Name",
    MSG_CMTRTS,         "Test_Request_To_Send_Received",
    MSG_XCSCSU,         "Set Conversation Security Userid",
    MSG_XCSCSP,         "Set Conversation Security Password",
    MSG_XCSCST,         "Set Conversation Security Type",
    MSG_XCECST,         "Extract_Conversation_Security_Type",
    MSG_XCECSU,         "Extract_Conversation_Security_Userid",
    MSG_XCMSSI,         "Set_CPIC_Side_Information",
    MSG_XCMESI,         "Extract_CPIC_Side_Information",
    MSG_XCMDSI,         "Delete_CPIC_Side_Information",
    MAX_MESSAGE,        "Invalid verb name"
};

CPICERR_MESSAGE cpicerr_return_codes[] = {
    CM_OK,                          "CM_OK",
    CM_ALLOCATE_FAILURE_NO_RETRY,   "CM_ALLOCATE_FAILURE_NO_RETRY",
    CM_ALLOCATE_FAILURE_RETRY,      "CM_ALLOCATE_FAILURE_RETRY",
    CM_CONVERSATION_TYPE_MISMATCH,  "CM_CONVERSATION_TYPE_MISMATCH",
    CM_PIP_NOT_SPECIFIED_CORRECTLY, "CM_PIP_NOT_SPECIFIED_CORRECTLY",
    CM_SECURITY_NOT_VALID,          "CM_SECURITY_NOT_VALID",
    CM_TP_NOT_AVAILABLE_NO_RETRY,   "CM_TP_NOT_AVAILABLE_NO_RETRY",
    CM_TP_NOT_AVAILABLE_RETRY,      "CM_TP_NOT_AVAILABLE_RETRY",
    CM_DEALLOCATED_ABEND,           "CM_DEALLOCATED_ABEND",
    CM_DEALLOCATED_NORMAL,          "CM_DEALLOCATED_NORMAL",
    CM_PARAMETER_ERROR,             "CM_PARAMETER_ERROR",
    CM_PRODUCT_SPECIFIC_ERROR,      "CM_PRODUCT_SPECIFIC_ERROR",
    CM_PROGRAM_ERROR_NO_TRUNC,      "CM_PROGRAM_ERROR_NO_TRUNC",
    CM_PROGRAM_ERROR_PURGING,       "CM_PROGRAM_ERROR_PURGING",
    CM_PROGRAM_ERROR_TRUNC,         "CM_PROGRAM_ERROR_TRUNC",
    CM_PROGRAM_PARAMETER_CHECK,     "CM_PROGRAM_PARAMETER_CHECK",
    CM_PROGRAM_STATE_CHECK,         "CM_PROGRAM_STATE_CHECK",
    CM_RESOURCE_FAILURE_NO_RETRY,   "CM_RESOURCE_FAILURE_NO_RETRY",
    CM_RESOURCE_FAILURE_RETRY,      "CM_RESOURCE_FAILURE_RETRY",
    CM_UNSUCCESSFUL,                "CM_UNSUCCESSFUL",
    CM_DEALLOCATED_ABEND_SVC,       "CM_DEALLOCATED_ABEND_SVC",
    CM_DEALLOCATED_ABEND_TIMER,     "CM_DEALLOCATED_ABEND_TIMER",
    CM_SVC_ERROR_NO_TRUNC,          "CM_SVC_ERROR_NO_TRUNC",
    CM_SVC_ERROR_PURGING,           "CM_SVC_ERROR_PURGING",
    CM_SVC_ERROR_TRUNC,             "CM_SVC_ERROR_TRUNC",
    MAX_MESSAGE,                    "Invalid Return Code Value"
};

CPICERR_MESSAGE cpicerr_rc_classes[] = {
    BACKOUT_RECEIVED,   "BACKOUT_RECEIVED",
    CONTINUE,           "CONTINUE",
    ERROR_RECEIVED,     "ERROR_RECEIVED",
    SECURITY_NOT_VALID, "SECURITY_NOT_VALID",
    RETRY_CONV,         "RETRY_CONVERSATION",
    RETRY_CONV_BO,      "RETRY_CONVERSATION_BACKOUT",
    RETRY_VERB,         "RETRY_LAST_VERB",
    UNRECOVERABLE,      "UNRECOVERABLE",
    UNRECOVERABLE_BO,   "UNRECOVERABLE_BACKOUT",
    MAX_MESSAGE,        "Invalid Return Code Class"
};

CPICERR_MESSAGE cpicerr_states_conv[] = {
    CM_INITIALIZE_STATE,            "Initialize state",
    CM_SEND_STATE,                  "Send state",
    CM_RECEIVE_STATE,               "Receive state",
    CM_SEND_PENDING_STATE,          "Send pending state",
    CM_CONFIRM_STATE,               "Confirm state",
    CM_CONFIRM_SEND_STATE,          "Confirm send state",
    CM_CONFIRM_DEALLOCATE_STATE,    "Confirm deallocate state",
    CMECS_NOT_SUPPORTED,            "CMECS not supported",
    MAX_MESSAGE,                    "Invalid Conversation State value"
};

CPICERR_MESSAGE cpicerr_sync_levels[] = {
    CM_NONE,            "None",
    CM_CONFIRM,         "Confirm",
    MAX_MESSAGE,        "Invalid Sync Level value"
};

CPICERR_MESSAGE cpicerr_conv_types[] = {
    CM_BASIC_CONVERSATION , "Basic",
    CM_MAPPED_CONVERSATION, "Mapped",
    MAX_MESSAGE,            "Invalid conversation type value"
};

CPICERR_MESSAGE_LIST message_list[] = {
    CPIC_SYNC_LEVELS,   cpicerr_sync_levels,
    CPIC_CONV_TYPES,    cpicerr_conv_types,
    CPIC_STATES_CONV,   cpicerr_states_conv,
    CPIC_RC_CLASSES,    cpicerr_rc_classes,
    CPIC_RETURN_CODES,  cpicerr_return_codes,
    CPIC_VERBS_SHORT,   cpicerr_verbs_short,
    CPIC_VERBS_LONG,    cpicerr_verbs_long,
    MAX_MESSAGE,        NULL
};

/*****************************************************************************
 *
 *  cpicerr_get_message()
 *
 *  Loop through the message arrays looking for the message type and
 *  message index.  Return a pointer to the appropriate string.
 *
 *  This procedure will never return NULL, thus is safe to use in
 *  printf() type calls.
 *
 *****************************************************************************/
char *
cpicerr_get_message(CPICERR_MESSAGE_TYPE message_type, CM_INT32 index)
{
    unsigned int count;                     /* counter through the array     */

    CPICERR_MESSAGE * messages;

    for (count = 0; message_list[count].type < MAX_MESSAGE ; count++ ) {
        if (message_list[count].type == message_type) break;
    }

    if ( (messages = message_list[count].list) == NULL ) {
        return "Message list not found.";
    }

    for (count = 0; messages[count].index < MAX_MESSAGE; count++) {
       if (messages[count].index == index) break;
    }
    return messages[count].message;
}

/*****************************************************************************
 *
 *  cpicerr_show_product_info
 *
 *****************************************************************************/
void
cpicerr_show_product_info(CPICERR * cpicerr)
{
    if (cpicerr->conv_rc == CM_ALLOCATE_FAILURE_NO_RETRY ||
        cpicerr->conv_rc == CM_ALLOCATE_FAILURE_RETRY ) {
#if defined(GET_OS2_SENSE_DATA)
#if defined(FAPI)
        if (get_machine_mode()) {
#endif
             write_error(
             "\n  Retrying Allocate to extract OS/2 sense data:\n");
            cpicerr_os2_appc_allocate(cpicerr);
#if defined(FAPI)
        }
#endif
#endif

    }
}

#ifdef GET_OS2_SENSE_DATA

cpicerr_os2_appc_allocate(CPICERR * cpicerr)
/*
 * This procedure retries the allocate that failed in CPI-C.  We are not
 * retrying the allocate to establish the connection, but to get more
 * error information about why the first allocate failed.
 *
 * The OS/2 APPC api is used to extract the sense data, which provides
 * a more specify reason for an allocation failure.
 */
{
   TP_STARTED tp_started;                    /* Declare a verb control block */
   TP_STARTED *ptr_tp_started = (TP_STARTED *)&tp_started;
   int    length;                            /* length of lu_alias           */
   char   plu_alias[8+1];
   char   fqplu_name[17+1];

   ALLOCATE allocate;                        /* Declare a verb control block */
   ALLOCATE *ptr_allocate = (ALLOCATE *)&allocate;

   TP_ENDED tp_ended;                        /* Declare a verb control block */
   TP_ENDED *ptr_tp_ended = (TP_ENDED *)&tp_ended;

   CLEAR_VCB(tp_started);                    /* Zero the verb control block  */
   tp_started.opcode = AP_TP_STARTED;        /* APPC verb - TP_STARTED       */

   memset( tp_started.lu_alias, (int)'\0', sizeof(tp_started.lu_alias));

   BLANK_STRING(tp_started.tp_name);         /* Set 64-byte string to blanks */
   ascii_to_ebcdic_field(tp_started.tp_name,
                         sizeof(tp_started.tp_name));

   APPC ((ULONG) (TP_STARTED far *)ptr_tp_started); /* Issue the verb        */

   if (tp_started.primary_rc == AP_OK) {
       CLEAR_VCB(allocate);                  /* Zero the vcb                 */
       allocate.opcode = AP_B_ALLOCATE;      /* Verb - ALLOCATE              */
       allocate.opext = AP_MAPPED;           /* Basic Conversation type      */

                                             /* Set the TP_ID */
       memcpy (allocate.tp_id, tp_started.tp_id, sizeof(allocate.tp_id));
       allocate.sync_level = AP_CONFIRM;     /* Sync level-confirm           */
       allocate.rtn_ctl = AP_WHEN_SESSION_FREE;/* avoid deadlock             */
       allocate.security = AP_NONE;          /* Set security type            */

       if (!parse_destination(cpicerr->partner_LU_name,
                              plu_alias,
                              fqplu_name)) {
          if ((length = strlen(plu_alias)) != 0) {
             BLANK_STRING(allocate.plu_alias);
             memcpy ( allocate.plu_alias,
                      plu_alias,
                      min(length, sizeof(allocate.plu_alias)));
                                             /* Set PLU_ALIAS                */
          } else {
             memset (allocate.plu_alias,(int)'\0',sizeof(allocate.plu_alias));
             BLANK_STRING(allocate.fqplu_name);  /* Set FQ PLU NAME          */
             memcpy ( allocate.fqplu_name,
                      fqplu_name,
                      min(strlen(fqplu_name), sizeof(allocate.fqplu_name)));
             TOUPPER_STRING(allocate.fqplu_name, 17);
             ascii_to_ebcdic_field(allocate.fqplu_name,
                                   sizeof(allocate.fqplu_name));
          }

       } else {
          BLANK_STRING(allocate.plu_alias);
          memcpy ( allocate.plu_alias,
                   "UNKNOWN",
                   7);
       }

       BLANK_STRING(allocate.tp_name);       /* Set 64-byte string to blanks */
       ascii_to_ebcdic_field(allocate.tp_name,
                             sizeof(allocate.tp_name));

       BLANK_STRING(allocate.mode_name);     /* Set 8-byte string to blanks  */
          memcpy ( allocate.mode_name,
                   cpicerr->mode_name,
                   min(strlen(cpicerr->mode_name),
                       sizeof(allocate.mode_name)));
          TOUPPER_STRING(allocate.mode_name, 8);
       ascii_to_ebcdic_field(allocate.mode_name,
                             sizeof(allocate.mode_name));

       APPC((ULONG) (ALLOCATE far *) ptr_allocate);  /* Issue the verb       */

       if (allocate.primary_rc != AP_OK) {
        write_error(
        "\t OS/2 Sense Data: %08lX\n", SWAP4(allocate.sense_data));

       }

   } else {                                  /* Save the returned tp_id      */
       return (tp_started.primary_rc);
   }
   CLEAR_VCB(tp_ended);                      /* Zero the verb control block  */
   tp_ended.opcode = AP_TP_ENDED;            /* Set the verb opcode          */
                                             /* Set the tp_id  */
   memcpy (tp_ended.tp_id, tp_started.tp_id, sizeof(tp_ended.tp_id));
   tp_ended.type = AP_SOFT;                  /* type: AP_HARD or AP_SOFT     */

   APPC((ULONG) (TP_ENDED far *) ptr_tp_ended); /* Issue the verb.           */
}

int
parse_destination(char * in_string,
                  char * plu_alias,
                  char * fqplu_name)
/*
 * This procedure is used by cpicerr_os2_appc_allocate verb
 */
{
   if (strchr(in_string, '.') != NULL) {
      if (in_string[0] == '.') {
          in_string++;
      }
      strcpy(fqplu_name, in_string);
      plu_alias[0] = '\0';
   } else {
      strcpy(plu_alias, in_string);
      fqplu_name[0] = '\0';
   }
   return 0;

}

#endif


CPICERRS.H

/*****************************************************************************
 *
 *  MODULE NAME: CPICERRS.H
 *
 *  COPYRIGHTS:
 *             This module contains code made available by IBM
 *             Corporation on an AS IS basis.  Any one receiving the
 *             module is considered to be licensed under IBM copyrights
 *             to use the IBM-provided source code in any way he or she
 *             deems fit, including copying it, compiling it, modifying
 *             it, and redistributing it, with or without
 *             modifications.  No license under any IBM patents or
 *             patent applications is to be implied from this copyright
 *             license.
 *
 *             A user of the module should understand that IBM cannot
 *             provide technical support for the module and will not be
 *             responsible for any consequences of use of the program.
 *
 *             Any notices, including this one, are not to be removed
 *             from the module without the prior written consent of
 *             IBM.
 *
 *  AUTHOR:    Peter J. Schwaller
 *             VNET:     PJS at RALVM6           Tie Line: 444-4376
 *             Internet: pjs@ralvm6.vnet.ibm.com     (919) 254-4376
 *
 *             John Q. Walker
 *             VNET:     JOHNQ at RALVM6         Tie Line: 444-4414
 *             Internet: johnq@ralvm6.vnet.ibm.com   (919) 254-4414
 *
 *  AVAILABILITY:
 *             These sample programs and source are also available on
 *             CompuServe through the APPC Information Exchange.  To get
 *             to the APPC forum just type 'GO APPC' from any CompuServe
 *             prompt.  The samples are available in the Sample Programs
 *             library section.  Just search on the keyword CPICPGMS to
 *             find all the samples in this series.
 *
 *             Updates for the sample programs and support for many more
 *             CPI-C platforms will also be made available on CompuServe.
 *
 *  RELATED FILES:
 *             Use CPICERR.C
 *
 *  CHANGE HISTORY:
 *  Date       Description
 *  08/05/92   Version 2.31 of APING, ATELL and AREXEC released to CompuServe.
 *             This version was also distributed at the APPC/APPN Platform
 *             Developer's Conference held in Raleigh, NC.
 *  08/13/92   Changed cpicerr_handle_rc() to a macro referencing
 *             cpicerr_handle_rc_extended().  The macro adds the current
 *             source file and line number to improve source code debugging.
 *  11/04/92   Changed version from char to unsigned char.
 *  11/15/92   Changed reply structure to include 2 byte indicator.
 *  11/17/92   Added send/receipt of operating system string.
 *             Added cpicerr_exchange_version_plus() to return the string.
 *
 *****************************************************************************/

/*****************************************************************************
 *
 * OVERVIEW OF CPICERR CALLS
 *
 * cpicerr_new()                      Creates a CPICERR object.
 *                                    This must be done before any other
 *                                    cpicerr calls can be used.
 *
 * These calls set values in the cpicerr object structure and affect how
 * cpicerr_handle_rc reacts to errors.
 *
 * cpicerr_set_log_file_name()        What filename to use for logging
 * cpicerr_set_log_file_path()        Where the filename is
 * cpicerr_set_program_name()         String - Output as part of log info
 * cpicerr_set_program_info()         String - Output as part of log info
 * cpicerr_set_major_version()        8 bit int - see cpicerr_exchange_version
 * cpicerr_set_minor_version()        8 bit int - see cpicerr_exchange_version
 * cpicerr_set_conv_id()              Used to extract conversation state info
 * cpicerr_set_exit_level()           Level of error on which to exit
 * cpicerr_set_show_level()           Level of error on which to show errors
 * cpicerr_set_log_level()            Level of error on which to log errors
 *
 * cpicerr_handle_rc()                Should be called by the program for all
 *                                    UNEXPECTED return codes.
 *                                    Functions performed are:
 *                                       Classification of the return code
 *                                       Showing partial info to end user
 *                                       Logging complete info to disk
 *                                    This is a macro that actually expands
 *                                    to cpicerr_handle_rc_extended().
 *
 * cpicerr_destroy()                  Destroys the CPICERR  object.
 *
 *
 * cpicerr_classify_rc()              These are internal calls used by
 * cpicerr_show_rc()                  other cpicerr_handle_rc.
 * cpicerr_log_cpicerr()
 * cpicerr_get_message()
 * cpicerr_set_rc_info()
 * cpicerr_show_product_info()
 *
 *****************************************************************************/

#ifndef INCL_CPICERR
#define INCL_CPICERR

/*
 * Include the C standard I/O library for the FILE structure definition.
 */
#include <stdio.h>

/*
 * Collection of routines with special ported version for each platform
 * The only thing that is used from CPICDEFS.H is the correct setting
 * of the SHORT_IDENTIFIERS identifier.
 */
#include "cpicdefs.h"

#if defined(SHORT_IDENTIFIERS)
/*
 * The C/370 compiler requires that identifier be unique in the 1st 8 chars.
 * If any new functions are added to this file, they should also be added to
 * this list, with a unique mapping.
 */
#    define  cpicerr_build_reply_image      cebri
#    define  cpicerr_conv_types             ceconv_type
#    define  cpicerr_classify_rc            cecr
#    define  cpicerr_create_reply           cecrep
#    define  cpicerr_destroy                cedestroy
#    define  cpicerr_destroy_reply          cedr
#    define  cpicerr_extract_reply_image    ceeri
#    define  cpicerr_exchange_version       ceev
#    define  cpicerr_exchange_version_plus  ceevp
#    define  cpicerr_get_message            cegm
#    define  cpicerr_handle_rc_extended     cehre
#    define  cpicerr_log_cpicerr            celf
#    define  cpicerr_set_major_version      cemajor
#    define  cpicerr_set_minor_version      ceminor
#    define  cpicerr_new                    cenew
#    define  cpicerr_recv_appl_error        cerae
#    define  cpicerr_return_codes           cerc
#    define  cpicerr_rc_classes             cercclass
#    define  cpicerr_send_appl_error        cesae
#    define  cpicerr_set_conv_id            cesci
#    define  cpicerr_set_error_reply        ceser
#    define  cpicerr_set_rc_info            cesetr
#    define  cpicerr_set_log_file_name      ceslfn
#    define  cpicerr_set_log_file_path      ceslfp
#    define  cpicerr_set_log_level          cesll
#    define  cpicerr_set_program_info       cespi
#    define  cpicerr_set_program_name       cespn
#    define  cpicerr_show_product_info      cespri
#    define  cpicerr_show_rc                cesr
#    define  cpicerr_show_reply             cesrep
#    define  cpicerr_set_show_level         cessl
#    define  cpicerr_states_conv            cestate
#    define  cpicerr_set_exit_level         cesxl
#    define  cpicerr_sync_levels            cesync
#    define  cpicerr_verbs_long             cvlong
#    define  cpicerr_verbs_short            cvshort
#endif

/* Needed for the time_t structure used within the CPICINIT structure */
#include <time.h>

/*
 * Specify the maximum sizes for a variety of CPI-C fields.
 * The +1 is added to allow room for the C null terminator for strings.
 */
#define  MAX_LU_ALIAS      (8+1)
#define  MAX_PLU_ALIAS     (8+1)
#define  MAX_TP_NAME       (64+1)
#define  MAX_SYM_DEST_NAME (8+1)
#define  MAX_FQPLU_NAME    (17+1)
#define  MAX_DESTINATION   (17+1)
#define  MAX_MODE_NAME     (8+1)
#define  MAX_USERID        (8+1)
#define  MAX_PASSWORD      (8+1)

#define  MAX_MESSAGE_TEXT_LENGTH    (2048)

/*****************************************************************************
 * These values are used to break the CPI-C return codes down into classes.
 * They are organized from errors that can be ignored, to fatal errors
 * which cannot be recovered from.
 *
 * The values ALL_ERRORS and NO_ERRORS are used as extremes for the
 * cpicerr_set_exit_level() and cpicerr_set_show_level() calls, and are
 * not actual classification values.
 *****************************************************************************/
typedef enum {
    RC_OK = 0,
    ALL_ERRORS,
    CONTINUE          ,
    RETRY_VERB        ,
    SECURITY_NOT_VALID,
    ERROR_RECEIVED    ,
    RETRY_CONV        ,
    RETRY_CONV_BO     ,
    BACKOUT_RECEIVED  ,
    UNRECOVERABLE     ,
    UNRECOVERABLE_BO  ,
    NO_ERRORS
} CPIC_RC_HANDLING;

/*
 * Since CPI-C does not provide actual values that correspond to each CPI-C
 * call, we have to invent our own so that we can put the call names into
 * our message lists.
 */
typedef enum {
    MSG_CMACCP,
    MSG_CMALLC,
    MSG_CMCFM ,
    MSG_CMCFMD,
    MSG_CMDEAL,
    MSG_CMECS ,
    MSG_CMECT ,
    MSG_CMEMN ,
    MSG_CMEPLN,
    MSG_CMESL ,
    MSG_CMFLUS,
    MSG_CMINIT,
    MSG_CMPTR ,
    MSG_CMRCV ,
    MSG_CMRTS ,
    MSG_CMSCT ,
    MSG_CMSDT ,
    MSG_CMSED ,
    MSG_CMSEND,
    MSG_CMSERR,
    MSG_CMSF  ,
    MSG_CMSLD ,
    MSG_CMSMN ,
    MSG_CMSPLN,
    MSG_CMSPTR,
    MSG_CMSRC ,
    MSG_CMSRT ,
    MSG_CMSSL ,
    MSG_CMSST ,
    MSG_CMSTPN,
    MSG_CMTRTS,
    MSG_XCSCSU,
    MSG_XCSCST,
    MSG_XCSCSP,
    MSG_XCECST,
    MSG_XCECSU,
    MSG_XCMSSI,
    MSG_XCMESI,
    MSG_XCMDSI
} CPIC_VERB_INDEX;

/*****************************************************************************
 *
 * CPICERR_REPLY
 *
 * This structure is a reusable reply format.  This can be used either
 * as part of a data reply, or as an error reply after a send_error()
 * call.  See CPICERR.C for complete details of how this structure
 * is formatted on a CPI-C conversation.
 *****************************************************************************/

typedef enum next_state {
    NEXT_SEND,
    NEXT_RECEIVE,
    NEXT_DEALLOCATE,
    NEXT_DEALLOCATE_AND_EXIT
} NEXT_STATE;

typedef enum reply_response {
    REPLY_POSITIVE_COMPLETE     = 1,
    REPLY_POSITIVE_INTERMEDIATE = 2,
    REPLY_NEGATIVE_TRANSIENT    = 3,
    REPLY_NEGATIVE_PERMANENT    = 4
} REPLY_RESPONSE;

typedef struct cpicerr_reply {
    unsigned short      length;
    unsigned short      buffer_length;
    char *              buffer;
} CPICERR_REPLY;

/*
 * The format of the reply buffer is:
 * Offset   Length   Field
 * ------   ------   -----
 *    0        2     Reply Indicator (0xFF01)
 *    2        1     response
 *    3        1     reserved (must be 0)
 *    4        2     message category
 *    6        4     primary code
 *   10        4     secondary code
 * The following two fields are optional:
 *   12        2     primary message length
 *   14        x     primary message text
 * The following two fields are optional and should follow the primary message:
 *  14+x       2     secondary message length
 *  16+x       y     secondary message text
 */

#define CPICERR_REPLY_INDICATOR_VALUE   (0xFF01)
#define CPICERR_REPLY_MIN_LENGTH        (14)

/* offsets into the reply buffer for each field */
#define CPICERR_REPLY_INDICATOR         (0)
#define CPICERR_REPLY_RESPONSE          (2)
#define CPICERR_REPLY_RESERVED          (3)
#define CPICERR_REPLY_CATEGORY          (4)
#define CPICERR_REPLY_PRIMARY           (6)
#define CPICERR_REPLY_SECONDARY        (10)
#define CPICERR_REPLY_PRIMARY_LENGTH   (14)
#define CPICERR_REPLY_PRIMARY_TEXT     (16)

/*****************************************************************************
 *
 * CPICERR
 *
 * This structure contains all of the information necessary to for all
 * cpicerr calls.  All of the fields which need to be changed by the
 * application program should be modified through the cpicerr_set...()
 * calls.  The application program should never have to access the
 * cpicerr structure directly.
 *
 * When an unexpected return code occurs, this structure is used to
 * determine what error processing should occur, including:
 *   display of minimal error information
 *   logging of complete error information
 *     where to log the data
 *
 * If a field is added to this structure, be sure to add the correct
 * initialization values for the field in the cpicerr_new() function.
 *****************************************************************************/

typedef struct error_handler_cpicerr {
    char *        program_name;             /* program's name                */
    char *        program_info;             /* program information           */
    CPIC_VERB_INDEX verb_index;             /* index of the failing verb     */
    CM_RETURN_CODE conv_rc;                 /* return code from CPI-C        */
    CM_CONVERSATION_STATE conversation_state;
    CM_CONVERSATION_TYPE  conversation_type;
    CM_SYNC_LEVEL         sync_level;
    char *        log_file_path;            /* path for the log file         */
    char *        log_file_name;            /* where to log errors           */
    unsigned char conversation_id[8];       /* CPI-C Conversation ID         */
    int           conv_id_set;              /* Was conv_id set?              */
    CM_INT32      mode_name_length;         /* mode_name length              */
    CM_INT32      partner_LU_name_length;   /* partner_LU_name length        */
    unsigned char mode_name[MAX_MODE_NAME]; /* mode_name                     */
    unsigned char partner_LU_name[MAX_FQPLU_NAME]; /* partner_LU_name        */
    CPIC_RC_HANDLING exit_level;            /* should we exit on an error?   */
    CPIC_RC_HANDLING show_level;            /* should we show errors?        */
    CPIC_RC_HANDLING log_level;             /* should we log errors?         */
    unsigned char major_version;            /* Major version number          */
    unsigned char minor_version;            /* Minor version number          */
    time_t        program_start_time;       /* time of cpicerr init          */
    time_t        program_error_time;       /* time of error report          */
    int           handle_error;             /* should cpicerr process all    */
                                            /* ERROR_RECEIVED ret codes?     */
} CPICERR;

/*****************************************************************************
 * The following are the structures and constants used for handling all of
 * message strings in CPICERR.
 *****************************************************************************/

/*
 * The cpicerr_message structure contains an index and the text message
 * associated with that index.  In most cases, the index will be an actual
 * CPI-C return value.  For example,
 * index = CM_SEND_STATE, message = "Send state"
 */
typedef struct cpicerr_message {
    CM_INT32   index;
    char *     message;
} CPICERR_MESSAGE;

/*
 * This enum gives each list a label and a value to make it easier to
 * process all of the lists.
 */
typedef enum cpicerr_message_type {
    CPIC_SYNC_LEVELS  = 1,
    CPIC_CONV_TYPES   = 2,
    CPIC_STATES_CONV  = 3,
    CPIC_RC_CLASSES   = 4,
    CPIC_RETURN_CODES = 5,
    CPIC_VERBS_SHORT  = 6,
    CPIC_VERBS_LONG   = 7
} CPICERR_MESSAGE_TYPE;

/*
 * The CPICERR_MESSAGE_LIST structure is what actually ties a
 * CPICERR_MESSAGE_TYPE value to the CPICERR_MESSAGE list that it relates
 * to.  A table is defined in CPICERR.C that allows the cpicerr_get_message
 * routine to correlate a message label to its corresponding message list.
 */
typedef struct cpicerr_message_list {
    CPICERR_MESSAGE_TYPE type;
    CPICERR_MESSAGE *    list;
} CPICERR_MESSAGE_LIST;

/*
 * The following value is used to mark the end of CPICERR_MESSAGE lists.
 */
#define  MAX_MESSAGE       32767

/*
 * Set the size of the buffer used by the cpicerr_exchange_version() call.
 */
#define  EXCHANGE_BUFFER_SIZE    128

/*
 * If these CPI-C calls are not supported, these values will be returned
 * instead of a valid CPI-C values.  In the message tables, the values
 * below will result in a message of "XXX not supported".  This allows
 * us to differentiate between a call not being available on a platform,
 * and an unrecognized value returned on a call.
 */
#define CMECS_NOT_SUPPORTED              (CM_CONVERSATION_STATE) 99

/*
 * Used by the cpicerr_exchange_version() routine.  Future flows may
 * be prefaced with new "command values" taken from this list.
 */
typedef enum cpicerr_command {
    CPICERR_EXCHANGE_VERSION = 1,
    CPICERR_EXCHANGE_OPSYS_STRING = 2
} CPICERR_COMMAND;

/*****************************************************************************
 * Function prototypes for routines that can be called via an application
 * program.  See CPICERR.C for a full description of each function and
 * how to use it.
 *****************************************************************************/
CPICERR *
       cpicerr_new(void);

void   cpicerr_show_reply(       CPICERR_REPLY * reply);
CPICERR_REPLY *
       cpicerr_create_reply(     unsigned int primary_message_buffer_size,
                                 unsigned int secondary_message_buffer_size);
void   cpicerr_destroy_reply(    CPICERR_REPLY * reply);
unsigned int
       cpicerr_build_reply_image(CPICERR_REPLY * reply,
                                 char *          reply_buffer,
                                 unsigned int    reply_buffer_length);
unsigned int
       cpicerr_extract_reply_image(CPICERR_REPLY * reply,
                                 char *          reply_buffer,
                                 unsigned int    reply_buffer_length);
void   cpicerr_set_error_reply(  CPICERR_REPLY * reply,
                                 REPLY_RESPONSE  response,
                                 unsigned int    message_category,
                                 unsigned long   primary_code,
                                 char *          primary_message_text,
                                 unsigned long   secondary_code,
                                 char *          secondary_message_text);
int    cpicerr_recv_appl_error(  CPICERR *  cpicerr,
                                 CPICERR_REPLY * reply);
int    cpicerr_send_appl_error(  CPICERR *  cpicerr,
                                 NEXT_STATE next_state,
                                 CPICERR_REPLY * reply);
int    cpicerr_set_program_name( CPICERR *            cpicerr,
                                 char *               program_name);
int    cpicerr_set_program_info( CPICERR *            cpicerr,
                                 char *               program_info);
int    cpicerr_set_major_version(CPICERR *            cpicerr,
                                 unsigned char        major_version);
int    cpicerr_set_minor_version(CPICERR *            cpicerr,
                                 unsigned char        minor_version);
int    cpicerr_set_conv_id(      CPICERR *            cpicerr,
                                 unsigned char *      cm_conv_id);
int    cpicerr_set_log_file_name(CPICERR *            cpicerr,
                                 char *               log_file_name);
int    cpicerr_set_log_file_path(CPICERR *            cpicerr,
                                 char *               log_file_path);
int    cpicerr_set_exit_level(   CPICERR *            cpicerr,
                                 CPIC_RC_HANDLING     exit_level);
int    cpicerr_set_show_level(   CPICERR *            cpicerr,
                                 CPIC_RC_HANDLING     show_level);
int    cpicerr_set_log_level(    CPICERR *            cpicerr,
                                 CPIC_RC_HANDLING     log_level);
CPIC_RC_HANDLING
cpicerr_handle_rc_extended(      CPICERR *            cpicerr,
                                 CPIC_VERB_INDEX      verb_index,
                                 CM_RETURN_CODE       conv_rc,
                                 char *               file_name,
                                 int                  file_line);
/*
 * Provide an alternative interface for handling unexpected CPI-C
 * return codes.  This macro saves the programming from having
 * to type the __FILE__ and __LINE__ macros for each call.
 */
#define cpicerr_handle_rc(x,y,z)                    \
        cpicerr_handle_rc_extended(x,y,z, __FILE__, __LINE__)

int    cpicerr_exchange_version( CPICERR *            cpicerr,
                                 unsigned char *      cm_conv_id,
                                 CM_INT32             conv_state,
                                 unsigned char *      partner_major_version,
                                 unsigned char *      partner_minor_version);
int    cpicerr_exchange_version_plus(
                                 CPICERR *            cpicerr,
                                 unsigned char *      cm_conv_id,
                                 CM_INT32             conv_state,
                                 unsigned char *      partner_major_version,
                                 unsigned char *      partner_minor_version,
                                 unsigned char *      opsys_string,
                                 unsigned int         opsys_string_length);
void   cpicerr_destroy(          CPICERR *            cpicerr);

#endif


CPICINIT.C

/* need to look for SUPPORTS_SETTING_SECURITY before using XC_SECURITY*     */
/*****************************************************************************
 *
 *  MODULE NAME: CPICINIT.C
 *
 *  COPYRIGHTS:
 *             This module contains code made available by IBM
 *             Corporation on an AS IS basis.  Any one receiving the
 *             module is considered to be licensed under IBM copyrights
 *             to use the IBM-provided source code in any way he or she
 *             deems fit, including copying it, compiling it, modifying
 *             it, and redistributing it, with or without
 *             modifications.  No license under any IBM patents or
 *             patent applications is to be implied from this copyright
 *             license.
 *
 *             A user of the module should understand that IBM cannot
 *             provide technical support for the module and will not be
 *             responsible for any consequences of use of the program.
 *
 *             Any notices, including this one, are not to be removed
 *             from the module without the prior written consent of
 *             IBM.
 *
 *  AUTHOR:    Peter J. Schwaller
 *             VNET:     PJS at RALVM6           Tie Line: 444-4376
 *             Internet: pjs@ralvm6.vnet.ibm.com     (919) 254-4376
 *
 *  FUNCTION:  Provides procedures to be used in programs to help them set up
 *             all of the partner program information for CPI-C.  All of the
 *             information is stored in a CPICINIT structure.  The
 *             information is stored in the structure using default and set
 *             calls.
 *
 *             The cpicinit_setup_conversation procedure can then be used
 *             to issue the CPI-C calls to setup a conversation_id.
 *             All of the CPI-C calls from CMINIT to just before CMALLC
 *             are issued.  The program should issue CMALLC on its own
 *             so it can control error handling.
 *
 *             By collecting all of the partner program information within
 *             a single object, restarting a conversation that has failed
 *             is made easier, since the information does not have to
 *             be collected or read from a profile again.
 *
 *  AVAILABILITY:
 *             These sample programs and source are also available on
 *             CompuServe through the APPC Information Exchange.  To get
 *             to the APPC forum just type 'GO APPC' from any CompuServe
 *             prompt.  The samples are available in the Sample Programs
 *             library section.  Just search on the keyword CPICPGMS to
 *             find all the samples in this series.
 *
 *             Updates for the sample programs and support for many more
 *             CPI-C platforms will also be made available on CompuServe.
 *
 *  RELATED FILES:
 *             Uses CPICINIT.H
 *
 *  CHANGE HISTORY:
 *  Date       Description
 *  08/05/92   Version 2.31 of APING, ATELL and AREXEC released to CompuServe.
 *             This version was also distributed at the APPC/APPN Platform
 *             Developer's Conference held in Raleigh, NC.
 *  08/13/92   Changed all printf and fprintf calls to use a write_*() call.
 *  08/18/92   Fixed problem with imbedded \0 added setting def_sym_dest_name
 *             in cpicinit_default_sym_dest_name().  This fixes a problem
 *             seen in MVS.
 *  08/23/92   Removed cpicinit_default_destination() and associated
 *             processing.   After review, we decided that this call was
 *             extraneous and that cpicinit_default_sym_dest_name() was
 *             a better mechanism for a default destination.
 *  11/13/92   Changed most return values from TRUE/FALSE to CPICINIT_OK and
 *             other return codes.
 *
 *****************************************************************************/

/*****************************************************************************
 *
 * OVERVIEW OF CPICINIT CALLS
 *
 * cpicinit_new()                     Creates a CPICINIT object.
 *                                    This must be done before any other
 *                                    cpicinit calls can be used.
 *
 * cpicinit_default_tp_name()         These calls set the initial values
 * cpicinit_default_mode_name()       for CPICINIT parameters.  These should
 * cpicinit_default_sym_dest_name()   all be issued right after the CPICINIT
 *                                    object is created.  Usually, these calls
 *                                    should set the values that should be used
 *                                    only if the user does not specify new
 *                                    values.
 *
 * cpicinit_set_tp_name()             These calls also set the values for
 * cpicinit_set_mode_name()           CPICINIT parameters.  These calls should
 * cpicinit_set_destination()         be used to set values from user input
 *                                    or profile values.  These calls should be
 *                                    used after the user has specified values,
 *                                    through command line parameters, program
 *                                    profiles, or interactive input.
 *
 *   Security calls - only available where supported by CPI-C
 * cpicinit_set_userid                Sets the userid for the conversation.
 * cpicinit_set_password              Sets the password for the conversation.
 * cpicinit_query_password_needed     If a userid was set, then a password
 *                                    is required.
 * cpicinit_get_password              Let cpicinit prompt the user for a
 *                                    password.
 * cpicinit_set_security_type         Set the security type for this conv.
 *
 * cpicinit_setup_conversation()      Handles all CMINIT and set calls.
 *                                    Should be used by the calling program
 *                                    instead of CMINIT.  See description
 *                                    of the procedure for more details.
 *
 * cpicinit_destroy()                 Destroys the CPICINIT object.
 *
 * cpicinit_pln_valid()               These are internal calls used by
 * cpicinit_mode_valid()              cpicinit_setup_conversation.
 *
 *****************************************************************************/

#if defined(WIN32) || defined(WINDOWS)
#include <windows.h>
#endif

#include "wincpic.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* CPI-C error handling routines */
#include "cpicerrs.h"

/* CPI-C initialization routines */
#include "cpicinit.h"

/* Collection of routines with special ported version for each platform */
#include "cpicport.h"

/******************************************************************************
 *
 *  cpicinit_new
 *
 *  Creates a new CPICINIT object and returns its handle to the caller.
 *  This object handle must be used on all subsequent cpicinit calls.
 *  All internal fields are initialized appropriately.
 *
 *  If memory cannot be allocated for the CPICINIT object, NULL will be
 *  returned as the result of the call.  Otherwise, the return value will
 *  be a pointer to the CPICINIT object.
 *
 *****************************************************************************/
CPICINIT *
cpicinit_new(void)
{
    CPICINIT * cpicinit;

    cpicinit = (CPICINIT *) malloc(sizeof(*cpicinit));
    if (cpicinit == NULL) {
        return NULL;
    }

    /* Initialize the entire structure to 0's */
    memset(cpicinit,
           0,
           sizeof(*cpicinit));

    /* Set the default symbolic destination name to BLANKs. */
    /* This is the "default-default" if nothing else is set. */
    memset(cpicinit -> def_sym_dest_name,
           ' ',
           sizeof(cpicinit->def_sym_dest_name));
    cpicinit->def_sym_dest_name[sizeof(cpicinit->def_sym_dest_name)-1] = '\0';

    /* Indicate that the values have not yet been SET */
    cpicinit -> set_mode_name = NOT_SET;
    cpicinit -> set_destination = NOT_SET;
    cpicinit -> set_tp_name = NOT_SET;
    cpicinit -> set_userid = NOT_SET;
    cpicinit -> set_password = NOT_SET;
    cpicinit -> security_type = XC_SECURITY_SAME;

    /*
     * This flag is usedby other cpicinit calls to decide whether or not
     * to show errors encountered during processing.
     *
     * If this value is turned off, the cpicinit routines will not produce
     * any output, even in the event of an error.
     */
    cpicinit -> show_error = SET;

    /* return a pointer to the structure */
    return cpicinit;
}

/******************************************************************************
 *
 *  cpicinit_default_sym_dest_name
 *
 *  Sets a symbolic destination name to be used if no call is made to
 *  cpicinit_set_destination().
 *
 *  Returns:
 *    CPICINIT_OK,     if there was no error.
 *    CPICINIT_ERROR,  if there was an error.
 *
 *****************************************************************************/
int
cpicinit_default_sym_dest_name( CPICINIT * cpicinit,
                                char *     def_sym_dest_name)
/*
 * Set the default symbolic destination name to be used if no destination
 * is ever set.
 */
{
    int length;
    int rc;

    /* Set the default symbolic destination name to BLANKs. */
    memset(cpicinit -> def_sym_dest_name,
           ' ',
           sizeof(cpicinit->def_sym_dest_name));
    cpicinit->def_sym_dest_name[sizeof(cpicinit->def_sym_dest_name)-1] = '\0';

    length = strlen(def_sym_dest_name);

    if (length < sizeof(cpicinit->def_sym_dest_name)) {
        rc = CPICINIT_ERROR;
        memcpy(cpicinit->def_sym_dest_name,
               def_sym_dest_name,
               length);
    } else {
        rc = CPICINIT_OK;
    }

    return rc;
}

/******************************************************************************
 *
 *  cpicinit_default_tp_name
 *
 *  Sets a symbolic destination name to be used if no call is made to
 *  cpicinit_set_destination().
 *
 *  Returns:
 *    CPICINIT_OK,     if there was no error.
 *    CPICINIT_ERROR,  if there was an error.
 *
 *****************************************************************************/
int
cpicinit_default_tp_name(CPICINIT * cpicinit,
                         char *     tp_name)
/* initialize the tp_name */
{
    int length;
    int rc;

    length = strlen(tp_name);

    if (length < sizeof(cpicinit->tp_name)) {
        rc = CPICINIT_ERROR;
        memcpy(cpicinit->tp_name,
               tp_name,
               length);
        cpicinit->tp_name[length] = '\0';
    } else {
        rc = CPICINIT_OK;
        if (cpicinit -> show_error) {
             write_error(
                     "The TP name you specified is too long:\n%s\n",
                     tp_name);
             write_error(
                    "The maximum length of a TP name is %u characters.\n",
                    sizeof(cpicinit->tp_name)-1);
             write_error(
                     "TP name of %s will be used.\n", cpicinit->tp_name);
        }
    }

    return rc;
}

/******************************************************************************
 *
 *  cpicinit_default_mode_name
 *
 *  Sets the default mode name to be used if there is no call made to
 *  cpicinit_set_mode_name().
 *
 *  Returns:
 *    CPICINIT_OK,     if there was no error.
 *    CPICINIT_ERROR,  if there was an error.
 *
 *****************************************************************************/
int
cpicinit_default_mode_name(CPICINIT * cpicinit,
                           char *     mode_name)
/* initialize the mode name */
{
    int length;
    int rc;

    length = strlen(mode_name);

    if (length < sizeof(cpicinit->mode_name)) {
        rc = CPICINIT_ERROR;
        memcpy(cpicinit->mode_name,
               mode_name,
               length);
        cpicinit->mode_name[length] = '\0';
        strupr(cpicinit->mode_name);
    } else {
        rc = CPICINIT_OK;
        if (cpicinit -> show_error) {
            write_error(
                    "The mode name you specified is too long:\n%s\n",
                    mode_name);
            write_error(
                   "The maximum length of a mode name is %u characters.\n",
                   sizeof(cpicinit->mode_name)-1);
            write_error(
                   "Mode name of %s will be used.\n", cpicinit->mode_name);
        }
    }

    return rc;
}

/*
 * The major difference between the default calls above and the set calls
 * below is the setting of a flag in the set calls indicating that the user
 * has specified this value.  This flag is used later as an indication that
 * any value set by the symbolic destination name entries should be
 * overridden.
 */

/******************************************************************************
 *
 *  cpicinit_set_tp_name
 *
 *  Returns:
 *    CPICINIT_OK,     if there was no error.
 *    CPICINIT_ERROR,  if there was an error.
 *
 *****************************************************************************/
int
cpicinit_set_tp_name(CPICINIT * cpicinit,
                     char *     tp_name)
{
    cpicinit->set_tp_name = SET;
    return cpicinit_default_tp_name(cpicinit, tp_name);
}

/******************************************************************************
 *
 *  cpicinit_set_mode_name
 *
 *  Returns:
 *    CPICINIT_OK,     if there was no error.
 *    CPICINIT_ERROR,  if there was an error.
 *
 *****************************************************************************/
int
cpicinit_set_mode_name(CPICINIT * cpicinit,
                       char *     mode_name)
{
    cpicinit->set_mode_name = SET;
    return cpicinit_default_mode_name(cpicinit, mode_name);
}

/******************************************************************************
 *
 *  cpicinit_set_destination
 *
 *  Returns:
 *    CPICINIT_OK,     if there was no error.
 *    CPICINIT_ERROR,  if there was an error.
 *
 *****************************************************************************/
int
cpicinit_set_destination(CPICINIT * cpicinit,
                         char *     destination)
{
    int length;
    int rc;

    cpicinit->set_destination = SET;

    length = strlen(destination);

    if (length < sizeof(cpicinit->destination)) {
        rc = CPICINIT_ERROR;
        memcpy(cpicinit->destination,
               destination,
               length);
        cpicinit->destination[length] = '\0';
    } else {
        rc = CPICINIT_OK;
        if (cpicinit -> show_error) {
            write_error(
                    "The destination you specified is too long:\n%s\n",
                    destination);
            write_error(
            "The maximum length of a destination is %u characters.\n",
                sizeof(cpicinit->destination)-1);
            write_error(
                    "Destination %s will be used.\n", cpicinit->destination);
        }
    }

    return rc;
}

/******************************************************************************
 *
 *  cpicinit_set_userid
 *
 *  Sets the userid to be used for security on the conversation.  Issuing
 *  this call implies that the conversation will use security=PROGRAM;
 *  a password will also be required in order to get a conversation.
 *  The password should be set using either the cpicinit_set_password() call
 *  or the cpicinit_get_password() call.
 *
 *  Returns:
 *    CPICINIT_OK,     if there was no error.
 *    CPICINIT_ERROR,  if there was an error.
 *
 *****************************************************************************/
int
cpicinit_set_userid(CPICINIT * cpicinit,
                    char *     userid)
/* initialize the userid */
{
    int length;
    int rc;

    length = strlen(userid);

    if (length < sizeof(cpicinit->userid)) {
        rc = CPICINIT_ERROR;
        memcpy(cpicinit->userid,
               userid,
               length);
        cpicinit->userid[length] = '\0';
        cpicinit->set_userid = SET;
        cpicinit->security_type = XC_SECURITY_PROGRAM;
    } else {
        rc = CPICINIT_OK;
        if (cpicinit -> show_error) {
            write_error(
                    "The userid you specified is too long:\n%s\n",
                    userid);
            write_error(
                   "The maximum length of a userid is %u characters.\n",
                   sizeof(cpicinit->userid)-1);
        }
    }

    return rc;

}
/******************************************************************************
 *
 *  cpicinit_query_password_needed
 *
 *  Can be used by the program to inquire whether a password will be required
 *  to establish a conversation.  This can be used by a program instead of
 *  keeping track itself of whether a userid has been specified.  If a password
 *  is required, the password should be set using either the
 *  cpicinit_set_password() call or the cpicinit_get_password() call.
 *
 *  Returns:
 *    TRUE   if password is required
 *    FALSE  if no password should be set
 *
 *****************************************************************************/
int
cpicinit_query_password_needed(CPICINIT * cpicinit)
{
    if (cpicinit->set_userid == SET && cpicinit->set_password == NOT_SET) {
        return TRUE;
    } else {
        return FALSE;
    }
}

/******************************************************************************
 *
 *  cpicinit_set_password
 *
 *  Sets the password to be used with security=PROGRAM.  This call should be
 *  used in conjunction with the cpicinit_set_userid() call.
 *
 *  Returns:
 *    CPICINIT_OK,     if there was no error.
 *    CPICINIT_ERROR,  if there was an error.
 *
 *****************************************************************************/
int
cpicinit_set_password(CPICINIT * cpicinit,
                    char *     password)
/* initialize the password */
{
    int length;
    int rc;

    length = strlen(password);

    if (length < sizeof(cpicinit->password)) {
        rc = CPICINIT_ERROR;
        memcpy(cpicinit->password,
               password,
               length);
        cpicinit->password[length] = '\0';
        cpicinit->set_password = SET;
        cpicinit->security_type = XC_SECURITY_PROGRAM;
    } else {
        rc = CPICINIT_OK;
        if (cpicinit -> show_error) {
            write_error(
                    "The password you specified is too long:\n%s\n",
                    password);
            write_error(
                   "The maximum length of a password is %u characters.\n",
                   sizeof(cpicinit->password)-1);
        }
    }

    return rc;
}

/******************************************************************************
 *
 *  cpicinit_get_password
 *
 *****************************************************************************/
void
cpicinit_get_password(CPICINIT * cpicinit)
{
    write_output("\nPassword: ");           /* display the prompt            */

    if (!get_password(cpicinit->password, sizeof(cpicinit->password)-1)) {
        cpicinit->set_password = SET;
        cpicinit->security_type = XC_SECURITY_PROGRAM;
    }
    return;
}

/******************************************************************************
 *
 *  cpicinit_set_security_type
 *
 *****************************************************************************/
void
cpicinit_set_security_type(CPICINIT * cpicinit,
                           unsigned long security_type)
{
    cpicinit->security_type = security_type;
}

/******************************************************************************
 *
 *  cpicinit_setup_conversation
 *
 *  The goal of this procedure is to initialize a CPI-C conversation ID
 *  and make sure that partner destination information is all specified.
 *  This destination information includes partner LU name, mode name,
 *  and TP name.
 *
 *  To be as flexible as possible, the destination parameter can serve
 *  a dual role.  If specified, the destination is first used as the
 *  symbolic destination name.  If this does not produce a valid partner
 *  LU name, another symbolic destination name will be used and the
 *  destination parameter will be used instead as a partner LU name.
 *
 *  AN OVERVIEW OF THE PROCEDURE
 *
 *  If the destination was SET
 *      use destination as a symbolic destination name
 *      If CMINIT failed or there was no partner LU name extracted
 *          use the default symbolic destination name or all blanks
 *          use the destination as a partner LU name
 *
 *****************************************************************************/
int
cpicinit_setup_conversation(CPICINIT *      cpicinit,
                            unsigned char * cm_conv_id,
                            CPICERR *       cpicerr)
{
    CM_INT32 cm_rc = CM_OK;
    CM_INT32 length;
    int      dest_len;
    char sym_dest_name[9];

    /* check if a destination was set by the cpicinit_set_destination() call */
    if (cpicinit->set_destination == SET) {

        /* check if the destination could be a symbolic destination name     */
        if (((dest_len = strlen(cpicinit->destination)) <
                                                         MAX_SYM_DEST_NAME)) {
            /*
             * Try to use the specified destination as a symbolic
             * destination name.  A symbolic destination name is blank
             * padded and must contain all uppercase characters.
             */

            memset(sym_dest_name,           /* Initialize the sym dest name  */
                   ' ',                     /* to all blanks.                */
                   sizeof(sym_dest_name));

            memcpy(sym_dest_name,           /* Copy the destination into the */
                   cpicinit->destination,   /* sym dest name.                */
                   dest_len);
                                            /* place the null terminator     */
            sym_dest_name[MAX_SYM_DEST_NAME - 1] = '\0';

            strupr(sym_dest_name);          /* sym dest must be uppercase    */

            /*
             * Since we are using the destination as a sym_dest, make sure
             * we don't also use it as a partner LU name.
             */
            cpicinit->set_destination = NOT_SET;

            cminit(cm_conv_id,
                   (unsigned char *)sym_dest_name,
                   &cm_rc);

        } else {
            /*
             * The destination was too long to be a symbolic destination name.
             */
        }

        if (cm_rc != CM_OK || !cpicinit_pln_valid(cm_conv_id)) {

            /*
             * Since the destination was not a sym_dest, we can use it
             * later as a partner LU name.
             */
            cpicinit->set_destination = SET;

            /*
             * Try to use the default symbolic destination name.  If
             * configured, this symbolic destination can be used to
             * initialize the partner LU name and mode name entries,
             * or left blank.
             */
            cminit(cm_conv_id,
                   (unsigned char *)cpicinit->def_sym_dest_name,
                   &cm_rc);

            if (cm_rc != CM_OK) {
                /*
                 * Nothing else worked, so let's try the blank symbolic
                 * destination name.  All CPI-C values will be initialized
                 * to non usable values.
                 */
                cminit(cm_conv_id,
                       (unsigned char *)BLANK_SYM_DEST_NAME,
                       &cm_rc);
                if (cm_rc != CM_OK) {
                    return cpicerr_handle_rc(cpicerr, MSG_CMINIT, cm_rc);
                }

                /*
                 * Since the blank symbolic destination name was used,
                 * we must set the partner LU name and mode even if we
                 * use the defaults.
                 */
                cpicinit->set_destination = cpicinit->set_mode_name = SET;

            }

        }

    } else {
        /*
         * Although no destination was specified, we can try to continue
         * by using the default symbolic destination name.  If this entry
         * is configured with a valid partner LU, we may be able to
         * establish a connection with a partner.
         */
        cminit(cm_conv_id,
               (unsigned char * )cpicinit->def_sym_dest_name,
               &cm_rc);
        if (cm_rc != CM_OK) {
            /*
             * Fill in conversation information for CPI-C error reporting.
             */
            cpicerr_set_conv_id(cpicerr, cm_conv_id);
            return cpicerr_handle_rc(cpicerr, MSG_CMINIT, cm_rc);
        }
    }

    /*
     * Fill in conversation information for CPI-C error reporting.
     */
    cpicerr_set_conv_id(cpicerr, cm_conv_id);

    if (cpicinit->set_destination == SET) {
        CM_INT32 dest_length;
        dest_length = strlen(cpicinit->destination);

#if defined(PLN_COULD_REQUIRE_LOWERCASE)
        /*
         * strupr should only be performed if the partner LU name is
         * a fully qualified name.
         */
#ifdef FAPI
        if ((strchr(cpicinit->destination,'.') != NULL) ||
            (get_machine_mode() == 0)) {
            strupr(cpicinit->destination);
        }
#else
        if (strchr(cpicinit->destination,'.') != NULL) {
            strupr(cpicinit->destination);
        }
#endif
#else
        strupr(cpicinit->destination);
#endif

        cmspln(cm_conv_id,                  /* Set partner LU name           */
               (unsigned char *)cpicinit->destination,
               &dest_length,
               &cm_rc);
        if (cm_rc != CM_OK)
            return cpicerr_handle_rc(cpicerr, MSG_CMSPLN, cm_rc);
    }

    /*
     * Since there is no way to query the TP name, we will always set it.
     * The default TP name can be overridden on the cpicinit_set_tp_name
     * call.
     */

    length = strlen(cpicinit->tp_name);
    cmstpn(cm_conv_id,                      /* Set TP name                   */
           (unsigned char *)cpicinit->tp_name,
           &length,
           &cm_rc);
    if (cm_rc != CM_OK)
        return cpicerr_handle_rc(cpicerr, MSG_CMSTPN, cm_rc);

    /*
     * If there was a cpicinit_set_mode_name call made previously or
     * present mode name is not initialized, we need to set the mode
     * name.
     */
    if ((cpicinit->set_mode_name == SET) ||
                                         !cpicinit_mode_valid(cm_conv_id)) {
        length = strlen(cpicinit->mode_name);
        cmsmn(cm_conv_id,                   /* Set mode name                 */
              (unsigned char *)cpicinit->mode_name,
              &length,
              &cm_rc);
        if (cm_rc != CM_OK)
            return cpicerr_handle_rc(cpicerr, MSG_CMSMN, cm_rc);
    }

#if defined(SUPPORTS_SETTING_SECURITY)
    /*
     * If there was a cpicinit_set_userid AND cpicinit_set_password call made
     * previously, we need to set the security parameters.  The security
     * functions are not available on all CPI-C platforms.
     */

    if (cpicinit->security_type == XC_SECURITY_NONE) {
        CM_INT32 security_type;
        security_type = XC_SECURITY_NONE;
        xcscst(cm_conv_id,                  /* Set Security Type             */
               &security_type,
               &cm_rc);
        if (cm_rc != CM_OK)
            return cpicerr_handle_rc(cpicerr, MSG_XCSCST, cm_rc);
    } else {
        if ((cpicinit->set_userid == SET) && (cpicinit->set_password == SET)) {
            CM_INT32 security_type;
            security_type = XC_SECURITY_PROGRAM;
            xcscst(cm_conv_id,              /* Set Security Type             */
                   &security_type,
                   &cm_rc);
            if (cm_rc != CM_OK)
                return cpicerr_handle_rc(cpicerr, MSG_XCSCST, cm_rc);

            length = strlen(cpicinit->userid);
            xcscsu(cm_conv_id,              /* Set Security Userid           */
                   (unsigned char *)cpicinit->userid,
                   &length,
                   &cm_rc);
            if (cm_rc != CM_OK)
                return cpicerr_handle_rc(cpicerr, MSG_XCSCSU, cm_rc);

            length = strlen(cpicinit->password);
            xcscsp(cm_conv_id,              /* Set Security Password         */
                   (unsigned char *)cpicinit->password,
                   &length,
                   &cm_rc);
            if (cm_rc != CM_OK)
                return cpicerr_handle_rc(cpicerr, MSG_XCSCSP, cm_rc);

        }
    }

#endif

    return FALSE;
}

/******************************************************************************
 *
 *  cpicinit_destroy
 *
 *  This function is the inverse of the cpicinit_new() call.  All memory
 *  allocated by cpicinit_new() is freed.
 *
 *  This function has no return code.
 *
 *****************************************************************************/
void
cpicinit_destroy(CPICINIT * cpicinit)
{
    free(cpicinit);
    return;
}

/******************************************************************************
 *
 *  cpicinit_pln_valid
 *
 *  Internal function, used by cpic_setup_conversation().
 *
 *  This function is used after a cminit() call to ensure that there was
 *  a partner LU name configured with the symbolic destination used on
 *  cminit().  If there was no partner LU name configured with the symbolic
 *  destination name, a partner LU name will have to be set with the cmspln()
 *  call.
 *
 *  Returns:
 *    TRUE  if a partner LU name was configured
 *    FALSE is there was no partner LU name configured
 *
 *****************************************************************************/
int
cpicinit_pln_valid(unsigned char * cm_conv_id)
{
    unsigned char  destination[MAX_DESTINATION]; /* variable to store the    */
                                            /* extracted partner LU name     */
    CM_INT32       length = 0;              /* length of partner LU name     */
    CM_RETURN_CODE cm_rc;                   /* CPI-C return code             */
    int            rc;                      /* procedure return code         */

    cmepln(cm_conv_id,                      /* extract partner LU name       */
           destination,
           &length,                         /* length of partner LU name     */
           &cm_rc );

    if (cm_rc != CM_OK || (length == 1 && destination[0] == ' ')) {
        rc = FALSE;
    } else {
        rc = TRUE;
    }
    return rc;
}

/******************************************************************************
 *
 *  cpicinit_mode_valid
 *
 *  Internal function, used by cpic_setup_conversation().
 *
 *  This function is used after a cminit() call to ensure that there was
 *  a mode name configured with the symbolic destination used on  cminit().
 *  If there was no mode name configured with the symbolic destination name,
 *  a partner LU name will have to be set with the cmsmn() call.
 *
 *  Returns:
 *    TRUE  if a mode name was configured
 *    FALSE is there was no mode name configured
 *
 *****************************************************************************/
cpicinit_mode_valid(unsigned char * cm_conv_id)
{
    unsigned char  mode[MAX_MODE_NAME];     /* variable to store the         */
                                            /* extracted mode name           */
    CM_INT32       length = 0;              /* length of mode name           */
    CM_RETURN_CODE cm_rc;                   /* CPI-C return code             */
    int            rc;                      /* procedure return code         */

    cmemn(cm_conv_id,                       /* extract mode name             */
          mode,
          &length,
          &cm_rc );

    if (cm_rc != CM_OK || length == 0) {
        rc = FALSE;
    } else {
        rc = TRUE;
    }
    return rc;

}


CPICINIT.H

/*****************************************************************************
 *
 *  MODULE NAME: CPICINIT.H
 *
 *  COPYRIGHTS:
 *             This module contains code made available by IBM
 *             Corporation on an AS IS basis.  Any one receiving the
 *             module is considered to be licensed under IBM copyrights
 *             to use the IBM-provided source code in any way he or she
 *             deems fit, including copying it, compiling it, modifying
 *             it, and redistributing it, with or without
 *             modifications.  No license under any IBM patents or
 *             patent applications is to be implied from this copyright
 *             license.
 *
 *             A user of the module should understand that IBM cannot
 *             provide technical support for the module and will not be
 *             responsible for any consequences of use of the program.
 *
 *             Any notices, including this one, are not to be removed
 *             from the module without the prior written consent of
 *             IBM.
 *
 *  AUTHOR:    Peter J. Schwaller
 *             VNET:     PJS at RALVM6           Tie Line: 444-4376
 *             Internet: pjs@ralvm6.vnet.ibm.com     (919) 254-4376
 *
 *  AVAILABILITY:
 *             These sample programs and source are also available on
 *             CompuServe through the APPC Information Exchange.  To get
 *             to the APPC forum just type 'GO APPC' from any CompuServe
 *             prompt.  The samples are available in the Sample Programs
 *             library section.  Just search on the keyword CPICPGMS to
 *             find all the samples in this series.
 *
 *             Updates for the sample programs and support for many more
 *             CPI-C platforms will also be made available on CompuServe.
 *
 *  RELATED FILES:
 *             CPICINIT.C
 *
 *  CHANGE HISTORY:
 *  Date       Description
 *  08/05/92   Version 2.31 of APING, ATELL and AREXEC released to CompuServe.
 *             This version was also distributed at the APPC/APPN Platform
 *             Developer's Conference held in Raleigh, NC.
 *  08/23/92   Removed cpicinit_default_destination() and associated
 *             processing.   After review, we decided that this call was
 *             extraneous and that cpicinit_default_sym_dest_name() was
 *             a better mechanism for a default destination.
 *  11/13/92   Changed most return values from TRUE/FALSE to CPICINIT_OK and
 *             other return codes.
 *
 *****************************************************************************/

/*****************************************************************************
 *
 * OVERVIEW OF CPICINIT CALLS
 *
 * cpicinit_new()                     Creates a CPICINIT object.
 *                                    This must be done before any other
 *                                    cpicinit calls can be used.
 *
 * cpicinit_default_tp_name()         These calls set the initial values
 * cpicinit_default_mode_name()       for CPICINIT parameters.  These should
 * cpicinit_default_sym_dest_name()   all be issued right after the CPICINIT
 *                                    object is created.
 *
 * cpicinit_set_tp_name()             These calls also set the values for
 * cpicinit_set_mode_name()           CPICINIT parameters.  These calls should
 * cpicinit_set_destination()         be used to set values from user input
 *                                    or profile values.
 *
 *   Security calls - only available where supported by CPI-C
 * cpicinit_set_userid                Sets the userid for the conversation.
 * cpicinit_set_password              Sets the password for the conversation.
 * cpicinit_query_password_needed     If a userid was set, then a password
 *                                    is required.
 * cpicinit_get_password              Let cpicinit prompt the user for a
 *                                    password.
 * cpicinit_set_security_type         Set the security type for this conv.
 *
 * cpicinit_setup_conversation()      Handles all CMINIT and set calls.
 *                                    Should be used by the calling program
 *                                    instead of CMINIT.  See description
 *                                    of the procedure for more details.
 *
 * cpicinit_destroy()                 Destroys the CPICINIT object.
 *
 * cpicinit_pln_valid()               These are internal calls used by
 * cpicinit_mode_valid()              cpicinit_setup_conversation.
 *
 *****************************************************************************/

#ifndef INCL_CPICINIT
#define INCL_CPICINIT

/*
 * Collection of routines with special ported version for each platform
 * The only thing that is used from CPICDEFS.H is the correct setting
 * of the SHORT_IDENTIFIERS identifier.
 */
#include "cpicdefs.h"

#if defined(SHORT_IDENTIFIERS)
/*
 * Some compilers require that identifiers be unique in the 1st 8 chars.
 * Any new functions should be added to this list.
 */
#define cpicinit_default_mode_name        cidmode
#define cpicinit_default_sym_dest_name    cidsdn
#define cpicinit_default_tp_name          cidtp
#define cpicinit_destroy                  cid
#define cpicinit_get_password             cigp
#define cpicinit_mode_valid               cimv
#define cpicinit_new                      cinew
#define cpicinit_pln_valid                cipv
#define cpicinit_query_password_needed    ciqpn
#define cpicinit_set_destination          cisdest
#define cpicinit_set_mode_name            cismode
#define cpicinit_set_password             cisp
#define cpicinit_set_tp_name              cistp
#define cpicinit_set_userid               cisu
#define cpicinit_setup_conversation       cisc
#endif

/*
 * A string of 8 blanks is a special symbolic destination name.  We define
 * this constant here rather than typing out 8 blanks within the code.
 */
#define  BLANK_SYM_DEST_NAME     "        "

/*
 * Return code values for most of the CPICINIT calls.
 */

#define CPICINIT_OK     (0)
#define CPICINIT_ERROR  (1)

/*
 * This structure contains all of the information necessary to for all
 * cpicinit calls.  The standard CPI-C destination and partner information
 * is stored.  These values can be changed by the program through the
 * use of cpicinit_default... and cpicinit_set... calls.
 */

typedef struct cpicinit {
    char          def_sym_dest_name[MAX_SYM_DEST_NAME];
                                            /* Default symbolic dest name    */
    char          tp_name[MAX_TP_NAME];     /* Transaction Program name      */
    int           set_tp_name;              /* Was tp name set?              */
    char          mode_name[MAX_MODE_NAME]; /* Mode name                     */
    int           set_mode_name;            /* Was mode name set?            */
    char          destination[MAX_FQPLU_NAME];
                                            /* Destination - may be either   */
                                            /* a sym dest name or a partner  */
                                            /* lu name                       */
    int           set_destination;          /* Was destination set?          */
    char          userid[MAX_USERID];       /* Userid                        */
    int           set_userid;               /* Was userid set?               */
    char          password[MAX_PASSWORD];   /* Password                      */
    int           set_password;             /* Was password set?             */
    int           show_error;               /* Should we show errors?        */
    unsigned long security_type;            /* Security type                 */
} CPICINIT;

/*
 * Some of the fields in the CPICINIT object indicate whether another
 * field has been set or not.  These fields will have one of the following
 * values.  defines have been used to make the code more readable and to
 * protect against the need to change these values in the future.
 */
#define  SET      1
#define  NOT_SET  0

/* function prototypes for this module */

/* create a new CPICINIT object */
CPICINIT * cpicinit_new(void);

/* Initialize defaults for CPICINIT values */
int cpicinit_default_tp_name(       CPICINIT *      cpicinit,
                                    char *          tp_name);
int cpicinit_default_mode_name(     CPICINIT *      cpicinit,
                                    char *          mode_name);
int cpicinit_default_sym_dest_name( CPICINIT *      cpicinit,
                                    char *          def_sym_dest_name);

/* Set new values, based on user input or profile values */
int cpicinit_set_tp_name(           CPICINIT *      cpicinit,
                                    char *          tp_name);
int cpicinit_set_mode_name(         CPICINIT *      cpicinit,
                                    char *          mode_name);
int cpicinit_set_destination(       CPICINIT *      cpicinit,
                                    char *          destination);
int cpicinit_set_userid(            CPICINIT *      cpicinit,
                                    char *          userid);
int cpicinit_set_password(          CPICINIT *      cpicinit,
                                    char *          password);
int cpicinit_query_password_needed( CPICINIT *      cpicinit);
void cpicinit_get_password(         CPICINIT *      cpicinit);
void cpicinit_set_security_type(    CPICINIT *      cpicinit,
                                    unsigned long   security_type);

/* This call will prepare the conversation id for the Allocate call */
int cpicinit_setup_conversation(    CPICINIT *      cpicinit,
                                    unsigned char * cm_conv_id,
                                    CPICERR *       cpicerr);

/* destroys the CPICINIT object created with cpicinit_new() */
void cpicinit_destroy(              CPICINIT *      cpicinit);

/* Internal routines */
int cpicinit_pln_valid(             unsigned char * cm_conv_id);
int cpicinit_mode_valid(            unsigned char * cm_conv_id);

#endif


CPICPORT.C

/*****************************************************************************
 *
 *  MODULE NAME: CPICPORT.C
 *
 *  COPYRIGHTS:
 *             This module contains code made available by IBM
 *             Corporation on an AS IS basis.  Any one receiving the
 *             module is considered to be licensed under IBM copyrights
 *             to use the IBM-provided source code in any way he or she
 *             deems fit, including copying it, compiling it, modifying
 *             it, and redistributing it, with or without
 *             modifications.  No license under any IBM patents or
 *             patent applications is to be implied from this copyright
 *             license.
 *
 *             A user of the module should understand that IBM cannot
 *             provide technical support for the module and will not be
 *             responsible for any consequences of use of the program.
 *
 *             Any notices, including this one, are not to be removed
 *             from the module without the prior written consent of
 *             IBM.
 *
 *  AUTHOR:    Peter J. Schwaller
 *             VNET:     PJS at RALVM6           Tie Line: 444-4376
 *             Internet: pjs@ralvm6.vnet.ibm.com     (919) 254-4376
 *
 *  FUNCTION:  Contains procedures to that may have to be rewritten for
 *             different environments.
 *
 *  AVAILABILITY:
 *             These sample programs and source are also available on
 *             CompuServe through the APPC Information Exchange.  To get
 *             to the APPC forum just type 'GO APPC' from any CompuServe
 *             prompt.  The samples are available in the Sample Programs
 *             library section.  Just search on the keyword CPICPGMS to
 *             find all the samples in this series.
 *
 *             Updates for the sample programs and support for many more
 *             CPI-C platforms will also be made available on CompuServe.
 *
 *  RELATED FILES:
 *             CPICPORT.H
 *
 *  PORTABILITY NOTES:
 *             This file is the home of all operating system specific
 *             functions.  The following is a summary of the routines
 *             in this file and where they are used:
 *
 *             write_output()
 *                 displays a text string to normal output
 *
 *             write_error()
 *                 displays error text to error output
 *
 *             write_log()
 *                 logs text to an opened log file
 *                 if the log file was not open, will display to error output
 *
 *             display_message()
 *                 Delivers a text message.
 *                 Used by ATELLD.C.
 *
 *             get_time()
 *                 Returns time in milliseconds.
 *                 Used by APING.C.
 *
 *             alloc_cpic_buffer()
 *                 Allocates the best memory buffer for CPI-C performance.
 *                 Used by APING.C and APINGD.C.
 *
 *             show_info()
 *                 Displays an array of text strings.
 *                 Used by all files with a main() function.
 *
 *             get_machine_mode()
 *                 For family API applications; determines whether we
 *                 are running under OS/2 or DOS.
 *
 *             get_password()
 *                 Used to request that the user enter a password.
 *                 If possible, the password will not display while the
 *                 user types it.
 *                 Used by CPICINIT.C
 *
 *             execute_and_send_output()
 *                 Execute the specified command and send the output back
 *                 to the client side.
 *                 This routine is called by AREXECD.C.
 *
 *             do_exit()
 *                 Exit properly for the environment.  This is usually
 *                 necessary for GUI environments.
 *
 *             free_cpic_buffer()
 *                 Frees the memory allocated by alloc_cpic_buffer().
 *                 Used by MPINGD.C.
 *
 *  CHANGE HISTORY:
 *  Date       Description
 *  08/05/92   Version 2.31 of APING, ATELL and AREXEC released to CompuServe.
 *             This version was also distributed at the APPC/APPN Platform
 *             Developer's Conference held in Raleigh, NC.
 *  08/13/92   Added the write_*() calls.
 *             Changed all printf and fprintf calls to use a write_*() call.
 *  08/19/92   Added workaround for problem with system() returning non-null
 *             even when command succeeded.  The fix is in DOS and FAPI
 *             versions of execute_and_send_output().
 *  08/20/92   Fixed alloc_cpic_buffer() so it will use a shared buffer
 *             under OS/2 2.0.
 *  08/23/92   Fixed extra rc definition for AS/400 definition of the
 *             execute_and_send_output() routine.
 *  08/24/92   Version 2.32 released to CompuServe.
 *  08/25/92   Changed DOS and AIX execute_and_send_output() to use the
 *             tempnam() function instead of the tmpnam() function.  This
 *             results in tempfiles being written to the TMP directory.
 *  09/22/92   Version 2.33 released to CompuServe.
 *  01/07/93   Added OS2_20 to list of conditional compile directives in
 *             the following routines:
 *               get_time
 *               get_password
 *               set_echo
 *               execute_and_send_output
 *  11/11/94   Wrote free_cpic_buffer()
 *
 *****************************************************************************/

#if defined(WIN32) || defined(WINDOWS)
#include <windows.h>
#endif

#include "wincpic.h"

#include "cpicerrs.h"

/* Set up constant declarations */
#include "cpicdefs.h"

#include "cpicport.h"

/* standard C include files */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <time.h>

extern char ebcdic_to_ascii_table[];
extern char ascii_to_ebcdic_table[];

void write_error(char *fmt, ...)
{
    /*=========================================================================
     *
     *=======================================================================*/

    va_list args;

    va_start(args, fmt);
    vfprintf(stderr, fmt, args);
    va_end(args);
}

void write_output(char *fmt, ...)
{
    /*=========================================================================
     *
     *=======================================================================*/

    va_list args;

    va_start(args, fmt);
    vfprintf(stdout, fmt, args);
    va_end(args);
}

void write_log(FILE * file, char *fmt, ...)
{
    /*=========================================================================
     *
     *=======================================================================*/

    va_list args;

    va_start(args, fmt);
    if (file == NULL) {
        write_error(fmt, args);
    } else {
        vfprintf(file, fmt, args);
    }
    va_end(args);
}

/*
 * display_message()
 *       delivers a text message
 *       used by ATELLD.C
 *
 *    default
 *       Uses C library functions to print the message.
 */
void
display_message(char * origin, char * dest_userid, char * message)
{

    char timestamp[TIMELENGTH];
    struct tm * newtime;
    time_t ltime;

    time(&ltime);
    newtime = localtime(&ltime);
    strcpy(timestamp, asctime(newtime));

    /* eliminate new line character for display */
    timestamp[strlen(timestamp)-1] = '\0';

    write_output("\n\n  msg from %s ", origin);
    if (dest_userid[0] != '\0') {
        write_output("to user %s ", dest_userid);
    }
    write_output("on %s:", timestamp);
    write_output("\n\n     %s\n\n",message);
}

/*
 * get_time()
 *       returns time in milliseconds
 *       used by APING.C
 *
 */
unsigned long
get_time(void)
{

    return GetTickCount();

}

/*
 * alloc_cpic_buffer()
 */

char CM_PTR
alloc_cpic_buffer (unsigned int size)
{
   return malloc(size);
}

/*
 * freec_cpic_buffer()
 */

void
free_cpic_buffer( void *memblock )
{
free( memblock );
}

/*
 * show_info()
 *
 *       This procedure displays a block of text information on the the
 *       screen.  The input argument is an array of strings to be output,
 *       one string per line.  A NULL array element indicates the end of
 *       the strings.
 *
 *    default
 *       print out all of the text strings in the array using the cpicport
 *       write_output() call.
 */
void
show_info(char * * text)
{
    int i;

    for ( i = 0; text[i] != NULL; i++ ) {
        write_output("%s\n", text[i]);
    }
    return;
}

/*
 * get_password()
 *
 *    Gets a password from the user.  Where possible, this routine should
 *    disable echoing of keystrokes for security reasons.
 *
 *    Returns
 *       0 - password was successfully input
 *       1 - password variable was not updated successfully
 */
int
get_password(char * password, int max_length)
{
    int rc;
    int length;

    /* There is no portable way to disable echoing of input keystrokes.      */
    /* If a platform does support turning off echo, this section should be   */
    /* rewritten and ifdef'ed.                                               */

    if (NULL != fgets(password, max_length+1, stdin)) {
        length = strlen(password);
        if (length > 0 && length < max_length) {
            if (password[length-1] == '\n') { /* remove the trailing         */
                password[length-1] = '\0';    /* newline if it exists        */
            }
            rc = 0;
        } else {
            rc = 1;
        }

    } else {
        rc = 1;
    }

    return rc;
}

/*
 * do_exit()
 *
 * Exit properly for the environment we're running in.
 */
void
do_exit(int rc)
{
#ifdef EXITTHREAD
    ExitThread(rc);
#else
    WinCPICCleanup();
    exit(rc);
#endif
}

/*
 * The ASCII<-->EBCDIC character set translation routines are implemented
 * below.  These procedures should never be called directly, but should
 * be accessed through the convert_to_ascii() and convert_from_ascii()
 * macros.  This frees the application program from knowing whether
 * it is being coded on an ASCII or EBCDIC computer (this must be
 * determined in the macro definition in CPICPORT.H).
 */

void   ascii_to_ebcdic_field  (unsigned char * ascii_field,
                               unsigned int field_size)
{
    unsigned int i;

    for (i = 0;
         i < field_size;
         ascii_field[i] = ascii_to_ebcdic_table[(unsigned)ascii_field[i]],i++);
}

void  ascii_to_ebcdic_string (unsigned char * ascii_string)
{
    ascii_to_ebcdic_field(ascii_string, strlen((char*)ascii_string));
}

void   ebcdic_to_ascii_field  (unsigned char * ebcdic_field,
                               unsigned int field_size)
{
    unsigned int i;

    for (i = 0;
         i < field_size;
         ebcdic_field[i] =
                         ebcdic_to_ascii_table[(unsigned)ebcdic_field[i]],i++);
}

void  ebcdic_to_ascii_string (unsigned char * ebcdic_string)
{
    ebcdic_to_ascii_field(ebcdic_string, strlen((char*)ebcdic_string));
}

/* ASCII to EBCDIC translate table (only UGL character set) */

char ascii_to_ebcdic_table[] = {
"\x00\x01\x02\x03\x37\x2D\x2E\x2F\x16\x05\x15\x0B\x0C\x0D\x0E\x0F"  /* 00-0F */
"\x10\x11\x12\x13\x3C\x3D\x32\x26\x18\x19\x3F\x27\x22\x1D\x35\x1F"  /* 10-1F */
"\x40\x5A\x7F\x7B\x5B\x6C\x50\x7D\x4D\x5D\x5C\x4E\x6B\x60\x4B\x61"  /* 20-2F */
"\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\x7A\x5E\x4C\x7E\x6E\x6F"  /* 30-3F */
"\x7C\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xD1\xD2\xD3\xD4\xD5\xD6"  /* 40-4F */
"\xD7\xD8\xD9\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xAD\xE0\xBD\x5F\x6D"  /* 50-5F */
"\x79\x81\x82\x83\x84\x85\x86\x87\x88\x89\x91\x92\x93\x94\x95\x96"  /* 60-6F */
"\x97\x98\x99\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xC0\x4F\xD0\xA1\x07"  /* 70-7F */
"\x43\x20\x21\x1C\x23\xEB\x24\x9B\x71\x28\x38\x49\x90\xBA\xEC\xDF"  /* 80-8F */
"\x45\x29\x2A\x9D\x72\x2B\x8A\x9A\x67\x56\x64\x4A\x53\x68\x59\x46"  /* 90-9F */
"\xEA\xDA\x2C\xDE\x8B\x55\x41\xFE\x58\x51\x52\x48\x69\xDB\x8E\x8D"  /* A0-AF */
"\x73\x74\x75\xFA\x15\xB0\xB1\xB3\xB4\xB5\x6A\xB7\xB8\xB9\xCC\xBC"  /* B0-BF */
"\xAB\x3E\x3B\x0A\xBF\x8F\x3A\x14\xA0\x17\xCB\xCA\x1A\x1B\x9C\x04"  /* C0-CF */
"\x34\xEF\x1E\x06\x08\x09\x77\x70\xBE\xBB\xAC\x54\x63\x65\x66\x62"  /* D0-DF */
"\x30\x42\x47\x57\xEE\x33\xB6\xE1\xCD\xED\x36\x44\xCE\xCF\x31\xAA"  /* E0-EF */
"\xFC\x9E\xAE\x8C\xDD\xDC\x39\xFB\x80\xAF\xFD\x78\x76\xB2\x9F\xFF"  /* F0-FF */
};

/* EBCDIC to ASCII translate table (only UGL character set) */

char ebcdic_to_ascii_table[] = {
"\x00\x01\x02\x03\xCF\x09\xD3\x7F\xD4\xD5\xC3\x0B\x0C\x0D\x0E\x0F"  /* 00-0F */
"\x10\x11\x12\x13\xC7\x0A\x08\xC9\x18\x19\xCC\xCD\x83\x1D\xD2\x1F"  /* 10-1F */
"\x81\x82\x1C\x84\x86\x0A\x17\x1B\x89\x91\x92\x95\xA2\x05\x06\x07"  /* 20-2F */
"\xE0\xEE\x16\xE5\xD0\x1E\xEA\x04\x8A\xF6\xC6\xC2\x14\x15\xC1\x1A"  /* 30-3F */
"\x20\xA6\xE1\x80\xEB\x90\x9F\xE2\xAB\x8B\x9B\x2E\x3C\x28\x2B\x7C"  /* 40-4F */
"\x26\xA9\xAA\x9C\xDB\xA5\x99\xE3\xA8\x9E\x21\x24\x2A\x29\x3B\x5E"  /* 50-5F */
"\x2D\x2F\xDF\xDC\x9A\xDD\xDE\x98\x9D\xAC\xBA\x2C\x25\x5F\x3E\x3F"  /* 60-6F */
"\xD7\x88\x94\xB0\xB1\xB2\xFC\xD6\xFB\x60\x3A\x23\x40\x27\x3D\x22"  /* 70-7F */
"\xF8\x61\x62\x63\x64\x65\x66\x67\x68\x69\x96\xA4\xF3\xAF\xAE\xC5"  /* 80-8F */
"\x8C\x6A\x6B\x6C\x6D\x6E\x6F\x70\x71\x72\x97\x87\xCE\x93\xF1\xFE"  /* 90-9F */
"\xC8\x7E\x73\x74\x75\x76\x77\x78\x79\x7A\xEF\xC0\xDA\x5B\xF2\xF9"  /* A0-AF */
"\xB5\xB6\xFD\xB7\xB8\xB9\xE6\xBB\xBC\xBD\x8D\xD9\xBF\x5D\xD8\xC4"  /* B0-BF */
"\x7B\x41\x42\x43\x44\x45\x46\x47\x48\x49\xCB\xCA\xBE\xE8\xEC\xED"  /* C0-CF */
"\x7D\x4A\x4B\x4C\x4D\x4E\x4F\x50\x51\x52\xA1\xAD\xF5\xF4\xA3\x8F"  /* D0-DF */
"\x5C\xE7\x53\x54\x55\x56\x57\x58\x59\x5A\xA0\x85\x8E\xE9\xE4\xD1"  /* E0-EF */
"\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\xB3\xF7\xF0\xFA\xA7\xFF"  /* F0-FF */
};

void
execute_and_send_output(char * command,
                        unsigned char * cm_conv_id,
                        struct error_handler_cpicerr * cpicerr)
{

   DWORD lasterror = 0;
   HANDLE hWritePipe = NULL;
   HANDLE hReadPipe = NULL;
   SECURITY_ATTRIBUTES SecAttrib = {0};
   STARTUPINFO StartInfo = {0};
   PROCESS_INFORMATION ProcInfo = {0};
   unsigned char * buffer=NULL;
   DWORD BufferSize=10000;
   DWORD BytesRead=0;
   DWORD BytesInPipe=0;
   DWORD WaitState=0;
   BOOL ProcessDead=FALSE;
   CM_INT32 rts_received;
   CM_INT32 cm_rc;
   char NewCommand[255] = {0};

   strcpy(NewCommand,"cmd /c ");
   strcat(NewCommand,command);

   buffer=malloc(BufferSize+2);

   SecAttrib.nLength = sizeof(SECURITY_ATTRIBUTES);
   SecAttrib.lpSecurityDescriptor = NULL;
   SecAttrib.bInheritHandle = TRUE;

   /**************************************************************************/
   /* Create named pipe and get handle to read end of the pipe.              */
   /**************************************************************************/
   if ((hReadPipe = CreateNamedPipe("\\\\.\\pipe\\arexecpipe",
               PIPE_ACCESS_INBOUND, PIPE_TYPE_BYTE, 1, BufferSize,
               BufferSize, 1000,&SecAttrib)) == INVALID_HANDLE_VALUE)
   {
      lasterror = GetLastError();
      printf("CreateNamedPipe returned %d",lasterror);
      return;
   }

   /**************************************************************************/
   /* Open a handle to the write end of the pipe, NB must be inheritable     */
   /**************************************************************************/
   if ((hWritePipe = CreateFile("\\\\.\\pipe\\arexecpipe",GENERIC_WRITE, 0,
                &SecAttrib, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL))
        == INVALID_HANDLE_VALUE)
   {
      lasterror = GetLastError();
      printf("CreateFile returned %d",lasterror);
      return;
   }

   /**************************************************************************/
   /* Now create the process, with its stdout and stderr pipes attatched to  */
   /* the write end of the named pipe.                                       */
   /**************************************************************************/
   StartInfo.cb = sizeof(STARTUPINFO);
   StartInfo.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
   StartInfo.wShowWindow = SW_HIDE;
   StartInfo.hStdInput = NULL;
   StartInfo.hStdOutput = hWritePipe;
   StartInfo.hStdError = hWritePipe;
   ProcessDead = FALSE;

   if (!CreateProcess(NULL,NewCommand, NULL, NULL, TRUE, 0, NULL, NULL,
                      &StartInfo, &ProcInfo))
   {
      lasterror=GetLastError();
      printf("CreateProcess returned %d\n",lasterror);
      return;
   }

   /**************************************************************************/
   /* Loop whilst the process is still running                               */
   /**************************************************************************/
   while (!ProcessDead)
   {
      /***********************************************************************/
      /* Check the process handle to see if it is dead yet.                  */
      /***********************************************************************/
      WaitState = WaitForSingleObject(ProcInfo.hProcess,1);
      if (WaitState != WAIT_TIMEOUT)
      {
         ProcessDead = TRUE;
      }

      /***********************************************************************/
      /* Peek in the named pipe for some data, don't just do a ReadFile as   */
      /* we may never get any more data!                                     */
      /* If there is some then read it out of the pipe and send it.          */
      /***********************************************************************/
      if (!PeekNamedPipe(hReadPipe, NULL, 0, NULL, &BytesInPipe, NULL))
      {
         lasterror=GetLastError();
         printf("PeekNamePipe returned %d\n",lasterror);
         return;
      }
      if (BytesInPipe != 0)
      {
         memset(buffer,'\0',BufferSize+1);
         if (!ReadFile(hReadPipe, buffer, BufferSize, &BytesRead, NULL))
         {
            lasterror=GetLastError();
            printf("ReadFile returned %d\n",lasterror);
            return;
         }
         cmsend(cm_conv_id,(unsigned char *) buffer,(CM_INT32 *)&BytesRead,
                &rts_received,&cm_rc);
         if (cm_rc) cpicerr_handle_rc(cpicerr, MSG_CMSEND, cm_rc);
      }
   }
   do
   {
      /***********************************************************************/
      /* The process is now dead, but there could still be some data in the  */
      /* pipe, so we do a peek/read loop until the number of bytes left is 0 */
      /***********************************************************************/
      if (!PeekNamedPipe(hReadPipe, NULL, 0, NULL, &BytesInPipe, NULL))
      {
         lasterror=GetLastError();
         printf("PeekNamePipe returned %d\n",lasterror);
         return;
      }
      if (BytesInPipe != 0)
      {
         memset(buffer,'\0',BufferSize+1);
         if (!ReadFile(hReadPipe, buffer, BufferSize, &BytesRead, NULL))
         {
            lasterror=GetLastError();
            printf("ReadFile returned %d\n",lasterror);
            return;
         }
         cmsend(cm_conv_id,(unsigned char *) buffer,(CM_INT32 *)&BytesRead,
                &rts_received,&cm_rc);
         if (cm_rc) cpicerr_handle_rc(cpicerr, MSG_CMSEND, cm_rc);
      }
   } while (BytesInPipe != 0);

   CloseHandle(hReadPipe);
   CloseHandle(hWritePipe);

   return;

}


CPICPORT.H

/*****************************************************************************
 *
 *  MODULE NAME: CPICPORT.H
 *
 *  COPYRIGHTS:
 *             This module contains code made available by IBM
 *             Corporation on an AS IS basis.  Any one receiving the
 *             module is considered to be licensed under IBM copyrights
 *             to use the IBM-provided source code in any way he or she
 *             deems fit, including copying it, compiling it, modifying
 *             it, and redistributing it, with or without
 *             modifications.  No license under any IBM patents or
 *             patent applications is to be implied from this copyright
 *             license.
 *
 *             A user of the module should understand that IBM cannot
 *             provide technical support for the module and will not be
 *             responsible for any consequences of use of the program.
 *
 *             Any notices, including this one, are not to be removed
 *             from the module without the prior written consent of
 *             IBM.
 *
 *  AUTHOR:    Peter J. Schwaller
 *             VNET:     PJS at RALVM6           Tie Line: 444-4376
 *             Internet: pjs@ralvm6.vnet.ibm.com     (919) 254-4376
 *
 *  AVAILABILITY:
 *             These sample programs and source are also available on
 *             CompuServe through the APPC Information Exchange.  To get
 *             to the APPC forum just type 'GO APPC' from any CompuServe
 *             prompt.  The samples are available in the Sample Programs
 *             library section.  Just search on the keyword CPICPGMS to
 *             find all the samples in this series.
 *
 *             Updates for the sample programs and support for many more
 *             CPI-C platforms will also be made available on CompuServe.
 *
 *  RELATED FILES:
 *             Used by CPICPORT.C
 *
 *  CHANGE HISTORY:
 *  Date       Description
 *  08/05/92   Version 2.31 of APING, ATELL and AREXEC released to CompuServe.
 *             This version was also distributed at the APPC/APPN Platform
 *             Developer's Conference held in Raleigh, NC.
 *  08/13/92   Added the write_*() calls.
 *             Changed all printf and fprintf calls to use a write_*() call.
 *
 *****************************************************************************/

#ifndef INCL_CPICPORT
#define INCL_CPICPORT

/*
 * This file contains all of the non-portable code for the CPI-C toolkit.
 * Conditional compilation directives determine what code will be compiled
 * for which platform.  The following constants have been established
 * to identify the supported platforms:
 *    DOS   - Networking Services/DOS
 *    OS2   - Extended Services for OS/2
 *    FAPI  - Compiling for both DOS and OS2
 *    AIX   - SNA Services
 *    OS400 - OS/400 (for the AS/400)
 *    MVS   - MVS ESA 4.2 or greater
 *    VM    - VM/CMS ESA 1.0 or greater
 *    WIN32 - Windows NT v3.1
 *    WINDOWS - Windows v3.1
 * These constants should be defined outside of the source code on
 * your compiler command line (e.g., cc -DOS2 foo.c).
 *
 * If you are porting to another platform, you should only use new constant
 * if you don't fit into any of the above categories.  To avoid name
 * conflicts please contact the supplier of this file, indicated above.
 */

/*
 * Set up the various constant definitions for:
 *    EBCDIC_HOST
 *    BIG_ENDIAN
 *    SHORT_IDENTIFIERS
 *    DOES_NOT_NEED_STRUPR
 *    SUPPORTS_SETTING_SECURITY
 *    PLN_COULD_REQUIRE_LOWERCASE
 *    ECS_NOT_SUPPORTED
 */
#include "cpicdefs.h"

/*
 * Include CPICERRS.H file for the definition of the CPICERR structure.
 */
#include "cpicerrs.h"

#define TIMELENGTH                 26
#define MAX_MESSAGE_LENGTH        (300+1)

#ifdef SHORT_IDENTIFIERS
/* The C/370 compiler requires that identifier be unique in the 1st 8 chars. */
    #define  ascii_to_ebcdic_field        a2efield
    #define  ascii_to_ebcdic_table        a2etab
    #define  ebcdic_to_ascii_field        e2afield
    #define  ebcdic_to_ascii_table        e2atab
#endif

#ifdef EBCDIC_HOST

#define convert_to_ascii(buffer, len)  ebcdic_to_ascii_field(buffer,len)
#define convert_from_ascii(buffer, len) ascii_to_ebcdic_field(buffer,len)

#else

#define convert_to_ascii(buffer, len)
#define convert_from_ascii(buffer, len)

#endif

/*
 * If we are on a big endian machine, we will convert the integer format
 * to little endian before sending the integers.  This is done through
 * a set of macros to swap the byte order.
 *
 * Programs should use the following macros before sending:
 *   convert_long_to_network     -- 32 bit integers
 *   convert_short_to_network    -- 16 bit integers
 *
 * Programs should use the following macros after receiving:
 *   convert_long_from_network   -- 32 bit integers
 *   convert_short_from_network  -- 16 bit integers
 *
 * Application code should use these macros before sending or receiving
 * any integers.  On a big endian platform, the macros will return a
 * a byte swapping of the integer.  On a little endian platform,
 * the original integer will be returned unmodified (no-op).
 */
#ifdef BIG_ENDIAN

#define  convert_long_to_network(x)        SWAP4(x)
#define  convert_short_to_network(x)       SWAP2(x)
#define  convert_long_from_network(x)      SWAP4(x)
#define  convert_short_from_network(x)     SWAP2(x)

#    ifndef SWAP2
/* Macro SWAP2 swaps the bytes in a two-byte integer         */
#    define SWAP2(s)       (unsigned short)(((s)<<8)|(((s)&0xFF00)>>8))
#    endif

#    ifndef SWAP4
/* Macro SWAP4 swaps the bytes in a four-byte integer        */
#    define SWAP4(l)       (unsigned long)(((l)<<24)|(((l)&0xFF000000)>>24)\
                           |(((l)&0xFF00)<<8)|(((l)&0xFF0000)>>8))
#    endif

#else
/*
 * If the platform is little endian, we don't have to do any byte
 * order changing of the integers.
 */
#define  convert_long_to_network(x)        x
#define  convert_short_to_network(x)       x
#define  convert_long_from_network(x)      x
#define  convert_short_from_network(x)     x
#    ifndef SWAP2
/* Macro SWAP2 swaps the bytes in a two-byte integer         */
#    define SWAP2(s)       (unsigned short)(((s)<<8)|(((s)&0xFF00)>>8))
#    endif

#    ifndef SWAP4
/* Macro SWAP4 swaps the bytes in a four-byte integer        */
#    define SWAP4(l)       (unsigned long)(((l)<<24)|(((l)&0xFF000000)>>24)\
                           |(((l)&0xFF00)<<8)|(((l)&0xFF0000)>>8))
#    endif

#endif

/*
 * Function prototypes
 */

void           write_error(            char *fmt, ...);
void           write_output(           char *fmt, ...);
void           write_log(              FILE * file,
                                       char *fmt, ...);

char CM_PTR    alloc_cpic_buffer(      unsigned int size);

void           show_info(              char * * text);

void           set_echo(               int mode);

int            get_password(           char * password,
                                       int max_length);

unsigned long  get_time(               void);

void           display_message(        char * destination,
                                       char * userid,
                                       char * message);

void           datetime(               char *);

void           do_exit(                int rc);

void           ascii_to_ebcdic_field(  unsigned char * ascii_field,
                                       unsigned int field_size);
void           ebcdic_to_ascii_field(  unsigned char * ebcdic_field,
                                       unsigned int field_size);

void           execute_and_send_output(char * command,
                                       unsigned char * cm_conv_id,
                                       struct error_handler_cpicerr * cpicerr);
void   free_cpic_buffer(   void *memblock );
#endif


GETOPT.C

/*****************************************************************************
 *
 *  MODULE NAME : GETOPT.C
 *
 *  COPYRIGHTS:
 *             This module contains code made available by IBM
 *             Corporation on an AS IS basis.  Any one receiving the
 *             module is considered to be licensed under IBM copyrights
 *             to use the IBM-provided source code in any way he or she
 *             deems fit, including copying it, compiling it, modifying
 *             it, and redistributing it, with or without
 *             modifications.  No license under any IBM patents or
 *             patent applications is to be implied from this copyright
 *             license.
 *
 *             A user of the module should understand that IBM cannot
 *             provide technical support for the module and will not be
 *             responsible for any consequences of use of the program.
 *
 *             Any notices, including this one, are not to be removed
 *             from the module without the prior written consent of
 *             IBM.
 *
 *  AUTHOR:   Original author:
 *                 G. R. Blair (BOBBLAIR at AUSVM1)
 *                 Internet: bobblair@bobblair.austin.ibm.com
 *
 *            Extensively revised by:
 *                 John Q. Walker II, Ph.D. (JOHHQ at RALVM6)
 *                 Internet: johnq@ralvm6.vnet.ibm.com
 *
 *****************************************************************************/

/******************************************************************************
 * getopt()
 *
 * The getopt() function is a command line parser.  It returns the next
 * option character in argv that matches an option character in opstring.
 *
 * The argv argument points to an array of argc+1 elements containing argc
 * pointers to character strings followed by a null pointer.
 *
 * The opstring argument points to a string of option characters; if an
 * option character is followed by a colon, the option is expected to have
 * an argument that may or may not be separated from it by white space.
 * The external variable optarg is set to point to the start of the option
 * argument on return from getopt().
 *
 * The getopt() function places in optind the argv index of the next argument
 * to be processed.  The system initializes the external variable optind to
 * 1 before the first call to getopt().
 *
 * When all options have been processed (that is, up to the first nonoption
 * argument), getopt() returns EOF.  The special option "--" may be used to
 * delimit the end of the options; EOF will be returned, and "--" will be
 * skipped.
 *
 * The getopt() function returns a question mark (?) when it encounters an
 * option character not included in opstring.  This error message can be
 * disabled by setting opterr to zero.  Otherwise, it returns the option
 * character that was detected.
 *
 * If the special option "--" is detected, or all options have been
 * processed, EOF is returned.
 *
 * Options are marked by either a minus sign (-) or a slash (/).
 *
 * No errors are defined.
 *****************************************************************************/

#include <stdio.h>                  /* for EOF */
#include <string.h>                 /* for strchr() */

/* static (global) variables that are specified as exported by getopt() */
char *optarg = NULL;    /* pointer to the start of the option argument  */
int   optind = 1;       /* number of the next argv[] to be evaluated    */
int   opterr = 1;       /* non-zero if a question mark should be returned
                           when a non-valid option character is detected */

/* handle possible future character set concerns by putting this in a macro */
#define _next_char(string)  (char)(*(string+1))

int getopt(int argc, char *argv[], char *opstring)
{
    static char *pIndexPosition = NULL; /* place inside current argv string */
    char *pArgString = NULL;        /* where to start from next */
    char *pOptString;               /* the string in our program */

    if (pIndexPosition != NULL) {
        /* we last left off inside an argv string */
        if (*(++pIndexPosition)) {
            /* there is more to come in the most recent argv */
            pArgString = pIndexPosition;
        }
    }

    if (pArgString == NULL) {
        /* we didn't leave off in the middle of an argv string */
        if (optind >= argc) {
            /* more command-line arguments than the argument count */
            pIndexPosition = NULL;  /* not in the middle of anything */
            return EOF;             /* used up all command-line arguments */
        }

        /*---------------------------------------------------------------------
         * If the next argv[] is not an option, there can be no more options.
         *-------------------------------------------------------------------*/
        pArgString = argv[optind++]; /* set this to the next argument ptr */

        if (('/' != *pArgString) && /* doesn't start with a slash or a dash? */
            ('-' != *pArgString)) {
            --optind;               /* point to current arg once we're done */
            optarg = NULL;          /* no argument follows the option */
            pIndexPosition = NULL;  /* not in the middle of anything */
            return EOF;             /* used up all the command-line flags */
        }

        /* check for special end-of-flags markers */
        if ((strcmp(pArgString, "-") == 0) ||
            (strcmp(pArgString, "--") == 0)) {
            optarg = NULL;          /* no argument follows the option */
            pIndexPosition = NULL;  /* not in the middle of anything */
            return EOF;             /* encountered the special flag */
        }

        pArgString++;               /* look past the / or - */
    }

    if (':' == *pArgString) {       /* is it a colon? */
        /*---------------------------------------------------------------------
         * Rare case: if opterr is non-zero, return a question mark;
         * otherwise, just return the colon we're on.
         *-------------------------------------------------------------------*/
        return (opterr ? (int)'?' : (int)':');
    }
    else if ((pOptString = strchr(opstring, *pArgString)) == 0) {
        /*---------------------------------------------------------------------
         * The letter on the command-line wasn't any good.
         *-------------------------------------------------------------------*/
        optarg = NULL;              /* no argument follows the option */
        pIndexPosition = NULL;      /* not in the middle of anything */
        return (opterr ? (int)'?' : (int)*pArgString);
    }
    else {
        /*---------------------------------------------------------------------
         * The letter on the command-line matches one we expect to see
         *-------------------------------------------------------------------*/
        if (':' == _next_char(pOptString)) { /* is the next letter a colon? */
            /* It is a colon.  Look for an argument string. */
            if ('\0' != _next_char(pArgString)) {  /* argument in this argv? */
                optarg = &pArgString[1];   /* Yes, it is */
            }
            else {
                /*-------------------------------------------------------------
                 * The argument string must be in the next argv.
                 * But, what if there is none (bad input from the user)?
                 * In that case, return the letter, and optarg as NULL.
                 *-----------------------------------------------------------*/
                if (optind < argc)
                    optarg = argv[optind++];
                else {
                    optarg = NULL;
                    return (opterr ? (int)'?' : (int)*pArgString);
                }
            }
            pIndexPosition = NULL;  /* not in the middle of anything */
        }
        else {
            /* it's not a colon, so just return the letter */
            optarg = NULL;          /* no argument follows the option */
            pIndexPosition = pArgString;    /* point to the letter we're on */
        }
        return (int)*pArgString;    /* return the letter that matched */
    }
}


GETOPT.H

/*****************************************************************************
 *
 *  MODULE NAME : GETOPT.H
 *
 *  COPYRIGHTS:
 *             This module contains code made available by IBM
 *             Corporation on an AS IS basis.  Any one receiving the
 *             module is considered to be licensed under IBM copyrights
 *             to use the IBM-provided source code in any way he or she
 *             deems fit, including copying it, compiling it, modifying
 *             it, and redistributing it, with or without
 *             modifications.  No license under any IBM patents or
 *             patent applications is to be implied from this copyright
 *             license.
 *
 *             A user of the module should understand that IBM cannot
 *             provide technical support for the module and will not be
 *             responsible for any consequences of use of the program.
 *
 *             Any notices, including this one, are not to be removed
 *             from the module without the prior written consent of
 *             IBM.
 *
 *  AUTHOR:   Original author:
 *                 G. R. Blair (BOBBLAIR at AUSVM1)
 *                 Internet: bobblair@bobblair.austin.ibm.com
 *
 *            Extensively revised by:
 *                 John Q. Walker II, Ph.D. (JOHHQ at RALVM6)
 *                 Internet: johnq@ralvm6.vnet.ibm.com
 *
 *****************************************************************************/

extern char * optarg;
extern int    optind;

int getopt ( int argc, char **argv, char *optstring);


SLI3270.C

/*****************************************************************************/
/* SLI Sample Program                                                        */
/*                                                                           */
/* (C) Copyright 1990 - 1993 Data Connection Ltd.                            */
/*                                                                           */
/* This program is a crude 3270 emulator.  It uses the SLI API to access the */
/* LU-LU session.  Outbound data from the host is displayed on the screen    */
/* unformatted.                                                              */
/*                                                                           */
/* If the outbound data is an RQD request an automatic positive response     */
/* is sent.  Inbound data can be entered at the keyboard and is sent on      */
/* the current session.                                                      */
/*                                                                           */
/* The program is invoked with two parameters - the name of the LUA LU       */
/* to use and a logon string.  This is converted to upper case and must      */
/* match an LUA LU in the configuration file.  This LU should be configured  */
/* for a 327? on the host.                                                   */
/*                                                                           */
/* The program can only access hosts that use simple unformatted logon       */
/* requests                                                                  */
/*                                                                           */
/* To exit the program type ] (right square bracket)                         */
/*                                                                           */
/* The CSV CONVERT function is used to convert data between ASCII and        */
/* EBCDIC.  This uses the type G conversion table, so the environment        */
/* variable COMTBLG must be set to point to a suitable file.                 */
/*                                                                           */
/* Keys that work are:                                                       */
/*                                                                           */
/*     Alt-1 to Alt-9 are PF1 to PF9                                         */
/*     F2 sends a CLEAR to the host                                          */
/*     backspace                                                             */
/*     Letters, numbers and symbols                                          */
/*     ] terminates the program                                              */
/*                                                                           */
/*****************************************************************************/

#ifdef WIN32
  #include <windows.h>
#else
  #define INCL_DOS
  #define INCL_DOSERRORS
  #include <os2.h>
#endif

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <conio.h>
#include <lua_c.h>
#include <acssvcc.h>

/*****************************************************************************/
/* Program constants                                                         */
/*****************************************************************************/
#define DATASIZE 4096                  /* Max size of RU                     */
#define EXITKEY  0x5D                  /* ']'   to exit program              */

#define BETB     1                     /* Between brackets                   */
#define SEND     2                     /* In bracket and can send            */
#define RECV     3                     /* In bracket, but cannot send        */

/*****************************************************************************/
/* State variables                                                           */
/*                                                                           */
/* We can only have one read or write verb outstanding per flow.  These      */
/* state variables keep track of these verbs and what we need to do with     */
/* them.                                                                     */
/*****************************************************************************/

UINT  write_state;
#define NO_WRITE              1
#define WRITE_OUTSTANDING     2        /* waiting for write to complete      */
#define WRITE_QUEUED          3        /* have data to write                 */

UINT  read_state;
#define NO_READ               1
#define READ_OUTSTANDING      2        /* read verb outstanding              */
#define RSP_QUEUED            3        /* have a response to write           */
#define RSP_OUTSTANDING       4        /* waiting for a response to complete */

UINT  rpq_state;
#define NO_RPQ                1
#define RPQ_OUTSTANDING       2        /* waiting for an RPQ to complete     */
#define RPQ_QUEUED            3        /* have RPQ to send                   */

/***************************************************************************/
/* Global variables                                                        */
/*                                                                         */
/* Inbound data is  prefixed with the <enter> key AID plus a two byte      */
/* cursor address.                                                         */
/***************************************************************************/
LUA_VERB_RECORD recv_verb;             /* SLI read verb                    */
LUA_VERB_RECORD other_verb;            /* SLI init, write or term verb     */
LUA_VERB_RECORD rpq_verb;              /* SLI_WRITE for RPQs               */

UCHAR rpq_data[] = {

  0x88,
  0x00, 0x0C, 0x81, 0x80, 0x80, 0x81, 0x85, 0x86, 0x87, 0x88, 0x95, 0xA6,

  0x00, 0x17, 0x81, 0x81, 0x21, 0x00, 0x00, 0x50, 0x00, 0x18, 0x00, 0x00,
  0x0A, 0x02, 0xE5, 0x00, 0x02, 0x00, 0x6F, 0x09, 0x10, 0x07, 0x80,

  0x00, 0x14, 0x81, 0x85, 0x02, 0x00, 0x09, 0x0E, 0x00, 0x00, 0x00, 0x00,
  0x07, 0x00, 0x00, 0x00, 0x02, 0xB9, 0x01, 0x1D,

  0x00, 0x16, 0x81, 0x86, 0x00, 0x08, 0x00, 0xF4, 0xF1, 0x00, 0xF2, 0x00,
  0xF3, 0x00, 0xF4, 0x00, 0xF5, 0x00, 0xF6, 0x00, 0xF7, 0x00,

  0x00, 0x0D, 0x81, 0x87, 0x04, 0x00, 0xF0, 0xF1, 0x00, 0xF2, 0x00, 0xF4,
  0x00,

  0x00, 0x07, 0x81, 0x88, 0x00, 0x01, 0x02,

  0x00, 0x0C, 0x81, 0x95, 0x00, 0x00, 0x7F, 0xFF, 0x7F, 0xFF, 0x01, 0x01,

  0x00, 0x11, 0x81, 0xA6, 0x00, 0x00, 0x0B, 0x01, 0x00, 0x00, 0x50, 0x00,
  0x18, 0x00, 0x50, 0x00, 0x18};

#define RPQ_LENGTH (sizeof(rpq_data))

unsigned char read_data[DATASIZE];     /* Outbound RU                       */
#define WRITE_EXTRA 6
unsigned char write_array[DATASIZE + WRITE_EXTRA] =
                                          {0x7d, 0x40, 0x40, 0x11, 0x40, 0x40};
                                       /* Inbound RU                         */
unsigned char *write_data = write_array + WRITE_EXTRA;
                                       /* Pointer to inbound RU              */
UINT data_offset = 0;                  /* offset into read_data              */
UINT write_len   = 0;                  /* length of write data               */

struct convert convert_to_asc;         /* Outbound convert verb              */
struct convert convert_to_ebc;         /* Inbound convert verb               */

#ifdef WIN32
  HANDLE write_event = 0l;             /* write arm verb semaphore           */
  HANDLE read_event  = 0l;             /* read arm verb semaphore            */
  HANDLE init_event  = 0l;             /* SLI_OPEN semaphore                 */
  HANDLE term_event  = 0l;             /* SLI_CLOSE semaphore                */
  HANDLE rpq_event   = 0l;
  HANDLE std_input;                    /* standard input handle              */
  HANDLE std_output;                   /* standard output handle             */
#else
  ULONG write_sema4 = 0l;              /* write arm verb semaphore           */
  ULONG read_sema4  = 0l;              /* read arm verb semaphore            */
  ULONG init_sema4  = 0l;              /* SLI_OPEN semaphore                 */
  ULONG term_sema4  = 0l;              /* SLI_CLOSE semaphore                */
  ULONG rpq_sema4   = 0l;
#endif

BOOL terminating = FALSE;              /* are we already in closedown() ?    */

unsigned long sense = 0l;              /* sense code                         */

unsigned char lu_name[9] = "        "; /* LU name                            */
unsigned long sid;                     /* SLI session ID                     */
int send_state = BETB;                 /* LU session send state              */

#define LOGON_MAX  25                  /* maximum logon string length        */

UCHAR logon_data[LOGON_MAX+1] = {0};
UCHAR logon_length   = 0;

#ifdef WIN32
  #define OPENPOST        (ULONG) init_event
  #define CLOSEPOST       (ULONG) term_event
  #define RECVPOST        (ULONG) read_event
  #define SENDPOST        (ULONG) write_event
  #define RPQPOST         (ULONG) rpq_event
#else
  #define OPENPOST        (unsigned long) ((UCHAR FAR *) &init_sema4)
  #define CLOSEPOST       (unsigned long) ((UCHAR FAR *) &term_sema4)
  #define RECVPOST        (unsigned long) ((UCHAR FAR *) &read_sema4)
  #define SENDPOST        (unsigned long) ((UCHAR FAR *) &write_sema4)
  #define RPQPOST         (unsigned long) ((UCHAR FAR *) &rpq_sema4)
#endif

/*****************************************************************************/
/* Function declarations.                                                    */
/*****************************************************************************/

#ifdef WIN32
  #define LOADDS
#else
  #define LOADDS _loadds
#endif

void pascal far LOADDS SLI (LUA_VERB_RECORD FAR *);

#ifdef WIN32
  void _CRTAPI1 main(int argc, char *argv[]);
#else
  int             main                (int  argc,char  * *argv);
#endif
void            csv_init            (void);
int             issue_verb          (unsigned int  type);
void            issue_receive       (void);
void            other_done          (LUA_VERB_RECORD *verb);
void            recv_done           (LUA_VERB_RECORD *verb);
void            issue_rsp           (unsigned long  sense);
void            rsp_done            (LUA_VERB_RECORD *verb);
void            closedown           (void);
BOOL            do_send             (void);
void            wait_verb_complete  (LUA_VERB_RECORD *verb);
void            set_semaphore       (LUA_VERB_RECORD *verb);
BOOL            do_keyboard_stuff   (void);
BOOL            check_verb_complete (LUA_VERB_RECORD *verb);
void            parse_data          (UCHAR FAR *, USHORT);
BOOL            issue_rpq           (void);

/**PROC+**********************************************************************/
/* Name:      main                                                           */
/*                                                                           */
/* Purpose:   set up CSV convert verbs                                       */
/*                                                                           */
/* Returns:   none                                                           */
/*                                                                           */
/* Params:    argv, argc                                                     */
/*                                                                           */
/* Operation: Get LU name and logon string from command line                 */
/*            do some initialisation                                         */
/*            Issue SLI_OPEN to get LU-LU   session                          */
/*            Loop reading data from keyboard and host, and send keyboard    */
/*            data to host                                                   */
/**PROC-**********************************************************************/
#ifdef WIN32
  void _CRTAPI1 main(int argc, char *argv[])
#else
int main(argc, argv)
int argc;
char *argv[];
#endif
{
  BOOL whole_data;       /* we have a whole data string to write             */
  BOOL verb_complete;    /* Has a verb completed                             */
  BOOL write_ok;         /* Has write worked?                                */
  BOOL rpq_ok;           /* Has rpq worked                                   */
  int ii;
#ifdef WIN32
  HANDLE handle_list [6];
#endif
#ifdef DOS5
  DEFINEMUXSEMLIST(sem_list,5)
  USHORT           sem_index;
#endif

  printf("SLI simple 3270 application\n");

  /***************************************************************************/
  /* Validate parameter usage and get LU name.                               */
  /***************************************************************************/
  if (argc != 3)
  {
    printf("Usage: SLI3270 luname \"logon string\"\n");
    exit(1);
  }
  if (strlen(argv[1]) > 8)
  {
    printf("LU name too long\n");
    exit(1);
  }
  if (strlen(argv[2]) > LOGON_MAX)
  {
    printf("Logon string too long");
  }

  /***************************************************************************/
  /* Convert to upper case                                                   */
  /***************************************************************************/
  ii=0;
  while (argv[1][ii])
  {
    if (argv[1][ii] >= 'a' && argv[1][ii] <= 'z')
    {
      argv[1][ii] = argv[1][ii] - 'a' + 'A';
    }
    ii++;
  }
  memcpy(lu_name, argv[1], strlen(argv[1]));
  printf("lu_name %s\n", lu_name);

  /***************************************************************************/
  /* Set up logon message                                                    */
  /***************************************************************************/
  strcpy (logon_data, argv[2]);
  logon_length = strlen(logon_data);
  convert_to_ebc.opcode       = SV_CONVERT;
  convert_to_ebc.direction    = SV_ASCII_TO_EBCDIC;
  convert_to_ebc.char_set     = SV_G;
  convert_to_ebc.source       = logon_data;
  convert_to_ebc.target       = logon_data;
  convert_to_ebc.len          = (unsigned short) logon_length;
  ACSSVC_C((LONG)((UCHAR FAR *)&convert_to_ebc));

  /***************************************************************************/
  /* Set up CSV convert verbs                                                */
  /***************************************************************************/
  csv_init();

#ifdef WIN32
  /***************************************************************************/
  /* Create the necessary events                                             */
  /***************************************************************************/
  write_event = CreateEvent (NULL, TRUE, FALSE, NULL);
  read_event  = CreateEvent (NULL, TRUE, FALSE, NULL);
  init_event  = CreateEvent (NULL, TRUE, FALSE, NULL);
  term_event  = CreateEvent (NULL, TRUE, FALSE, NULL);
  rpq_event   = CreateEvent (NULL, TRUE, FALSE, NULL);

  /***************************************************************************/
  /* Get standard input and output handles.                                  */
  /***************************************************************************/
  std_input  = GetStdHandle (STD_INPUT_HANDLE );
  std_output = GetStdHandle (STD_OUTPUT_HANDLE);
  if ((std_input  == INVALID_HANDLE_VALUE) ||
      (std_output == INVALID_HANDLE_VALUE))
  {
    printf("GetStdHandle Failed, %d", GetLastError());
    exit (1);
  }

  /***************************************************************************/
  /* Set up list of handles to wait for                                      */
  /***************************************************************************/
  handle_list[0] = write_event;
  handle_list[1] = read_event;
  handle_list[2] = init_event;
  handle_list[3] = term_event;
  handle_list[4] = rpq_event;
  handle_list[5] = std_input;
#endif

#ifdef DOS5
  /***************************************************************************/
  /* Set semaphores to begin with                                            */
  /***************************************************************************/
  DosSemSet (&init_sema4);
  DosSemSet (&term_sema4);
  DosSemSet (&read_sema4);
  DosSemSet (&write_sema4);
  DosSemSet (&rpq_sema4);

  sem_list.amxs[0].hsem = &init_sema4;
  sem_list.amxs[1].hsem = &term_sema4;
  sem_list.amxs[2].hsem = &read_sema4;
  sem_list.amxs[3].hsem = &write_sema4;
  sem_list.amxs[4].hsem = &rpq_sema4;
#endif

  /***************************************************************************/
  /* Initialize SLI (get LU-LU session)                                      */
  /***************************************************************************/
  if (issue_verb((unsigned int) LUA_OPCODE_SLI_OPEN))
  {
    printf("OPEN failed\n");
    exit(1);
  }

  /***************************************************************************/
  /* Set initial states                                                      */
  /***************************************************************************/
  write_state = NO_WRITE;
  read_state  = NO_READ;
  rpq_state   = NO_RPQ;

  /***************************************************************************/
  /* Loop reading inbound data from the keyboard                             */
  /***************************************************************************/
  while (!terminating)
  {
    /*************************************************************************/
    /* Determine if we can pause                                             */
    /*************************************************************************/
    if (  (read_state != NO_READ)   &&

          ((write_state != WRITE_QUEUED)    ||
           (rpq_state   != NO_RPQ)          ||
           (read_state  == RSP_OUTSTANDING) ||
           (read_state  == RSP_QUEUED)) &&

          ((rpq_state   != RPQ_QUEUED)        ||
           (write_state == WRITE_OUTSTANDING) ||
           (read_state  == RSP_OUTSTANDING)   ||
           (read_state  == RSP_QUEUED))    &&

          ((read_state != RSP_QUEUED) ||
           (write_state == WRITE_OUTSTANDING)))
    {
#ifdef WIN32
      WaitForMultipleObjects (6, handle_list, FALSE, INFINITE);
#else
      if (!kbhit())
      {
        DosMuxSemWait (&sem_index, &sem_list, 200l);
      }
#endif
    }

    /*************************************************************************/
    /* First check on the state of write verbs                               */
    /*************************************************************************/
    if ((write_state == NO_WRITE)        &&
        (rpq_state   == NO_RPQ)          &&
        (read_state  != RSP_QUEUED)      &&
        (read_state  != RSP_OUTSTANDING))
    {
      /***********************************************************************/
      /* Don't write here if we need to respond, since we can only have one  */
      /* write outstanding on each flow.                                     */
      /* Here we want to check if we have a whole string of data (i.e.  that */
      /* the user has hit enter).  If so write the data to the HOST          */
      /***********************************************************************/
      whole_data = do_keyboard_stuff ();
      if (whole_data)
      {
        /*********************************************************************/
        /* Attempt to write the data.  May not work because of bracket state */
        /*********************************************************************/

        write_ok = do_send ();
        if (write_ok)
        {
          /*******************************************************************/
          /* If verb completes async change state to indicate this o.w.      */
          /* call the verb done processor                                    */
          /*******************************************************************/
          if (other_verb.common.lua_flag2.async)
          {
            write_state = WRITE_OUTSTANDING;
          }
          else
          {
            other_done (&other_verb);
            write_state = NO_WRITE;
          }
        }
        else
        {
          /*******************************************************************/
          /* Failed to do a write.  Queue up data                            */
          /*******************************************************************/
          write_state = WRITE_QUEUED;
        }
      }
    }
    else if ((write_state == WRITE_QUEUED)    &&
             (rpq_state   == NO_RPQ)          &&
             (read_state  != RSP_OUTSTANDING) &&
             (read_state  != RSP_QUEUED))
    {
      /***********************************************************************/
      /* Attempt to write the data.  May not work because of bracket state   */
      /***********************************************************************/
      write_ok = do_send ();
      if (write_ok)
      {
        /*********************************************************************/
        /* If verb completes async change state to indicate this o.w.  call  */
        /* the verb done processor                                           */
        /*********************************************************************/
        if (other_verb.common.lua_flag2.async)
        {
          write_state = WRITE_OUTSTANDING;
        }
        else
        {
          other_done (&other_verb);
          write_state = NO_WRITE;
        }
      }

    }
    else if (write_state == WRITE_OUTSTANDING)
    {
      /***********************************************************************/
      /* We have a SLI_WRITE outstanding so check if it is complete          */
      /***********************************************************************/
      verb_complete = check_verb_complete (&other_verb);
      if (verb_complete)
      {
        other_done (&other_verb);
        write_state = NO_WRITE;
      }
    }

    /*************************************************************************/
    /* Now check the RPQ state                                               */
    /*************************************************************************/
    if ((rpq_state   == RPQ_QUEUED)        &&
        (write_state != WRITE_OUTSTANDING) &&
        (read_state  != RSP_OUTSTANDING)   &&
        (read_state  != RSP_QUEUED))
    {
      /***********************************************************************/
      /* We have an RPQ queued up.  Try to send it                           */
      /***********************************************************************/
      rpq_ok = issue_rpq ();
      if (rpq_ok)
      {
        /*********************************************************************/
        /* If verb completes async change state to indicate this o.w.  call  */
        /* the verb done processor                                           */
        /*********************************************************************/
        if (rpq_verb.common.lua_flag2.async)
        {
          rpq_state = WRITE_OUTSTANDING;
        }
        else
        {
          other_done (&rpq_verb);
          write_state = NO_RPQ;
        }
      }
    }
    else if (rpq_state == RPQ_OUTSTANDING)
    {
      /***********************************************************************/
      /* Check for verb completion                                           */
      /***********************************************************************/
      verb_complete = check_verb_complete (&rpq_verb);
      if (verb_complete)
      {
        other_done (&rpq_verb);
        rpq_state = NO_RPQ;
      }

    }

    /*************************************************************************/
    /* Now check on read verbs                                               */
    /*************************************************************************/
    if (read_state == NO_READ)
    {
      issue_receive ();
      if (recv_verb.common.lua_flag2.async)
      {
        read_state = READ_OUTSTANDING;
      }
      else
      {
        /*********************************************************************/
        /* recv_done will change read_state to RESPONSE_QUEUED or NO_READ    */
        /*********************************************************************/
        recv_done (&recv_verb);
      }
    }
    else if (read_state == READ_OUTSTANDING)
    {
      verb_complete = check_verb_complete (&recv_verb);
      if (verb_complete)
      {
        /*********************************************************************/
        /* recv_done will change read_state to RESPONSE_QUEUED or NO_READ    */
        /*********************************************************************/
        recv_done (&recv_verb);
      }
    }
    else if (read_state == RSP_QUEUED)
    {
      /***********************************************************************/
      /* We need to respond to a message read.  Do a write if one is         */
      /* availible.                                                          */
      /***********************************************************************/
      if (write_state != WRITE_OUTSTANDING)
      {
        /*********************************************************************/
        /* The response is issued using the recv_verb                        */
        /*********************************************************************/
        issue_rsp (sense);
        if (recv_verb.common.lua_flag2.async)
        {
          read_state = RSP_OUTSTANDING;
        }
        else
        {
          rsp_done (&recv_verb);
          read_state = NO_READ;
        }
      }
    }
    else if (read_state == RSP_OUTSTANDING)
    {
      /***********************************************************************/
      /* Check to see if the response is complete                            */
      /***********************************************************************/
      verb_complete = check_verb_complete (&recv_verb);
      if (verb_complete)
      {
        rsp_done (&recv_verb);
        read_state = NO_READ;
      }
    }
    else
    {
      /***********************************************************************/
      /* Invalid state                                                       */
      /***********************************************************************/
      printf("invalid state");
    }

  }  /* while not terminating                                                */

}  /* main ()                                                                */

/**PROC+**********************************************************************/
/* Name:      csv_init                                                       */
/*                                                                           */
/* Purpose:   set up CSV convert verbs                                       */
/*                                                                           */
/* Returns:   void                                                           */
/*                                                                           */
/* Params:    none                                                           */
/*                                                                           */
/* Operation: sets uup two CSV convert verbs, one in each direction.  Only   */
/* the length needs to be set when the verb is called                        */
/**PROC-**********************************************************************/
void csv_init (void)
{
  convert_to_asc.opcode       = SV_CONVERT;
  convert_to_asc.direction    = SV_EBCDIC_TO_ASCII;
  convert_to_asc.char_set     = SV_G;
  convert_to_asc.source       = read_data;
  convert_to_asc.target       = read_data;

  convert_to_ebc.opcode       = SV_CONVERT;
  convert_to_ebc.direction    = SV_ASCII_TO_EBCDIC;
  convert_to_ebc.char_set     = SV_G;
  convert_to_ebc.source       = write_data;
  convert_to_ebc.target       = write_data;
}  /* csv_init                                                               */

/**PROC+**********************************************************************/
/* Name:      issue_verb                                                     */
/*                                                                           */
/* Purpose:   build a verb and send it                                       */
/*                                                                           */
/* Returns:   int - non-zero => verb failed                                  */
/*                                                                           */
/* Params:    IN type - type of verb to issue                                */
/*                                                                           */
/* Operation: Build an OPEN or CLOSE verb and issue it.  Wait for the verb   */
/*            to complete                                                    */
/**PROC-**********************************************************************/
int issue_verb(type)
unsigned int  type;
{
  memset(&other_verb, 0, sizeof(other_verb));
  other_verb.common.lua_verb             = LUA_VERB_SLI;

other_verb.common.lua_opcode           = type;
  if (type == LUA_OPCODE_SLI_OPEN)
  {
    other_verb.common.lua_verb_length    = sizeof(struct LUA_COMMON)+
                            sizeof(other_verb.specific.open.lua_init_type) +
                            sizeof(other_verb.specific.open.lua_resv65) +
                            sizeof(other_verb.specific.open.lua_wait) +
                            sizeof(other_verb.specific.open.lua_ending_delim);
    memcpy(other_verb.common.lua_luname, lu_name, 8);
    other_verb.common.lua_post_handle    = OPENPOST;
    other_verb.specific.open.lua_init_type = LUA_INIT_TYPE_SEC_LOG;
    other_verb.common.lua_data_length    = logon_length;
    other_verb.common.lua_data_ptr       = logon_data;
    other_verb.specific.open.lua_open_extension[0].lua_routine_type =
                                                         LUA_ROUTINE_TYPE_END;
  }
  else
  {
    other_verb.common.lua_verb_length    = sizeof(struct LUA_COMMON);
    other_verb.common.lua_sid            = sid;
    other_verb.common.lua_post_handle    = CLOSEPOST;
  }
  SLI((LUA_VERB_RECORD FAR *) &other_verb);
  if (other_verb.common.lua_flag2.async)
  {
    wait_verb_complete (&other_verb);
  }
  other_done(&other_verb);

  return(other_verb.common.lua_prim_rc != LUA_OK);
}  /* issue_verb                                                             */

/**PROC+**********************************************************************/
/* Name:      issue_receive                                                  */
/*                                                                           */
/* Purpose:   build a recv verb and send it                                  */
/*                                                                           */
/* Returns:   void                                                           */
/*                                                                           */
/* Params:    none                                                           */
/*                                                                           */
/* Operation: Build a read verb for all flows and issue it                   */
/**PROC-**********************************************************************/
void issue_receive (void)
{
  memset(&recv_verb, 0, sizeof(recv_verb));
  memset(read_data, 0, DATASIZE);
  recv_verb.common.lua_verb             = LUA_VERB_SLI;
  recv_verb.common.lua_verb_length      = sizeof(struct LUA_COMMON);
  recv_verb.common.lua_opcode           = LUA_OPCODE_SLI_RECEIVE;
  recv_verb.common.lua_sid              = sid;
  recv_verb.common.lua_max_length       = DATASIZE;
  recv_verb.common.lua_data_ptr         = (char *) read_data;
  recv_verb.common.lua_post_handle      = RECVPOST;
  recv_verb.common.lua_flag1.lu_norm    = 1;
  recv_verb.common.lua_flag1.lu_exp     = 1;
  recv_verb.common.lua_flag1.sscp_norm  = 1;
  recv_verb.common.lua_flag1.sscp_exp   = 1;
  SLI((LUA_VERB_RECORD FAR *)&recv_verb);

}  /* issue_receive                                                          */

/**PROC+**********************************************************************/
/* Name:      other_done                                                     */
/*                                                                           */
/* Purpose:   handle completed open, send or close verb                      */
/*                                                                           */
/* Returns:   void                                                           */
/*                                                                           */
/* Params:    IN verb - completed verb                                       */
/*                                                                           */
/* Operation: write status to he screen                                      */
/**PROC-**********************************************************************/
void other_done(verb)
LUA_VERB_RECORD *verb;
{
  switch (verb->common.lua_opcode)
  {
    case LUA_OPCODE_SLI_OPEN:
         if (verb->common.lua_prim_rc == LUA_OK)
         {
           printf("session active\n");
           sid = verb->common.lua_sid;
         }
         else
         {
           printf("OPEN failed, (%4.4x, %8.8lx)\n",
                  verb->common.lua_prim_rc, verb->common.lua_sec_rc);
         }
         break;

    case LUA_OPCODE_SLI_SEND:
         if (verb->common.lua_prim_rc != LUA_OK)
         {
           printf("SEND failed, (%4.4x, %8.8lx)\n",
                  verb->common.lua_prim_rc, verb->common.lua_sec_rc);
         }
         break;

    case LUA_OPCODE_SLI_CLOSE:
         printf("Terminated\n");
         break;

  }
}  /* other_done                                                             */

/**PROC+**********************************************************************/
/* Name:      recv_done                                                      */
/*                                                                           */
/* Purpose:   handle completed receive verb                                  */
/*                                                                           */
/* Returns:   void                                                           */
/*                                                                           */
/* Params:    IN verb - completed read verb                                  */
/*                                                                           */
/* Operation: If the completed verb contains data, first parse it and then   */
/*            display it.                                                    */
/*            If message is a BIND or an UNBIND, set the LU-LU session state */
/*            accordingly                                                    */
/*            Also check for EB, CD and change session state flags           */
/**PROC-**********************************************************************/
void recv_done(verb)
LUA_VERB_RECORD *verb;
{
  int            count;         /* number of bytes of data left to display   */
  unsigned char *data;          /* pointer to next data to display           */
  BOOL           rsp_required = FALSE; /* is a resonse required              */

  sense = 0l;

  if (verb->common.lua_prim_rc == LUA_OK)
  {
    if (verb->common.lua_message_type == LUA_MESSAGE_TYPE_LU_DATA)
    {
      if (verb->common.lua_data_length > 0)
      {
        /*********************************************************************/
        /* We now have to parse the data.  For the moment we only look for   */
        /* RPQ requests.                                                     */
        /*********************************************************************/
        parse_data (read_data, (USHORT) (verb->common.lua_data_length));

        /*********************************************************************/
        /* Display outbound data.                                            */
        /*********************************************************************/
        convert_to_asc.len = (unsigned short) verb->common.lua_data_length;
        ACSSVC_C((LONG)((UCHAR FAR *)&convert_to_asc));
        data  = read_data;
        count = strlen(data);
        while (count > 0)
        {
          if (count <= 80)
          {
            printf("%s\n",data);
            count = 0;
          }
          else
          {
            printf("%80.80s\n",data);
            count -= 80;
            data  += 80;
          }
        }
      }

      /***********************************************************************/
      /* Set session state.                                                  */
      /***********************************************************************/
      if (verb->common.lua_rh.ebi)
      {
        printf("EB\n");
        send_state = BETB;
      }
      else if (verb->common.lua_rh.cdi)
      {
        printf("CD\n");
        send_state = SEND;
      }
      else
      {
        send_state = RECV;
      }
    }

    /*************************************************************************/
    /* Respond to any RQD request.                                           */
    /*************************************************************************/
    if ((verb->common.lua_message_type != LUA_MESSAGE_TYPE_RSP)  &&
        (verb->common.lua_rh.ri == 0))                  /* definite response */
    {
      rsp_required = TRUE;
    }
  }
  else         /* primary rc not OK - read failed - stop now */
  {
    printf("RECEIVE ERROR, primary rc = %4.4x, secondary rc = %8.8lx\n",
           verb->common.lua_prim_rc, verb->common.lua_sec_rc);
    closedown();
  }

  /***************************************************************************/
  /* Continue processing with either another recv or a send  positive rsp.   */
  /* (The callback from issuing response will then issue another recv) If    */
  /* we've already gone into closedown, stop here                            */
  /***************************************************************************/
  if (!terminating)
  {
    if (rsp_required)
    {
      read_state = RSP_QUEUED;
    }
    else
    {
      read_state = NO_READ;
    }
  }
}  /* recv_done                                                              */

/**PROC+**********************************************************************/
/* Name:      issue_rsp                                                      */
/*                                                                           */
/* Purpose:   issue a response for a request from the host                   */
/*                                                                           */
/* Returns:   void                                                           */
/*                                                                           */
/* Params:    IN sense - sense code for the response                         */
/*                                                                           */
/* Operation: Build a response and write it out.  Uses the recv verb control */
/*            block because it has the necessary information.                */
/*                                                                           */
/**PROC-**********************************************************************/
void issue_rsp(sense)
unsigned long sense;
{
  recv_verb.common.lua_opcode           = LUA_OPCODE_SLI_SEND;
  recv_verb.common.lua_max_length       = 0;
  recv_verb.common.lua_post_handle      = (ULONG) RECVPOST;
  recv_verb.common.lua_message_type     = LUA_MESSAGE_TYPE_RSP;
  recv_verb.common.lua_verb_length      = sizeof(struct LUA_COMMON) +
                                sizeof(recv_verb.specific.lua_sequence_number);

  recv_verb.common.lua_rh.rri           = 1;        /* response              */
  recv_verb.common.lua_flag1.lu_norm    = 0;
  recv_verb.common.lua_flag1.lu_exp     = 0;
  recv_verb.common.lua_flag1.sscp_norm  = 0;
  recv_verb.common.lua_flag1.sscp_exp   = 0;

  /***************************************************************************/
  /* If we have been given a sense code this must be a negative response     */
  /***************************************************************************/
  if (sense)
  {
    recv_verb.common.lua_data_length      = 4;
    memcpy(read_data, &sense, 4);
    recv_verb.common.lua_rh.ri            = 1;       /* negative rsp         */
  }
  else
  {
    recv_verb.common.lua_data_length      = 0;
    recv_verb.common.lua_rh.ri            = 0;       /* positive rsp         */
  }
  /***************************************************************************/
  /* Send the response back on the flow from which the request came          */
  /***************************************************************************/
  if (recv_verb.common.lua_flag2.lu_norm)
  {
    recv_verb.common.lua_flag1.lu_norm = 1;
  }
  else if (recv_verb.common.lua_flag2.lu_exp)
  {
    recv_verb.common.lua_flag1.lu_exp = 1;
  }
  else if (recv_verb.common.lua_flag2.sscp_norm)
  {
    recv_verb.common.lua_flag1.sscp_norm = 1;
  }
  else if (recv_verb.common.lua_flag2.sscp_exp)
  {
    recv_verb.common.lua_flag1.sscp_exp = 1;
  }
  /***************************************************************************/
  /* Send out the verb                                                       */
  /***************************************************************************/
  SLI((LUA_VERB_RECORD FAR *)&recv_verb);              /* write the response */
}  /* issue_rsp                                                              */

/**PROC+**********************************************************************/
/* Name:      rsp_done                                                       */
/*                                                                           */
/* Purpose:   handle completion of SLI_SEND, when SLI_SEND has written a     */
/*            response                                                       */
/*                                                                           */
/* Returns:   void                                                           */
/*                                                                           */
/* Params:    IN verb - completed verb                                       */
/*                                                                           */
/* Operation: If verb did not complete OK print a message and terminate      */
/*                                                                           */
/**PROC-**********************************************************************/
void rsp_done (verb)
LUA_VERB_RECORD *verb;
{
  if (verb->common.lua_prim_rc != LUA_OK)    /* failed to write the response */
  {                                          /* so stop here                 */
    printf("SEND for response failed, (%4.4x, %8.8lx)\n",
            verb->common.lua_prim_rc, verb->common.lua_sec_rc);
    closedown();
  }
}  /* rsp_done                                                               */

/**PROC+**********************************************************************/
/* Name:      closedown                                                      */
/*                                                                           */
/* Purpose:   terminate the application cleanly                              */
/*                                                                           */
/* Returns:   void                                                           */
/*                                                                           */
/* Params:    none                                                           */
/*                                                                           */
/* Operation: Issue an SLI_CLOSE and wait for it to complete.  Then exit     */
/*                                                                           */
/**PROC-**********************************************************************/
void closedown (void)
{
  if (!terminating)                /* check we haven't already got here from */
  {                                /* another section of the code            */
    terminating = TRUE;            /* then make sure we can't get here again */
    printf("Closedown\n");
    if (issue_verb((unsigned int) LUA_OPCODE_SLI_CLOSE))
    {
      exit(1);
    }
    exit(0);
  }
}  /* closedown                                                              */

/**PROC+**********************************************************************/
/* Name:      check_verb_complete                                            */
/*                                                                           */
/* Purpose:   check for asynchronous verb completion                         */
/*                                                                           */
/* Returns:   BOOL - TRUE => verb has completed                              */
/*                                                                           */
/* Params:    IN - verb - pointer to verb control block                      */
/*                                                                           */
/* Operation: OS specific                                                    */
/*                                                                           */
/**PROC-**********************************************************************/
BOOL check_verb_complete (verb)

LUA_VERB_RECORD * verb;
{
#ifdef DOS5
  /***************************************************************************/
  /* OS/2 case.  The verb is complete when the semaphore is cleared          */
  /***************************************************************************/
  USHORT RetCode;                     /* Holds return code from OS calls     */
  BOOL   verb_complete;               /* Has the verb completed              */

  verb_complete = FALSE;

  RetCode = DosSemRequest ((HSEM)verb->common.lua_post_handle, 0l);

  if (RetCode != NO_ERROR)
  {
    if (RetCode != ERROR_SEM_TIMEOUT)
    {
      /***********************************************************************/
      /* If we get here we've got problems                                   */
      /***********************************************************************/
      printf("Bad DosSemRequest return code, %d", RetCode);
    }
  }
  else
  {
    verb_complete = TRUE;
    set_semaphore (verb);
  }

  return(verb_complete);
#endif

#ifdef PDOS
  /***************************************************************************/
  /* DOS case.  The verb is complete when the lua_post_handle is cleared     */
  /***************************************************************************/
  BOOL   verb_complete;               /* Has the verb completed              */

  verb_complete = FALSE;

  if (!verb->common.lua_post_handle)
  {
    verb_complete = TRUE;
    set_semaphore (verb);
  }

  return(verb_complete);
#endif

#ifdef WIN32
  /***************************************************************************/
  /* Win32 case.  The verb is complete when the semaphore is cleared         */
  /***************************************************************************/
  DWORD  RetCode;

  RetCode = WaitForSingleObject((HANDLE) verb->common.lua_post_handle, 0l);

  if (RetCode == 0)
  {
    set_semaphore (verb);
  }
  return((RetCode == 0));
#endif
}  /* check_verb_complete                                                    */

/**PROC+**********************************************************************/
/* Name:      wait_verb_complete                                             */
/*                                                                           */
/* Purpose:   waits for async verb completion                                */
/*                                                                           */
/* Returns:   void                                                           */
/*                                                                           */
/* Params:    IN - verb - pointer to verb control block                      */
/*                                                                           */
/* Operation: Wait for the lua_post_handle to become clear                   */
/*                                                                           */
/**PROC-**********************************************************************/
void wait_verb_complete (verb)

LUA_VERB_RECORD * verb ;
{
#ifdef DOS5
  USHORT RetCode;                     /* Holds return code from OS calls     */

  RetCode = DosSemRequest ((HSEM)verb->common.lua_post_handle, -1l);
#endif

#ifdef WIN32
  DWORD RetCode;

  RetCode = WaitForSingleObject((HANDLE)verb->common.lua_post_handle,
                                                                   (DWORD)-1l);
#endif
  set_semaphore (verb);

}  /* wait_verb_complete                                                     */

/**PROC+**********************************************************************/
/* Name:      set_semaphore                                                  */
/*                                                                           */
/* Purpose:   sets verb completion flags                                     */
/*                                                                           */
/* Returns:   void                                                           */
/*                                                                           */
/* Params:    IN - verb - pointer to vcb                                     */
/*                                                                           */
/* Operation: set the lua_post_handle to the non-signaled state              */
/*                                                                           */
/**PROC-**********************************************************************/
void set_semaphore (verb)

LUA_VERB_RECORD * verb;
{
#ifdef DOS5
  USHORT RetCode;
  RetCode = DosSemSet ((HSEM)verb->common.lua_post_handle);
#endif

#ifdef WIN32
  /*W32***********************************************************************/
  /*W32* Win 32 case.  Clear the event                                       */
  /*W32***********************************************************************/
  ResetEvent ((HANDLE) verb->common.lua_post_handle);
#endif
}  /* set_semaphore                                                          */

/**PROC+**********************************************************************/
/* Name:      do_keyboard_stuff                                              */
/*                                                                           */
/* Purpose:   poll keyboard for data                                         */
/*                                                                           */
/* Returns:   BOOL - TRUE => we have data to send                            */
/*                                                                           */
/* Params:    none                                                           */
/*                                                                           */
/* Operation: poll keyboard                                                  */
/*            if some data                                                   */
/*              read it in                                                   */
/*              add it to data buffer                                        */
/*              if data is <enter>                                           */
/*                return TRUE                                                */
/*              endif                                                        */
/*            endif                                                          */
/*            return false                                                   */
/*                                                                           */
/* Also keep track of which session is being used, and if user wishes to     */
/* exit.                                                                     */
/*                                                                           */
/**PROC-**********************************************************************/
BOOL do_keyboard_stuff (void)

{
  BOOL   data_to_send;             /* do we have data to send?               */
  INT    input_char;               /* an input character                     */
#ifdef WIN32
  INPUT_RECORD input_record;       /* input record                           */
  DWORD        no_read;            /* number of input records read           */
#endif

  data_to_send = FALSE;

  /***************************************************************************/
  /* Check if keyboard has been hit                                          */
  /***************************************************************************/
#ifdef WIN32
  while ((!data_to_send) &&
         (!terminating)  &&
         (PeekConsoleInput (std_input, &input_record, 1, &no_read)) &&
         (no_read > 0))
#else
  while ((!data_to_send) && (!terminating) && (kbhit()))
#endif
  {
#ifndef WIN32
    input_char = getche ();
    if ((input_char == 0) || (input_char == 0xE0))
#else
    ReadConsoleInput (std_input, &input_record, 1, &no_read);

    if ((input_record.EventType == KEY_EVENT) &&
        (input_record.Event.KeyEvent.bKeyDown) &&
        ((input_record.Event.KeyEvent.dwControlKeyState == LEFT_ALT_PRESSED) ||
         ((input_record.Event.KeyEvent.wVirtualKeyCode >= VK_F1 ) &&
          (input_record.Event.KeyEvent.wVirtualKeyCode <= VK_F24))))
#endif
    {
      /***********************************************************************/
      /* Not an ascii char                                                   */
      /***********************************************************************/
#ifndef WIN32
      input_char = getch ();
#endif

      /***********************************************************************/
      /* Check for function keys and ALT-??                                  */
      /***********************************************************************/
#ifndef WIN32
      if (input_char == 60)               /* F2                              */
#else
      if (input_record.Event.KeyEvent.wVirtualKeyCode == VK_F2)
#endif
      {
        /*********************************************************************/
        /* Send a Clear.                                                     */
        /*********************************************************************/
        printf("sending clear\n");
        *write_data = 0x6d;
        write_len   = 1;
        data_to_send = TRUE;
      }
#ifndef WIN32
      else if ((input_char >= 120) && (input_char <= 129))
#else
      else if ((input_record.Event.KeyEvent.wVirtualKeyCode >= '0') &&
               (input_record.Event.KeyEvent.wVirtualKeyCode <= '9'))
#endif
      {
        /*********************************************************************/
        /* ALT-1 to ALT-0                                                    */
        /* 0xF1 is the AID code for PF1                                                              */
        /* Follow this with the cursor address                               */
        /*********************************************************************/
#ifndef WIN32
        *write_data = 0xF1 + input_char - 120;
#else
        *write_data = 0xF0 + input_record.Event.KeyEvent.wVirtualKeyCode - '0';
#endif
        *(write_data + 1) = 0x40;
        *(write_data + 2) = 0x40;
        write_len = 3;
        data_to_send = TRUE;

      }
    }
    else
#ifdef WIN32
        if ((input_record.EventType == KEY_EVENT ) &&
            (input_record.Event.KeyEvent.bKeyDown))
#endif
    {
      /***********************************************************************/
      /* An ASCII char                                                       */
      /***********************************************************************/
#ifdef WIN32
      input_char = input_record.Event.KeyEvent.uChar.AsciiChar;
#endif
      if (input_char == EXITKEY)
      {
        closedown();
      }
      else
      {
        /*********************************************************************/
        /* Key should be real data.  Add it to the string                    */
        /* For Windows NT echo the character to the screen                   */
        /*********************************************************************/

        if (input_char == 13)
        {
#ifdef WIN32
          WriteConsole (std_output, &input_char, 1, &no_read, NULL);
#endif
          /*******************************************************************/
          /* If user pressed return key then send the data.  Reset           */
          /* data_offset for next data.                                      */
          /*******************************************************************/
          write_data[data_offset++] = '\0';
          data_to_send = TRUE;
          data_offset  = 0;

          /*******************************************************************/
          /* Convert data to EBCDIC                                          */
          /*******************************************************************/
          convert_to_ebc.len = (unsigned short) (strlen(write_data) );
          if (convert_to_ebc.len > 0)
          {
            ACSSVC_C((LONG)((UCHAR FAR *)&convert_to_ebc));
          }
          write_len = convert_to_ebc.len;

          /*******************************************************************/
          /* Allow for ENTER AID if necessary                                */
          /*******************************************************************/
          write_data -= WRITE_EXTRA;
          write_len  += WRITE_EXTRA;
        }
        else if (input_char == 8)
        {
#ifdef WIN32
          WriteConsole (std_output, &input_char, 1, &no_read, NULL);
#endif
          /*******************************************************************/

/* Backspace.  Delete last char                                    */
          /*******************************************************************/
          if (data_offset > 0)
          {
            data_offset--;
          }
        }
        else if (input_char != 0)
        {
#ifdef WIN32
          WriteConsole (std_output, &input_char, 1, &no_read, NULL);
#endif
          write_data[data_offset++] = (UCHAR) input_char;
        }
      }
    }
  }  /* if (kbhit())                                                         */

  return(data_to_send);
}  /* do_keyboard_stuff                                                      */

/**PROC+**********************************************************************/
/* Name:      do_send                                                        */
/*                                                                           */
/* Purpose:   issue a write verb                                             */
/*                                                                           */
/* Returns:   BOOL - TRUE => data sent OK                                    */
/*                                                                           */
/* Params:    none                                                           */
/*                                                                           */
/* Operation: convert data to EBCDIC and send it on the right session        */
/*                                                                           */
/**PROC-**********************************************************************/
BOOL do_send (void)

{
  BOOL ok;

  ok = TRUE;

  /***************************************************************************/
  /* Set up the vcb                                                          */
  /***************************************************************************/
  memset(&other_verb, 0, sizeof(other_verb));
  other_verb.common.lua_verb             = LUA_VERB_SLI;
  other_verb.common.lua_verb_length      = sizeof(struct LUA_COMMON) +
                         sizeof(other_verb.specific.lua_sequence_number);
  other_verb.common.lua_opcode           = LUA_OPCODE_SLI_SEND;
  other_verb.common.lua_sid              = sid;
  other_verb.common.lua_message_type     = LUA_MESSAGE_TYPE_LU_DATA;
  other_verb.common.lua_data_length      = write_len;
  other_verb.common.lua_data_ptr         = (char far *) write_data;
  other_verb.common.lua_post_handle      = SENDPOST;
  other_verb.common.lua_rh.dr1i          = 1;

  /***************************************************************************/
  /* On the LU session we must add the <enter> key prefix.  All inbound      */
  /* requests flow RQE with the BBI and CDI flags set depending on the       */
  /* current session state.                                                  */
  /***************************************************************************/
  other_verb.common.lua_rh.ri          = 1;
  if (send_state == BETB)
  {
    /*************************************************************************/
    /* Between bracket, so open bracket and give direction.  Note that we    */
    /* can do this since we will always be contention winner.                */
    /*************************************************************************/
    other_verb.common.lua_rh.bbi         = 1;
    other_verb.common.lua_rh.cdi         = 1;
    send_state = RECV;
  }
  else if (send_state = SEND)
  {
    /*************************************************************************/
    /* In bracket and we have direction, so simply give direction.           */
    /*************************************************************************/
    other_verb.common.lua_rh.cdi         = 1;
    send_state = RECV;
  }
  else
  {
    /*************************************************************************/
    /* In bracket and we do not have direction, so do not send.              */
    /*************************************************************************/
    printf("Wait\n");
    ok = FALSE;
  }

  if (ok)
  {
    /*************************************************************************/
    /* Issue write verb                                                      */
    /*************************************************************************/
    SLI((LUA_VERB_RECORD FAR *) &other_verb);

    /*************************************************************************/
    /* Reset write_data to point to correct point in write_array             */
    /*************************************************************************/
    write_data = write_array + WRITE_EXTRA;
  }

  return(ok);
}  /* do_send ()                                                            */

/**PROC+**********************************************************************/
/* Name:      parse_data                                                     */
/*                                                                           */
/* Purpose:   parse data from the host                                       */
/*                                                                           */
/* Returns:   void                                                           */
/*                                                                           */
/* Params:    IN  data - pointer to data                                     */
/*                                                                           */
/* Operation: Looks through data for read partition query from host.  Could  */
/* be expanded to format data from host                                      */
/*                                                                           */
/**PROC-**********************************************************************/
void parse_data (data, length)

UCHAR FAR * data;
USHORT      length;
{
  USHORT  field_length;

  switch (*(data++))
  {
    case 0xF3:                       /* write structured field               */
      /***********************************************************************/
      /* Next byte is the WCC - ignore                                       */
      /***********************************************************************/
      data++;
      length -= 2;

      while (length > 0)
      {
        /*********************************************************************/
        /* We're just looking for a Read Partion query                       */
        /*********************************************************************/
        field_length = (((USHORT) *data) << 8) || ((USHORT) *(data + 1));
        if ((*(data+2) == 0x01) &&   /* Read partition                       */
            (*(data+3) == 0xFF) &&   /* Query                                */
            (*(data+4) == 0x02))     /* not a list                           */
        {
          /*******************************************************************/
          /* Build an RPQ and flag it to be sent                             */
          /*******************************************************************/
          rpq_state = RPQ_QUEUED;
        }
        data   += field_length;
        length -= field_length;
      }

      break;

    default:
      break;

  }
}  /* parse_data                                                             */

/**PROC+**********************************************************************/
/* Name:      issue_rpq                                                      */
/*                                                                           */
/* Purpose:   issue a response to a Read partition query                     */
/*                                                                           */
/* Returns:   BOOL - TRUE => sent OK                                         */
/*                                                                           */
/* Params:    none                                                           */
/*                                                                           */
/* Operation: Builds and sends an RPQ reesponse                              */
/*                                                                           */
/**PROC-**********************************************************************/
BOOL issue_rpq (void)

{
  BOOL issue_rpq;

  issue_rpq = TRUE;
  /***************************************************************************/
  /* Set up the vcb                                                          */
  /***************************************************************************/
  memset(&rpq_verb, 0, sizeof(rpq_verb));
  rpq_verb.common.lua_verb             = LUA_VERB_SLI;
  rpq_verb.common.lua_verb_length      = sizeof(struct LUA_COMMON) +
                         sizeof(rpq_verb.specific.lua_sequence_number);
  rpq_verb.common.lua_opcode           = LUA_OPCODE_SLI_SEND;
  rpq_verb.common.lua_sid              = sid;
  rpq_verb.common.lua_message_type     = LUA_MESSAGE_TYPE_LU_DATA;
  rpq_verb.common.lua_data_length      = RPQ_LENGTH;
  rpq_verb.common.lua_data_ptr         = (char far *) rpq_data;
  rpq_verb.common.lua_post_handle      = RPQPOST;
  rpq_verb.common.lua_rh.dr1i          = 1;

  /***************************************************************************/
  /* On the LU session we must add the <enter> key prefix.  All inbound      */
  /* requests flow RQE with the BBI and CDI flags set depending on the       */
  /* current session state.                                                  */
  /***************************************************************************/
  rpq_verb.common.lua_flag1.lu_norm  = 0;
  rpq_verb.common.lua_rh.ri          = 1;
  if (send_state == BETB)
  {
    /*************************************************************************/
    /* Between bracket, so open bracket and give direction.  Note that we    */
    /* can do this since we will always be contention winner.                */
    /*************************************************************************/
    rpq_verb.common.lua_rh.bbi         = 1;
    rpq_verb.common.lua_rh.cdi         = 1;
    send_state = RECV;
  }
  else if (send_state = SEND)
  {
    /*************************************************************************/
    /* In bracket and we have direction, so simply give direction.           */
    /*************************************************************************/
    rpq_verb.common.lua_rh.cdi         = 1;
    send_state = RECV;
  }
  else
  {
    /*************************************************************************/
    /* In bracket and we do not have direction, so do not send.              */
    /*************************************************************************/
    printf("Wait\n");
    issue_rpq = FALSE;
  }

  if (issue_rpq)
  {
    SLI((LUA_VERB_RECORD FAR *) &rpq_verb);
  }

  return(issue_rpq);
}  /* issue_rpq                                                              */


WSLI3270.C

/**MOD+***********************************************************************/
/* Module:  wsli3270.c                                                       */
/*                                                                           */
/* Purpose: Simple 3270 emulator                                             */
/*                                                                           */
/* (C) COPYRIGHT DATA CONNECTION LIMITED 1991                                */
/*                                                                           */
/**MOD-***********************************************************************/

/*****************************************************************************/
/* SLI Sample Program                                                        */
/*                                                                           */
/* (C) Copyright 1990 - 1993 Data Connection Ltd.                            */
/*                                                                           */
/* This program is a crude 3270 emulator.  It uses the SLI API to access     */
/* the LU session.  Outbound data from the host is displayed on the screen   */
/* unformatted.  If the outbound data is an RQD request an automatic         */
/* positive response is sent.  Inbound data can be entered at the keyboard   */
/*                                                                           */
/*                                                                           */
/* The program is invoked with two parameters - the name of the LUA LU       */
/* to use and a logon string.  This is must match an LUA LU in the           */
/* configuration file.  This LU should be configured for a 327? on the host. */
/*                                                                           */
/* The program can only access hosts that use simple unformatted logon       */
/* requests                                                                  */
/*                                                                           */
/* The CSV CONVERT function is used to convert data between ASCII and        */
/* EBCDIC.  This uses the type G conversion table, so the environment        */
/* variable COMTBLG must be set to point to a suitable file.                 */
/*                                                                           */
/* Keys that work are:                                                       */
/*                                                                           */
/*     Alt-1 to Alt-9 are PF1 to PF9                                         */
/*     backspace                                                             */
/*     Letters, numbers and symbols                                          */
/*                                                                           */
/*****************************************************************************/

/*****************************************************************************/
/* SYSTEM INCLUDES                                                           */
/*****************************************************************************/
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <winlua.h>
#include <wincsv.h>

typedef unsigned char  UCHAR;
typedef unsigned short USHORT;
typedef unsigned long  ULONG;

/*****************************************************************************/
/* Program constants                                                         */
/*****************************************************************************/
#define DATASIZE 4096                  /* Max size of RU                     */

#define BETB     1                     /* Between brackets                   */
#define SEND     2                     /* In bracket and can send            */
#define RECV     3                     /* In bracket, but cannot send        */

#define APPNAME                "WSLI3270"
#define SLI_WINDOW_MESSAGE     "WinSLI"

#define DISPLAY_BUFFER_SIZE    3200    /* size of circular display buffer    */

/*****************************************************************************/
/* State variables                                                           */
/*                                                                           */
/* We can only have one read or write verb outstanding per flow.  These      */
/* state variables keep track of these verbs and what we need to do with     */
/* them.                                                                     */
/*****************************************************************************/

UINT  write_state;
#define NO_WRITE              1
#define WRITE_OUTSTANDING     2        /* waiting for write to complete      */
#define WRITE_QUEUED          3        /* have data to write                 */

UINT  read_state;
#define NO_READ               1
#define READ_OUTSTANDING      2        /* read verb outstanding              */
#define RSP_QUEUED            3        /* have a response to write           */
#define RSP_OUTSTANDING       4        /* waiting for a response to complete */

UINT  rpq_state;
#define NO_RPQ                1
#define RPQ_OUTSTANDING       2        /* waiting for an RPQ to complete     */
#define RPQ_QUEUED            3        /* have RPQ to send                   */

/***************************************************************************/
/* Global variables                                                        */
/*                                                                         */
/***************************************************************************/
LUA_VERB_RECORD read_verb;             /* SLI recv verb                    */
LUA_VERB_RECORD close_verb;             /* SLI recv verb                    */
LUA_VERB_RECORD other_verb;            /* SLI open or send verb            */
LUA_VERB_RECORD rpq_verb;              /* SLI_SEND for RPQs                */

UCHAR rpq_data[] = {

  0x88,
  0x00, 0x0C, 0x81, 0x80, 0x80, 0x81, 0x85, 0x86, 0x87, 0x88, 0x95, 0xA6,

  0x00, 0x17, 0x81, 0x81, 0x21, 0x00, 0x00, 0x50, 0x00, 0x18, 0x00, 0x00,
  0x0A, 0x02, 0xE5, 0x00, 0x02, 0x00, 0x6F, 0x09, 0x10, 0x07, 0x80,

  0x00, 0x14, 0x81, 0x85, 0x02, 0x00, 0x09, 0x0E, 0x00, 0x00, 0x00, 0x00,
  0x07, 0x00, 0x00, 0x00, 0x02, 0xB9, 0x01, 0x1D,

  0x00, 0x16, 0x81, 0x86, 0x00, 0x08, 0x00, 0xF4, 0xF1, 0x00, 0xF2, 0x00,
  0xF3, 0x00, 0xF4, 0x00, 0xF5, 0x00, 0xF6, 0x00, 0xF7, 0x00,

  0x00, 0x0D, 0x81, 0x87, 0x04, 0x00, 0xF0, 0xF1, 0x00, 0xF2, 0x00, 0xF4,
  0x00,

  0x00, 0x07, 0x81, 0x88, 0x00, 0x01, 0x02,

  0x00, 0x0C, 0x81, 0x95, 0x00, 0x00, 0x7F, 0xFF, 0x7F, 0xFF, 0x01, 0x01,

  0x00, 0x11, 0x81, 0xA6, 0x00, 0x00, 0x0B, 0x01, 0x00, 0x00, 0x50, 0x00,
  0x18, 0x00, 0x50, 0x00, 0x18};

#define RPQ_LENGTH (sizeof(rpq_data))

UCHAR display_buffer[DISPLAY_BUFFER_SIZE] = {0};
UINT  buffer_end   = 0;             /* end of data in display buffer         */
BOOL  wrapped      = FALSE;         /* has buffer wrapped?                   */

/*****************************************************************************/
/* Global Variables                                                          */
/*****************************************************************************/
HANDLE hInst = 0;                   /* current instance                      */
UINT   verb_complete = 0;           /* verb completion message               */
HANDLE hWnd = 0;                    /* Handle to program window              */

BOOL   init_done = FALSE;           /* SLI_OPEN completed                    */

unsigned char read_data[DATASIZE];     /* Outbound RU                       */
#define WRITE_EXTRA 6
unsigned char write_array[DATASIZE + WRITE_EXTRA] =
                                          {0x7d, 0x40, 0x40, 0x11, 0x40, 0x40};
                                       /* Inbound RU                         */
unsigned char *write_data = write_array + WRITE_EXTRA;
                                       /* Pointer to inbound RU              */
UINT data_offset = 0;                  /* offset into read_data              */
UINT write_len   = 0;                  /* length of write data               */

struct convert convert_to_asc;         /* Outbound convert verb              */
struct convert convert_to_ebc;         /* Inbound convert verb               */

BOOL terminating = FALSE;              /* are we already in closedown() ?    */

unsigned long sense = 0l;              /* sense code                         */

unsigned char lu_name[9] = "        "; /* LU name                            */
unsigned long sid;                     /* SLI session ID                     */
int send_state = BETB;                 /* LU session send state              */

#define LOGON_MAX  25                  /* maximum logon string length        */

UCHAR logon_data[LOGON_MAX+1] = {0};
UCHAR logon_length   = 0;

UCHAR  string[64]={0};

void            csv_init            (void);
void            issue_init          (void);
void            issue_close        (void);
void            issue_read          (void);
void            other_done          (LUA_VERB_RECORD FAR *verb);
void            read_done           (LUA_VERB_RECORD FAR *verb);
void            issue_rsp           (unsigned long  sense);
void            rsp_done            (LUA_VERB_RECORD FAR *verb);
void            closedown           (void);
BOOL            do_write            (void);
void            wait_verb_complete  (LUA_VERB_RECORD FAR *verb);
void            set_semaphore       (LUA_VERB_RECORD FAR *verb);
BOOL            do_keyboard_stuff   (UINT, UCHAR);
BOOL            check_verb_complete (LUA_VERB_RECORD FAR *verb);
void            parse_data          (UCHAR FAR *, USHORT);
BOOL            issue_rpq           (void);
void            verb_complete_proc  (LUA_VERB_RECORD FAR * verb);
void          add_to_display_buffer (UCHAR FAR *);

/*****************************************************************************/
/* Windows function prototypes.                                              */
/*****************************************************************************/
int PASCAL WinMain(HINSTANCE, HINSTANCE, LPSTR, int);
long FAR PASCAL WndProc(HWND, UINT, UINT, LONG);

/***PROC+*********************************************************************/
/* Name:     WinMain                                                         */
/*                                                                           */
/* Purpose:  Main Windows function                                           */
/*                                                                           */
/* Params:   IN current instance handle                                      */
/*           IN previous instance handle                                     */
/*           IN pointer to command line                                      */
/*           IN show window type                                             */
/*                                                                           */
/* Returns:  int                                                             */
/*                                                                           */
/**PROC-**********************************************************************/

int PASCAL WinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow)

HINSTANCE hInstance;                         /* current instance             */
HINSTANCE hPrevInstance;                     /* previous instance            */
LPSTR     lpCmdLine;                         /* command line                 */
int       nCmdShow;                          /* show-window type (open/icon) */
{
  /***************************************************************************/
  /* LOCAL VARIABLES                                                         */
  /***************************************************************************/
  MSG   msg;                                 /* message                      */
  int   xscreen;
  int   yscreen;
  int   delta;
  WNDCLASS class;
  LUADATA  LUAData;
  UINT  cmdlength;                           /* length of command line       */
  UINT  lu_name_length;                      /* length of LU name            */

  if (!hPrevInstance)
  {
    class.style         = CS_HREDRAW | CS_VREDRAW;
    class.lpfnWndProc   = (WNDPROC)WndProc;
    class.cbClsExtra    = 0;
    class.cbWndExtra    = 0;
    class.hInstance     = hInstance;
    class.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
    class.hCursor       = LoadCursor(NULL, IDC_ARROW);
    class.hbrBackground = GetStockObject(WHITE_BRUSH);
    class.lpszMenuName  = NULL;
    class.lpszClassName = APPNAME;

    if (!RegisterClass(&class))
    {
      return (0);
    }
  }

  /***************************************************************************/
  /* save current instance handle                                            */
  /***************************************************************************/
  hInst = hInstance;

  /***************************************************************************/
  /* Set initial states                                                      */
  /***************************************************************************/
  write_state = NO_WRITE;
  read_state  = NO_READ;
  rpq_state   = NO_RPQ;

  /***************************************************************************/
  /* get details of display                                                  */
  /***************************************************************************/
  xscreen = GetSystemMetrics(SM_CXSCREEN);
  yscreen = GetSystemMetrics(SM_CYSCREEN);

  /***************************************************************************/
  /* invent somewhere to put window                                          */
  /***************************************************************************/
  delta = xscreen / 100;

  /***************************************************************************/
  /* create display window                                                   */
  /***************************************************************************/
  hWnd = CreateWindow(APPNAME,                    /* window class            */
        "SLI3270",                                /* window name             */
        WS_OVERLAPPEDWINDOW,                      /* window style            */
        (xscreen/2) + delta,                      /* x position              */
        yscreen / 8,                              /* y position              */
        (xscreen / 2) - (delta * 2),              /* width                   */
        3 * yscreen / 4,                          /* height                  */
        NULL,                                     /* parent handle           */
        NULL,                                     /* menu or child ID        */
        hInstance,                                /* instance                */
        NULL);                                    /* additional info         */

  if (!hWnd)
  {
    return (0);
  }

  ShowWindow(hWnd, nCmdShow);                     /* Shows the window        */
  UpdateWindow(hWnd);                             /* Sends WM_PAINT message  */

  /***************************************************************************/
  /* Register message used for async completion                              */
  /***************************************************************************/
  verb_complete = RegisterWindowMessage(SLI_WINDOW_MESSAGE);

  if (verb_complete == 0)
  {
    return (0);
  }

  WinSLIStartup (0x0001, &LUAData);

  /***************************************************************************/
  /* Issue SLI_OPEN to get the ball rolling.  Setup up the LU name           */
  /***************************************************************************/
  cmdlength = _fstrlen(lpCmdLine);
  for (lu_name_length = 0; lu_name_length < cmdlength; lu_name_length++)
  {
    if (lpCmdLine[lu_name_length] == ' ')
    {
      break;
    }
    else
    {
      lu_name[lu_name_length] = lpCmdLine[lu_name_length];
    }
  }
  if (lu_name_length == cmdlength)
  {
    /*************************************************************************/
    /* Need  a logon string                                                  */
    /*************************************************************************/
    closedown();
  }
  else
  {
    while (lpCmdLine[lu_name_length] == ' ')
    {
      lu_name_length++;
    }
    logon_length = _fstrlen(lpCmdLine + lu_name_length);
    _fstrncpy ((UCHAR FAR *)logon_data,
               lpCmdLine + lu_name_length,
               logon_length);
    convert_to_ebc.opcode       = SV_CONVERT;
    convert_to_ebc.direction    = SV_ASCII_TO_EBCDIC;
    convert_to_ebc.char_set     = SV_G;
    convert_to_ebc.source       = logon_data;
    convert_to_ebc.target       = logon_data;
    convert_to_ebc.len          = (unsigned short) logon_length;
    ACSSVC_C((LONG)((UCHAR FAR *)&convert_to_ebc));

    /*************************************************************************/
    /* Set up CSV convert verbs                                              */
    /*************************************************************************/
    csv_init();

    issue_init ();
  }

  while(GetMessage(&msg,NULL,0,0))
  {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }

  return msg.wParam;         /* save exit parameter for return               */
}

/***PROC+*********************************************************************/
/* Name:     WndProc                                                         */
/*                                                                           */
/* Purpose:  Windows call-back routine                                       */
/*                                                                           */
/* Params:   IN window handle                                                */
/*           IN message type                                                 */
/*           IN additional information 1                                     */
/*           IN additional information 2                                     */
/*                                                                           */
/* Returns:  long                                                            */
/*                                                                           */
/* Operation:set up window characteristics and register it                   */
/*           return success or failure                                       */
/*                                                                           */
/**PROC-**********************************************************************/

long FAR PASCAL WndProc(hWnd, message, wParam, lParam)
HWND hWnd;                                /* window handle                   */
UINT message;                             /* type of message                 */
UINT wParam;                              /* additional information          */
LONG lParam;                              /* additional information          */
{
  /***************************************************************************/
  /* LOCAL VARIABLES                                                         */
  /***************************************************************************/
  HDC          hDC;              /* handle to our device context             */
  TEXTMETRIC   tm;               /* holds info on the current font           */
  PAINTSTRUCT  ps;               /*  paint structure                         */
  RECT         rc;               /* holds info on the size oof our window    */
  ULONG        width;            /* width of window in chars                 */
  ULONG        height;           /* height of window in chars                */
  UINT         buffer_offset;    /* offset withing display buffer            */
  UINT         buffer_length;    /* length of data in buffer                 */
  UINT         out_length;       /* lngth of text to output                  */
  UINT         out_length2;      /* and again                                */
  static int   xChar, yChar;     /* width & height of the average char       */
  int          position;         /* number of lines down the screen          */
  BOOL         whole_data;       /* do we have data to send?                 */

  if ((message == verb_complete) &&
      (!terminating))
  {
    /*************************************************************************/
    /* Message is SLI verb completed.  We are not terminating, so process    */
    /* the message.                                                          */
    /*************************************************************************/
    verb_complete_proc ((LUA_VERB_RECORD FAR *) lParam);
    return(0l);
  }

  switch (message)
  {
    case WM_CREATE:
      /***********************************************************************/
      /* Window creation message.  Get some infomation about our font and    */
      /* save it for later                                                   */
      /***********************************************************************/
      hDC= GetDC(hWnd);
      GetTextMetrics (hDC, &tm);
      xChar = (int)tm.tmAveCharWidth;
      yChar = (int)(tm.tmHeight + tm.tmExternalLeading);
      ReleaseDC(hWnd, hDC);
      return 0l;
      break;

    case WM_CHAR:
    case WM_SYSCHAR:
      /***********************************************************************/
      /* Character or Alt+char hit                                           */
      /***********************************************************************/
      if ((init_done)                 &&
          (!terminating)              &&
          (write_state == NO_WRITE)   &&
          (rpq_state   == NO_RPQ)     &&
          (read_state  != RSP_QUEUED) &&
          (read_state  != RSP_OUTSTANDING))
      {
        /*********************************************************************/
        /* Someone pressed a key and we are in a state to deal with it       */
        /*********************************************************************/
        whole_data = do_keyboard_stuff (message, (UCHAR)wParam);
        if (whole_data)
        {
          /*******************************************************************/
          /* We have data to send                                            */
          /* Attempt to write the data out                                   */
          /*******************************************************************/
          if (do_write())
          {
            write_state = WRITE_OUTSTANDING;
          }
          else
          {
            write_state = WRITE_QUEUED;
          }
        }
        return(0l);
      }
      break;

    case WM_PAINT:
      BeginPaint (hWnd, &ps);
      /***********************************************************************/
      /* Determine the size of the Window in characters                      */
      /***********************************************************************/
      GetClientRect (hWnd, &rc);
      height = rc.bottom / yChar;
      width  = rc.right  / xChar;
      /***********************************************************************/
      /* subtract 2 from height for the status line                          */
      /***********************************************************************/
      height -= 2;
      /***********************************************************************/
      /* Work out how early in the buffer we can start                       */
      /***********************************************************************/
      if (!wrapped)
      {
        /*********************************************************************/
        /* buffer is not wrapped                                             */
        /*********************************************************************/
        buffer_length = buffer_end;
        buffer_offset = (buffer_length > (width * height)) ?
                         buffer_end - (width * height) :
                         0;
      }
      else
      {
        /*********************************************************************/
        /* buffer is wrapped                                                 */
        /*********************************************************************/
        buffer_length = DISPLAY_BUFFER_SIZE;
        if (buffer_length > width * height)
        {
          /*******************************************************************/
          /* buffer is too big.  Work out where best to start in the buffer/ */
          /*******************************************************************/
          if (buffer_end > width * height)
          {
            buffer_offset = buffer_end - (width * height);
          }
          else
          {
            buffer_offset = DISPLAY_BUFFER_SIZE +
                            buffer_end -
                            (width * height);
          }
        }
        else
        {
          /*******************************************************************/
          /* We can fit everything in                                        */
          /*******************************************************************/
          buffer_offset = buffer_end;
        }
      }
      /***********************************************************************/
      /* We've worked out where to start printing from, so start printing    */
      /***********************************************************************/
      position = 0;
      out_length = width;
      if (buffer_offset > buffer_end)
      {
        while (buffer_offset < DISPLAY_BUFFER_SIZE)
        {
          if (DISPLAY_BUFFER_SIZE - buffer_offset < width)
          {
            out_length = DISPLAY_BUFFER_SIZE - buffer_offset;
          }
          else
          {
            out_length = width;
          }
          TextOut(ps.hdc,
                  0,
                  yChar * position,
                  display_buffer + buffer_offset,
                  out_length);
          buffer_offset += out_length;
          position++;
        }
        buffer_offset = 0;
      }
      /***********************************************************************/
      /* If last line printed was a partial line, print the rest of the line */
      /***********************************************************************/
      if (out_length < width)
      {
        out_length2 = out_length;
        if (buffer_end < (width - out_length))
        {
          out_length = buffer_end;
        }
        else
        {
          out_length = width - out_length;
        }
        TextOut(ps.hdc,
                xChar * out_length2,
                yChar * (position - 1),
                display_buffer,
                out_length);
        buffer_offset += out_length;

      }
      /***********************************************************************/
      /* Now print to the end of the buffer                                  */
      /***********************************************************************/
      while (buffer_offset < buffer_end)
      {
        if (buffer_end - buffer_offset < width)
        {
          out_length = buffer_end - buffer_offset;
        }
        else
        {
          out_length = width;
        }
        TextOut(ps.hdc,
                0,
                yChar * position,
                display_buffer + buffer_offset,
                out_length);
        buffer_offset += out_length;
        position++;
      }
      /***********************************************************************/
      /* Now fill in the status line                                         */
      /***********************************************************************/
      out_length  = wsprintf (string,
                              "%s",
                   (UCHAR FAR *)((send_state == BETB) ? "BETB" : "INB"));
      TextOut(ps.hdc,
              0,
              (UINT) (yChar * (height + 2)),
              string,
              out_length);
      /***********************************************************************/
      /* Now write the input line.  Only in NO_WRITE state, o.w.  data is in */
      /* EBCDIC                                                              */
      /***********************************************************************/
      if (write_state == NO_WRITE)
      {
        TextOut(ps.hdc,
                0,
                (UINT) (yChar * (height+1)),
                write_data,
                data_offset);
      }
      EndPaint(hWnd, &ps);
      break;

    case WM_CLOSE:
      if (!terminating)
      {
        /*********************************************************************/
        /* Tell the SLI we are going                                         */
        /*********************************************************************/
        terminating = TRUE;
        issue_close();
        WinSLICleanup();
      }
      break;

    case WM_DESTROY:

PostQuitMessage(0);
      return 0l;
      break;

    default:
      break;
  }
  return (DefWindowProc(hWnd, message, wParam, lParam));
}

/**PROC+**********************************************************************/
/* Name:      verb_complete_proc                                             */
/*                                                                           */
/* Purpose:   deal with verb completion                                      */
/*                                                                           */
/* Returns:   void                                                           */
/*                                                                           */
/* Params:    IN verb - pointer to completed verb                            */
/*                                                                           */
/* Operation: Determine which verb has completed and perform the appropriate */
/* action                                                                    */
/**PROC-**********************************************************************/
VOID verb_complete_proc (verb)

LUA_VERB_RECORD FAR * verb;
{

  switch (verb->common.lua_opcode)
  {

    case LUA_OPCODE_SLI_CLOSE:
      break;

    case LUA_OPCODE_SLI_OPEN:
      /***********************************************************************/
      /* Received ACTLU.  Save the SID for use on other verbs, flag init     */
      /* done so that we can write data, and issue first read                */
      /***********************************************************************/
      sid = other_verb.common.lua_sid;
      init_done = TRUE;
      issue_read();
      break;

    case LUA_OPCODE_SLI_RECEIVE:
      /***********************************************************************/
      /* Read completed.  Call read dne processor to display data and        */
      /* determine if a response is required.                                */
      /* If no response is required issue a new read                         */
      /* if a response is required issue the response if we can              */
      /***********************************************************************/
      read_done (verb);
      if (terminating == TRUE)
        break;
      if (read_state == NO_READ)
      {
        /*********************************************************************/
        /* No work to do, issue a new read verb                              */
        /*********************************************************************/
        read_state = READ_OUTSTANDING;
        issue_read();
      }
      else if ((read_state  == RSP_QUEUED) &&
               (write_state != WRITE_OUTSTANDING) &&
               (rpq_state   != RPQ_OUTSTANDING))
      {
        /*********************************************************************/
        /* We need to send a response to the RU we have just received.  The  */
        /* write flow is free, so send it                                    */
        /*********************************************************************/
        read_state = RSP_OUTSTANDING;
        issue_rsp (sense);
      }

      if ((rpq_state   == RPQ_QUEUED) &&
          (write_state != WRITE_OUTSTANDING) &&
          (read_state  != RSP_OUTSTANDING))
      {
        /*********************************************************************/
        /* We need to send a read partition query response and we can, so do */
        /* it.                                                               */
        /*********************************************************************/
        if (issue_rpq())
        {
          rpq_state = RPQ_OUTSTANDING;
        }
      }
      break;

    case LUA_OPCODE_SLI_SEND:
      /***********************************************************************/
      /* Determine what kind of SLI_SEND completed                          */
      /* either                                                              */
      /* -  writing user data                                                */
      /* -  writing a RPQ response                                           */
      /* -  writing a RSP to host data                                       */
      /***********************************************************************/
      if (write_state == WRITE_OUTSTANDING)
      {
        write_state = NO_WRITE;
        write_data[0] = '\0';
      }
      else if (rpq_state == RPQ_OUTSTANDING)
      {
        rpq_state = NO_RPQ;
      }
      else if (read_state == RSP_OUTSTANDING)
      {
        read_state = NO_READ;
        issue_read();
      }
      /***********************************************************************/
      /* Perform the next action depending on what work their is to do.      */
      /***********************************************************************/
      if (read_state == RSP_QUEUED)
      {
        /*********************************************************************/
        /* We need to issue a RSP to an RU we have received.  Do it          */
        /*********************************************************************/
        read_state = RSP_OUTSTANDING;
        issue_rsp (sense);
      }
      else if (rpq_state == RPQ_QUEUED)
      {
        /*********************************************************************/
        /* We need to issue a response to a read partion query.  Send it if  */
        /* the bracket state allows.                                         */
        /*********************************************************************/
        if (issue_rpq())
        {
          rpq_state = RPQ_OUTSTANDING;
        }
      }
      else if (write_state == WRITE_QUEUED)
      {
        /*********************************************************************/
        /* We have user data to send to the Host.  Send it if the bracket    */
        /* state allows                                                      */
        /*********************************************************************/
        if (do_write())
        {
          write_state = WRITE_OUTSTANDING;
        }
      }
      break;

    default:
      break;
  }
}  /* verb_complete_proc                                                     */

/**PROC+**********************************************************************/
/* Name:      csv_init                                                       */
/*                                                                           */
/* Purpose:   set up CSV convert verbs                                       */
/*                                                                           */
/* Returns:   void                                                           */
/*                                                                           */
/* Params:    none                                                           */
/*                                                                           */
/* Operation: sets uup two CSV convert verbs, one in each direction.  Only   */
/* the length needs to be set when the verb is called                        */
/**PROC-**********************************************************************/
void csv_init (void)
{
  convert_to_asc.opcode       = SV_CONVERT;
  convert_to_asc.direction    = SV_EBCDIC_TO_ASCII;
  convert_to_asc.char_set     = SV_G;
  convert_to_asc.source       = read_data;
  convert_to_asc.target       = read_data;

  convert_to_ebc.opcode       = SV_CONVERT;
  convert_to_ebc.direction    = SV_ASCII_TO_EBCDIC;
  convert_to_ebc.char_set     = SV_G;
  convert_to_ebc.source       = write_data;
  convert_to_ebc.target       = write_data;
}  /* csv_init                                                               */

/**PROC+**********************************************************************/
/* Name:      issue_init                                                     */
/*                                                                           */
/* Purpose:   build a verb and send it                                       */
/*                                                                           */
/* Returns:   void                                                           */
/*                                                                           */
/* Params:    None                                                           */
/*                                                                           */
/* Operation: Build an OPEN verb and issue it.                               */
/**PROC-**********************************************************************/
void issue_init(void)
{
  memset(&other_verb, 0, sizeof(other_verb));
  other_verb.common.lua_verb             = LUA_VERB_SLI;
  other_verb.common.lua_opcode           = LUA_OPCODE_SLI_OPEN;
  other_verb.common.lua_verb_length    = sizeof(struct LUA_COMMON)+
                          sizeof(other_verb.specific.open.lua_init_type) +
                          sizeof(other_verb.specific.open.lua_resv65) +
                          sizeof(other_verb.specific.open.lua_wait) +
                          sizeof(other_verb.specific.open.lua_ending_delim);
  memcpy(other_verb.common.lua_luname, lu_name, 8);
  other_verb.specific.open.lua_init_type = LUA_INIT_TYPE_SEC_LOG;
  other_verb.common.lua_data_length      = logon_length;
  other_verb.common.lua_data_ptr         = logon_data;
  other_verb.specific.open.lua_open_extension[0].lua_routine_type =
                                                          LUA_ROUTINE_TYPE_END;

  WinSLI(hWnd, (LUA_VERB_RECORD FAR *) &other_verb);

  /***************************************************************************/
  /* The sid is valid immediately.  Save it now.                             */
  /***************************************************************************/
  sid = other_verb.common.lua_sid;

  if (!other_verb.common.lua_flag2.async)
  {
    /*************************************************************************/
    /* Verb completed immediately.  Post our own completion message          */
    /*************************************************************************/
    PostMessage (hWnd, verb_complete, 0, (LPARAM) (VOID FAR *)&other_verb);
  }

}  /* issue_init                                                             */

/**PROC+**********************************************************************/
/* Name:      issue_read                                                     */
/*                                                                           */
/* Purpose:   build a read verb and send it                                  */
/*                                                                           */
/* Returns:   void                                                           */
/*                                                                           */
/* Params:    none                                                           */
/*                                                                           */
/* Operation: Build a read verb for all flows and issue it                   */
/**PROC-**********************************************************************/
void issue_read (void)
{
  memset(&read_verb, 0, sizeof(read_verb));
  memset(read_data, 0, DATASIZE);
  read_verb.common.lua_verb             = LUA_VERB_SLI;
  read_verb.common.lua_verb_length      = sizeof(struct LUA_COMMON);
  read_verb.common.lua_opcode           = LUA_OPCODE_SLI_RECEIVE;
  read_verb.common.lua_sid              = sid;
  read_verb.common.lua_max_length       = DATASIZE;
  read_verb.common.lua_data_ptr         = (char FAR *) read_data;
  read_verb.common.lua_flag1.lu_norm    = 1;
  read_verb.common.lua_flag1.lu_exp     = 1;
  read_verb.common.lua_flag1.sscp_norm  = 1;
  read_verb.common.lua_flag1.sscp_exp   = 1;
  WinSLI(hWnd, (LUA_VERB_RECORD FAR *)&(read_verb));
  if (!read_verb.common.lua_flag2.async)
  {
    /*************************************************************************/
    /* Verb completed immediately.  Post our own completion message          */
    /*************************************************************************/
    PostMessage (hWnd, verb_complete, 0, (LPARAM) (VOID FAR *)&read_verb);
  }

}  /* issue_read                                                             */

void issue_close (void)
{
  memset(&close_verb, 0, sizeof(close_verb));
  memset(read_data, 0, DATASIZE);
  close_verb.common.lua_verb             = LUA_VERB_SLI;
  close_verb.common.lua_verb_length      = sizeof(struct LUA_COMMON);
  close_verb.common.lua_opcode           = LUA_OPCODE_SLI_CLOSE;
  close_verb.common.lua_sid              = sid;
  WinSLI(hWnd, (LUA_VERB_RECORD FAR *)&(close_verb));
  if (!close_verb.common.lua_flag2.async)
  {
    /*************************************************************************/
    /* Verb completed immediately.  Post our own completion message          */
    /*************************************************************************/
    PostMessage (hWnd, verb_complete, 0, (LPARAM) (VOID FAR *)&close_verb);
  }

}  /* issue_read                                                             */

/**PROC+**********************************************************************/
/* Name:      read_done                                                      */
/*                                                                           */
/* Purpose:   handle completed read verb                                     */
/*                                                                           */
/* Returns:   void                                                           */
/*                                                                           */
/* Params:    IN verb - completed read verb                                  */
/*                                                                           */
/* Operation: If the completed verb contains data, first parse it and then   */
/*            display it.                                                    */
/*            If message is a BIND or an UNBIND, set the LU-LU session state */
/*            accordingly                                                    */
/*            Also check for EB, CD and change session state flags           */
/**PROC-**********************************************************************/
void read_done(verb)
LUA_VERB_RECORD FAR * verb;
{
  BOOL           rsp_required = FALSE; /* is a resonse required              */

  sense = 0l;

  if (verb->common.lua_prim_rc == LUA_OK)
  {
    if (verb->common.lua_message_type == LUA_MESSAGE_TYPE_LU_DATA)
    {
      if (verb->common.lua_data_length > 0)
      {
        /*********************************************************************/
        /* We now have to parse the data.  For the moment we only look for   */
        /* RPQ requests.                                                     */
        /*********************************************************************/
        parse_data (read_data, (USHORT) (verb->common.lua_data_length));

        /*********************************************************************/
        /* Display outbound data.                                            */
        /*********************************************************************/
        convert_to_asc.len = (unsigned short) verb->common.lua_data_length;
        ACSSVC_C((LONG)((UCHAR FAR *)&convert_to_asc));
        add_to_display_buffer (read_data);
      }

      /***********************************************************************/
      /* Set session state.                                                  */
      /***********************************************************************/
      if (verb->common.lua_rh.ebi)
      {
        send_state = BETB;
      }
      else if (verb->common.lua_rh.cdi)
      {
        send_state = SEND;
      }
      else
      {
        send_state = RECV;
      }
      InvalidateRect (hWnd, NULL, TRUE);
    }

    /*************************************************************************/
    /* Respond to any RQD request.                                           */
    /*************************************************************************/
    if ((verb->common.lua_message_type != LUA_MESSAGE_TYPE_RSP)  &&
        (verb->common.lua_rh.ri == 0))                  /* definite response */
    {
      rsp_required = TRUE;
    }
  }
  else if (verb->common.lua_prim_rc != LUA_STATUS)
  {
    closedown();
  }

  /***************************************************************************/
  /* Continue processing with either another read or a write positive rsp.   */
  /* (The callback from issuing response will then issue another read) If    */
  /* we've already gone into closedown, stop here                            */
  /***************************************************************************/
  if (!terminating)
  {
    if (rsp_required)
    {
      read_state = RSP_QUEUED;
    }
    else
    {
      read_state = NO_READ;
    }
  }
}  /* read_done                                                              */

/**PROC+**********************************************************************/
/* Name:      issue_rsp                                                      */
/*                                                                           */
/* Purpose:   issue a response for a request from the host                   */
/*                                                                           */
/* Returns:   void                                                           */
/*                                                                           */
/* Params:    IN sense - sense code for the response                         */
/*                                                                           */
/* Operation: Build a response and write it out.  Uses the read verb control */
/*            block because it has the necessary information.                */
/*                                                                           */
/**PROC-**********************************************************************/
void issue_rsp(sense)
unsigned long sense;
{
  read_verb.common.lua_opcode           = LUA_OPCODE_SLI_SEND;
  read_verb.common.lua_max_length       = 0;
  read_verb.common.lua_verb_length      = sizeof(struct LUA_COMMON) +
                                sizeof(read_verb.specific.lua_sequence_number);
  read_verb.common.lua_rh.rri           = 1;         /* response             */
  read_verb.common.lua_post_handle      = NULL;
  read_verb.common.lua_flag1.lu_norm    = 0;
  read_verb.common.lua_flag1.lu_exp     = 0;
  read_verb.common.lua_flag1.sscp_norm  = 0;
  read_verb.common.lua_flag1.sscp_exp   = 0;
  read_verb.common.lua_message_type     = LUA_MESSAGE_TYPE_RSP;
  memset(read_verb.common.lua_resv56, '\0', 7);

#if 0
  read_verb.common.lua_rh.eci           = 0;
  read_verb.common.lua_rh.bci           = 0;
  read_verb.common.lua_rh.dr1i          = 0;
  read_verb.common.lua_rh.dr2i          = 0;
  read_verb.common.lua_rh.qri           = 0;
  read_verb.common.lua_rh.pi            = 0;
  read_verb.common.lua_rh.bbi           = 0;
  read_verb.common.lua_rh.ebi           = 0;
  read_verb.common.lua_rh.cdi           = 0;
  read_verb.common.lua_rh.csi           = 0;
  read_verb.common.lua_rh.edi           = 0;
  read_verb.common.lua_rh.pdi           = 0;
#endif

  /***************************************************************************/
  /* If we have been given a sense code this must be a negative response     */
  /***************************************************************************/
  if (sense)
  {
    read_verb.common.lua_data_length      = 4;
    memcpy(read_data, &sense, 4);
    read_verb.common.lua_rh.ri            = 1;       /* negative rsp         */
  }
  else
  {
    read_verb.common.lua_data_ptr         = NULL;
    read_verb.common.lua_data_length      = 0;
    read_verb.common.lua_rh.ri            = 0;       /* positive rsp         */
  }
  /***************************************************************************/
  /* Send the response back on the flow from which the request came          */
  /***************************************************************************/
  if (read_verb.common.lua_flag2.lu_norm)
  {
    read_verb.common.lua_flag1.lu_norm = 1;
    read_verb.common.lua_flag2.lu_norm = 0;
  }
  else if (read_verb.common.lua_flag2.lu_exp)
  {
    read_verb.common.lua_flag1.lu_exp = 1;
    read_verb.common.lua_flag2.lu_exp = 0;
  }
  else if (read_verb.common.lua_flag2.sscp_norm)
  {
    read_verb.common.lua_flag1.sscp_norm = 1;
    read_verb.common.lua_flag2.sscp_norm = 0;
  }
  else if (read_verb.common.lua_flag2.sscp_exp)
  {
    read_verb.common.lua_flag1.sscp_exp = 1;
    read_verb.common.lua_flag2.sscp_exp = 0;
  }
  read_verb.common.lua_flag2.async = 0;
  /***************************************************************************/
  /* Send out the verb                                                       */
  /***************************************************************************/
  WinSLI(hWnd, (LUA_VERB_RECORD FAR *)&read_verb);     /* write the response */
  if (!read_verb.common.lua_flag2.async)
  {
    /*************************************************************************/
    /* Verb completed immediately.  Post our own completion message          */
    /*************************************************************************/
    PostMessage (hWnd, verb_complete, 0, (LPARAM) (VOID FAR *)&read_verb);
  }
}  /* issue_rsp                                                              */

/**PROC+**********************************************************************/
/* Name:      rsp_done                                                       */
/*                                                                           */
/* Purpose:   handle completion of SLI_SEND, when SLI_SEND has written a     */
/*            response                                                       */
/*                                                                           */
/* Returns:   void                                                           */
/*                                                                           */
/* Params:    IN verb - completed verb                                       */
/*                                                                           */
/* Operation: If verb did not complete OK terminate                          */
/*                                                                           */
/**PROC-**********************************************************************/
void rsp_done (verb)
LUA_VERB_RECORD FAR * verb;
{
  if (verb->common.lua_prim_rc != LUA_OK)   /* failed to write the response */
  {                                         /* so stop here                 */
    closedown();
  }
}  /* rsp_done                                                               */

/**PROC+**********************************************************************/
/* Name:      closedown                                                      */
/*                                                                           */
/* Purpose:   terminate the application cleanly                              */
/*                                                                           */
/* Returns:   void                                                           */
/*                                                                           */
/* Params:    none                                                           */
/*                                                                           */
/* Operation: Issue a WinSLICleanup() to tell SLI the we are about to go,    */
/* and then post a close message so that we do.                              */
/*                                                                           */
/**PROC-**********************************************************************/
void closedown (void)
{
  if (!terminating)                /* check we haven't already got here from */
  {                                /* another section of the code            */
    terminating = TRUE;            /* then make sure we can't get here again */
    issue_close();
    WinSLICleanup();
    PostMessage (hWnd, WM_CLOSE, 0, 0l);
  }
}  /* closedown                                                              */

/**PROC+**********************************************************************/
/* Name:      do_keyboard_stuff                                              */
/*                                                                           */
/* Purpose:   poll keyboard for data                                         */
/*                                                                           */
/* Returns:   BOOL - TRUE => we have data to send                            */
/*                                                                           */
/* Params:    none                                                           */
/*                                                                           */
/* Operation: Look for Alt-1 to Alt-9 and ascii chars.                       */
/*                                                                           */
/**PROC-**********************************************************************/
BOOL do_keyboard_stuff (type, key)

UINT  type;
UCHAR key;

{
  BOOL   data_to_send;             /* do we have data to send?               */

  data_to_send = FALSE;

  if (type == WM_SYSCHAR)
  {
    /*************************************************************************/
    /* Alt key pressed                                                       */
    /*************************************************************************/
    if ((key >= '1') &&
        (key <= '9'))
    {
      /***********************************************************************/
      /* ALT-1 to ALT-9 0xF1 is the AID code for PF1.  Follow this with the  */
      /* cursor address                                                      */
      /***********************************************************************/
      *write_data = 0xF1 + key - '1';
      *(write_data + 1) = 0x40;
      *(write_data + 2) = 0x40;
      write_len = 3;
      data_to_send = TRUE;
    }
  }
  else
  {
    /*************************************************************************/
    /* Ordinary ASCII char                                                   */
    /* Key should be real data.  Add it to the string                        */
    /*************************************************************************/
    if (key == 13)
    {
      /***********************************************************************/
      /* If user pressed return key then send the data.  Reset data_offset   */
      /* for next data.                                                      */
      /***********************************************************************/
      write_data[data_offset++] = '\0';
      data_to_send = TRUE;
      data_offset  = 0;

      /***********************************************************************/
      /* Convert data to EBCDIC                                              */
      /***********************************************************************/
      convert_to_ebc.len = (unsigned short) (strlen(write_data) );
      if (convert_to_ebc.len > 0)
      {
        ACSSVC_C((LONG)((UCHAR FAR *)&convert_to_ebc));
      }
      write_len = convert_to_ebc.len;

      /***********************************************************************/
      /* Allow for ENTER AID if necessary                                    */
      /***********************************************************************/
      write_data -= WRITE_EXTRA;
      write_len  += WRITE_EXTRA;
    }
    else if (key == 8)
    {
      /***********************************************************************/
      /* Backspace.  Delete last char                                        */
      /***********************************************************************/
      if (data_offset > 0)
      {
        data_offset--;
      }
    }
    else if (key != 0)
    {
      /***********************************************************************/
      /* ASCII char.  Add it to the data buffer                              */
      /***********************************************************************/
      write_data[data_offset++] = (UCHAR) key;

    }
    /*************************************************************************/
    /* Invalidate our window so that the new data is displayed               */
    /*************************************************************************/
    InvalidateRect (hWnd, NULL, TRUE);
  }

  return(data_to_send);
}  /* do_keyboard_stuff                                                      */

/**PROC+**********************************************************************/
/* Name:      do_write                                                       */
/*                                                                           */

/* Purpose:   issue a write verb                                             */
/*                                                                           */
/* Returns:   BOOL - TRUE => data sent OK                                    */
/*                                                                           */
/* Params:    none                                                           */
/*                                                                           */
/* Operation: convert data to EBCDIC and send it on the right session        */
/*                                                                           */
/**PROC-**********************************************************************/
BOOL do_write (void)

{
  BOOL ok;

  ok = TRUE;

  /***************************************************************************/
  /* Set up the vcb                                                          */
  /***************************************************************************/
  memset(&other_verb, 0, sizeof(other_verb));
  other_verb.common.lua_verb             = LUA_VERB_SLI;
  other_verb.common.lua_verb_length      = sizeof(struct LUA_COMMON) +
                         sizeof(other_verb.specific.lua_sequence_number);
  other_verb.common.lua_opcode           = LUA_OPCODE_SLI_SEND;
  other_verb.common.lua_message_type     = LUA_MESSAGE_TYPE_LU_DATA;
  other_verb.common.lua_sid              = sid;
  other_verb.common.lua_data_length      = write_len;
  other_verb.common.lua_data_ptr         = (char far *) write_data;
  other_verb.common.lua_rh.dr1i          = 1;

  /***************************************************************************/
  /* On the LU session we must add the <enter> key prefix.  All inbound      */
  /* requests flow RQE with the BBI and CDI flags set depending on the       */
  /* current session state.                                                  */
  /***************************************************************************/
  other_verb.common.lua_rh.ri          = 1;
  if (send_state == BETB)
  {
    /*************************************************************************/
    /* Between bracket, so open bracket and give direction.  Note that we    */
    /* can do this since we will always be contention winner.                */
    /*************************************************************************/
    other_verb.common.lua_rh.bbi         = 1;
    other_verb.common.lua_rh.cdi         = 1;
    send_state = RECV;
  }
  else if (send_state = SEND)
  {
    /*************************************************************************/
    /* In bracket and we have direction, so simply give direction.           */
    /*************************************************************************/
    other_verb.common.lua_rh.cdi         = 1;
    send_state = RECV;
  }
  else
  {
    /*************************************************************************/
    /* In bracket and we do not have direction, so do not send.              */
    /*************************************************************************/
    ok = FALSE;
  }

  if (ok)
  {
    /*************************************************************************/
    /* Issue write verb                                                      */
    /*************************************************************************/
    WinSLI(hWnd, (LUA_VERB_RECORD FAR *) &other_verb);
    if (!other_verb.common.lua_flag2.async)
    {
      /***********************************************************************/
      /* Verb completed immediately.  Post our own completion message        */
      /***********************************************************************/
      PostMessage (hWnd, verb_complete, 0, (LPARAM) (VOID FAR *)&other_verb);
    }

    /*************************************************************************/
    /* Reset write_data to point to correct point in write_array             */
    /*************************************************************************/
    write_data = write_array + WRITE_EXTRA;
  }

  return(ok);
}  /* do_write ()                                                            */

/**PROC+**********************************************************************/
/* Name:      parse_data                                                     */
/*                                                                           */
/* Purpose:   parse data from the host                                       */
/*                                                                           */
/* Returns:   void                                                           */
/*                                                                           */
/* Params:    IN  data - pointer to data                                     */
/*                                                                           */
/* Operation: Looks through data for read partition query from host.  Could  */
/* be expanded to format data from host                                      */
/*                                                                           */
/**PROC-**********************************************************************/
void parse_data (data, length)

UCHAR FAR * data;
USHORT      length;
{
  USHORT  field_length;

  switch (*(data++))
  {
    case 0xF3:                       /* write structured field               */
      /***********************************************************************/
      /* Next byte is the WCC - ignore                                       */
      /***********************************************************************/
      data++;
      length -= 2;

      while (length > 0)
      {
        /*********************************************************************/
        /* We're just looking for a Read Partion query                       */
        /*********************************************************************/
        field_length = (((USHORT) *data) << 8) || ((USHORT) *(data + 1));
        if ((*(data+2) == 0x01) &&   /* Read partition                       */
            (*(data+3) == 0xFF) &&   /* Query                                */
            (*(data+4) == 0x02))     /* not a list                           */
        {
          /*******************************************************************/
          /* Build an RPQ and flag it to be sent                             */
          /*******************************************************************/
          rpq_state = RPQ_QUEUED;
        }
        data   += field_length;
        length -= field_length;
      }

      break;

    default:
      break;

  }
}  /* parse_data                                                             */

/**PROC+**********************************************************************/
/* Name:      issue_rpq                                                      */
/*                                                                           */
/* Purpose:   issue a response to a Read partition query                     */
/*                                                                           */
/* Returns:   BOOL - TRUE => sent OK                                         */
/*                                                                           */
/* Params:    none                                                           */
/*                                                                           */
/* Operation: Builds and sends an RPQ reesponse                              */
/*                                                                           */
/**PROC-**********************************************************************/
BOOL issue_rpq (void)

{
  BOOL issue_rpq;

  issue_rpq = TRUE;
  /***************************************************************************/
  /* Set up the vcb                                                          */
  /***************************************************************************/
  memset(&rpq_verb, 0, sizeof(rpq_verb));
  rpq_verb.common.lua_verb             = LUA_VERB_SLI;
  rpq_verb.common.lua_verb_length      = sizeof(struct LUA_COMMON) +
                         sizeof(rpq_verb.specific.lua_sequence_number);
  rpq_verb.common.lua_opcode           = LUA_OPCODE_SLI_SEND;
  rpq_verb.common.lua_sid              = sid;
  rpq_verb.common.lua_message_type     = LUA_MESSAGE_TYPE_LU_DATA;
  rpq_verb.common.lua_data_length      = RPQ_LENGTH;
  rpq_verb.common.lua_data_ptr         = (char far *) rpq_data;
  rpq_verb.common.lua_rh.dr1i          = 1;

  /***************************************************************************/
  /* All inbound requests flow RQD with the BBI and CDI flags set depending  */
  /* on the current session state.                                           */
  /***************************************************************************/
  rpq_verb.common.lua_rh.ri          = 1;
  if (send_state == BETB)
  {
    /*************************************************************************/
    /* Between bracket, so open bracket and give direction.  Note that we    */
    /* can do this since we will always be contention winner.                */
    /*************************************************************************/
    rpq_verb.common.lua_rh.bbi         = 1;
    rpq_verb.common.lua_rh.cdi         = 1;
    send_state = RECV;
  }
  else if (send_state = SEND)
  {
    /*************************************************************************/
    /* In bracket and we have direction, so simply give direction.           */
    /*************************************************************************/
    rpq_verb.common.lua_rh.cdi         = 1;
    send_state = RECV;
  }
  else
  {
    /*************************************************************************/
    /* In bracket and we do not have direction, so do not send.              */
    /*************************************************************************/
    issue_rpq = FALSE;
  }

  if (issue_rpq)
  {
    WinSLI(hWnd, (LUA_VERB_RECORD FAR *) &rpq_verb);
    if (!rpq_verb.common.lua_flag2.async)
    {
      /***********************************************************************/
      /* Verb completed immediately.  Post our own completion message        */
      /***********************************************************************/
      PostMessage (hWnd, verb_complete, 0, (LPARAM) (VOID FAR *)&rpq_verb);
    }
  }

  return(issue_rpq);
}  /* issue_rpq                                                              */

/**PROC+**********************************************************************/
/* Name:      add_to_display_buffer                                          */
/*                                                                           */
/* Purpose:   add data to the display buffer                                 */
/*                                                                           */
/* Returns:   void                                                           */
/*                                                                           */
/* Params:    IN data - data to be displayed                                 */
/*                                                                           */
/* Operation: Add the zero terminated data string to the circular display    */
/* buffer.  Cope with wrapping                                               */
/*                                                                           */
/**PROC-**********************************************************************/
void add_to_display_buffer (data)

UCHAR FAR * data;
{
  UINT data_length;         /* length of new data to display                 */
  BOOL need_to_wrap;        /* do we need to wrap to the start of the buffer */
  UINT copy_length;         /* length of data to copy                        */

  data_length = _fstrlen (data);

  /***************************************************************************/
  /* Work out whether the data will go over the end of the dispaly buffer.   */
  /* If it will, wrap it back over the start of the buffer.                  */
  /***************************************************************************/
  need_to_wrap = (buffer_end + data_length > DISPLAY_BUFFER_SIZE);

  if (need_to_wrap)
  {
    copy_length = DISPLAY_BUFFER_SIZE - buffer_end;
    wrapped = TRUE;
  }
  else
  {
    copy_length = data_length;
  }

  /***************************************************************************/
  /* Copy as much data as will fit into the buffer                           */
  /***************************************************************************/
  _fmemcpy ((UCHAR FAR *) (display_buffer + buffer_end),
            data,
            copy_length);

  /***************************************************************************/
  /* If there is more data to copy, copy it to the start of the buffer       */
  /* Set up the new end point.                                               */
  /***************************************************************************/
  if (need_to_wrap)
  {
    _fmemcpy ((UCHAR FAR *)display_buffer,
              data + copy_length,
              data_length - copy_length);

    buffer_end   = data_length - copy_length;
  }
  else
  {
    buffer_end   += data_length;
  }

  /***************************************************************************/
  /* Cause the window to be re-painted                                       */
  /***************************************************************************/
  InvalidateRect (hWnd, NULL, TRUE);

}  /* add_to_display_buffer                                                  */


README.TXT

PRODUCT NAME: Microsoft (R) SNA Server
FEATURE NAME: Microsoft(R) OLE DB Provider for AS/400 and VSAM
VERSION NUMBER: 4.0

This document contains answers to the most commonly asked
questions about using and building the sample applications
included with the Microsoft OLE DB Provider for AS/400 and
VSAM. Please take a few moments to read this document.

***********************
*IMPORTANT INFORMATION*
***********************

The \SAMPLES directory includes subdirectories
containing the following utilities and information:

The file named SAMPLE.HCD contains examples of the host
column definitions required to access host VSAM files. This file
is documented in the Microsoft SNA Server SDK, in the section
titled Microsoft OLE DB Provider for AS/400 and VSAM.

OLE DB Sample
The OLEDB directory contains a modified Microsoft OLE DB Query
Sample application. This application allows you to connect
to the Microsoft OLE DB Provider for AS/400 and VSAM, open a table window,
type the host file name, return a rowset and browse the contents.
You can navigate the table, using seek and set range on
indexed files.

To open a rowset, type the following command in the comamnd window:
exec open library/file
You can use "/" or "." depending on whether you are accessing a host
mainframe or AS/400 file.

This sample is written to access bookmarkable file types only. You will
not be able to access mainframe SAM, ESDS, PDS/PDSE because these data
set types do not support bookmarks.

You may not be able to build this sample application. The SNA Server
CD-ROM does not include a copy of the OLE DB SDK version 1.5. If you
do not have the OLE DB SDK version 1.5 installed, then you need to
include or copy the following files, from the SDK\SAMPLES\OLEDB
directory on the SNA Server CD-ROM, into your project.

SDK\SAMPLES\OLEDB\INCLUDE
adoid.h
adoint.h
msdadc.h
msdaguid.h
msdasql.h
oledb.h
oledberr.h
transact.h

ADO Visual Basic Sample
The ADO_VB directory contains a Microsoft OLE DB Provider for
AS/400 and VSAM ADO Visual Basic version 5.0 sample application.
This application allows you to open a connection through the
Microsoft OLE DB Provider for AS/400 and VSAM, open a recordset
and browse the records of a host file. This sample is built
with ActiveX Data Objects version 1.5. Before building with
Visual Basic sample, be sure to include the Microsoft ActiveX
Data Objects 1.5 Library in your project. You can do this from
inside the VB IDE by selecting References on the Project menu.

The sample application can be built with Microsoft Visual Basic
version 5.0.

ADO VBScript Sample
The ADO_VBS directory contains a Microsoft OLE DB Provider for
AS/400 and VSAM ADO VBScript sample web client application.
This web client application allows you to open a connection
through the Microsoft OLE DB Provider for AS/400 and VSAM,
open a recordset and browse the records of a host file.

Active Server Pages Sample
The ASP directory contains two Microsoft OLE DB Provider for
AS/400 and VSAM Active Server Pages sample web server applications.
These applications require Microsoft Internet Information Server
version 3.0 or higher with Active Server Pages installed.

1. REPORT.ASP will return and display the first ten (10) records in a host file
to a client browser in a table view.

2. OLEDBDDM.ASP and GLOBAL.ASA combine to display columns of a host file
in a a form view.

You will need to edit the host table names in order to use these samples
in your environment.

Off-Line Demo Link Service
The following are the steps necessary to test drive the OLE DB Provider for
AS/400 and VSAM feature of SNA Server 4.0 using the demo host link services.
To demonstrate the OLE DB Provider for AS/400 and VSAM with Visual Basic, all you
need is the sample VB program that is installed with the provider.

1. From the Microsoft SNA Server Manager, add a new Link Service, choosing the
"SNA OLE DB Demo" script file.
2. From the Microsoft SNA Server Manager, select the AS/400 Wizard from the Tools menu.
Configure a new connection (using the above Link Service) as follows:
- Network Name: APPN
- Control Point Name: DDMDEMO
- SDLC settings: (leave as default)
- Connection Name: DDMDEMO
- Remote APPC LU Alias: DDMDEMO
- Local APPC LU Alias: LOCAL
3. From the Microsoft SNA Server Manager, start the SNA Server Service. The
newly-created "DDMDEMO" connection should go to "On Demand" status.
4. From the SNA OLE DB Management console, create a new Data Source as follows:
- Data Source Type: AS/400
- Remote LU Alias: DDMDEMO
- Local LU Alias: LOCAL
- APPC Mode: QPCSUPP
- RDB Name: DDMDEMO
- Default Library: PUBS
- Host CCSID: 37
- Host Column Description: (leave as default "unselected")
5. From the Run line of the Start menu, type "ado_vb" and click OK to load the ADO VB consumer sample application.
- Click the Connect button to use the ADO Connection.Open method. This will establish an APPC session between the OLE DB Provider for AS/400 and VSAM computer and the demo AS/400.
- Enter "USERID" and "password", then click OK to log onto the demo host.
- Click the Open button to use the ADO Recordset.Open method.
- In the open dialog, enter "pubs/authors", then click OK  to continue. The recordset is composed of the records in the "authors" AS/400 physical data file in the AS/400 library "pubs". The sample AS/400 data is based on that in the authors table from the Microsoft SQL Server pubs sample database.
- Click the Next button to view the next record in the recordset using the ADO Recordset.MoveNext method.
- Click the Close button to close the host file using the ADO Recordset.Close method.
- Click the Disconnect button to end the APPC session using the ADO Connection.Close method.
- Exit the ADO consumer sample application.

Last updated on 11/21/97. (C) Microsoft Corporation


FORMVIEW.FRM

VERSION 5.00
Begin VB.Form frmFormView
   BorderStyle     =   1  'Fixed Single
   Caption         =   "Microsoft ADO Consumer Sample"
   ClientHeight    =   6345
   ClientLeft      =   3375
   ClientTop       =   2235
   ClientWidth     =   8280
   LinkTopic       =   "Form1"
   MaxButton       =   0   'False
   ScaleHeight     =   6345
   ScaleWidth      =   8280
   WhatsThisHelp   =   -1  'True
   Begin VB.TextBox tbData
      Enabled         =   0   'False
      Height          =   315
      Index           =   0
      Left            =   1440
      Locked          =   -1  'True
      TabIndex        =   1
      TabStop         =   0   'False
      Top             =   720
      Width           =   5175
   End
   Begin VB.TextBox tbData
      Enabled         =   0   'False
      Height          =   315
      Index           =   8
      Left            =   1440
      Locked          =   -1  'True
      TabIndex        =   2
      TabStop         =   0   'False
      Top             =   4560
      Width           =   5175
   End
   Begin VB.TextBox tbData
      Enabled         =   0   'False
      Height          =   315
      Index           =   7
      Left            =   1440
      Locked          =   -1  'True
      TabIndex        =   5
      TabStop         =   0   'False
      Top             =   4080
      Width           =   5175
   End
   Begin VB.TextBox tbData
      Enabled         =   0   'False
      Height          =   315
      Index           =   6
      Left            =   1440
      Locked          =   -1  'True
      TabIndex        =   6
      TabStop         =   0   'False
      Top             =   3600
      Width           =   5175
   End
   Begin VB.TextBox tbData
      Enabled         =   0   'False
      Height          =   315
      Index           =   5
      Left            =   1440
      Locked          =   -1  'True
      TabIndex        =   7
      TabStop         =   0   'False
      Top             =   3090
      Width           =   5175
   End
   Begin VB.TextBox tbData
      Enabled         =   0   'False
      Height          =   315
      Index           =   4
      Left            =   1440
      Locked          =   -1  'True
      TabIndex        =   8
      TabStop         =   0   'False
      Top             =   2640
      Width           =   5175
   End
   Begin VB.TextBox tbData
      Enabled         =   0   'False
      Height          =   315
      Index           =   3
      Left            =   1440
      Locked          =   -1  'True
      TabIndex        =   9
      TabStop         =   0   'False
      Top             =   2160
      Width           =   5175
   End
   Begin VB.TextBox tbData
      Enabled         =   0   'False
      Height          =   315
      Index           =   2
      Left            =   1440
      Locked          =   -1  'True
      TabIndex        =   10
      TabStop         =   0   'False
      Top             =   1680
      Width           =   5175
   End
   Begin VB.TextBox tbData
      Enabled         =   0   'False
      Height          =   315
      Index           =   1
      Left            =   1440
      Locked          =   -1  'True
      TabIndex        =   11
      TabStop         =   0   'False
      Top             =   1200
      Width           =   5175
   End
   Begin VB.CommandButton cmdDisconnect
      Caption         =   "Disconnect"
      Height          =   375
      Left            =   6960
      TabIndex        =   4
      Top             =   720
      Width           =   1095
   End
   Begin VB.CommandButton cmdCommand
      Caption         =   "Execute Cmd"
      Height          =   375
      Left            =   6960
      TabIndex        =   3
      Top             =   2760
      Width           =   1095
   End
   Begin VB.TextBox txtSource
      Enabled         =   0   'False
      Height          =   285
      Left            =   3240
      Locked          =   -1  'True
      TabIndex        =   12
      TabStop         =   0   'False
      Top             =   225
      Width           =   3375
   End
   Begin VB.CommandButton cmdConnect
      Caption         =   "Connect"
      Height          =   375
      Left            =   6960
      TabIndex        =   0
      Top             =   240
      Width           =   1095
   End
   Begin VB.CommandButton cmdFilter
      Caption         =   "Filte&r"
      Height          =   375
      Left            =   3000
      TabIndex        =   13
      Top             =   5760
      Width           =   1095
   End
   Begin VB.CommandButton cmdFind
      Caption         =   "F&ind"
      Height          =   375
      Left            =   4200
      TabIndex        =   14
      Top             =   5760
      Width           =   1095
   End
   Begin VB.CommandButton cmdSort
      Caption         =   "&Sort"
      Height          =   375
      Left            =   1800
      TabIndex        =   15
      Top             =   5760
      Width           =   1095
   End
   Begin VB.CommandButton cmdAddBookmark
      Caption         =   "Add"
      Height          =   375
      Left            =   240
      TabIndex        =   17
      Top             =   5760
      Width           =   1095
   End
   Begin VB.ListBox lstBookmark
      Enabled         =   0   'False
      Height          =   450
      ItemData        =   "FormView.frx":0000
      Left            =   240
      List            =   "FormView.frx":0002
      TabIndex        =   42
      Top             =   5280
      Width           =   1095
   End
   Begin VB.CommandButton cmdDelete
      Caption         =   "Dele&te"
      Height          =   375
      Left            =   6960
      TabIndex        =   16
      Top             =   4800
      Width           =   1095
   End
   Begin VB.CommandButton cmdUpdate
      Caption         =   "&Update"
      Height          =   375
      Left            =   6960
      TabIndex        =   18
      Top             =   5760
      Width           =   1095
   End
   Begin VB.CommandButton cmdCancel
      Caption         =   "Canc&el"
      Height          =   375
      Left            =   6960
      TabIndex        =   19
      Top             =   5280
      Width           =   1095
   End
   Begin VB.CommandButton cmdAdd
      Caption         =   "&Add"
      Height          =   375
      Left            =   6960
      TabIndex        =   20
      Top             =   4320
      Width           =   1095
   End
   Begin VB.CommandButton cmdEdit
      Caption         =   "&Edit"
      Height          =   375
      Left            =   6960
      TabIndex        =   21
      Top             =   3840
      Width           =   1095
   End
   Begin VB.Frame Frame2
      Caption         =   "Editing"
      Height          =   2655
      Left            =   6840
      TabIndex        =   33
      Top             =   3600
      Width           =   1335
   End
   Begin VB.CommandButton cmdMoveLast
      Caption         =   "&Last >|"
      Height          =   375
      Left            =   5400
      TabIndex        =   34
      Top             =   5280
      Width           =   1095
   End
   Begin VB.CommandButton cmdMoveNext
      Caption         =   "&Next >"
      Height          =   375
      Left            =   4200
      TabIndex        =   35
      Top             =   5280
      Width           =   1095
   End
   Begin VB.CommandButton cmdMovePrevious
      Caption         =   "< &Previous"
      Height          =   375
      Left            =   3000
      TabIndex        =   36
      Top             =   5280
      Width           =   1095
   End
   Begin VB.CommandButton cmdMoveFirst
      Caption         =   "|< &First"
      Height          =   375
      Left            =   1800
      TabIndex        =   37
      Top             =   5280
      Width           =   1095
   End
   Begin VB.Frame Frame1
      Caption         =   "Navigation"
      Height          =   1215
      Left            =   1680
      TabIndex        =   38
      Top             =   5040
      Width           =   4935
   End
   Begin VB.CommandButton cmdCloseRS
      Caption         =   "Close RS"
      Height          =   375
      Left            =   6960
      TabIndex        =   39
      Top             =   1920
      Width           =   1095
   End
   Begin VB.CommandButton cmdExit
      Caption         =   "Exit"
      Height          =   375
      Left            =   6960
      TabIndex        =   40
      TabStop         =   0   'False
      Top             =   3120
      Visible         =   0   'False
      Width           =   1095
   End
   Begin VB.CommandButton cmdOpenRS
      Caption         =   "Open RS"
      Height          =   375
      Left            =   6960
      TabIndex        =   41
      Top             =   1440
      Width           =   1095
   End
   Begin VB.Frame frmBookmark
      Caption         =   "Bookmark"
      Height          =   1215
      Left            =   120
      TabIndex        =   32
      Top             =   5040
      Width           =   1335
   End
   Begin VB.Label lblSource
      Caption         =   "Source"
      BeginProperty Font
         Name            =   "MS Sans Serif"
         Size            =   8.25
         Charset         =   0
         Weight          =   700
         Underline       =   0   'False
         Italic          =   0   'False
         Strikethrough   =   0   'False
      EndProperty
      Height          =   255
      Left            =   2520
      TabIndex        =   44
      Top             =   240
      Width           =   735
   End
   Begin VB.Label lblColumnName
      Caption         =   "Column 1"
      Height          =   255
      Index           =   0
      Left            =   120
      TabIndex        =   43
      Top             =   720
      Width           =   1215
   End
   Begin VB.Label lblColumnData
      Caption         =   "Data"
      BeginProperty Font
         Name            =   "MS Sans Serif"
         Size            =   8.25
         Charset         =   0
         Weight          =   700
         Underline       =   0   'False
         Italic          =   0   'False
         Strikethrough   =   0   'False
      EndProperty
      Height          =   255
      Left            =   1440
      TabIndex        =   31
      Top             =   240
      Width           =   615
   End
   Begin VB.Label lblColumnName
      Caption         =   "Column"
      BeginProperty Font
         Name            =   "MS Sans Serif"
         Size            =   8.25
         Charset         =   0
         Weight          =   700
         Underline       =   0   'False
         Italic          =   0   'False
         Strikethrough   =   0   'False
      EndProperty
      Height          =   255
      Index           =   9
      Left            =   120
      TabIndex        =   30
      Top             =   240
      Width           =   1215
   End
   Begin VB.Label lblColumnName
      Caption         =   "Column 2"
      Height          =   255
      Index           =   1
      Left            =   120
      TabIndex        =   29
      Top             =   1230
      Width           =   1215
   End
   Begin VB.Label lblColumnName
      Caption         =   "Column 3"
      Height          =   255
      Index           =   2
      Left            =   120
      TabIndex        =   28
      Top             =   1710
      Width           =   1215
   End
   Begin VB.Label lblColumnName
      Caption         =   "Column 4"
      Height          =   255
      Index           =   3
      Left            =   120
      TabIndex        =   27
      Top             =   2160
      Width           =   1215
   End
   Begin VB.Label lblColumnName
      Caption         =   "Column 5"
      Height          =   255
      Index           =   4
      Left            =   120
      TabIndex        =   26
      Top             =   2640
      Width           =   1215
   End
   Begin VB.Label lblColumnName
      Caption         =   "Column 6"
      Height          =   255
      Index           =   5
      Left            =   120
      TabIndex        =   25
      Top             =   3120
      Width           =   1215
   End
   Begin VB.Label lblColumnName
      Caption         =   "Column 7"
      Height          =   255
      Index           =   6
      Left            =   120
      TabIndex        =   24
      Top             =   3600
      Width           =   1215
   End
   Begin VB.Label lblColumnName
      Caption         =   "Column 8"
      Height          =   255
      Index           =   7
      Left            =   120
      TabIndex        =   23
      Top             =   4080
      Width           =   1215
   End
   Begin VB.Label lblColumnName
      Caption         =   "Column 9"
      Height          =   255
      Index           =   8
      Left            =   120
      TabIndex        =   22
      Top             =   4560
      Width           =   1215
   End
   Begin VB.Menu mnuFile
      Caption         =   "&File"
      Begin VB.Menu mnuFile_Connect
         Caption         =   "Co&nnect"
      End
      Begin VB.Menu mnuFile_Disconnect
         Caption         =   "&Disconnect"
      End
      Begin VB.Menu mnuFile_Separator1
         Caption         =   "-"
      End
      Begin VB.Menu mnuFile_OpenRS
         Caption         =   "&Open"
      End
      Begin VB.Menu mnuFile_CloseRS
         Caption         =   "&Close"
      End
      Begin VB.Menu mnuFile_Separator2
         Caption         =   "-"
      End
      Begin VB.Menu mnuFile_Command
         Caption         =   "&Execute"
      End
      Begin VB.Menu mnuFile_Separator3
         Caption         =   "-"
      End
      Begin VB.Menu mnuFile_Exit
         Caption         =   "E&xit"
      End
   End
   Begin VB.Menu mnuEdit
      Caption         =   "&Edit"
      Begin VB.Menu mnuEdit_Edit
         Caption         =   "&Edit Record"
      End
      Begin VB.Menu mnuEdit_Add
         Caption         =   "&Add Record"
      End
      Begin VB.Menu mnuEdit_Separator1
         Caption         =   "-"
      End
      Begin VB.Menu mnuEdit_Delete
         Caption         =   "&Delete Record"
      End
      Begin VB.Menu mnuEdit_Separator2
         Caption         =   "-"
      End
      Begin VB.Menu mnuEdit_Update
         Caption         =   "&Update Records"
      End
      Begin VB.Menu mnuEdit_Cancel
         Caption         =   "&Cancel Edit"
      End
   End
   Begin VB.Menu mnuNavigate
      Caption         =   "&Navigate"
      Begin VB.Menu mnuNavigate_MoveFirst
         Caption         =   "&First"
      End
      Begin VB.Menu mnuNavigate_MovePrevious
         Caption         =   "&Previous"
      End
      Begin VB.Menu mnuNavigate_MoveNext
         Caption         =   "&Next"
      End
      Begin VB.Menu mnuNavigate_MoveLast
         Caption         =   "&Last"
      End
      Begin VB.Menu mnuNavigate_Divider1
         Caption         =   "-"
      End
      Begin VB.Menu mnuNavigate_Sort
         Caption         =   "&Sort"
      End
      Begin VB.Menu mnuNavigate_Filter
         Caption         =   "Filte&r"
      End
      Begin VB.Menu mnuNavigate_Find
         Caption         =   "F&ind"
      End
   End
   Begin VB.Menu mnuBookmark
      Caption         =   "&Bookmark"
      Begin VB.Menu mnuBookmark_AddBookmark
         Caption         =   "&Add Bookmark"
      End
      Begin VB.Menu mnuBookmark_GetBookmark
         Caption         =   "&Get Bookmark"
      End
   End
   Begin VB.Menu mnuHelp
      Caption         =   "&Help"
      Begin VB.Menu mnuHelp_About
         Caption         =   "&About..."
      End
   End
End
Attribute VB_Name = "frmFormView"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
Dim BookmarkVals() As Variant
Dim Conn As Object
Dim CmdCL As Object
Dim RS As Object
Dim DefaultFile As String

Private Sub cmdCommand_Click()
'This subroutine handles the input and execution of a command

    On Error GoTo Err

    ' Create command object
    Set CmdCL = Nothing
    Set CmdCL = CreateObject("ADODB.Command.1.5")
    Set CmdCL.ActiveConnection = Conn

    ' Obtain user input for the AS/400 command
    UserInput = InputBox("Which AS/400 command do you want to execute?", "Microsoft ADO Consumer Sample", "AS/400 CL Command")
    If UserInput = "" Then Exit Sub

    CmdCL.CommandText = "Exec Command " & UserInput

    ' Execute the command
    CmdCL.Execute adCmdText

    ' Display source command used
    txtSource.Text = CmdCL.CommandText
    txtSource.Enabled = True

    ' Set behavior of user interface
    cmdCommand.Enabled = True
    mnuFile_Command.Enabled = True

    Exit Sub

Err:
    MsgBox Error, vbExclamation, "Microsoft ADO Consumer Sample"
    DisplayErrors

    Set CmdCL = Nothing

End Sub

Private Sub cmdConnect_Click()
'This subroutine handles connecting to a host machine

    On Error GoTo Err

    Set Conn = Nothing
    Set Conn = CreateObject("ADODB.Connection.1.5")

    'There are a number of different ways to open a connection.  You can pass the "Open" method
    ' all of the appropriate connection information, if you know that information in advance.
    ' You would use the syntax:
    ' Conn.Open "Provider=SNAOLEDB;Data Source=###;User ID=###;Password=###;Remote LU=###;Local LU=###;Mode=###;RDB=###;CCSID=###;CodePage=###"
    ' NOTE: Not all of those parameters are required--the Data Source contains the remote LU, local LU, etc.
    '  information so if you specify a data source you should not specify that information.
    '  The simplest form of an open command that would contain all necessary information would be:
    ' Conn.Open "Provider=SNAOLEDB;Data Source=###;User ID=###;Password=###"

    'However, in this case we would like the user to input the connection information, so we will
    'not specify any connection information (except the provider, which is REQUIRED ALWAYS unless you
    'set it via the connection.provider property)

        Conn.ConnectionString = "Provider=SNAOLEDB"
        Conn.Open

    ' This automatically causes a dialog to pop up asking the user for data source, user name, and password

    ' Set behavior of user interface
    cmdConnect.Enabled = False
    mnuFile_Connect.Enabled = False
    cmdOpenRS.Enabled = True
    mnuFile_OpenRS.Enabled = True
    cmdCommand.Enabled = True
    mnuFile_Command.Enabled = True
    cmdDisconnect.Enabled = True
    mnuFile_Disconnect.Enabled = True

    Exit Sub

Err:
    MsgBox Error, vbExclamation, "Microsoft ADO Consumer Sample"
    DisplayErrors
    For x = 0 To 8
        lblColumnName(x) = ""
    Next x

    Set Conn = Nothing

End Sub

Private Sub cmdDisconnect_Click()
'This subroutine handles disconnecting from a host machine
    On Error Resume Next

    'Just in case the user did not properly close the recordset:
    Set RS = Nothing
    Set RScmd = Nothing

    'First we close the connection.  This disconnects us from the host machine
    Conn.Close
    'But closing an object does not completely eliminate its memory usage--we must set the object to nothing
    Set Conn = Nothing

    ' Set behavior of user interface
    DisableButtons
    DisableMenus
    cmdConnect.Enabled = True
    mnuFile_Connect.Enabled = True

    For x = 0 To 8
        lblColumnName(x) = ""
    Next x

    ClearTextBoxes
    txtSource.Text = ""
    DisableText

End Sub

Private Sub cmdFilter_Click()
'This subroutine sets up a filter

    On Error GoTo Err   'Error handling

    'First we input from the user information about the filter
    UserInput = InputBox("Find record?", "Microsoft ADO Consumer Sample", "AU_ID > '2' AND AU_ID < '5'")

    RS.Filter = UserInput   'Filter can be set to a string that contains multiple criteria strings
                            'separated by 'AND'.  Filter works in three ways:
                            '(1) If one criteria string is used then any operator can be used.
                            '(2) If two criteria strings are used then
                                '-if the column name is the same in both strings, then the criteria strings must define a contiguous range
                                '-if the column names are different, then the operators must be the same.
                            '(3) If three or more criteria strings are used then the operator must be the same for all.

    ' Display a single recordset row (single host table record)
    DisplayRow

    ' Set behavior of user interface
    EnableButtons
    EnableMenus
    cmdConnect.Enabled = False
    mnuFile_Connect.Enabled = False
    cmdOpenRS.Enabled = False
    mnuFile_OpenRS.Enabled = False
    cmdCancel.Enabled = False
    mnuEdit_Cancel.Enabled = False
    cmdUpdate.Enabled = False
    mnuEdit_Update.Enabled = False

    ' Display source command used
    txtSource.Text = RS.Source

    ' Display only first 9 fields of recordset (first 9 host columns)
    Columns = RS.Fields.Count
    If Columns > 9 Then Columns = 9
    For x = 0 To Columns - 1
        lblColumnName(x) = RS.Fields(x).Name
    Next x

    Exit Sub

Err:
     MsgBox Error, vbExclamation, "Microsoft ADO Consumer Sample"
     DisplayErrors
     Resume Next

End Sub

Private Sub cmdFind_Click()
'This subroutine handles searching for a record
    On Error GoTo Err

    'First input the characteristics to search for
    UserInput = InputBox("Find record?", "Microsoft ADO Consumer Sample", "AU_ID > 3")

    RS.Find UserInput   'The argument for the Find method takes a criteria string
                        'that has three parts: ColumnName, Operator, Value.
                        'In the example, the ColumnName is "AU_ID", the Operator is ">"
                        ' greater than, and the Value is "2".
                        'Find only works with files that are keyed by column name.

    ' Display a single recordset row (single host table record)
    DisplayRow

    ' Set behavior of user interface
    EnableButtons
    EnableMenus
    cmdConnect.Enabled = False
    mnuFile_Connect.Enabled = False
    cmdOpenRS.Enabled = False
    mnuFile_OpenRS.Enabled = False
    cmdCancel.Enabled = False
    mnuEdit_Cancel.Enabled = False
    cmdUpdate.Enabled = False
    mnuEdit_Update.Enabled = False

    ' Display source command used
    txtSource.Text = RS.Source

    ' Display only first 9 fields of recordset (first 9 host columns)
    Columns = RS.Fields.Count
    If Columns > 9 Then Columns = 9
    For x = 0 To Columns - 1
        lblColumnName(x) = RS.Fields(x).Name
    Next x

    Exit Sub

Err:
     MsgBox Error, vbExclamation, "Microsoft ADO Consumer Sample"
     DisplayErrors
     Resume Next

End Sub

Private Sub cmdSort_Click()
'This subroutine handles sorting a recordset
    On Error GoTo Err

    UserInput = InputBox("Sort records?", "Microsoft ADO Consumer Sample", "AU_ID")

    RS.Sort = UserInput 'The Sort method uses a single property equal to the name
                        'of the column you want to sort on.

    ' Display a single recordset row (single host table record)
    DisplayRow

    ' Set behavior of user interface
    EnableButtons
    EnableMenus
    cmdConnect.Enabled = False
    mnuFile_Connect.Enabled = False
    cmdOpenRS.Enabled = False
    mnuFile_OpenRS.Enabled = False
    cmdCancel.Enabled = False
    mnuEdit_Cancel.Enabled = False
    cmdUpdate.Enabled = False
    mnuEdit_Update.Enabled = False

    ' Display source command used
    txtSource.Text = RS.Source

    ' Display only first 9 fields of recordset (first 9 host columns)
    Columns = RS.Fields.Count
    If Columns > 9 Then Columns = 9
    For x = 0 To Columns - 1
        lblColumnName(x) = RS.Fields(x).Name
    Next x

    Exit Sub

Err:
     MsgBox Error, vbExclamation, "Microsoft ADO Consumer Sample"
     DisplayErrors
     Resume Next

End Sub

Private Sub Form_Load()
'This subroutine loads the form and displays it correctly on the screen
    On Error Resume Next

    ReDim BookmarkVals(1)
    ReDim BookmarkNames(1)

    ' Set behavior of user interface
    For x = 0 To 8
        lblColumnName(x) = ""
        tbData(x).TabStop = False
    Next x
    ClearTextBoxes

    DisableButtons
    DisableMenus

    DefaultFile = "Library/Table"

    cmdConnect.Enabled = True
    cmdUpdate.Enabled = False

End Sub

Public Sub DisplayErrors()
'This subroutine cycles through any errors generated and displays them on the screen
    Dim Msg As String
    Dim I As Integer
    Dim MyErr As Object

    On Error Resume Next    'continue even if an error occurs

    For I = 1 To Conn.Errors.Count
        Set MyErr = Conn.Errors(I - 1)
        Msg = "[Description: " & MyErr.Description & "][" & MyErr.Number & "]"
        MsgBox Msg, vbExclamation, "Microsoft ADO Consumer Sample"
        Set MyErr = Nothing
    Next I
    Conn.Errors.Clear
    Exit Sub

End Sub

Private Sub cmdOpenRS_Click()
'This subroutine handles input of a file name and opening a recordset
    On Error GoTo Err

    Set RS = Nothing
    Set RS = CreateObject("ADODB.Recordset.1.5")

    ' Open ADO Recordset - option 1 - allow the user to choose host table
        DefaultFile = InputBox("Which table do you want to open?", "Microsoft ADO Consumer Sample", DefaultFile)
        If DefaultFile = "" Then Exit Sub

    ' Required OpenRS method parameters: CursorType = 2 "adOpenDynamic"; LockType = 4 "adLockOptimistic"; Options = 1 "adCmdText"
    RS.Open "Exec Open " & DefaultFile, Conn, adOpenDynamic, adLockOptimistic, adCmdText

    ' Open ADO Recordset - option 2 - specify target host table for user
        ' RS.Open "Exec Open LIBRARY/FILE", Conn, 2, 4, 1

    ' Display a single recordset row (single host table record)
    DisplayRow
    EnableText

    ' Set behavior of user interface
    EnableButtons
    EnableMenus
    cmdCloseRS.Enabled = True
    mnuFile_CloseRS.Enabled = True
    cmdConnect.Enabled = False
    mnuFile_Connect.Enabled = False
    cmdOpenRS.Enabled = False
    mnuFile_OpenRS.Enabled = False
    cmdCancel.Enabled = False
    mnuEdit_Cancel.Enabled = False
    cmdUpdate.Enabled = False
    mnuEdit_Update.Enabled = False

    ' Display only first 9 fields of recordset (first 9 host columns)
    Columns = RS.Fields.Count
    If Columns > 9 Then Columns = 9
    For x = 0 To Columns - 1
        lblColumnName(x) = RS.Fields(x).Name
    Next x

    ' Display source command used
    txtSource.Text = RS.Source

    Exit Sub

Err:
    MsgBox Error, vbExclamation, "Microsoft ADO Consumer Sample"
    DisplayErrors

    Set RS = Nothing

    ' Set behavior of user interface
    For x = 0 To 8
        lblColumnName(x) = ""
    Next x
    ClearTextBoxes
    EnableButtons
    cmdConnect.Enabled = True
    cmdCloseRS.Enabled = True

End Sub

Private Sub cmdCloseRS_Click()
'This subroutine closes the open recordset
    On Error Resume Next

    'First we close the recordset--this ends our lock on the file
    RS.Close
    'But at this point we are still using up memory with the recordset object, so we must free that
    ' memory by setting the object to nothing
    Set RS = Nothing

    ' Set behavior of user interface
    For x = 0 To 8
        lblColumnName(x) = ""
    Next x

    ClearTextBoxes
    txtSource.Text = ""
    DisableText

    DisableButtons
    DisableMenus

    cmdDisconnect.Enabled = True
    mnuFile_Disconnect.Enabled = True
    cmdOpenRS.Enabled = True
    mnuFile_OpenRS.Enabled = True
    cmdOpenRS.SetFocus

End Sub

Private Sub cmdExit_Click()
'This subroutine ensures that all memory has been freed up prior to exit
    On Error Resume Next

    Set RS = Nothing
    Set CmdCL = Nothing
    Set Conn = Nothing

End Sub

Private Sub cmdGetBookmark_Click()
'This subroutine moves the cursor to the saved bookmark location

    On Error GoTo Err

    RS.Bookmark = BookmarkVals(lstBookmark.ListIndex + 2)

    ' Display a single recordset row (single host table record)
    DisplayRow

    ' Set behavior of user interface
    cmdMoveFirst.Enabled = True
    cmdMovePrevious.Enabled = True
    cmdMoveNext.Enabled = True
    cmdMoveLast.Enabled = True
    Exit Sub

Err:
    MsgBox Error, vbExclamation, "Microsoft ADO Consumer Sample"
    DisplayErrors

End Sub

Sub ClearTextBoxes()
'This subroutine clears the 9 field boxes on the screen
    On Error Resume Next

    For x = 0 To 8
        tbData(x) = ""
    Next x

End Sub

Sub LockTextBoxes(bLocked)
    On Error Resume Next

    For x = 0 To 8
        tbData(x).Locked = bLocked
    Next x

    For x = 0 To 8
        tbData(x).TabStop = True
    Next x

End Sub

Public Sub DisplayRow()
'This subroutine reads one set of 8 fields from the file and displays them on the screen
    On Error GoTo Err

    Columns = RS.Fields.Count
    If Columns > 9 Then Columns = 9     'we only have space for 9 fields in our form
    For x = 0 To Columns - 1
        tbData(x).Text = RTrim(RS(x))   'rtrim removes all the blank space on the right of a string
    Next
    Exit Sub

Err:
    MsgBox Error, vbExclamation, "Microsoft ADO Consumer Sample"
    DisplayErrors

End Sub

Sub EnableButtons()
'This subroutine enables all the buttons on the screen
    On Error Resume Next

    cmdConnect.Enabled = True
    cmdOpenRS.Enabled = True
    cmdCloseRS.Enabled = True
    cmdCommand.Enabled = True
    cmdEdit.Enabled = True
    cmdMoveFirst.Enabled = True
    cmdMoveLast.Enabled = True
    cmdMoveNext.Enabled = True
    cmdMovePrevious.Enabled = True
    cmdSort.Enabled = True
    cmdFilter.Enabled = True
    cmdFind.Enabled = True

    cmdAdd.Enabled = True
    cmdDelete.Enabled = True
    cmdAddBookmark.Enabled = True
    cmdGetBookmark.Enabled = True
    lstBookmark.Enabled = True

End Sub

Sub DisableButtons()
'This subroutine disables (greys out) all the buttons on the screen
    On Error Resume Next

    cmdConnect.Enabled = False
    cmdOpenRS.Enabled = False
    cmdCloseRS.Enabled = False
    cmdDisconnect.Enabled = False
    cmdCommand.Enabled = False

    cmdAdd.Enabled = False
    cmdCancel.Enabled = False
    cmdDelete.Enabled = False

    cmdEdit.Enabled = False
    cmdMoveFirst.Enabled = False
    cmdMovePrevious.Enabled = False
    cmdMoveNext.Enabled = False
    cmdMoveLast.Enabled = False
    cmdSort.Enabled = False
    cmdFilter.Enabled = False
    cmdFind.Enabled = False

    cmdAddBookmark.Enabled = False
    cmdGetBookmark.Enabled = False

End Sub

Sub EnableMenus()
'This subroutine enables all the possible menu items
    On Error Resume Next

    mnuFile_Connect.Enabled = True
    mnuFile_OpenRS.Enabled = True
    mnuFile_CloseRS.Enabled = True
    mnuFile_Command.Enabled = True
    mnuFile_Disconnect.Enabled = False

    mnuEdit_Edit.Enabled = True
    mnuEdit_Add.Enabled = True
    mnuEdit_Delete.Enabled = True

    mnuNavigate_MoveFirst.Enabled = True
    mnuNavigate_MovePrevious.Enabled = True
    mnuNavigate_MoveNext.Enabled = True
    mnuNavigate_MoveLast.Enabled = True
    mnuNavigate_Sort.Enabled = True
    mnuNavigate_Filter.Enabled = True
    mnuNavigate_Find.Enabled = True

    mnuBookmark_AddBookmark.Enabled = True
    mnuBookmark_GetBookmark.Enabled = True

End Sub

Sub DisableMenus()
'This subroutine disables (greys out) all the possible menu items
    On Error Resume Next

    mnuFile_Connect.Enabled = True
    mnuFile_OpenRS.Enabled = False
    mnuFile_CloseRS.Enabled = False
    mnuFile_Command.Enabled = False
    mnuFile_Disconnect.Enabled = False

    mnuEdit_Edit.Enabled = False
    mnuEdit_Add.Enabled = False
    mnuEdit_Cancel.Enabled = False
    mnuEdit_Delete.Enabled = False
    mnuEdit_Update.Enabled = False
    mnuNavigate_MoveFirst.Enabled = False
    mnuNavigate_MovePrevious.Enabled = False
    mnuNavigate_MoveNext.Enabled = False
    mnuNavigate_MoveLast.Enabled = False
    mnuNavigate_Sort.Enabled = False
    mnuNavigate_Filter.Enabled = False
    mnuNavigate_Find.Enabled = False

    mnuBookmark_AddBookmark.Enabled = False
    mnuBookmark_GetBookmark.Enabled = False

End Sub

Sub EnableText()
    On Error Resume Next

    tbData(0).Enabled = True
    tbData(1).Enabled = True
    tbData(2).Enabled = True
    tbData(3).Enabled = True
    tbData(4).Enabled = True
    tbData(5).Enabled = True
    tbData(6).Enabled = True
    tbData(7).Enabled = True
    tbData(8).Enabled = True
    tbData(9).Enabled = True
    txtSource.Enabled = True

End Sub

Sub DisableText()
    On Error Resume Next

    tbData(0).Enabled = False
    tbData(1).Enabled = False
    tbData(2).Enabled = False
    tbData(3).Enabled = False
    tbData(4).Enabled = False
    tbData(5).Enabled = False
    tbData(6).Enabled = False
    tbData(7).Enabled = False
    tbData(8).Enabled = False
    tbData(9).Enabled = False
    txtSource.Enabled = False
    lstBookmark.TabStop = False

End Sub

Private Sub cmdMoveFirst_Click()
    On Error GoTo Err

    RS.MoveFirst

    ' Display a single recordset row (single host table record)
    DisplayRow

    ' Set behavior of user interface
    cmdMoveFirst.Enabled = False
    cmdMovePrevious.Enabled = False
    cmdMoveLast.Enabled = True
    cmdMoveNext.Enabled = True
    Exit Sub

Err:
    MsgBox Error, vbExclamation, "Microsoft ADO Consumer Sample"
    DisplayErrors
End Sub

Private Sub cmdMovePrevious_Click()
    On Error GoTo Err

    If RS.BOF = False Then
        RS.MovePrevious
        If RS.BOF = False Then
            DisplayRow
            cmdMoveLast.Enabled = True
            cmdMoveNext.Enabled = True
        Else
            cmdMoveFirst.Enabled = False
            cmdMovePrevious.Enabled = False
            cmdMoveLast.Enabled = True
            cmdMoveNext.Enabled = True
        End If
    End If
    Exit Sub

Err:
    MsgBox Error, vbExclamation, "Microsoft ADO Consumer Sample"
    DisplayErrors

End Sub

Private Sub cmdMoveNext_Click()
    On Error GoTo Err

    If RS.EOF = False Then
        RS.MoveNext
        If RS.EOF = False Then
            DisplayRow
            cmdMoveFirst.Enabled = True
            cmdMovePrevious.Enabled = True
        Else
            cmdMoveFirst.Enabled = True
            cmdMovePrevious.Enabled = True
            cmdMoveLast.Enabled = False
            cmdMoveNext.Enabled = False
        End If
    End If
    Exit Sub

Err:
    MsgBox Error, vbExclamation, "Microsoft ADO Consumer Sample"
    DisplayErrors

End Sub

Private Sub cmdMoveLast_Click()
    On Error GoTo Err

    RS.MoveLast

    ' Display a single recordset row (single host table record)
    DisplayRow

    ' Set behavior of user interface
    cmdMoveFirst.Enabled = True
    cmdMovePrevious.Enabled = True
    cmdMoveLast.Enabled = False
    cmdMoveNext.Enabled = False
    Exit Sub

Err:
    MsgBox Error, vbExclamation, "Microsoft ADO Consumer Sample"
    DisplayErrors

End Sub

Private Sub cmdAddBookmark_Click()
    On Error GoTo Err

    User_Input = InputBox("What do you want to label the bookmark?", "Add Bookmark", "")
    If User_Input <> "" Then
        ReDim Preserve BookmarkVals(UBound(BookmarkVals) + 1)
        lstBookmark.AddItem User_Input
        BookmarkVals(UBound(BookmarkVals)) = RS.Bookmark
    End If
    Exit Sub

    lstBookmark.TabStop = True

Err:
    MsgBox Error, vbExclamation, "Microsoft ADO Consumer Sample"
    DisplayErrors

End Sub

Private Sub lstBookmark_DblClick()
    On Error GoTo Err

    RS.Bookmark = BookmarkVals(lstBookmark.ListIndex + 2)

    ' Display a single recordset row (single host table record)
    DisplayRow

    ' Set behavior of user interface
    cmdMoveFirst.Enabled = True
    cmdMovePrevious.Enabled = True
    cmdMoveNext.Enabled = True
    cmdMoveLast.Enabled = True
    Exit Sub

Err:
    MsgBox Error, vbExclamation, "Microsoft ADO Consumer Sample"
    DisplayErrors

End Sub

Private Sub cmdEdit_Click()
    ' On Error GoTo Err
    On Error Resume Next

    ' Set behavior of user interface
    DisableButtons
    DisableMenus
    cmdCancel.Enabled = True
    mnuEdit_Cancel.Enabled = True
    cmdUpdate.Enabled = True
    mnuEdit_Update.Enabled = True
    LockTextBoxes False

' Err:
    ' MsgBox Error, vbExclamation, "Microsoft ADO Consumer Sample"
    ' DisplayErrors

End Sub

Private Sub cmdAdd_Click()
    On Error GoTo Err

    RS.AddNew

    ' Set behavior of user interface
    ClearTextBoxes
    EnableButtons
    cmdCancel.Enabled = True
    mnuEdit_Cancel.Enabled = True
    cmdUpdate.Enabled = True
    mnuEdit_Update.Enabled = True
    LockTextBoxes False
    For x = 0 To 8
        tbData(x).TabStop = True
    Next x
    Exit Sub

Err:
    MsgBox Error, vbExclamation, "Microsoft ADO Consumer Sample"
    DisplayErrors

End Sub

Private Sub cmdCancel_Click()
    On Error GoTo Err

    RS.CancelUpdate

    ' Display a single recordset row (single host table record)
    DisplayRow

    ' Set behavior of user interface
    EnableButtons
    cmdCancel.Enabled = False
    mnuEdit_Cancel.Enabled = False
    cmdUpdate.Enabled = False
    mnuEdit_Update.Enabled = False
    LockTextBoxes True
    For x = 0 To 8
        tbData(x).TabStop = False
    Next x
    Exit Sub

Err:
    MsgBox Error, vbExclamation, "Microsoft ADO Consumer Sample"
    DisplayErrors

End Sub

Private Sub cmdUpdate_Click()
    On Error GoTo Err

    Columns = RS.Fields.Count
    If Columns > 9 Then Columns = 9
    For x = 0 To Columns - 1
        RS(x) = LTrim(tbData(x))
    Next

    ' Note: If you open a file with locktype adLockBatchOptimistic (only valid for bookmarkable host files)
    '   then you must update with RS.UpdateBatch
    RS.Update

    ' Display a single recordset row (single host table record)
    DisplayRow

    ' Set behavior of user interface
    EnableButtons
    EnableMenus
    cmdCancel.Enabled = False
    mnuEdit_Cancel.Enabled = False
    cmdUpdate.Enabled = False
    mnuEdit_Update.Enabled = False
    LockTextBoxes True
    For x = 0 To 8
        tbData(x).TabStop = True
    Next x
    Exit Sub

Err:
    MsgBox Error, vbExclamation, "Microsoft ADO Consumer Sample"
    DisplayErrors

End Sub

Private Sub cmdDelete_Click()
    On Error GoTo Err

    If MsgBox("Are you sure you want to delete this row?", vbYesNo, "Microsoft ADO Consumer Sample") = vbNo Then
        Exit Sub
    Else
        RS.Delete
        ' Note: If you open a file with locktype adLockBatchOptimistic (only valid for bookmarkable host files)
        '   then you must update with RS.UpdateBatch
        RS.Update
        RS.MoveFirst

        ' Display a single recordset row (single host table record)
        DisplayRow

        ' Set behavior of user interface
        cmdMoveNext.Enabled = True
        cmdMoveLast.Enabled = True
    End If
    Exit Sub

Err:
    MsgBox Error, vbExclamation, "Microsoft ADO Consumer Sample"
    DisplayErrors

End Sub

Private Sub mnuBookmark_AddBookmark_Click()
    On Error Resume Next

    cmdAddBookmark_Click

End Sub

Private Sub mnuBookmark_GetBookmark_Click()
    On Error Resume Next

    cmdGetBookmark_Click

End Sub

Private Sub mnuEdit_Add_Click()
    On Error Resume Next

    cmdAdd_Click

End Sub

Private Sub mnuEdit_Cancel_Click()
    On Error Resume Next

    cmdCancel_Click

End Sub

Private Sub mnuEdit_Delete_Click()
    On Error Resume Next

    cmdDelete_Click

End Sub

Private Sub mnuEdit_Edit_Click()
    On Error Resume Next

    cmdEdit_Click

End Sub

Private Sub mnuEdit_Update_Click()
    On Error Resume Next

    cmdUpdate_Click

End Sub

Private Sub mnuFile_CloseRS_Click()
    On Error Resume Next

    cmdCloseRS_Click

End Sub

Private Sub mnuFile_Close_Click()
    On Error Resume Next

    cmdCloseRS_Click

End Sub

Private Sub mnuFile_Command_Click()
    On Error Resume Next

    cmdCommand_Click

End Sub

Private Sub mnuFile_Disconnect_Click()
    On Error Resume Next

    cmdDisconnect_Click

End Sub

Private Sub mnuFile_Exit_Click()
    On Error Resume Next

    cmdExit_Click

End Sub

Private Sub mnuFile_Connect_Click()
    On Error Resume Next

    cmdConnect_Click

End Sub

Private Sub mnuFile_OpenRS_Click()
    On Error Resume Next

    cmdOpenRS_Click

End Sub

Private Sub mnuFile_New_Click()

End Sub

Private Sub mnuHelp_About_Click()
    On Error Resume Next

    Load frmAbout
    frmAbout.Show

End Sub

Private Sub mnuNavigate_Filter_Click()
    On Error Resume Next

    cmdFilter_Click

End Sub

Private Sub mnuNavigate_Find_Click()
    On Error Resume Next

    cmdFind_Click

End Sub

Private Sub mnuNavigate_MoveFirst_Click()
    On Error Resume Next

    cmdMoveFirst_Click

End Sub

Private Sub mnuNavigate_MoveLast_Click()
    On Error Resume Next

    cmdMoveLast_Click

End Sub

Private Sub mnuNavigate_MoveNext_Click()
    On Error Resume Next

    cmdMoveNext_Click

End Sub

Private Sub mnuNavigate_MovePrevious_Click()
    On Error Resume Next

    cmdMovePrevious_Click

End Sub

Private Sub mnuNavigate_Sort_Click()
    On Error Resume Next

    cmdSort_Click

End Sub


FRMABOUT.FRM

VERSION 5.00
Begin VB.Form frmAbout
   Caption         =   "Form1"
   ClientHeight    =   3225
   ClientLeft      =   60
   ClientTop       =   345
   ClientWidth     =   5790
   LinkTopic       =   "Form1"
   ScaleHeight     =   3225
   ScaleWidth      =   5790
   StartUpPosition =   3  'Windows Default
   Begin VB.CommandButton cmdOK
      Caption         =   "&OK"
      Height          =   375
      Left            =   4560
      TabIndex        =   0
      Top             =   2640
      Width           =   1095
   End
   Begin VB.PictureBox Picture1
      Height          =   975
      Left            =   120
      ScaleHeight     =   915
      ScaleWidth      =   1035
      TabIndex        =   7
      Top             =   240
      Width           =   1095
   End
   Begin VB.Label Label7
      Caption         =   "All rights reserved."
      Height          =   255
      Left            =   1680
      TabIndex        =   9
      Top             =   1440
      Width           =   1455
   End
   Begin VB.Label lblCopyright
      Caption         =   "Copyright (C) 1987-1997 Microsoft Corporation"
      Height          =   255
      Left            =   1680
      TabIndex        =   8
      Top             =   1200
      Width           =   3375
   End
   Begin VB.Label Label6
      Caption         =   $"frmAbout.frx":0000
      Height          =   855
      Left            =   120
      TabIndex        =   6
      Top             =   2280
      Width           =   4215
   End
   Begin VB.Label Label5
      Caption         =   "Warning: This computer program is protected by copyright law and international treaties."
      Height          =   375
      Left            =   120
      TabIndex        =   5
      Top             =   1920
      Width           =   4335
   End
   Begin VB.Line Line1
      DrawMode        =   1  'Blackness
      X1              =   120
      X2              =   5640
      Y1              =   1800
      Y2              =   1800
   End
   Begin VB.Label Label4
      Caption         =   "Pre-Release Version (Build 3.0.0)"
      Height          =   255
      Left            =   1680
      TabIndex        =   4
      Top             =   960
      Width           =   2895
   End
   Begin VB.Label Label3
      Caption         =   "ADO Consumer Sample"
      Height          =   375
      Left            =   1680
      TabIndex        =   3
      Top             =   720
      Width           =   1095
   End
   Begin VB.Label Label2
      Caption         =   "Microsoft Corporation"
      Height          =   255
      Left            =   1680
      TabIndex        =   2
      Top             =   240
      Width           =   1815
   End
   Begin VB.Label Label1
      Caption         =   "Microsoft OLE DB Data Provider for AS/400 and VSAM"
      Height          =   375
      Left            =   1680
      TabIndex        =   1
      Top             =   480
      Width           =   3975
   End
End
Attribute VB_Name = "frmAbout"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
Private Sub cmdOK_Click()
    On Error Resume Next

    frmAbout.Hide
    Unload frmAbout

End Sub


OLEDBDDM.ASP

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>

<head>
<title>Microsoft OLE DB Provider for AS/400 and VSAM - Active Server Pages Sample</title>
</head>

<body bgcolor="#FFFFFF">

<div align="center"><center>

<table border="0">
    <tr>
        <em><strong>Microsoft OLE DB Provider for AS/400 and VSAM - Active Server Pages Sample</strong></em></font></td>
    </tr>
</table>
</center></div>

<form action="oledbddm.asp" method="POST">
    <input type="hidden" name="action" value="30"><div
    align="center"><center><table border="0" cellspacing="0"
    width="300">
        <tr>
            <td align="right"><font size="3">First Name:</font></td>
            <td><font size="3"><% =rtrim(Session("RS")(2))%> </font></td>
        </tr>
        <tr>
            <td align="right"><font size="3">Last Name:</font></td>
            <td><font size="3"><% =rtrim(Session("RS")(1))%></font></td>
        </tr>
        <tr>
            <td align="right"><font size="3">Address:</font></td>
            <td><font size="3"><% =rtrim(Session("RS")(4))%></font></td>
        </tr>
        <tr>
            <td align="right"><font size="3">ID:</font></td>
            <td><font size="3"><% =rtrim(Session("RS")(0))%></font></td>
        </tr>
        <tr>
            <td align="right"><font size="3">City &amp; State:</font></td>
            <td><font size="3"><% =rtrim(Session("RS")(5)) & "       "%><% =rtrim(Session("RS")(6))%></font></td>
        </tr>
        <tr>
            <td align="right"><font size="3">Zip Code:</font></td>
            <td><font size="3"><% =rtrim(Session("RS")(7))%></font></td>
        </tr>
        <tr>
            <td align="right"><font size="3">Phone Number:</font></td>
            <td><font size="3"><% =rtrim(Session("RS")(3))%></font></td>
        </tr>
        <tr>
            <td align="right"><font size="3">Contract:</font></td>
            <td><font size="3"><% =rtrim(Session("RS")(8))%></font></td>
        </tr>

    </table>
    </center></div>
</form>

<form>
    <div align="center"><center><table border="0" cellspacing="0" nowrap >
        <tr>
            <input
type="button"
name="MoveFirst"
value="&lt;&lt; First"
            onclick="top.location='oledbddm.asp?action=1'">

<input
type="button"
name="MovePrevious"
            value="&lt; Prev "
            onclick="top.location='oledbddm.asp?action=2'">

            <input type="button" name="MoveNext"
            value="Next &gt; "
            onclick="top.location='oledbddm.asp?action=3'">

            <input type="button" name="MoveLast"
            value="Last  &gt;&gt;"
            onclick="top.location='oledbddm.asp?action=4'">

        </tr>
    </table>
    </center></div>
</form>

</body>
</html>

<%
'This sample is based on host tables similar to those in the Microsoft SQL Server Pubs sample database

On Error Resume Next

If Session("Connect Error") = True Then
Session("Connect Error") = False
Response.Redirect "message.asp"
End If

Select Case Request("action")
Case 1
Session("RS").MoveFirst

Case 2
Session("RS").MovePrevious
If Session("RS").BOF = True Then
Session("RS").MoveFirst
End If

Case 3
Session("RS").MoveNext
If Session("RS").EOF = True Then
Session("RS").MoveLast
End If

Case 4
Session("RS").MoveLast

Case 11
If Session("RS").Source = "PUBS/AUID" Then
BM = Session("RS").Bookmark
Set Session("RS") = Nothing
Set Session("RS") = Session("RS2")
Session("RS").Bookmark = BM
Else
BM = Session("RS").Bookmark
Set Session("RS") = Nothing
Set Session("RS") = Session("RS1")
Session("RS").Bookmark = BM
End If

Case 30
Session("RS")(3) = RTrim(CStr(Request("Phone")))
Session("RS")(4) = RTrim(CStr(Request("Address")))
Session("RS")(5) = RTrim(CStr(Request("City")))
Session("RS")(6) = RTrim(CStr(Request("State")))
Session("RS")(7) = RTrim(CStr(Request("Zip")))
Session("RS")(8) = RTrim(CStr(Request("Contract")))
Session("RS").UpdateBatch
End Select
%>

<!--
' Last updated on 10/9/97.
' Copyright 1997 Microsoft Corporation
-->


REPORT.ASP

<%@ LANGUAGE="VBSCRIPT" %>

<HTML>
<HEAD>

<META NAME="GENERATOR" Content="Microsoft Visual InterDev 1.0">
<META HTTP-EQUIV="Content-Type" content="text/html; charset=iso-8859-1">

<TITLE>Microsoft OLE DB Provider for AS/400 and VSAM - Active Server Pages Sample</TITLE>

</HEAD>

<BODY BGCOLOR=#FFFFFF>

<H2>Simple Example Retrieving All Results into an Array</H2>

<%
'This sample is based on host tables similar to those in the Microsoft SQL Server Pubs sample database

On Error Resume Next

Set Conn = Nothing

Set Conn = Server.CreateObject("ADODB.Connection")

Conn.Open "provider=SNAOLEDB;User ID=USERID;Password=PASSWORD;Data source=DATASOURCE"

    Set RS = Nothing

Set RS = Server.CreateObject("ADODB.Recordset")

RS.Open "exec open PUBS/AUTHORS", Conn, 2, 3, 1

' Session.ScriptTimeOut = nMinutes

%>

Here are the results from the query:<BR><I><B>  <%=sql%></I></B>

<P>
<P>

<TABLE BORDER=1>

<TR>

<% For i = 0 to RS.Fields.Count - 1 %>

<TD><B><% = RS(i).Name %></B></TD>

<% Next %>

</TR>

<%
' Put up to 10 rows in a 2d variant array
v=RS.GetRows(10)

RS.close

set RS = nothing
%>

<P>

<% For row = 0 to UBound(v,2)  ' iterate through the rows in the variant array %>

<TR>

<% For col = 0 to UBound(v,1) %>

<TD><% = v(col,row) %> </TD>

<% Next %>

</TR>

<% Next %>

</TABLE>

<BR>

<BR>

</BODY>
</HTML>

<!--
' Last updated on 11/18/97.
' Copyright 1997 Microsoft Corporation
-->


ARRAY.H

////////////////////////////////////////////////////////////////////////////////
// array.h
//
// Copyright (C) 1987-1997 By Microsoft Corp. All rights reserved.
// Copyright (C) 1997 Metawise Computing, Inc. All rights reserved.
//
////////////////////////////////////////////////////////////////////////////////

#ifndef _ARRAY_H_
#define _ARRAY_H_

////////////////////////////////////////////////////////////////////////////////
// Array definition similar to CArray in MFC.
// We are only implementing CPtrArray here.

class CPtrArray
{
public:
// Constructor/Destructor
CPtrArray (UINT nInitSize = 0);
~CPtrArray ();

// Attributes
int GetSize () const;
int GetUpperBound () const;
void SetSize (int nNewSize, int nGrowBy = -1);
void* GetBuffer () const;// return internal buffer (array of ptrs)

// Operations
void FreeExtra ();
void RemoveAll ();

// Accessing Elements (nIndex is zero-based)
void* GetAt (int nIndex) const;
void SetAt (int nIndex, void* pNewElem);
void*& ElementAt (int nIndex);

// Potentially Growing the Array
void SetAtGrow (int nIndex, void* pNewElem);
int Add (void* pNewElem);
int Append (const CPtrArray& src);
void Copy (const CPtrArray& src);

// Insert/Removal
void RemoveAt (int nIndex, int nCount = 1);

// Overloaded Operator Helpers
void* operator [] (int nIndex) const;
void*& operator [] (int nIndex);

protected:
// Implementations
void** m_pData;// buffer pointer
int m_nSize;// #elem in array
int m_nMaxSize;// max allocated
int m_nGrowBy;// grow amount
};

////////////////////////////////////////////////////////////////////////////////
// Inline implementations of CPtrArray

#ifndef _DEBUG

inline int CPtrArray::GetSize () const
{
return m_nSize;
}

inline int CPtrArray::GetUpperBound () const
{
return m_nSize - 1;
}

inline void CPtrArray::RemoveAll ()
{
SetSize (0);
}

inline void* CPtrArray::GetAt (int nIndex) const
{
assert(0 <= nIndex && nIndex < m_nSize);
return m_pData [nIndex];
}

inline void CPtrArray::SetAt (int nIndex, void* pNewElem)
{
assert(0 <= nIndex && nIndex < m_nSize);
m_pData[nIndex] = pNewElem;
}

inline int CPtrArray::Add (void* pNewElem)
{
int nIndex = m_nSize;

SetAtGrow (nIndex, pNewElem);
return nIndex;
}

inline void*& CPtrArray::ElementAt (int nIndex)
{
assert(0 <= nIndex && nIndex < m_nSize);
return m_pData [nIndex];
}

inline void* CPtrArray::operator [] (int nIndex) const
{
return GetAt (nIndex);
}

inline void*& CPtrArray::operator [] (int nIndex)
{
assert(0 <= nIndex && nIndex < m_nSize);
return m_pData [nIndex];
}

inline void* CPtrArray::GetBuffer () const
{
return (void*) m_pData;
}

#endif // _DEBUG

#ifndef min
#define min(X, Y) (((X) <= (Y)) ? (X) : (Y))
#endif

#ifndef max
#define max(X, Y) (((X) >= (Y)) ? (X) : (Y))
#endif

#endif // _ARRAY_H_


ATTRSTR.H

////////////////////////////////////////////////////////////////////////////////
// attrstr.h
//
// Copyright (C) 1987-1997 By Microsoft Corp. All rights reserved.
// Copyright (C) 1997 Metawise Computing, Inc. All rights reserved.
//
////////////////////////////////////////////////////////////////////////////////

#ifndef _ATTRSTR_H_
#define _ATTRSTR_H_

// An attribute string has the following syntax:
// attribute-string ::= attribute[;attribute[;attribute...]]
// attribute ::= attribute-keyword=attribute-value
// where ';' is m_chAttrSeparator and '=' is m_chKeyValueSeparator

// attribute-keyword is case insensitive
class CAttrString : public CString
{
public:
CAttrString ();
CAttrString (LPCSTR lpsz);
CAttrString (LPCWSTR lpsz);

TCHAR m_chAttrSeparator;// separator between attributes; default is ';'
TCHAR m_chKeyValueSeparator;// separator between key and value; default is '='

BOOL GetAttribute (LPCTSTR lpszKey, CString* pstrValue);
void AddAttribute (LPCTSTR lpszKey, LPCTSTR lpszValue, BOOL bOverwrite = TRUE);
void RemoveAttribute (LPCTSTR lpszKey, BOOL bAll = TRUE);

private:
// Implementations:
BOOL GetNextKeyValue (int* pnIndex, CString* pstrKey, CString* pstrValue);// to get 1st one, set *pnIndex to 0
};

#endif // _ATTRSTR_H_


CSTRING.H

////////////////////////////////////////////////////////////////////////////////
// string.h
//
// Copyright (C) 1987-1997 By Microsoft Corp. All rights reserved.
// Copyright (C) 1997 Metawise Computing, Inc. All rights reserved.
//
////////////////////////////////////////////////////////////////////////////////

#ifndef _STRING_H_
#define _STRING_H_

#ifndef __wtypes_h__
#include <wtypes.h>
#endif

#ifndef _INC_TCHAR
#include <tchar.h>
#endif
#include <assert.h>

extern TCHAR* g_NULL_STRING;
extern int _wcstombsz (char* mbstr, const wchar_t* wcstr, size_t count);
#ifndef NO_BSTR_SUPPORT
extern int _mbstowcsz (wchar_t* wcstr, const char* mbstr, size_t count);
#endif

////////////////////////////////////////////////////////////////////////////////
// String definition similar to CString in MFC

class CString
{
public:
// Constructors/Destructor
CString ();
CString (LPCSTR lpsz);
CString (LPCWSTR lpsz);
CString (const unsigned char* psz);
CString (const CString& str);
CString (REFGUID guid);
~CString ();

// Attributes & Operations
int GetLength () const;
BOOL IsEmpty () const;
void Empty ();// free up data
int Copy// return size of original string
(
OUT LPTSTR lpsz,// buffer to copy string to
IN int nSize// size of buffer
);

operator LPCTSTR() const;
LPTSTR Detach ();// return the string and detach it from this class

TCHAR GetAt (int nIndex) const;// 0 based

LPTSTR GetBuffer (int nMinLength);
LPTSTR GetBufferSetLength (int nNewLength);
void ReleaseBuffer (int nNewLength = -1);

void TrimLeft ();
void TrimRight ();
void TruncateAt (int n);

CString Left (int nCount) const;
CString Mid (int nFirst, int nCount) const;
CString Right (int nCount) const;

int Find (TCHAR ch) const;
int ReverseFind (TCHAR ch) const;

void MakeUpper ();
void MakeLower ();
void ReplaceBy (TCHAR chFrom, TCHAR chTo);

void AllocCopy (CString& dest, int nCopyLen, int nCopyIndex, int nExtraLen) const;

// Assignment operations
void AssignCopy (int nSrcLen, LPCTSTR lpszSrcData);
const CString& operator= (const CString& str);
const CString& operator= (TCHAR ch);
#ifdef _UNICODE
const CString& operator= (char ch);
#endif
const CString& operator= (LPCSTR lpsz);
const CString& operator= (LPCWSTR lpsz);
const CString& operator= (const unsigned char* psz);

// Concatenation operations
void ConcatCopy (int len1, LPCTSTR lpsz1, int len2, LPCTSTR lpsz2);
void ConcatInPlace (int len, LPCTSTR lpsz);
const CString& operator+= (const CString& str);
const CString& operator+= (TCHAR ch);
#ifdef _UNICODE
const CString& operator+= (char ch);
#endif
const CString& operator+= (LPCTSTR lpsz);

friend CString operator+ (const CString& str1, const CString& str2);

// Comparison operations
int Compare (LPCTSTR lpsz) const;
int CompareNoCase (LPCTSTR lpsz) const;

#ifndef NO_BSTR_SUPPORT
BSTR AllocSysString () const;
#endif

protected:
LPTSTR m_pchData;// buffer pointer
int m_nDataLength;// data length in TCHAR, excluding 0 terminator
int m_nBuffLength;// buffer length in TCHAR

void Init ();
void AllocBuffer
(
IN int nLen// #TCHARs to allocate
);

LPTSTR GetData () const;// return m_pchData if its not zero, else return s_NULL_STRING

// helper functions
static int PASCAL SafeStrlen// return #bytes (ANSI) or #Characters(Unicode), excluding terminating 0
(
IN LPCTSTR lpsz// could be 0
);
};

////////////////////////////////////////////////////////////////////////////////
// Compare helpers

BOOL operator== (const CString& str1, const CString& str2);
BOOL operator== (const CString& str1, LPCTSTR lpsz2);
BOOL operator== (LPCTSTR lpsz1, const CString& str2);
BOOL operator!= (const CString& str1, const CString& str2);
BOOL operator!= (const CString& str1, LPCTSTR lpsz2);
BOOL operator!= (LPCTSTR lpsz1, const CString& str2);
BOOL operator < (const CString& str1, const CString& str2);
BOOL operator < (const CString& str1, LPCTSTR lpsz2);
BOOL operator < (LPCTSTR lpsz1, const CString& str2);
BOOL operator > (const CString& str1, const CString& str2);
BOOL operator > (const CString& str1, LPCTSTR lpsz2);
BOOL operator > (LPCTSTR lpsz1, const CString& str2);
BOOL operator<= (const CString& str1, const CString& str2);
BOOL operator<= (const CString& str1, LPCTSTR lpsz2);
BOOL operator<= (LPCTSTR lpsz1, const CString& str2);
BOOL operator>= (const CString& str1, const CString& str2);
BOOL operator>= (const CString& str1, LPCTSTR lpsz2);
BOOL operator>= (LPCTSTR lpsz1, const CString& str2);

#ifndef _DEBUG

inline CString::~CString ()
{
Empty ();
}

inline void CString::Init ()
{
m_pchData = 0;
m_nDataLength = 0;
m_nBuffLength = 0;
}

inline BOOL CString::IsEmpty () const
{
return (0 == m_nDataLength);
}

inline int CString::GetLength () const
{
return m_nDataLength;
}

inline int PASCAL CString::SafeStrlen
(
IN LPCTSTR lpsz// could be 0
)
{
return (0 == lpsz) ? 0 : lstrlen(lpsz);
}

inline CString::operator LPCTSTR () const
{
return (LPCTSTR) GetData ();
}

inline TCHAR CString::GetAt (int nIndex) const
{
assert(nIndex >= 0);
assert(nIndex < m_nDataLength);
return GetData()[nIndex];
}

inline LPTSTR CString::GetData () const
{
return (m_pchData ? m_pchData : g_NULL_STRING);
}

////////////////////////////////////////////////////////////////////////////////
// Compare helpers

inline BOOL operator== (const CString& str1, const CString& str2)
{
return str1.Compare (str2) == 0;
}

inline BOOL operator== (const CString& str1, LPCTSTR lpsz2)
{
return str1.Compare (lpsz2) == 0;
}

inline BOOL operator== (LPCTSTR lpsz1, const CString& str2)
{
return str2.Compare (lpsz1) == 0;
}

inline BOOL operator!= (const CString& str1, const CString& str2)
{
return str1.Compare (str2) != 0;
}

inline BOOL operator!= (const CString& str1, LPCTSTR lpsz2)
{
return str1.Compare (lpsz2) != 0;
}

inline BOOL operator!= (LPCTSTR lpsz1, const CString& str2)
{
return str2.Compare (lpsz1) != 0;
}

inline BOOL operator < (const CString& str1, const CString& str2)
{
return str1.Compare (str2) < 0;
}

inline BOOL operator < (const CString& str1, LPCTSTR lpsz2)
{
return str1.Compare (lpsz2) < 0;
}

inline BOOL operator < (LPCTSTR lpsz1, const CString& str2)
{
return str2.Compare (lpsz1) > 0;
}

inline BOOL operator > (const CString& str1, const CString& str2)
{
return str1.Compare (str2) > 0;
}

inline BOOL operator > (const CString& str1, LPCTSTR lpsz2)
{
return str1.Compare (lpsz2) > 0;
}

inline BOOL operator > (LPCTSTR lpsz1, const CString& str2)
{
return str2.Compare (lpsz1) < 0;
}

inline BOOL operator<= (const CString& str1, const CString& str2)
{
return str1.Compare (str2) <= 0;
}

inline BOOL operator<= (const CString& str1, LPCTSTR lpsz2)
{
return str1.Compare (lpsz2) <= 0;
}

inline BOOL operator<= (LPCTSTR lpsz1, const CString& str2)
{
return str2.Compare (lpsz1) >= 0;
}

inline BOOL operator>= (const CString& str1, const CString& str2)
{
return str1.Compare (str2) >= 0;
}

inline BOOL operator>= (const CString& str1, LPCTSTR lpsz2)
{
return str1.Compare (lpsz2) >= 0;
}

inline BOOL operator>= (LPCTSTR lpsz1, const CString& str2)
{
return str2.Compare (lpsz1) <= 0;
}

inline int CString::Compare (LPCTSTR lpsz) const
{
return _tcscmp(GetData(), lpsz);
}

inline int CString::CompareNoCase (LPCTSTR lpsz) const
{
return _tcsicmp(GetData(), lpsz);
}

#endif // _DEBUG

#endif // _STRING_H_


HASH.H

////////////////////////////////////////////////////////////////////////////////
// hash.h
//
// Copyright (C) 1987-1997 By Microsoft Corp. All rights reserved.
// Copyright (C) 1997 Metawise Computing, Inc. All rights reserved.
//
////////////////////////////////////////////////////////////////////////////////

#ifndef _HASH_H_
#define _HASH_H_

class CHashItem
{
public:
CHashItem (void* pData);

void* m_pData;
CHashItem* m_pNext;
};

typedef CHashItem* PCHashItem;

// A hash table which uses a DWORD as its key.
// A generic pointer can be associated with the key.
class CDWordHashItem : public CHashItem
{
public:
CDWordHashItem (DWORD dwKey, void* pData);

DWORD m_dwKey;
};

class CStringHashItem : public CHashItem
{
public:
CStringHashItem (LPCTSTR lpszKey, void* pData);

CString m_strKey;
};

typedef struct
{
int nIndex;
CHashItem* pItem;

} HashEnumContext;

class CHashTable
{
public:
CHashTable (int nSize);
~CHashTable ();

// Operations
BOOL LookUp (DWORD dwKey, void** ppData);
BOOL Insert (DWORD dwKey, void* pData);
void* Delete (DWORD dwKey);

BOOL LookUp (LPCTSTR lpszKey, void** ppData);
BOOL Insert (LPCTSTR lpszKey, void* pData);
void* Delete (LPCTSTR lpszKey);

// methods to enumerate all items in hash table;
// if items are deleted in the middle of enumeration,
// these functions may cause crash!!!
DWORD FindFirst (LPTSTR* ppszKey, void** ppData);
BOOL FindNext (DWORD dwHandle, LPTSTR* ppszKey, void** ppData);
void FindClose (DWORD dwHandle);

protected:
// Implementations
void InsertItem (CHashItem* pItem, CHashItem** ppItems);

int Hash (DWORD dwKey);
CHashItem* FindItem (DWORD dwKey, CDWordHashItem** ppItems);
CHashItem* DeleteItem (DWORD dwKey, CDWordHashItem** ppItems);

int Hash (LPCTSTR lpszKey);
CHashItem* FindItem (LPCTSTR lpszKey, CStringHashItem** ppItems);
CHashItem* DeleteItem (LPCTSTR lpszKey, CStringHashItem** ppItems);

int m_nSize;// size of array m_ppItems[]
int m_nCount;// #items in hash table
CHashItem** m_ppItems;// array of item pointers
};

#endif // _HASH_H_


LIST.H

////////////////////////////////////////////////////////////////////////////////
// list.h
//
// Copyright (C) 1987-1997 By Microsoft Corp. All rights reserved.
// Copyright (C) 1997 Metawise Computing, Inc. All rights reserved.
//
////////////////////////////////////////////////////////////////////////////////

#ifndef _SLIST_H_
#define _SLIST_H_

class CListItem
{
public:
CListItem ();

virtual void FreeData ();

protected:
CListItem* m_pNext;
};

class CList
{
public:
CList ();

protected:
int m_nCount;// #items in list
CListItem* m_pHead;
CListItem* m_pTail;
};

#endif // _SLIST_H_


MAIN.CPP

//-----------------------------------------------------------------------------
// Microsoft OLE DB QURYDEMO Sample
// Copyright (C) 1994 - 1997 By Microsoft Corporation.
//
// @doc
//
// @module MAIN.CPP
//
//-----------------------------------------------------------------------------------
/*
PROGRAM: QURYDEMO
========

PURPOSE:
========
demonstrates a simple MDI application that allows a user to
simultaneously connect to multiple hetrogeneous databases
and perform SQL queries to get results.

FUNCTIONS:
==========
WinMain() - main routine
MainWndProc() - processes Main Application Window messages
MDIChildProc() - processes MDI child window messages
ToolbarProc() - processes tool bar messages
StatusbarProc() - processes Status bar messages
ConnectDlgProc() - processes Connection Dialog box messages
DisconnectDlgProc() - processes Disconnect Dialog box messages
AboutDlgProc() - processes messages for About dialog box
MDIChildDlgProc() - process messages for dummy child dialog box in MDI window
DrawBitmap() - draws bitmaps for toolbuttons

COMMENTS:
=========
Created by Microsoft Corporation.
*/

#include <stdio.h>
#include <string.h>
#include <time.h>

#include <windows.h>
#include <windowsx.h>

#ifdef CTL3D
#include "ctl3d.h"
#endif

#include "oledb.h"
#include "oledberr.h"

#include "qurydemo.h"

// Globals
HWNDhWndFrame;      // Main Frame Window handle
HWNDhWndCrsrList;   // hdbc(s) combobox on the tool bar
HWNDhWndStmtList;   // hstmt(s) combobox on the tool bar
HWNDhWndMDIClient;  // MDI Client window handle
HWNDhWndActiveChild;// Current active MDI Child window
HINSTANCEhAppInstance;   // Application instance

intiTimex;         // Time box (on the status bar) width
intiDatex;         // Date box (on the status bar) width
HWNDhWndToolbar;    // Toolbar window handle
HWNDhWndStatusbar;  // status bar handle
WORDwStatusText;    // Status text state
RECTrectStatusText; // Text box on the status bar

// Locals.
void FillPrompt(HWND hWnd);

/*
FUNCTION: WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
COMMENTS: Application Entry Routine.
  Register Classes. Create Main Window and MDI Child Window.
  Process Main Message Loop.
*/
int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
MSGmsg;//msg structure
WNDCLASSwc;//class structure
    HICONhMainIcon;//App Icon
    HICONhMDIChildIcon;//MDI Child Icon
HWNDhWndMDIChild;//temp MDI Child Window Handle
charszBuffer[MAXBUFLEN+1];//temp string buffer to check class name
BOOLbDialogMessage = FALSE;//temp boolean to check dilogbox msgs
HACCELhAccel;//accelerator table handle

hAppInstance = hInstance;

// Check if application is already running, if
// so make it active and bring it in focus
if (hWndFrame = FindWindow(OLEDBFRAMECLASS, NULL))
{
hWndFrame = GetLastActivePopup(hWndFrame);
if (IsIconic(hWndFrame))
OpenIcon(hWndFrame);
else
BringWindowToTop(hWndFrame);
ACTIVATEWINDOW(hWndFrame);
return (FALSE);
}

// Initialize OLEDB Interface
if (!InitEnvironment())
{
MessageBox(hWndFrame, INITERROR, EXECERROR, MB_OK|MB_ICONHAND);
return (FALSE);
}

// Register window classes for the application - Main Window Class
wc.style= 0;
wc.lpfnWndProc= MainWndProc;
wc.cbClsExtra= 0;
wc.cbWndExtra= 0;
wc.hInstance= hAppInstance;
wc.hIcon= hMainIcon = LoadIcon(hAppInstance, APPICON);
wc.hCursor= LoadCursor((HINSTANCE)NULL, IDC_ARROW);
wc.hbrBackground= (HBRUSH)(COLOR_APPWORKSPACE+1);
wc.lpszMenuName= QURYDEMOMENU;
wc.lpszClassName= OLEDBFRAMECLASS;

if (!RegisterClass(&wc))
{
MessageBox(hWndFrame, CLASSERROR, EXECERROR, MB_OK|MB_ICONHAND|MB_TASKMODAL);
return (FALSE);
}

// register Toolbar Class
wc.hIcon= (HICON)NULL;
wc.lpszMenuName=  NULL;
wc.lpfnWndProc= ToolbarProc;
wc.lpszClassName= OLEDBTOOLCLASS;

if (!RegisterClass(&wc))
{
MessageBox(hWndFrame, CLASSERROR, EXECERROR, MB_OK|MB_ICONHAND|MB_TASKMODAL);
return (FALSE);
}

// register Statusbar Class
wc.lpfnWndProc= StatusbarProc;
wc.lpszClassName= OLEDBSTATUSCLASS;

if (!RegisterClass(&wc))
{
MessageBox(hWndFrame, CLASSERROR, EXECERROR, MB_OK|MB_ICONHAND|MB_TASKMODAL);
return (FALSE);
}

// register MDI Child Window Class
wc.hIcon = hMDIChildIcon = LoadIcon(hAppInstance, QUERYWINDOWICON);
wc.hbrBackground= (HBRUSH)(COLOR_WINDOW+1);
wc.cbWndExtra= CBWNDEXTRA;
wc.lpszClassName= OLEDBMDICLASS;
wc.lpfnWndProc= MDIChildProc;

if (!RegisterClass(&wc))
{
MessageBox(hWndFrame, CLASSERROR, EXECERROR, MB_OK|MB_ICONHAND|MB_TASKMODAL);
return (FALSE);
}

// create Main window and the MDI Client window
if (!(hWndFrame = CreateWindow(OLEDBFRAMECLASS, APPTITLE, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
(HWND)NULL, (HMENU)NULL, hAppInstance, NULL)))
{
MessageBox(hWndFrame, CREATEMAINERR, EXECERROR, MB_OK|MB_ICONHAND|MB_TASKMODAL);
return (FALSE);
}

ShowWindow(hWndFrame, nCmdShow);
UpdateWindow(hWndFrame);

#ifdef CTL3D
// register 3D controls
Ctl3dRegister(hAppInstance);
Ctl3dAutoSubclass(hAppInstance);
#endif

// load accelerators
hAccel = LoadAccelerators(hAppInstance, APPACCELERATOR);

// acquire and dispatch messages until a WM_QUIT message is received
while (GetMessage(&msg, (HWND)NULL, 0, 0))
{

// check for App accelerators
if (TranslateAccelerator(hWndFrame, hAccel, &msg))
continue;

// check for MDI accelerators
if (TranslateMDISysAccel(hWndMDIClient, &msg))
continue;

        // each MDI Child has a modeless dialog in its client area
        // to provide tab controls. Check for Modeless Dialog msgs.
for (hWndMDIChild = GetWindow(hWndMDIClient, GW_CHILD); hWndMDIChild; hWndMDIChild = GetWindow(hWndMDIChild, GW_HWNDNEXT))
{
GetClassName(hWndMDIChild, szBuffer, MAXBUFLEN);
if (strcmp(szBuffer, OLEDBMDICLASS))
continue;
if (IsDialogMessage((HWND)GetWindowLong(hWndMDIChild, GWLAPP_HDLG), &msg))
{
bDialogMessage = TRUE;
break;
}
}

if (bDialogMessage)
{
bDialogMessage = FALSE;
continue;
        }

        // if the message does not need special processing, dispatch it
TranslateMessage(&msg);
DispatchMessage(&msg);
}

#ifdef CTL3D
// unregister 3D controls
Ctl3dUnregister(hAppInstance);
#endif

// Free memory used by OLEDB interface
FreeEnvironment();

// free Icon resources
if (hMainIcon)
DestroyIcon(hMainIcon);

if (hMDIChildIcon)
DestroyIcon(hMDIChildIcon);

return (msg.wParam);
}

/*
FUNCTION: MainWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
COMMENTS: Windows Callback procedure to handle Window messages.
          Menu Commands and System Command messages are handled by
          this main window.
*/
long CALLBACK MainWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
static intiToolY;         //remember Toolbar height for resize
static intiStatusY;       //remember Statusbar height for resize

case WM_CREATE:
        {
CLIENTCREATESTRUCTccs;//MDIclient window structure
HDChDC;//Device Context handle
SIZEsizeBar;//Size of a text bar
RECTrectCombo;  //Size of combo box

// Create child windows
                //1. combobox to display connections - DSN, SESSION
                //2. combobox to display HSTMT on current SESSION
                //3. Toolbar to put toolbuttons
                //4. Statusbat to display current action, date and time
                //5. MDI Client Window to process MDI children

hWndCrsrList = CreateWindow(COMBOBOXCLASS, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS |
WS_VSCROLL | CBS_DROPDOWNLIST | CBS_DISABLENOSCROLL, 5, 2, 180, 150, hWnd,
(HMENU)IDW_CRSRLIST, hAppInstance, NULL);

hWndStmtList = CreateWindow(COMBOBOXCLASS, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS |
WS_VSCROLL | CBS_DROPDOWNLIST | CBS_DISABLENOSCROLL, 190, 2, 150, 150, hWnd,
(HMENU)IDW_COMMANDLIST, hAppInstance, NULL);

hWndToolbar = CreateWindow(OLEDBTOOLCLASS, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS,
0, 0, 0, 0, hWnd, (HMENU)IDW_TOOLBAR, hAppInstance, NULL);

hWndStatusbar = CreateWindow(OLEDBSTATUSCLASS, NULL, WS_CHILD | WS_VISIBLE |
WS_CLIPSIBLINGS, 0, 0, 0, 0, hWnd, (HMENU)IDW_STATUSBAR, hAppInstance, NULL);

ccs.hWindowMenu = GetSubMenu(GetMenu(hWnd), WINDOWMENUPLACE);
ccs.idFirstChild = IDM_MDICHILD;

hWndMDIClient = CreateWindow(MDICLIENTCLASS, NULL, WS_CHILD | WS_CLIPSIBLINGS |
WS_CLIPCHILDREN | WS_VSCROLL | WS_HSCROLL | WS_VISIBLE, 0, 0, 0, 0, hWnd,
(HMENU)IDW_MDICLIENT, hAppInstance, (LPSTR)&ccs);

// check to see if any of the above window creation failed
if (!hWndCrsrList || !hWndStmtList || !hWndToolbar || !hWndStatusbar || !hWndMDIClient)
{
MessageBox(hWnd, CREATEMAINERR, EXECERROR, MB_OK|MB_ICONHAND|MB_TASKMODAL);
PostMessage(hWnd, WM_CLOSE, 0, 0);
break;
}

// calculate proper text height for tool and status bars
GetWindowRect(hWndStmtList, &rectCombo);
iToolY = rectCombo.bottom - rectCombo.top + TOOLBARMARGINY;

if (hDC = GetDC(hWndStatusbar))
{
GetTextExtentPoint(hDC, ALPHABETS, strlen(ALPHABETS), &sizeBar);
ReleaseDC(hWndStatusbar, hDC);
iStatusY = sizeBar.cy + STATUSBARMARGINY;
}
else
{
iStatusY = 0;
DestroyWindow(hWndStatusbar);
}

break;
}

// Limit minimum size of the main window
case WM_GETMINMAXINFO:

((MINMAXINFO FAR*)lParam)->ptMinTrackSize.x =
max(MINWIDTH, rectStatusText.right-rectStatusText.left+iTimex+iDatex+14);
((MINMAXINFO FAR*)lParam)->ptMinTrackSize.y = MINHEIGHT;
break;

// Resize children
case WM_SIZE:
        {
WORDwWidth = LOWORD(lParam);//width of rectangle
WORDwHeight = HIWORD(lParam);//height of rectangle

MoveWindow(hWndToolbar, 0, 0, wWidth, iToolY, TRUE);
MoveWindow(hWndStatusbar, 0, wHeight-iStatusY, wWidth, iStatusY, TRUE);
InvalidateRect(hWndStatusbar, NULL, TRUE);
MoveWindow(hWndMDIClient, 0, iToolY, wWidth, wHeight-iStatusY-iToolY, TRUE);
break;
        }

#ifdef CTL3D
// Inform 3D controls of color change
case WM_SYSCOLORCHANGE:

Ctl3dColorChange();
break;
#endif
// Close comboboxes if dropped down
case WM_SYSCOMMAND:

SendMessage(hWndCrsrList, CB_SHOWDROPDOWN, (WPARAM)FALSE, 0);
SendMessage(hWndStmtList, CB_SHOWDROPDOWN, (WPARAM)FALSE, 0);
return (DefFrameProc(hWnd, hWndMDIClient, message, wParam, lParam));

// Initialize popup menus
case WM_INITMENUPOPUP:
        {
        intiMenuId;//Menu ID being processed
intnItems;         //# of menu items
intnPos;           //Menu Position

        // Ignore the msg if it is for a system menu
if (HIWORD(lParam))
break;

// Go through the menu items for current popup menu
// and enable/disable menu item, if required
nItems = GetMenuItemCount((HMENU)wParam);
for (nPos = 0; nPos < nItems; nPos++)
switch (iMenuId = GetMenuItemID((HMENU)wParam, nPos))
{
case IDM_DISCONNECT:
case IDM_NEW:

EnableMenuItem( (HMENU)wParam, iMenuId, MF_BYCOMMAND|
((SendMessage(hWndCrsrList, CB_GETCOUNT, 0, 0) > 0)?
MF_ENABLED:MF_GRAYED) );
break;

case IDM_QUERY:
case IDM_TILE:
case IDM_CASCADE:
case IDM_ICONS:
case IDM_CLOSEALL:

case IDM_ASSERTIONS:
case IDM_CATALOGS:
case IDM_CHARACTER_SETS:
case IDM_CHECK_CONSTRAINTS:
case IDM_COLLATIONS:
case IDM_COLUMN_DOMAIN_USAGE:
case IDM_COLUMN_PRIVILEGES:
case IDM_COLUMNS:
case IDM_CONSTRAINT_COLUMN_USAGE:
case IDM_CONSTRAINT_TABLE_USAGE:
case IDM_FOREIGN_KEYS:
case IDM_INDEXES:
case IDM_KEY_COLUMN_USAGE:
case IDM_PRIMARY_KEYS:
case IDM_PROCEDURE_COLUMNS:
case IDM_PROCEDURE_PARAMETERS:
case IDM_PROCEDURES:
case IDM_PROVIDER_TYPES:
case IDM_REFERENTIAL_CONSTRAINTS:
case IDM_SCHEMATA:
case IDM_SQL_LANGUAGES:
case IDM_STATISTICS:
case IDM_TABLE_CONSTRAINTS:
case IDM_TABLE_PRIVILEGES:
case IDM_TABLES:
case IDM_TRANSLATIONS:
case IDM_USAGE_PRIVILEGES:
case IDM_VIEW_COLUMN_USAGE:
case IDM_VIEW_TABLE_USAGE:
case IDM_VIEWS:

EnableMenuItem( (HMENU)wParam, iMenuId, MF_BYCOMMAND|
(GetWindow(hWndMDIClient, GW_CHILD)?
MF_ENABLED:MF_GRAYED) );
break;

default:
break;
}
break;
}

// Update status bar to reflect menu selection
case WM_MENUSELECT:
{
            intiMenuFlag; //Check menu type
            HMENUhMenu;           //Menu Handle
            charszMenuName[MAXBUFLEN+1]; //Menu Name

            // store Menuitem ID as a state value for text display
wStatusText = GET_WM_MENUSELECT_CMD(wParam, lParam);

// process popup menus ie non menuitem selections
iMenuFlag = GET_WM_MENUSELECT_FLAGS(wParam, lParam);

// if the selected menu is a system popup menu
        // else if the selected menu is a popup menu check menu names
        // OR check if it is a control popup menu of maximized MDI Child window
        if (wStatusText && (iMenuFlag & MF_SYSMENU) && (iMenuFlag & MF_POPUP))
wStatusText = IDM_POPUPAPPSYS;
else if (wStatusText && (iMenuFlag & MF_POPUP))
{
hMenu = (HMENU)wStatusText;
GetMenuString(hMenu, 0, szMenuName, MAXBUFLEN, MF_BYPOSITION);
if (!strcmp(szMenuName, MENUITEMCONNECT))
wStatusText = IDM_POPUPLOGIN;
else if (!strcmp(szMenuName, MENUITEMQUERY))
wStatusText = IDM_POPUPQUERY;
else if (!strcmp(szMenuName, MENUITEMTILE))
wStatusText = IDM_POPUPSCHEMA;
else if (!strcmp(szMenuName, MENUITEMTYPES))
wStatusText = IDM_POPUPWINDOW;
else if (!strcmp(szMenuName, MENUITEMAPPHELP))
wStatusText = IDM_POPUPHELP;
else if (GetMenuString(hMenu, SC_NEXTWINDOW, szMenuName, MAXBUFLEN, MF_BYCOMMAND)>0)
wStatusText = IDM_POPUPMDISYS;
            else
                wStatusText = 0;
        }

// invalidate status bar for repaint
InvalidateRect(hWndStatusbar, &rectStatusText, TRUE);
break;
}

// Process menu commands
case WM_COMMAND:

switch (GET_WM_COMMAND_ID(wParam, lParam))
{
    DLGPROClpDlgProc;      //Procedure address for dialogbox

case IDM_INITIALIZE: // bring up connect dialog & do connect processing
lpDlgProc = MakeProcInstance(ConnectDlgProc, hAppInstance);
DialogBox(hAppInstance, INITDIALOG, hWnd,lpDlgProc);
FreeProcInstance(lpDlgProc);
break;

case IDM_DISCONNECT: // bringup disconnect dlg and do disconnects

lpDlgProc = MakeProcInstance(DisconnectDlgProc, hAppInstance);
DialogBox(hAppInstance, DISCONNECTDIALOG, hWnd,lpDlgProc);
FreeProcInstance(lpDlgProc);
break;

case IDM_QUERY: // process execute query request

ExecuteQuery();
break;

case IDM_ASSERTIONS: // Execute DBSCHEMA_ASSERTIONS request

GetSchemaRowset(DBSCHEMA_ASSERTIONS);
break;

case IDM_CATALOGS: // Execute DBSCHEMA_CATALOGS request

GetSchemaRowset(DBSCHEMA_CATALOGS);
break;

case IDM_CHARACTER_SETS: // Execute DBSCHEMA_CHARACTER_SETS request

GetSchemaRowset(DBSCHEMA_CHARACTER_SETS);
break;

case IDM_CHECK_CONSTRAINTS: // Execute DBSCHEMA_CHECK_CONSTRAINTS request

GetSchemaRowset(DBSCHEMA_CHECK_CONSTRAINTS);
break;

case IDM_COLLATIONS: // Execute DBSCHEMA_COLLATIONS request

GetSchemaRowset(DBSCHEMA_COLLATIONS);
break;

case IDM_COLUMN_DOMAIN_USAGE: // Execute DBSCHEMA_COLUMN_DOMAIN_USAGE request

GetSchemaRowset(DBSCHEMA_COLUMN_DOMAIN_USAGE);
break;

case IDM_COLUMN_PRIVILEGES: // Execute DBSCHEMA_COLUMN_PRIVELEGES request

GetSchemaRowset(DBSCHEMA_COLUMN_PRIVILEGES);
break;

case IDM_COLUMNS: // Execute DBSCHEMA_COLUMNS request

GetSchemaRowset(DBSCHEMA_COLUMNS);
break;

case IDM_CONSTRAINT_COLUMN_USAGE: // Execute DBSCHEMA_CONSTRAINT_COLUMN_USAGE request

GetSchemaRowset(DBSCHEMA_CONSTRAINT_COLUMN_USAGE);
break;

case IDM_CONSTRAINT_TABLE_USAGE: // Execute DBSCHEMA_CONSTRAINT_TABLE_USAGE request

GetSchemaRowset(DBSCHEMA_CONSTRAINT_TABLE_USAGE);
break;

case IDM_FOREIGN_KEYS: // Execute DBSCHEMA_FOREIGN_KEYS request

GetSchemaRowset(DBSCHEMA_FOREIGN_KEYS);
break;

case IDM_INDEXES: // Execute DBSCHEMA_INDEXES request

GetSchemaRowset(DBSCHEMA_INDEXES);
break;

case IDM_KEY_COLUMN_USAGE: // Execute DBSCHEMA_KEY_COLUMN_USAGE request

GetSchemaRowset(DBSCHEMA_KEY_COLUMN_USAGE);
break;

case IDM_PRIMARY_KEYS: // Execute DBSCHEMA_PRIMARY_KEYS request

GetSchemaRowset(DBSCHEMA_PRIMARY_KEYS);
break;

case IDM_PROCEDURE_COLUMNS: // Execute DBSCHEMA_PROCEDURE_COLUMNS request

GetSchemaRowset(DBSCHEMA_PROCEDURE_COLUMNS);
break;

case IDM_PROCEDURE_PARAMETERS: // Execute DBSCHEMA_PROCEDURE_PARAMETERS request

GetSchemaRowset(DBSCHEMA_PROCEDURE_PARAMETERS);
break;

case IDM_PROCEDURES: // Execute DBSCHEMA_PROCEDURES request

GetSchemaRowset(DBSCHEMA_PROCEDURES);
break;

case IDM_PROVIDER_TYPES: // Execute DBSCHEMA_TYPES request

GetSchemaRowset(DBSCHEMA_PROVIDER_TYPES);
break;

case IDM_REFERENTIAL_CONSTRAINTS: // Execute DBSCHEMA_REFERENTIAL_CONSTRAINTS request

GetSchemaRowset(DBSCHEMA_REFERENTIAL_CONSTRAINTS);
break;

case IDM_SCHEMATA: // Execute DBSCHEMA_SCHEMATA request

GetSchemaRowset(DBSCHEMA_SCHEMATA);
break;

case IDM_SQL_LANGUAGES: // Execute DBSCHEMA_SQL_LANGUAGES request

GetSchemaRowset(DBSCHEMA_SQL_LANGUAGES);
break;

case IDM_STATISTICS: // Execute DBSCHEMA_STATISTICS request

GetSchemaRowset(DBSCHEMA_STATISTICS);
break;

case IDM_TABLE_CONSTRAINTS: // Execute DBSCHEMA_TABLE_CONSTRAINTS request

GetSchemaRowset(DBSCHEMA_TABLE_CONSTRAINTS);
break;

case IDM_TABLE_PRIVILEGES: // Execute DBSCHEMA_TABLE_PRIVILEGES request

GetSchemaRowset(DBSCHEMA_TABLE_PRIVILEGES);
break;

case IDM_TABLES: // Execute DBSCHEMA_TABLES request

GetSchemaRowset(DBSCHEMA_TABLES);
break;

case IDM_TRANSLATIONS: // Execute DBSCHEMA_TRANSLATIONS request

GetSchemaRowset(DBSCHEMA_TRANSLATIONS);
break;

case IDM_USAGE_PRIVILEGES: // Execute DBSCHEMA_USAGE_PRIVILEGES request

GetSchemaRowset(DBSCHEMA_USAGE_PRIVILEGES);
break;

case IDM_VIEW_COLUMN_USAGE: // Execute DBSCHEMA_VIEW_COLUMN_USAGE request

GetSchemaRowset(DBSCHEMA_VIEW_COLUMN_USAGE);
break;

case IDM_VIEW_TABLE_USAGE: // Execute DBSCHEMA_VIEW_TABLE_USAGE request

GetSchemaRowset(DBSCHEMA_VIEW_TABLE_USAGE);
break;

case IDM_VIEWS: // Execute DBSCHEMA_VIEWS request

GetSchemaRowset(DBSCHEMA_VIEWS);
break;

case IDM_EXIT: // process exit request

SendMessage(hWndFrame, WM_CLOSE, 0, 0);
break;

case IDM_NEW: // create a new query window on current connect

NewICommandWindow();
break;

case IDM_TILE: // let MDI Client tile the MDI children

SendMessage(hWndMDIClient, WM_MDITILE, 0, 0);
break;

case IDM_CASCADE: // let MDI Client cascade MDI children

SendMessage(hWndMDIClient, WM_MDICASCADE, 0, 0);
break;

case IDM_ICONS: // let MDI Client arrange iconic MDI children

SendMessage(hWndMDIClient, WM_MDIICONARRANGE, 0, 0);
break;

case IDM_CLOSEALL: // Close all open windows and free hstmts
            {
HWNDhWndTemp;//temp window handle

// hide MDI Client Windows to avoid repaints
ShowWindow(hWndMDIClient,SW_HIDE);
while (hWndTemp = GetWindow(hWndMDIClient, GW_CHILD))
SendMessage(hWndMDIClient, WM_MDIDESTROY, (WPARAM)hWndTemp, 0);
ShowWindow(hWndMDIClient, SW_SHOW);
break;
}

case IDM_ABOUT: // bringup About dialog

lpDlgProc = MakeProcInstance(AboutDlgProc, hAppInstance);
DialogBox(hAppInstance, ABOUTDIALOG, hWnd, lpDlgProc);
FreeProcInstance(lpDlgProc);
break;

case IDW_CRSRLIST: // change current cursor ?

if (GET_WM_COMMAND_CMD(wParam, lParam) == CBN_SELCHANGE)
ChangeCurrentCursor(GET_WM_COMMAND_HWND(wParam, lParam));
else if (GET_WM_COMMAND_CMD(wParam, lParam) == CBN_KILLFOCUS)
PostMessage(GET_WM_COMMAND_HWND(wParam, lParam), CB_SHOWDROPDOWN,
(WPARAM)FALSE, 0);
else
DefFrameProc(hWnd, hWndMDIClient, WM_COMMAND, wParam, lParam);
break;

case IDW_COMMANDLIST: // change current Command ?

if (GET_WM_COMMAND_CMD(wParam, lParam) == CBN_SELCHANGE)
ChangeCurrentICommand(GET_WM_COMMAND_HWND(wParam, lParam));
else if (GET_WM_COMMAND_CMD(wParam, lParam) == CBN_KILLFOCUS)
PostMessage(GET_WM_COMMAND_HWND(wParam, lParam), CB_SHOWDROPDOWN,
(WPARAM)FALSE, 0);
else
DefFrameProc(hWnd, hWndMDIClient, WM_COMMAND, wParam, lParam);
break;

default:

DefFrameProc(hWnd, hWndMDIClient, WM_COMMAND, wParam, lParam);
break;
}
break;

case WM_CLOSE: //close all MDI windows, hdbcs & hstmts, else fail

return (CloseIDBCreateCommand() ? DefFrameProc(hWnd, hWndMDIClient, message, wParam, lParam) : FALSE);

case WM_DESTROY:

PostQuitMessage(0);
break;

default:
return (DefFrameProc(hWnd, hWndMDIClient, message, wParam, lParam));
}
return (0);
}

/*
FUNCTION: MDIChildProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
COMMENTS: Window Procedure for MDI Child windows
*/
long CALLBACK MDIChildProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
HWNDhWndTemp;//temporary window handle
        DLGPROClpDlgProc;//procedure address for modeless dialog

case WM_CREATE:

// create child windows
// 1. Modeless Dialog box in the background to process tabs
// 2. Static Text to display prompt
// 3. Edit Control to type SQL text
// 4. List Box to display results
// store dialog handle for future reference
// set focus to edit control

// if create failed due to low system resources
if (!hWnd)
break;

hWndActiveChild = hWnd;
lpDlgProc = MakeProcInstance(MDIChildDlgProc, hAppInstance);
hWndTemp = CreateDialog(hAppInstance, MDICHILDDIALOG, hWndActiveChild, lpDlgProc);

// check to see if the dialog was created?, if not destroy this window
if (!hWndTemp)
{
FreeProcInstance(lpDlgProc);
return (-1);
}

SetWindowLong(hWnd, GWLAPP_HDLG, (LONG)hWndTemp);
SetFocus(GetDlgItem(hWndTemp, IDTEXT_SQL));
break;

case WM_SIZE:
                {
WORDwWidth;//New Width of MDI Child
WORDwHeight;//New Height of MDI Child
HDChDC;//Device Context
charszBuffer[MAXBUFLEN+1];//Static Control Text
intnStrLen;//Buffer Length
SIZEsize;//Screen size for text display

// call default procedure first, to let MDI position the child & then move its children
DefMDIChildProc(hWnd, message, wParam, lParam);

    // move child windows with proper screen size for text display
wWidth = LOWORD(lParam);
wHeight = HIWORD(lParam);
hWndTemp = (HWND)GetWindowLong(hWnd, GWLAPP_HDLG);

nStrLen = GetWindowText(GetDlgItem(hWndTemp, IDTEXT_PRMPT), szBuffer, MAXBUFLEN);
hDC = GetDC(hWnd);
GetTextExtentPoint(hDC, szBuffer, nStrLen, &size);
ReleaseDC(hWnd, hDC);

        MoveWindow(hWndTemp, 0, 0, wWidth, wHeight, TRUE);
MoveWindow(GetDlgItem(hWndTemp, IDTEXT_PRMPT), 0, 0, size.cx+2, size.cy+2, TRUE);
MoveWindow(GetDlgItem(hWndTemp, IDTEXT_SQL), size.cx+3, 0, wWidth - (size.cx+2), size.cy+2, TRUE);
MoveWindow(GetDlgItem(hWndTemp, IDLIST_RSLT), 0, size.cy+3, wWidth, wHeight - (size.cy+2), TRUE);
break;
                }

case WM_MENUSELECT: // update status bar to reflect menu selection
                {
                intiMenuFlag;//Check menu type

// store the Menu Item Id as a state value for text display
wStatusText = GET_WM_MENUSELECT_CMD(wParam, lParam);

// if none of the menuitems was selected, check if
// the control popup menu is selected.
iMenuFlag = GET_WM_MENUSELECT_FLAGS(wParam, lParam);

// if the selected menu is a system popup menu
if (wStatusText && (iMenuFlag & MF_SYSMENU) && (iMenuFlag & MF_POPUP))
wStatusText = IDM_POPUPMDISYS;

// invalidate status bar for repaint
InvalidateRect(hWndStatusbar, &rectStatusText, TRUE);
break;
}

case WM_MDIACTIVATE:

// check if the display of comboboxes require a change
if (GET_WM_MDIACTIVATE_FACTIVATE(hWnd, wParam, lParam) &&
(hWndActiveChild) && (hWndActiveChild != hWnd))
{
hWndActiveChild = hWnd;
DisplayNewCrsrAndICommand();
}
break;

case WM_MOUSEACTIVATE:

        // current window has changed, update comboboxes.
hWndActiveChild = hWnd;
DisplayNewCrsrAndICommand();
break;

case WM_SETFOCUS:

// pass on the focus to the edit box for user to type in SQL
SetFocus(GetDlgItem((HWND)GetWindowLong(hWnd, GWLAPP_HDLG), IDTEXT_SQL));
break;

case WM_DESTROY:

        // check if the window was being destroyed while creation failed
        if (!hWnd)
break;

// close the window and free instance thunk for modeless dialog
CloseICommandWindow(hWnd);
lpDlgProc = (DLGPROC)GetWindowLong((HWND)GetWindowLong(hWnd, GWLAPP_HDLG), DWL_DLGPROC);
DestroyWindow((HWND)GetWindowLong(hWnd, GWLAPP_HDLG));
FreeProcInstance(lpDlgProc);

if (hWnd == hWndActiveChild)
hWndActiveChild = (HWND)NULL;
break;

default:
return (DefMDIChildProc(hWnd, message, wParam, lParam));
}
return (0);
}

/*
FUNCTION: ToolbarProc(HWND hWnd, UINT   message, WPARAM wParam, LPARAM lParam)
COMMENTS: callback window procedure for toolbar window.
  Handle pain and mouse messages to paint the toolbar and
  provide default button behaviour for toolbar buttons.
*/
long CALLBACK ToolbarProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
static HBITMAPhbmpNewQuery;//btn1 bitmap handle
static HBITMAPhbmpRunQuery;//btn2 bitmap handle
static RECTstNewQuery;//btn1 rectangle
static RECTstRunQuery;                 //btn2 rectangle
POINTstMousePosition;//current mouse pos
BOOLbButtonPosition;//mouse pos flag
static BOOLbNewQueryBtnDown;//was btn1 down before?
static BOOLbRunQueryBtnDown;//was btn2 down before?
static intnLastButtonDown;//Which btn was down before?

bNewQueryBtnDown = FALSE;
bRunQueryBtnDown = FALSE;
nLastButtonDown  = 0;

case WM_CREATE:

// load bitmaps for buttons
// initialize static rectangles for button positions on toolbar
// initialize state variable for status text display
hbmpNewQuery = LoadBitmap(hAppInstance, BMP_NEWQUERY);
hbmpRunQuery = LoadBitmap(hAppInstance, BMP_RUNQUERY);

stNewQuery.left   = BTTNX;
stNewQuery.right  = BTTNX+BTTNWIDTH+1;
stNewQuery.top    = BTTNY;
stNewQuery.bottom = BTTNY+BTTNHEIGHT+1;

        stRunQuery.left   = BTTNX+BTTNWIDTH+BTTNMARGIN;
        stRunQuery.right  = BTTNX+BTTNWIDTH+BTTNMARGIN+BTTNWIDTH+1;
        stRunQuery.top    = BTTNY;
        stRunQuery.bottom = BTTNY+BTTNHEIGHT+1;

wStatusText = 0;
break;

case WM_DESTROY:

// delete bitmap handles
if (hbmpNewQuery)
DeleteObject(hbmpNewQuery);
if (hbmpRunQuery)
DeleteObject(hbmpRunQuery);
break;

case WM_LBUTTONDOWN:

// Check if the mouse key lies on any one of the buttons
// if so, set state variable to reflect that button and
// invalidate proper regions on tool & status bars for update.
// set capture on mouse movements till the mouse key is
// released.
stMousePosition.x = LOWORD(lParam);
stMousePosition.y = HIWORD(lParam);

if (PtInRect(&stNewQuery, stMousePosition))
{
bNewQueryBtnDown = TRUE;
wStatusText = nLastButtonDown = IDM_NEW;
SetCapture(hWnd);
InvalidateRect(hWnd, &stNewQuery, TRUE);
InvalidateRect(hWndStatusbar, &rectStatusText, TRUE);

}
else if (PtInRect(&stRunQuery, stMousePosition))
{
bRunQueryBtnDown = TRUE;
wStatusText = nLastButtonDown = IDM_QUERY;
SetCapture(hWnd);
InvalidateRect(hWnd, &stRunQuery, TRUE);
InvalidateRect(hWndStatusbar, &rectStatusText, TRUE);
}
break;

case WM_LBUTTONUP:

// check if the mouse movements from key down movements
// were captured, if so process the key release state.
// if the key was released in the same button where it
// was pressed, it is equivalent to a button click.
if (hWnd != GetCapture())
break;

stMousePosition.x = LOWORD(lParam);
stMousePosition.y = HIWORD(lParam);

if (bNewQueryBtnDown && PtInRect(&stNewQuery, stMousePosition))
{
bNewQueryBtnDown = FALSE;
nLastButtonDown = 0;
InvalidateRect(hWnd, &stNewQuery, TRUE);
PostMessage(hWndFrame, WM_COMMAND, GET_WM_COMMAND_MPS(IDM_NEW, 0, 0));
SendMessage(hWndCrsrList, CB_SHOWDROPDOWN, (WPARAM)FALSE, 0);
SendMessage(hWndStmtList, CB_SHOWDROPDOWN, (WPARAM)FALSE, 0);
}
else if (bRunQueryBtnDown && PtInRect(&stRunQuery, stMousePosition))
{
bRunQueryBtnDown = FALSE;
nLastButtonDown = 0;
InvalidateRect(hWnd, &stRunQuery, TRUE);
PostMessage(hWndFrame, WM_COMMAND, GET_WM_COMMAND_MPS(IDM_QUERY, 0, 0));
SendMessage(hWndCrsrList, CB_SHOWDROPDOWN, (WPARAM)FALSE, 0);
SendMessage(hWndStmtList, CB_SHOWDROPDOWN, (WPARAM)FALSE, 0);
                }

ReleaseCapture();
wStatusText = 0;
InvalidateRect(hWndStatusbar, &rectStatusText, TRUE);
break;

case WM_MOUSEMOVE:

        // process mouse movement only if the mouse key was pressed
        // down and its movements were being captured. If the mouse
        // moves outside of the currently depressed button, it needs
        // to be drawn again with normal state.
if (hWnd != GetCapture())
break;

stMousePosition.x = LOWORD(lParam);
stMousePosition.y = HIWORD(lParam);

if (nLastButtonDown == IDM_NEW)
{
bButtonPosition = PtInRect(&stNewQuery, stMousePosition);
if (bNewQueryBtnDown != bButtonPosition)
{
bNewQueryBtnDown = bButtonPosition;
InvalidateRect(hWnd, &stNewQuery, TRUE);
}
}
else if (nLastButtonDown == IDM_QUERY)
{
bButtonPosition = PtInRect(&stRunQuery, stMousePosition);
if (bRunQueryBtnDown != bButtonPosition)
{
bRunQueryBtnDown = bButtonPosition;
InvalidateRect(hWnd, &stRunQuery, TRUE);
}
}
break;

case WM_PAINT:
                {
PAINTSTRUCTps;//paint structure
RECTrect;//rectangle for tool bar
HDChDC;//device context handle
intiWidth;//tool bar width
intiHeight;//tool bar height
HPENhLtGrayPen;//buttonface color pen
HPENhGrayPen;//buttonshadow color pen
intbtnx;//button x coordinate position

if (!(hDC = BeginPaint(hWnd, &ps)))
break;

GetClientRect(hWnd, &rect);
iWidth = rect.right;
iHeight = rect.bottom;

hLtGrayPen = CreatePen(PS_SOLID, 0, GetSysColor(COLOR_BTNFACE));
hGrayPen = CreatePen(PS_SOLID, 0, GetSysColor(COLOR_BTNSHADOW));

        // draw background and border
SelectObject(hDC, GetStockObject(LTGRAY_BRUSH));
SelectObject(hDC, hLtGrayPen);
Rectangle(hDC, 0, 0, iWidth, iHeight);

SelectObject(hDC, GetStockObject(BLACK_PEN));
MoveToEx(hDC, 0, iHeight-1, NULL);
LineTo(hDC, iWidth, iHeight-1);

SelectObject(hDC, GetStockObject(WHITE_PEN));
MoveToEx(hDC, 0, 0, NULL);
LineTo(hDC, iWidth, 0);

// draw tool bar buttons (new query, run query)
        // check state variables to draw proper button state
btnx = BTTNX;
SelectObject(hDC, GetStockObject(BLACK_PEN));
DRAWBTTNRECT(hDC, btnx, BTTNY, BTTNWIDTH, BTTNHEIGHT);
DrawBitmap(hDC, (bNewQueryBtnDown?btnx+3:btnx+2), (bNewQueryBtnDown?BTTNY+3:BTTNY+2), hbmpNewQuery);
SelectObject(hDC, (bNewQueryBtnDown ? hGrayPen : GetStockObject(WHITE_PEN)));
DRAWBTTNLIFT1(hDC, btnx, BTTNY, BTTNWIDTH, BTTNHEIGHT);
SelectObject(hDC, (bNewQueryBtnDown ? hLtGrayPen : hGrayPen));
DRAWBTTNLIFT2(hDC, btnx, BTTNY, BTTNWIDTH, BTTNHEIGHT);

btnx += BTTNWIDTH+BTTNMARGIN;
SelectObject(hDC, GetStockObject(BLACK_PEN));
DRAWBTTNRECT(hDC, btnx, BTTNY, BTTNWIDTH, BTTNHEIGHT);
DrawBitmap(hDC, (bRunQueryBtnDown?btnx+3:btnx+2), (bRunQueryBtnDown?BTTNY+3:BTTNY+2), hbmpRunQuery);
SelectObject(hDC, (bRunQueryBtnDown ? hGrayPen : GetStockObject(WHITE_PEN)));
DRAWBTTNLIFT1(hDC, btnx, BTTNY, BTTNWIDTH, BTTNHEIGHT);
SelectObject(hDC, (bRunQueryBtnDown ? hLtGrayPen : hGrayPen));
DRAWBTTNLIFT2(hDC, btnx, BTTNY, BTTNWIDTH, BTTNHEIGHT);

SelectObject(hDC, GetStockObject(WHITE_PEN));
EndPaint(hWnd, &ps);

// delete create objects
if (hLtGrayPen)
DeleteObject(hLtGrayPen);
if (hGrayPen)
DeleteObject(hGrayPen);
break;
                }

default:

return (DefWindowProc(hWnd, message, wParam, lParam));
}
return (0);
}

/*
FUNCTION: StatusbarProc(HWND hWnd, UINT   message, WPARAM wParam, LPARAM lParam)
COMMENTS: callback window procedure for status bar.
  process paint messages and timer messages to update current
  state, date and time
*/
long CALLBACK StatusbarProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
static RECT DateTimeRect; // remember for frequent updates

case WM_CREATE:
                {
HDChDC;// device context
SIZEsizeText;       // size of status text box
SIZEsizeTime;       // size of time display box
SIZEsizeDate;       // size of date display box

        // start a timer for periodic updates to date and time display
        // find out width of status text, date and time display boxes
SetTimer(hWnd,  (UINT)IDT_STATUSTIMER, (UINT)TIMERDELAY, NULL);

iTimex = iDatex = 0;
rectStatusText.left = 2;
rectStatusText.top = 3;

if (hDC = GetDC(hWnd))
{
GetTextExtentPoint(hDC, STATUSCONSTRAINT_COLUMN_USAGE, strlen(STATUSCONSTRAINT_COLUMN_USAGE), &sizeText);
GetTextExtentPoint(hDC, TIMETEXT, strlen(TIMETEXT), &sizeTime);
GetTextExtentPoint(hDC, DATETEXT, strlen(DATETEXT), &sizeDate);
ReleaseDC(hWnd, hDC);
rectStatusText.right = sizeText.cx + rectStatusText.left;
iTimex = sizeTime.cx;
iDatex = sizeDate.cx;
}
break;
                }

case WM_TIMER:

// invalidate only the date&time area for update
 InvalidateRect(hWnd, &DateTimeRect, TRUE);
break;

case WM_PAINT:
                {
HDChDC;//device context
PAINTSTRUCTps;//paint structure
RECTrect;               //status bar rect
intiWidth;             //status bar width
intiHeight;            //status bar height
HPENhLtGrayPen;         //btnface color pen
HPENhGrayPen;           //btnshadow color pen
charszText[MAXBUFLEN];  //text buffer for display
time_ttCurrentTime;       //current date&time
struct tmstTime;             //date&time structure

if (!(hDC = BeginPaint(hWnd, &ps)))
break;

GetClientRect(hWnd, &rect);
iWidth = rect.right;
iHeight = rect.bottom;
rectStatusText.bottom = iHeight-2;

hLtGrayPen = CreatePen(PS_SOLID, 0, GetSysColor(COLOR_BTNFACE));
hGrayPen = CreatePen(PS_SOLID, 0, GetSysColor(COLOR_BTNSHADOW));

// paint background and border
SelectObject(hDC, GetStockObject(LTGRAY_BRUSH));
SelectObject(hDC, hLtGrayPen);
Rectangle(hDC, 0, 0, iWidth, iHeight);

SelectObject(hDC, GetStockObject(BLACK_PEN));
MoveToEx(hDC, 0, 0, NULL);
LineTo(hDC, iWidth, 0);
SelectObject(hDC, GetStockObject(WHITE_PEN));
MoveToEx(hDC, 0, 1, NULL);
LineTo(hDC, iWidth, 1);

        // draw text boxes for status, time and date display
        SelectObject(hDC, hGrayPen);
        MoveToEx(hDC, rectStatusText.left, rectStatusText.bottom, NULL);
        LineTo(hDC, rectStatusText.left, rectStatusText.top);
        LineTo(hDC, rectStatusText.right, rectStatusText.top);
        SelectObject(hDC, GetStockObject(WHITE_PEN));
        LineTo(hDC, rectStatusText.right, rectStatusText.bottom);
        LineTo(hDC, rectStatusText.left, rectStatusText.bottom);

        SelectObject(hDC, hGrayPen);
        MoveToEx(hDC, iWidth-2, 3, NULL);
        LineTo(hDC, iWidth-iDatex-2, 3);
        LineTo(hDC, iWidth-iDatex-2, iHeight-2);
        SelectObject(hDC, GetStockObject(WHITE_PEN));
        LineTo(hDC, iWidth-2, iHeight-2);
        LineTo(hDC, iWidth-2, 3);

        SelectObject(hDC, hGrayPen);
        MoveToEx(hDC, iWidth-iDatex-6, 3, NULL);
        LineTo(hDC, iWidth-iTimex-iDatex-6, 3);
        LineTo(hDC, iWidth-iTimex-iDatex-6, iHeight-2);
        SelectObject(hDC, GetStockObject(WHITE_PEN));
        LineTo(hDC, iWidth-iDatex-6, iHeight-2);
        LineTo(hDC, iWidth-iDatex-6, 3);

// draw status text in the display box based on current
// value of wStatusText global flag
SetBkMode(hDC, TRANSPARENT);
SetTextColor(hDC, GetSysColor(COLOR_BTNTEXT));

switch (wStatusText)
{
case IDM_POPUPAPPSYS:

strcpy(szText, STATUSPOPUPAPPSYS);
break;

case IDM_POPUPMDISYS:

strcpy(szText, STATUSPOPUPMDISYS);
break;

case SC_RESTORE:

strcpy(szText, STATUSRESTORE);
break;

case SC_MOVE:

strcpy(szText, STATUSMOVE);
break;

case SC_SIZE:

strcpy(szText, STATUSSIZE);
break;

case SC_MINIMIZE:

strcpy(szText, STATUSMINIMIZE);
break;

case SC_MAXIMIZE:

strcpy(szText, STATUSMAXIMIZE);
break;

case SC_CLOSE:

strcpy(szText, STATUSCLOSE);
break;

case SC_NEXTWINDOW:

strcpy(szText, STATUSNEXTWINDOW);
break;

case SC_PREVWINDOW:

strcpy(szText, STATUSPREVWINDOW);
break;

case SC_TASKLIST:

strcpy(szText, STATUSTASKLIST);
break;

case IDM_POPUPLOGIN:

strcpy(szText, STATUSPOPUPLOGIN);
break;

case IDM_INITIALIZE:

strcpy(szText, STATUSCONNECT);
break;

case IDM_DISCONNECT:

strcpy(szText, STATUSDISCONNECT);
break;

case IDM_EXIT:

strcpy(szText, STATUSEXIT);
break;

case IDM_POPUPQUERY:

strcpy(szText, STATUSPOPUPQUERY);
break;

        case IDM_QUERY:

        strcpy(szText, STATUSQUERY);
        break;

case IDM_NEW:

        strcpy(szText, STATUSNEW);
break;

case IDM_POPUPSCHEMA:

strcpy(szText, STATUSPOPUPSCHEMA);
break;

case IDM_POPUPWINDOW:

strcpy(szText, STATUSPOPUPWINDOW);
break;

case IDM_TILE:

strcpy(szText, STATUSTILE);
break;

case IDM_CASCADE:

strcpy(szText, STATUSCASCADE);
break;

case IDM_ICONS:

strcpy(szText, STATUSICONS);
break;

case IDM_CLOSEALL:

strcpy(szText, STATUSCLOSEALL);
break;

case IDM_POPUPHELP:

strcpy(szText, STATUSPOPUPHELP);
break;

case IDM_APPHELP:

strcpy(szText, STATUSAPPHELP);
break;

case IDM_ABOUT:

strcpy(szText, STATUSABOUT);
break;

case IDM_ASSERTIONS:

strcpy(szText, STATUSASSERTIONS);
break;

case IDM_CATALOGS:

strcpy(szText, STATUSCATALOGS);
break;

case IDM_CHARACTER_SETS:

strcpy(szText, STATUSCHARACTER_SETS);
break;

case IDM_CHECK_CONSTRAINTS:

strcpy(szText, STATUSCHECK_CONSTRAINTS);
break;

case IDM_COLLATIONS:

strcpy(szText, STATUSCOLLATIONS);
break;

case IDM_COLUMN_DOMAIN_USAGE:

strcpy(szText, STATUSCOLUMN_DOMAIN_USAGE);
break;

case IDM_COLUMN_PRIVILEGES:

strcpy(szText, STATUSCOLUMN_PRIVILEGES);
break;

case IDM_COLUMNS:

strcpy(szText, STATUSCOLUMNS);
break;

case IDM_CONSTRAINT_COLUMN_USAGE:

strcpy(szText, STATUSCONSTRAINT_COLUMN_USAGE);
break;

case IDM_CONSTRAINT_TABLE_USAGE:

strcpy(szText, STATUSCONSTRAINT_TABLE_USAGE);
break;

case IDM_FOREIGN_KEYS:

strcpy(szText, STATUSFOREIGN_KEYS);
break;

case IDM_INDEXES:

strcpy(szText, STATUSINDEXES);
break;

case IDM_KEY_COLUMN_USAGE:

strcpy(szText, STATUSKEY_COLUMN_USAGE);
break;

case IDM_PRIMARY_KEYS:

strcpy(szText, STATUSPRIMARY_KEYS);
break;

case IDM_PROCEDURE_COLUMNS:

strcpy(szText, STATUSPROCEDURE_COLUMNS);
break;

case IDM_PROCEDURE_PARAMETERS:

strcpy(szText, STATUSPROCEDURE_PARAMETERS);
break;

case IDM_PROCEDURES:

strcpy(szText, STATUSPROCEDURES);
break;

case IDM_PROVIDER_TYPES:

strcpy(szText, STATUSPROVIDER_TYPES);
break;

case IDM_REFERENTIAL_CONSTRAINTS:

strcpy(szText, STATUSREFERENTIAL_CONSTRAINTS);
break;

case IDM_SCHEMATA:

strcpy(szText, STATUSSCHEMATA);
break;

case IDM_SQL_LANGUAGES:

strcpy(szText, STATUSSQL_LANGUAGES);
break;

case IDM_STATISTICS:

strcpy(szText, STATUSSTATISTICS);
break;

case IDM_TABLE_CONSTRAINTS:

strcpy(szText, STATUSTABLE_CONSTRAINTS);
break;

case IDM_TABLE_PRIVILEGES:

strcpy(szText, STATUSTABLE_PRIVILEGES);
break;

case IDM_TABLES:

strcpy(szText, STATUSTABLES);
break;

case IDM_TRANSLATIONS:

strcpy(szText, STATUSTRANSLATIONS);
break;

case IDM_USAGE_PRIVILEGES:

strcpy(szText, STATUSUSAGE_PRIVILEGES);
break;

case IDM_VIEW_COLUMN_USAGE:

strcpy(szText, STATUSVIEW_COLUMN_USAGE);
break;

case IDM_VIEW_TABLE_USAGE:

strcpy(szText, STATUSVIEW_TABLE_USAGE);
break;

case IDM_VIEWS:

strcpy(szText, STATUSVIEWS);
break;

default:

            if (wStatusText >= IDM_MDICHILD)
            sprintf(szText, STATUSMDICHILD, wStatusText-IDM_MDICHILD+1);
            else
        strcpy(szText, STATUSDEFAULT);
break;
}

DrawText(hDC, szText, strlen(szText), &rectStatusText, DT_LEFT);

    // get current date and time and display time in time box
time(&tCurrentTime);
stTime = *localtime(&tCurrentTime);
strftime(szText, MAXBUFLEN, TIMEFORMAT, &stTime);
rect.top = rectStatusText.top;
rect.bottom = rectStatusText.bottom;
rect.left  = iWidth-iTimex-iDatex-6;
rect.right = iWidth-iDatex-6;
DrawText(hDC, szText, strlen(szText), &rect, DT_LEFT);

// display date in date box
strftime(szText, MAXBUFLEN, DATEFORMAT, &stTime);
rect.left  = iWidth-iDatex-2;
rect.right = iWidth-2;
DrawText(hDC, szText, strlen(szText), &rect, DT_LEFT);

// remember the date&time rectangle to minimize painting
DateTimeRect.left   = iWidth-iTimex-iDatex-6;
DateTimeRect.right  = iWidth-2;
DateTimeRect.top    = rect.top;
DateTimeRect.bottom = rect.bottom;

EndPaint(hWnd, &ps);

// delete created objects
if (hLtGrayPen)
DeleteObject(hLtGrayPen);
if (hGrayPen)
DeleteObject(hGrayPen);
break;
                }

default:

return (DefWindowProc(hWnd, message, wParam, lParam));
break;
}
return (0);
}

/*
FUNCTION: ConnectDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
COMMENTS: Callback dialog box procedure for connect menu command
  displays a list of available data sources, asks for user
  name and password to pass default connection parameters
  for a data source connection
*/
BOOL CALLBACK ConnectDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
BOOL fResult = TRUE; // ENDDIALOG Return Code

switch (message)
{
HCURSORhOldCursor;// Default Cursor Shape

case WM_INITDIALOG:

        // display list of available providers
hOldCursor = SetCursor(LoadCursor((HINSTANCE)NULL, IDC_WAIT));
DisplayProviders(GetDlgItem(hWnd, IDCOMBO_PROVIDER));
//DisplayDataSource(hWnd);
FillPrompt(GetDlgItem(hWnd, IDCOMBO_PROMPT));
SetCursor(hOldCursor);
break;

case WM_COMMAND:

switch (GET_WM_COMMAND_ID(wParam, lParam))
{
// Make a connection using the supplied values
case IDOK:
hOldCursor = SetCursor(LoadCursor((HINSTANCE)NULL, IDC_WAIT));
            // Check if a DSO was provided for connection
if( ConnectDatabase(hWnd) )
EndDialog(hWnd, fResult);
SetCursor(hOldCursor);
break;

case IDCANCEL:
EndDialog(hWnd, FALSE);
break;

default:

return (FALSE);
break;
}
break;

default:

return (FALSE);
break;
}
return (TRUE);
}

/*
FUNCTION: DisconnectDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
COMMENTS: Callback dialog box procedure for disconnect dialog.
  provides a list of available SESSIONs and a list of HSTMTs
  for currently selected SESSION. Allows closure of all SESSIONs
  and HSTMTs one by one or in groups.
*/
BOOL CALLBACK DisconnectDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
static HWNDhListhdbc;//listbox that displays hdbc(s)
static HWNDhListstmt;      //listbox that displays hstmt(s)
static HWNDhPushOk;        //pushbutton to free hdbc
static HWNDhPushClose;     //pushbutton to free hstmt
static HWNDhPushCancel;    //pushbutton to close dialog

case WM_INITDIALOG:

// store handles for future reference
hListhdbc = GetDlgItem(hWnd, IDLIST_SESSION);
hListstmt = GetDlgItem(hWnd, IDLIST_COMMAND);
hPushOk = GetDlgItem(hWnd, IDDISCONNECT);
hPushCancel = GetDlgItem(hWnd, IDCANCEL);
hPushClose = GetDlgItem(hWnd, IDCLOSE_ACTVTY);

// display connected database handles and statements
DisplayConnections(hListhdbc);
DisplayICommands(hListstmt, hListhdbc, 0);

// enable or disable pushbuttons & listboxes to match available hdbc & hstmt
if (SendMessage(hListhdbc, LB_GETCOUNT, 0, 0)>0)
{
EnableWindow(hPushOk, TRUE);
if (SendMessage(hListstmt, LB_GETCOUNT, 0, 0)>0)
{
EnableWindow(hPushClose, TRUE);
SetFocus(hPushClose);
SendMessage(hPushClose, BM_SETSTYLE, (WPARAM)BS_DEFPUSHBUTTON, TRUE);
}
else
{
EnableWindow(hListstmt, FALSE);
EnableWindow(hPushClose, FALSE);
SetFocus(hPushOk);
SendMessage(hPushOk, BM_SETSTYLE, (WPARAM)BS_DEFPUSHBUTTON, TRUE);
}
}
else
{
EnableWindow(hListhdbc, FALSE);
EnableWindow(hListstmt, FALSE);
EnableWindow(hPushOk, FALSE);
SetFocus(hPushCancel);
SendMessage(hPushCancel, BM_SETSTYLE, (WPARAM)BS_DEFPUSHBUTTON, TRUE);
}

// return FALSE to prevent default focus.
return (FALSE);

case WM_COMMAND:

switch (GET_WM_COMMAND_ID(wParam, lParam))
{
case IDDISCONNECT:

            // Free current hdbc, display available hdbc(s)
FreeConnect(hListhdbc);
SendMessage(hListstmt, LB_RESETCONTENT, 0, 0);
DisplayConnections(hListhdbc);

            // update displayed hstmt(s) for current hdbc
            // enable or disable pushbuttons to match available
            // hdbc(s) and hstmt(s) for closure
if (SendMessage(hListhdbc, LB_GETCOUNT, 0, 0) > 0)
{
SendMessage(hListhdbc, LB_SETCURSEL, 0, 0);
EnableWindow(hListstmt, TRUE);
DisplayICommands(hListstmt, hListhdbc, 0);
if (SendMessage(hListstmt, LB_GETCOUNT, 0, 0)>0)
{
EnableWindow(hPushClose, TRUE);
SetFocus(hPushClose);
SendMessage(hPushClose, BM_SETSTYLE, (WPARAM)BS_DEFPUSHBUTTON, TRUE);
}
else
{
EnableWindow(hListstmt, FALSE);
EnableWindow(hPushClose, FALSE);
SetFocus(hPushOk);
SendMessage(hPushOk, BM_SETSTYLE, (WPARAM)BS_DEFPUSHBUTTON, TRUE);
}
}
else
{
EnableWindow(hListhdbc, FALSE);
EnableWindow(hPushOk, FALSE);
EnableWindow(hPushClose, FALSE);
SetFocus(hPushCancel);
SendMessage(hPushCancel, BM_SETSTYLE, (WPARAM)BS_DEFPUSHBUTTON, TRUE);
}
break;

case IDCANCEL:

// close dialog
EndDialog(hWnd, FALSE);
break;

case IDCLOSE_ACTVTY:
                        {
intnIndex;// counter to search for selected hstmt(s)

// go through all displayed hstmt(s) and free all highlighted ones
for (nIndex = (int)SendMessage(hListstmt, LB_GETCOUNT, 0, 0)-1;
nIndex >= 0; nIndex--)
if (SendMessage(hListstmt, LB_GETSEL, nIndex, 0))
FreeICommand(hListstmt, hListhdbc, nIndex);

// reset both hdbc(s) and hstmt(s) display
nIndex = (int)SendMessage(hListhdbc, LB_GETCURSEL, 0, 0);
DisplayConnections(hListhdbc);
SendMessage(hListhdbc, LB_SETCURSEL, nIndex, 0);
DisplayICommands(hListstmt, hListhdbc, nIndex);

// enable or disable pushbuttons to match available
// hdbc(s) and hstmt(s) for closure
if (SendMessage(hListstmt, LB_GETCOUNT, 0, 0)>0)
{
EnableWindow(hPushClose, TRUE);
SetFocus(hPushClose);
SendMessage(hPushClose, BM_SETSTYLE, (WPARAM)BS_DEFPUSHBUTTON, TRUE);
}
else
{
EnableWindow(hListstmt, FALSE);
EnableWindow(hPushClose, FALSE);
SetFocus(hPushOk);
SendMessage(hPushOk, BM_SETSTYLE, (WPARAM)BS_DEFPUSHBUTTON, TRUE);
}
break;
                        }

case IDLIST_SESSION:
// If the current selection in hdbc(s) has changed
// update the list of hstmt(s) to match the new hdbc
if (GET_WM_COMMAND_CMD(wParam, lParam) == LBN_SELCHANGE)
DisplayICommands(hListstmt, hListhdbc,
(UINT)SendMessage(GET_WM_COMMAND_HWND(wParam, lParam), LB_GETCURSEL, 0, 0));

            // Enable or disable hstmt listbox and close pushbutton accordingly
EnableWindow(hListstmt, (SendMessage(hListstmt, LB_GETCOUNT, 0, 0)>0));
EnableWindow(hPushClose, (SendMessage(hListstmt, LB_GETCOUNT, 0, 0)>0));
break;

default:

return (FALSE);
}
break;

default:

return (FALSE);
}

return (TRUE);
}

/*
FUNCTION: AboutDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
COMMENTS: Callback dialog box procedure for About dialog box
  displays the about information and closes upon selection of
  ok button
*/
BOOL CALLBACK AboutDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
if (message == WM_COMMAND)
{
EndDialog(hWnd, TRUE);
return (TRUE);
}
else
return (FALSE);
}

/*
FUNCTION: MDIChildDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
COMMENTS: Callback dialog box procedure for modeless child dialog box
  in each MDI Child Window. This dialog box simply processes
  the tab messages (by default) to allow switching from edit
  control (SQL Text) to list box control (Query results).
*/
BOOL CALLBACK MDIChildDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
return (FALSE);
}

/*
FUNCTION: DrawBitmap(HDC hDC, int iLeft, int iTop, HBITMAP hBitmap
COMMENTS: Draws a bitmap on given Device context with given bitmap
  handle at given location
*/

VOID FAR PASCAL DrawBitmap(HDC hDC, int iLeft, int iTop, HBITMAP hBitmap)
{
HDChMemDC;// Device Context in Memory
POINTstPoint;    // point structure for conversion from device to logical units
BITMAPstBitmap;
    HGDIOBJ hObject;

    // create a compatible device context in memory and select the bitmap
    // in to it.
if (!(hMemDC = CreateCompatibleDC(hDC))) return;

hObject = SelectObject(hMemDC, hBitmap);
SetMapMode(hMemDC, GetMapMode(hDC));

// Get bitmap size and convert it to logical units from device units.
GetObject(hBitmap, sizeof(BITMAP), &stBitmap);
stPoint.x = stBitmap.bmWidth;
stPoint.y = stBitmap.bmHeight;
DPtoLP(hDC, &stPoint, 1);

// bit block transfer the bitmap from memory device context to given
// device context at specified location
BitBlt(hDC, iLeft, iTop, stPoint.x, stPoint.y, hMemDC, 0, 0, SRCCOPY);

    // select original object in the memory device context and destroy it
SelectObject(hMemDC, hObject);
DeleteDC(hMemDC);
}

void FillPrompt(HWND hWnd)
{
const LPCTSTR rgPrompts[] =
{
"Prompt",
"Complete",
"Complete Required",
"No Prompt"
};

for (int i=0;  i<sizeof(rgPrompts) / sizeof(rgPrompts[0]); i++)
SendMessage(hWnd, CB_ADDSTRING, 0, (LPARAM) (LPCTSTR) rgPrompts[i]);
SendMessage(hWnd, CB_SETCURSEL, (WPARAM) DBPROMPT_COMPLETE - 1, 0L);
}

/********************************************* END OF FILE **************************************************/


MWWIN.H

////////////////////////////////////////////////////////////////////////////////
// mwwin.h
//
// Copyright (C) 1987-1997 By Microsoft Corp. All rights reserved.
// Copyright (C) 1997 Metawise Computing, Inc. All rights reserved.
//
////////////////////////////////////////////////////////////////////////////////

#include "cstring.h"

#ifndef _MWWIN_H_
#define _MWWIN_H_

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

class CWnd
{
public:
// Constructor/Destructor
CWnd ();

public:
// Initialization
BOOL Attach (HWND hWnd);
HWND Detach ();

// Window Access Functions
HWND GetParent () const;

// Window State Functions
BOOL EnableWindow (BOOL bEnable = TRUE);

// Window Text Functions
void GetWindowText (CString& strText) const;
int GetWindowTextLength () const;
void SetWindowText (LPCTSTR lpszText);

// Window Message Functions
LRESULT SendMessage (UINT nMessage, WPARAM wParam = 0, LPARAM lParam = 0);

// Attributes
HWND m_hWnd;
};

class CButton : public CWnd
{
public:
// Constructor/Destructor
CButton ();

public:
// General Operations
int GetCheck () const;
void SetCheck (int nCheck);
};

class CComboBox : public CWnd
{
public:
// Constructor/Destructor
CComboBox ();

public:
// General Operations
int GetCount () const;
int GetCurSel () const;
int SetCurSel (int nIndex);
int GetLBText (int nIndex, LPTSTR lpszText) const;
void GetLBText (int nIndex, CString& strText) const;
int GetLBTextLen (int nIndex) const;

// String Operations
int AddString (LPCTSTR lpszItem);
int DeleteString (UINT nIndex);
void ResetContent ();
int FindString (int nStartAfter, LPCTSTR lpszItem) const;
int SelectString (int nStartAfter, LPCTSTR lpszItem);
};

class CEdit : public CWnd
{
public:
// Constructor/Destructor
CEdit ();

// General Operations
BOOL GetModify () const;
void SetLimitText (UINT nMaxBytes);
};

class CListBox : public CWnd
{
public:
// Constructor/Destructor
CListBox ();

public:
// General Operations
int GetCount () const;// return LB_ERR if error occurs
DWORD GetItemData (int nIndex) const;
int SetItemData (int nIndex, DWORD dwData);
int GetText (int nIndex, LPTSTR lpszText) const;
void GetText (int nIndex, CString& strText) const;
int GetTextLen (int nIndex) const;

// Single-Selection Operations
int GetCurSel () const;
int SetCurSel (int nIndex);// nIndex is 0-based

// String Operation
int AddString (LPCTSTR lpszItem);
int DeleteString (UINT nIndex);// nIndex is 0-based
int SelectString (int nStartAfter, LPCTSTR lpszItem);
};

class CStatic : public CWnd
{
public:
// Constructor/Destructor
CStatic ();
};

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

#endif // _MWWIN_H_


OLEDBDDM.H

////////////////////////////////////////////////////////////////////////////////
// oledbddm.h
//
// Copyright (C) 1987-1997 By Microsoft Corp. All rights reserved.
// Copyright (C) 1997 Metawise Computing, Inc. All rights reserved.
//
////////////////////////////////////////////////////////////////////////////////

#ifndef _OLEDBDDM_H_
#define _OLEDBDDM_H_

////////////////////////////////////////////////////////////////////////////////
// Indicators of function parameter directions (input or output)
#ifndef IN
#define IN
#endif

#ifndef OUT
#define OUT
#endif

#ifndef INOUT
#define INOUT
#endif

#ifndef DONOTHING
#define DONOTHING
#endif

////////////////////////////////////////////////////////////////////////////////
// Macros:

#define NUMELEM(X)(sizeof(X) / sizeof(*(X)))

#ifndef VERIFY_POINTER
#define VERIFY_POINTER(PTR)\
if (0 == (PTR))\
return ResultFromScode(E_POINTER);\
else
#endif

#ifndef DELETE_POINTER
#define DELETE_POINTER(PTR)\
if (PTR)\
{\
delete (PTR);\
(PTR) = 0;\
} else
#endif

#ifndef DELETE_ARRAY
#define DELETE_ARRAY(PTR)\
if (PTR)\
{\
delete [] (PTR);\
(PTR) = 0;\
} else
#endif

#ifndef RELEASE_INTERFACE
#define RELEASE_INTERFACE(IFP)\
if (IFP)\
{\
(IFP)->Release ();\
(IFP) = 0;\
} else
#endif

#ifndef FREE_TASKMEM
#define FREE_TASKMEM(MEM)\
if (MEM)\
{\
CoTaskMemFree (MEM);\
(MEM) = 0;\
} else
#endif

// failed or warning
#ifndef MW_FAILED
#define MW_FAILED(RC)((RC) < 0)
#endif

// succeeded (without warning)
#ifndef MW_SUCCEEDED
#define MW_SUCCEEDED(RC)(0 <= (RC) && (RC) < 100)
#endif

////////////////////////////////////////////////////////////////////////////////
// System:

#include <assert.h>

////////////////////////////////////////////////////////////////////////////////
// Utils:

#ifndef __AFXWIN_H__

#include "cstring.h"
#include "array.h"
#include "hash.h"
#include "mwwin.h"
#include "registry.h"

#endif

#endif // _OLEDBDDM_H_


QUERY.CPP

//-----------------------------------------------------------------------------
// Microsoft OLE DB QURYDEMO Sample
// Copyright (C) 1994 - 1996 By Microsoft Corporation.
//
// @doc
//
// @module QURYDEMO.CPP
//
//-----------------------------------------------------------------------------------
/*
PROGRAM: QURYDEMO
========

PURPOSE:
========
demonstrates a simple MDI (Multiple Document Interface)application
that allows a user to simultaneously connect to multiple
hetrogeneous databases and perform SQL queries to get results.

FUNCTIONS:
==========
InitEnvironment() - Initialize OLE
DisplayProviders() - Display available Providers
ConnectDatabase() - Connect to a specific data source
DisplayConnections() - Display List of made connections
DisplayICommands() - Display list of ICommands
NewICommandWindow() - Open a new ICommand and update displays
ChangeCurrentCursor() - Change current cursor display
ChangeCurrentICommand() - Change current ICommand display
DisplayNewCrsrAndICommand() - update cusor & ICommand displays
FreeConnect() - free a IDBCreateCommand
FreeICommand() - free a ICommand
CloseICommandWindow() - close a ICommand window
ExecuteQuery() - execute a user specified query
CloseIDBCreateCommand() - check if all connections are closed
FreeEnvironment() - free OLE
ExecuteCommand() - Execute the ICommand and return an IRowset
GetDataFromRowset() - Get the Data from the IRowset
SetupBindings() - Set the binding structure
CreateAccessor() - Create Accessor to store the Data
GetData() - Get the Data out of the rowset

COMMENTS:
=========
Created by Microsoft Corporation.

The application uses MDI Child Window Titles to store values of
PROVIDER's and Command Objects. These values are also stored in the
comboboxes that are displayed on the toolbar.

*/
#define DBINITCONSTANTS
#define INITGUID

#include <stdio.h>
#include <string.h>
#include <windows.h>
#include <stddef.h>// offsetof
#include <assert.h>// assert

#include "oledb.h"
#include "oledberr.h"
#include "msdaguid.h"

#include "qurydemo.h"

// Globals
extern HWNDhWndFrame; // Main Frame Window handle
extern HWNDhWndCrsrList;// hdbc(s) combobox on the tool bar
extern HWNDhWndStmtList;// hstmt(s) combobox on the tool bar
extern HWNDhWndMDIClient;// MDI Client window handle
extern HWNDhWndActiveChild;// Current active MDI Child window
extern HINSTANCEhAppInstance;// Application instance

IMalloc* g_pIMalloc = NULL;

intnChildCount;// Number of child windows currently open
charszDispBuffer[MAXDISPLAYSIZE+1];// Display Buffer
unsigned charrgbData[MAX_COL][MAXDATALEN];// Results Data Array
long dwDataLen[MAX_COL];// Results Data Length Array

//charszErrorDescription [MAXDISPLAYSIZE+1];
//charszErrorSource      [MAXDISPLAYSIZE+1];

//Variables and constants used for provider enumeration
//#define   MAX_VALUE_NAME          300
//#define   MAX_PROVIDER_NUM        10
#define NUMELEM(p1) (sizeof(p1) / sizeof(p1[0]))
#define COLUMN_ALIGNVAL 8
#define ROUND_UP( Size, Amount ) (((DWORD)(Size) +  ((Amount) - 1)) & ~((Amount) - 1))

const ULONGDEF_SOURCES_CBMAXLEN= 64;
const ULONG MAX_NUM_PROVIDERS= 16;

IParseDisplayName*g_pIParse = NULL;
ULONGg_cProvNames;
CHARg_rgszProvName[MAX_NUM_PROVIDERS][DEF_SOURCES_CBMAXLEN];
WCHARg_rgwszParseName[MAX_NUM_PROVIDERS][DEF_SOURCES_CBMAXLEN];

/*
FUNCTION: InitEnvironment()
COMMENTS: Allocate an environment handle for OLE function calls.
*/
FAR PASCAL InitEnvironment()
{
HRESULThr;

// Initialize OLE
hr = CoInitialize( NULL );

if (FAILED(hr))
{
DumpErrorHResult( hr, (LPSTR)"CoInitialize FAILED!!" );
return FALSE;
}

    // Retrieve the task memory allocator
    hr = CoGetMalloc( MEMCTX_TASK, &g_pIMalloc );

if (FAILED(hr))
{
DumpErrorHResult( hr, (LPSTR)"CoGetMalloc FAILED!!" );
return FALSE;
}

// reset child window count
nChildCount = 0;

// Return TRUE
return TRUE;
}

/*
FUNCTION: DisplayProviders(HWND hWnd)
COMMENTS: Display a list of available Providers.
*/
void FAR PASCAL DisplayProviders(HWND hWnd)
{
    ULONGiProv;
HRESULThr;

// Initialize count of provider names
g_cProvNames = 0;

//Go to registry and get me the provider names
hr = EnumerateProviders();
if (FAILED (hr))
{
  MessageBox(hWndFrame,
             "There was an error retrieve providers from the OLE DB Enumerator",
             "Enumeration Error",
 MB_OK | MB_ICONERROR);
  return;
}

//All returned Providerss in the provided combo box for display.
SendMessage(hWnd, CB_RESETCONTENT, 0, 0);
for (iProv=0; iProv<g_cProvNames; iProv++)
   SendMessage(hWnd, CB_ADDSTRING, 0, (LPARAM)(LPSTR)g_rgszProvName[iProv]);

SendMessage(hWnd, CB_SETCURSEL, 0, 0);
}

/*
FUNCTION: EnumerateProviders
COMMENTS: Display a list of available providers
*/
HRESULT EnumerateProviders
(
)
{
HRESULThr;
ULONGul, cRows = 0;
ISourcesRowset*pISrcRowset = NULL;
IRowset*pIRowset = NULL;
IAccessor*pIAccessor = NULL;
BYTE*pData = NULL;
DWORDdwOffset;
HACCESSORhAccessor = NULL;
DBBINDINGrgBind[3];
HROWrghRows[MAX_NUM_PROVIDERS];
HROW*pRows = &rghRows[0];

enum enumSOURCES_COLUMNS {
eid_SOURCES_NAME = 1,
eid_SOURCES_PARSENAME,
eid_SOURCES_DESCRIPTION,
eid_SOURCES_TYPE,
eid_SOURCES_ISPARENT,
eid_SOURCES_CLSID,
};

static struct tagSOURCES
{
ULONGiOrdinal;
DBTYPEwType;
ULONGcbMaxLen;
} s_rgSources[] = {
eid_SOURCES_NAME,DBTYPE_STR,DEF_SOURCES_CBMAXLEN,
eid_SOURCES_PARSENAME,DBTYPE_WSTR,DEF_SOURCES_CBMAXLEN * sizeof(WCHAR),
eid_SOURCES_TYPE,DBTYPE_UI4,sizeof(ULONG),
};

memset(rghRows, 0, sizeof(rghRows));

// Initialize the OLE DB Enumerator
if( FAILED(hr = CoCreateInstance(CLSID_OLEDB_ENUMERATOR, NULL,
CLSCTX_INPROC_SERVER, IID_ISourcesRowset, (LPVOID*)&pISrcRowset)) )
{
DumpErrorHResult( hr, (LPSTR)"CoCreateInstance FAILED!!" );
goto EXIT;
}

// Retrieve the Rowset
if( FAILED(hr = pISrcRowset->GetSourcesRowset(NULL, IID_IRowset, 0, NULL,
(IUnknown**)&pIRowset)) )
{
DumpErrorHResult( hr, (LPSTR)"GetSourcesRowset FAILED!!" );
goto EXIT;
}

memset(rgBind, 0, sizeof(rgBind));

if( FAILED(hr = pIRowset->QueryInterface(IID_IAccessor, (LPVOID*)&pIAccessor)) )
{
DumpErrorHResult( hr, (LPSTR)"QI of IID_IAccessor from pIRowset failed" );
goto EXIT;
}

dwOffset = 0;
for(ul=0; ul< NUMELEM(s_rgSources); ul++)
{
rgBind[ul].dwPart= DBPART_VALUE | DBPART_LENGTH | DBPART_STATUS;
rgBind[ul].eParamIO= DBPARAMIO_NOTPARAM;
rgBind[ul].iOrdinal= s_rgSources[ul].iOrdinal;
rgBind[ul].wType= s_rgSources[ul].wType;
rgBind[ul].obValue= dwOffset + offsetof(COLUMNDATA,bData);
rgBind[ul].obLength= dwOffset + offsetof(COLUMNDATA,dwLength);
rgBind[ul].obStatus= dwOffset + offsetof(COLUMNDATA,wStatus);
rgBind[ul].cbMaxLen= s_rgSources[ul].cbMaxLen;
rgBind[ul].dwMemOwner= DBMEMOWNER_CLIENTOWNED;
dwOffset += rgBind[ul].cbMaxLen + offsetof( COLUMNDATA, bData );
dwOffset = ROUND_UP( dwOffset, COLUMN_ALIGNVAL );
}

if( FAILED(hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, NUMELEM(s_rgSources),
rgBind, dwOffset, &hAccessor, NULL)) )
{
DumpErrorHResult( hr, (LPSTR)"Accessor Creation failed!!" );
goto EXIT;
}

// Retrieve the providers
if( SUCCEEDED(hr = pIRowset->GetNextRows(NULL, 0, MAX_NUM_PROVIDERS, &cRows, &pRows)) )
{
// Allocate block of memory to retrieve the row data into.
pData = new BYTE[dwOffset];
if( pData == NULL )
{
DumpErrorHResult( E_OUTOFMEMORY, (LPSTR)"Unable to allocate memory for buffer");
goto EXIT;
}

// Loop over the rows of data, collecting providers and discarding
// enumerators..
for(ul=0; (ul<cRows) && (ul<MAX_NUM_PROVIDERS); ul++)
{
memset(pData, 0, dwOffset);

if( SUCCEEDED(hr = pIRowset->GetData(rghRows[ul], hAccessor, pData)) )
{
if( *((ULONG*)(pData + rgBind[2].obValue)) == DBSOURCETYPE_DATASOURCE )
{
// Store Provider Name
strcpy(g_rgszProvName[g_cProvNames], (CHAR*)(pData + rgBind[0].obValue));

// Store Parse Name
wcscpy(g_rgwszParseName[g_cProvNames], (WCHAR*)(pData + rgBind[1].obValue));

g_cProvNames++;
}
}
}
}
else
{
DumpErrorHResult( hr, (LPSTR)"GetNextRows failed to retrieve Providers" );
goto EXIT;
}

// Retrieve the IID_IParseDisplayName interface before returning
if( FAILED(hr = pISrcRowset->QueryInterface(IID_IParseDisplayName, (LPVOID*)&g_pIParse)) )
{
DumpErrorHResult( hr, (LPSTR)"QI for IParseDisplayName failed");
goto EXIT;
}

EXIT:
if( pData )
delete[] pData;

if( pIAccessor )
{
if( hAccessor )
{
if( FAILED(hr = pIAccessor->ReleaseAccessor(hAccessor, NULL)) )
DumpErrorHResult( hr, (LPSTR)"Release Accessor failed!!" );
}
pIAccessor->Release();
}

if( pIRowset )
{
if( cRows )
{
if( FAILED(hr = pIRowset->ReleaseRows(cRows, rghRows, NULL, NULL, NULL)) )
DumpErrorHResult( hr, (LPSTR)"Release of Row handles failed");
}
pIRowset->Release();
}

if( pISrcRowset )
pISrcRowset->Release();
return hr;
}

//**********************************************************************
//
// ConnectDatabase
//
// Purpose:
//
//     Initializes the PROVIDER and creates a IDBCreateCommand Object.
//
// Parameters:
//
// HWND hWnd                - handle to the window
//
// Return Value:
//     TRUE         - Success
//     FALSE        - Failure
//
// Function Calls:
//     Function                    Location
//
//     IDBInitialize::Release      provider's Command object
//
//
// Comments:
//
//
//**********************************************************************
BOOL FAR PASCAL ConnectDatabase(HWND hWnd)
{
// Global IDBInitialize Options
IMoniker*pIMoniker= NULL;// Moniker
IDBInitialize * pIDBInit= NULL;// IDBInitialize Object
IDBCreateCommand *pIDBCreateCommand= NULL;// IDBCreateCommand
IDBCreateSession *pIDBCreateSession= NULL; // IDBCreateSession
IDBProperties*pIDBProperties= NULL; // IDBProperties
IOpenRowset *pIOpenRowset= NULL;// IOpenRowset
IUnknown *pIUnknown= NULL;// IUnknown
HRESULThr;// HRESULT
DBPROPSETrgPropertySet[1];// Array of property sets
DBPROPrgProperties[5];// Array of property values
BOOLbReturn = FALSE;// Return Value
shortiPrompt;// DBPROP_INIT_PROMPT value.
ULONGiProp, iPDex;
ULONGchEaten;

// Strings for MSDASQL (CHAR & WCHAR)
CHARszProvName[MAXBUFLEN+1];// Provider String
CHARszDBName[MAXBUFLEN+1];// DSN String
WCHARwszBuff[MAXBUFLEN+1];// WCHAR buffer
CHARszUserName[MAXBUFLEN+1];// User Name
CHARszPassword[MAXBUFLEN+1];// Password
CHARszBuffer[MAXBUFLEN+1];// String Buffer
LRESULT nResult;// Return Code
BOOLfCommandWindow=FALSE;// Flag to indicate Command support

// Initialize Property Buffers
for(iProp=0; iProp<NUMELEM(rgProperties); iProp++)
VariantInit(&(rgProperties[iProp].vValue));

// check if enough windows are already open, refuse connection
if (nChildCount >= MAXCHILDWNDS)
{
MessageBox(hWndFrame, MAXCHILDEXCEEDED, MAXCHLDERR, MB_OK | MB_ICONHAND);
return (FALSE);
}

// Retrieve Provider values from the connect dialog box
GetDlgItemText(hWnd, IDCOMBO_PROVIDER, szProvName, MAXBUFLEN);
GetDlgItemText(hWnd, IDCOMBO_NAME,    szDBName,   MAXBUFLEN);
GetDlgItemText(hWnd, IDTEXT_USERID,    szUserName, MAXBUFLEN);
GetDlgItemText(hWnd, IDTEXT_PASSWORD,  szPassword, MAXBUFLEN);
if ((iPrompt = (short) SendMessage(GetDlgItem(hWnd, IDCOMBO_PROMPT), CB_GETCURSEL, 0, 0)) == CB_ERR)
iPrompt = -1;
else
++iPrompt;

    //Determine the ParseDisplayName
for(iPDex=0; iPDex<g_cProvNames; iPDex++)
    {
if( strcmp((const char *)szProvName, (const char *) g_rgszProvName[iPDex])==0 )
break;
}

if( iPDex >= g_cProvNames )
{
DumpErrorHResult( E_FAIL, (LPSTR)"Unknown Provider, Please Select Valid Provider" );
goto error;
}

if( FAILED(hr = g_pIParse->ParseDisplayName(NULL, g_rgwszParseName[iPDex], &chEaten, &pIMoniker)) )
{
DumpErrorHResult( hr, (LPSTR)"ParseDisplayName failed");
goto error;
}

if( FAILED(hr = BindMoniker(pIMoniker, 0, IID_IDBInitialize, (LPVOID*)&pIDBInit)) )
{
DumpErrorHResult( hr, (LPSTR)"BindMoniker failed");
goto error;
}

iProp = 0;
// If DataSource name specified, then create property node
if( *szDBName != '\0' )
{
// Fill in Data Source
rgProperties[iProp].dwPropertyID=DBPROP_INIT_DATASOURCE;
rgProperties[iProp].dwOptions=DBPROPOPTIONS_REQUIRED;
rgProperties[iProp].colid=DB_NULLID;
MultiByteToWideChar(CP_ACP, 0, szDBName, -1, wszBuff, MAXBUFLEN+1);
V_VT(&(rgProperties[iProp].vValue))=VT_BSTR;
V_BSTR(&(rgProperties[iProp].vValue))=SysAllocString(wszBuff);
iProp++;
}

// If User Name specified, then create property node
if( *szUserName != '\0' )
{
rgProperties[iProp].dwPropertyID=DBPROP_AUTH_USERID;
rgProperties[iProp].dwOptions=DBPROPOPTIONS_REQUIRED;
rgProperties[iProp].colid=DB_NULLID;
MultiByteToWideChar(CP_ACP, 0, szUserName, -1, wszBuff, MAXBUFLEN+1);
V_VT(&(rgProperties[iProp].vValue))=VT_BSTR;
V_BSTR(&(rgProperties[iProp].vValue))=SysAllocString(wszBuff);
iProp++;
}

// If Password specified, then create property node
if( *szPassword != '\0' )
{
rgProperties[iProp].dwPropertyID=DBPROP_AUTH_PASSWORD;
rgProperties[iProp].dwOptions=DBPROPOPTIONS_REQUIRED;
rgProperties[iProp].colid=DB_NULLID;
MultiByteToWideChar(CP_ACP, 0, szPassword, -1, wszBuff, MAXBUFLEN+1);
V_VT(&(rgProperties[iProp].vValue))=VT_BSTR;
V_BSTR(&(rgProperties[iProp].vValue))=SysAllocString(wszBuff);
iProp++;
}

// Set prompt level if one was given.
if (iPrompt != -1)
{
rgProperties[iProp].dwPropertyID=DBPROP_INIT_PROMPT;
rgProperties[iProp].dwOptions=DBPROPOPTIONS_REQUIRED;
rgProperties[iProp].colid=DB_NULLID;
rgProperties[iProp].vValue.vt= VT_I2;
rgProperties[iProp].vValue.iVal= iPrompt;
iProp++;

rgProperties[iProp].dwPropertyID=DBPROP_INIT_HWND;
rgProperties[iProp].dwOptions=DBPROPOPTIONS_REQUIRED;
rgProperties[iProp].colid=DB_NULLID;
rgProperties[iProp].vValue.vt= VT_I4;
rgProperties[iProp].vValue.lVal= (long) hWnd;
iProp++;
}

if( iProp )
{
// Identify Property Set
rgPropertySet[0].rgProperties=rgProperties;
rgPropertySet[0].cProperties=iProp;
rgPropertySet[0].guidPropertySet=DBPROPSET_DBINIT;

// from the DataSource Object get the Session Object
hr = pIDBInit->QueryInterface(
IID_IDBProperties,
(void**)&pIDBProperties );
if (FAILED(hr))
{
GetDetailedErrorInfo(hr, pIDBInit, IID_IDBInitialize, "QI for IDBProperties FAILED!!");
goto error;
}

// Set Connection Properties
hr = pIDBProperties->SetProperties(1, rgPropertySet);
if (FAILED(hr))
{
GetDetailedErrorInfo(hr, pIDBProperties, IID_IDBProperties, "IDBProperties->SetProperties FAILED!!");
goto error;
}
}

 // Initialize the PROVIDER
if( FAILED(hr = pIDBInit->Initialize()) )
{
        GetDetailedErrorInfo(hr, pIDBInit, IID_IDBInitialize, "IDBInit->Initialize FAILED!!");
goto error;
}

// from the DataSource Object get the Session Object
if( FAILED(hr = pIDBInit->QueryInterface(
IID_IDBCreateSession,
(void**)&pIDBCreateSession)) )
{
GetDetailedErrorInfo(hr, pIDBInit, IID_IDBInitialize, "QI for IDBCreateSession FAILED!!");
goto error;
}

// from the Session object, attempt to get an IUnknown
if( FAILED(hr = pIDBCreateSession->CreateSession(NULL, IID_IUnknown, (IUnknown**)&pIUnknown)) )
{
GetDetailedErrorInfo(hr, pIDBCreateSession, IID_IDBCreateSession, "IDBCreateSession->CreateSession FAILED!!");
goto error;
}

// from the Session object, attempt to get the IDBCreateCommand interface
if( FAILED(hr = pIUnknown->QueryInterface(
IID_IDBCreateCommand,
(void**)&pIDBCreateCommand)) )
{
// if no Command object support, attempt to get the IOpenRowset interface
hr = pIUnknown->QueryInterface(
IID_IOpenRowset,
(void**)&pIOpenRowset);
if (FAILED(hr))
{
        GetDetailedErrorInfo(hr, pIUnknown, IID_IUnknown, "QI for IOpenRowset FAILED!!");
goto error;
}
}
else
{
fCommandWindow = TRUE;
}

// update the combo-box with IUnknown from Session object
wsprintf( szBuffer, PROVIDER_SESSION_FORMAT, (LPSTR)szProvName, pIUnknown );

nResult = (UINT)SendMessage(hWndCrsrList, CB_ADDSTRING, 0, (LPARAM)(LPSTR)szBuffer);
SendMessage(hWndCrsrList, CB_SETCURSEL, (WPARAM)nResult, 0);
SendMessage(hWndCrsrList, CB_SELECTSTRING, 0, (LPARAM)(LPSTR)szBuffer);
ChangeCurrentCursor(hWndCrsrList);

if (fCommandWindow)
{
// Create a Command Object and its associated window.
NewICommandWindow();
}
else
{
NewIOpenRowsetWindow();
}

bReturn = TRUE;

error:
if( pIMoniker )
pIMoniker->Release();

// Initialize Property Buffers
for(iProp=0; iProp<NUMELEM(rgProperties); iProp++)
VariantClear(&(rgProperties[iProp].vValue));

// If we are successully connected, and the IParse pointer
// exists release it.
if( bReturn && g_pIParse )
{
g_pIParse->Release();
g_pIParse = NULL;
}

// Release IUnknown (for Session) on error only.
// The pointer value has been copied to the window.
if( (!bReturn) && (pIUnknown) )
pIUnknown->Release();

if( (pIDBCreateCommand) )
pIDBCreateCommand->Release();

if( (pIDBProperties) )
pIDBProperties->Release();

if( (pIOpenRowset) )
pIOpenRowset->Release();

if( pIDBCreateSession )
pIDBCreateSession->Release();

if(pIDBInit)
pIDBInit->Release();

return (bReturn);

}

//**********************************************************************
//
// DisplayConnections
//
// Purpose:
//
//     Display list of available hdbc(s) in the given list box.
//
// Parameters:
//
// HWND hWndhdbc               - handle to the window
//
// Return Value:
//
// Function Calls:
//     Function                    Location
//
//
//
// Comments:
//
//
//**********************************************************************
void FAR PASCAL DisplayConnections(HWND hWndhdbc)
{
ULONG nConnects;// # of Connections
ULONG count;// Count
charszBuffer[MAXBUFLEN+1];// String Buffer

// Read the information from the  combo-box
// on the tool bar and feed it in the given list box.
SendMessage(hWndhdbc, LB_RESETCONTENT, 0, 0);
nConnects = SendMessage(hWndCrsrList, CB_GETCOUNT, 0, 0);

for (count = 0; count < nConnects; count++)
{
SendMessage(hWndCrsrList, CB_GETLBTEXT, count, (LPARAM)(LPSTR)szBuffer);
SendMessage(hWndhdbc, LB_ADDSTRING, 0, (LPARAM)(LPSTR)szBuffer);
}

SendMessage(hWndhdbc, LB_SETCURSEL, 0, 0);
}

//**********************************************************************
//
// DisplayICommands
//
// Purpose:
//
//     Initializes the PROVIDER and creates a ICommand Object.
//     Display list of ICommand Objects for the currently selected PROVIDER.
//
// Parameters:
//
//  HWND hWndhdbc - handle to the PROVIDER
// HWND hWndhstmt                - handle to the ICommand
//int nCrsrIndex - Count
//
// Return Value:
//
// Function Calls:
//     Function                    Location
//
//
//
// Comments:
//
//
//**********************************************************************
void FAR PASCAL DisplayICommands(HWND hWndhstmt, HWND hWndhdbc, int nCrsrIndex)
{
charszBuffer[MAXBUFLEN+1];// MDI child window title
HWNDhWndChild;// MDI child window handle
IDBCreateCommand * pIDBCreate1;// IDBCreateCommand Object #1
IDBCreateCommand * pIDBCreate2;// IDBCreateCommand Object #2
ICommand *pICommand;// ICommand Object

// Reset the Command list box in the disconnect dialog box
SendMessage(hWndhstmt, LB_RESETCONTENT, 0, 0);

// Go through all available MDI child windows and check if the
// PROVIDER in the title matches the one selected in the list box.
// If they match, use the Command in the window title to create
// a new entry in the Command list box.
for (hWndChild=GetWindow(hWndMDIClient, GW_CHILD); hWndChild; hWndChild=GetWindow(hWndChild, GW_HWNDNEXT))
{
// Class name check is necessary as some of MDI child
// windows may be iconized by the user and MDIClient
// in such cases create additional windows (such as icon titles).
GetClassName(hWndChild, szBuffer, MAXBUFLEN);

if (strcmp(szBuffer, OLEDBMDICLASS))
continue;

GetWindowText(hWndChild, szBuffer, MAXBUFLEN);
sscanf(szBuffer, SCANSESSIONCOMMAND_FORMAT, &pIDBCreate1, &pICommand);

SendMessage(hWndhdbc, LB_GETTEXT, (WPARAM)nCrsrIndex, (LPARAM)(LPSTR)szBuffer);
sscanf(szBuffer, SCANSESSION_FORMAT, &pIDBCreate2);

if (pIDBCreate1 != pIDBCreate2)
continue;

wsprintf(szBuffer, ((hWndChild == hWndActiveChild) ?  CURQUERY_STRING:QUERY_STRING), pICommand);
SendMessage(hWndhstmt, LB_ADDSTRING, 0, (LPARAM)(LPSTR)szBuffer);
}
SendMessage(hWndhstmt, LB_SETSEL, TRUE, 0);
}

//**********************************************************************
//
// NewICommandWindow
//
// Purpose:
//
//     Create a new ICommand Object on the current IDBCreateCommand.
//
// Parameters:
//
// Return Value:
//
// Function Calls:
//     Function                         Location
//
//   IDBCreateCommand::CreateCommandprovider's Command object
//     ICommand::Release                provider's Command object
//
//
// Comments:
//
//
//**********************************************************************
void FAR PASCAL NewICommandWindow()
{
int nCurrenthdbc;// Current PROVIDER
charszBuffer[MAXBUFLEN+1];// String in PROVIDER ComboBox on Toolbar
charszProvName[MAXBUFLEN+1];// DSN String
MDICREATESTRUCTmcs;// MDI Child Window Create Struc

IDBCreateCommand * pIDBCreateCommand= NULL;// IDBCreateCommand Object
IOpenRowset * pIOpenRowset= NULL;// IOpenRowset Object
ICommand *pICommand= NULL;// ICommand Object
ICommand *pIUnknown= NULL;// IUnknown for session Object
HRESULThr;// HRESULT
BOOLbReturn= FALSE;// Return Value

// check if there is PROVIDER selected in the Combo-Box
if ((nCurrenthdbc = (int)SendMessage(hWndCrsrList, CB_GETCURSEL, 0, 0)) == CB_ERR)
{
MessageBox(hWndFrame, MAKECONNECT, NOSESSIONERROR, MB_OK | MB_ICONHAND);
return;
}

// check if the number of windows exceeds MAXCHILDWNDS
if (nChildCount >= MAXCHILDWNDS)
{
MessageBox(hWndFrame, MAXCHILDEXCEEDED, MAXCHLDERR, MB_OK | MB_ICONHAND);
return;
}

// Scan PROVIDER string and IDBCreateCommand value from the combo-box
SendMessage(hWndCrsrList, CB_GETLBTEXT, (WPARAM)nCurrenthdbc, (LPARAM)(LPSTR)szBuffer);

sscanf(szBuffer, SCANPROVIDERSESSION_FORMAT, szProvName, &pIUnknown);

// from the Session object, attempt to get the IDBCreateCommand interface
hr = pIUnknown->QueryInterface(
IID_IDBCreateCommand,
(void**)&pIDBCreateCommand);
if (FAILED(hr))
{
// if no Command object support, attempt to get the IOpenRowset interface
hr = pIUnknown->QueryInterface(
IID_IOpenRowset,
(void**)&pIOpenRowset);
if (FAILED(hr))
{
    GetDetailedErrorInfo(hr, pIUnknown, IID_IUnknown, "QI for IOpenRowset FAILED!!");
goto error;
}
}
else
{
// Create a command object
hr = pIDBCreateCommand->CreateCommand(NULL, IID_ICommand, (IUnknown**)&pICommand);

if (FAILED(hr))
{
       GetDetailedErrorInfo(hr, pIDBCreateCommand, IID_IDBCreateCommand, "pIDBCreateCommand->CreateCommand FAILED!!");
   goto error;
}
}

// create a new MDI client window. maximized, if the previous is so.
mcs.szClass = OLEDBMDICLASS;
mcs.szTitle = UNTITLED;
mcs.hOwner  = hAppInstance;
mcs.style   = hWndActiveChild && IsZoomed(hWndActiveChild) ? WS_MAXIMIZE : 0;
mcs.x = mcs.cx = mcs.y = mcs.cy = CW_USEDEFAULT;
hWndActiveChild = (HWND)(UINT)SendMessage(hWndMDIClient, WM_MDICREATE, 0, (LPARAM)(LPMDICREATESTRUCT)&mcs);

    // check if it was created, if it wasn't free up resource and flag warning
    if (!hWndActiveChild)
    {
MessageBox(hWndFrame, CREATECHILDERR, EXECERROR, MB_OK|MB_ICONEXCLAMATION|MB_TASKMODAL);
goto error;
    }

// Display the Provider string, PROVIDER and IDBCreateCommand/IOpenRowset in the title
// of newly created window. Increment the child window counter
 wsprintf(szBuffer, QUERY_STRING, pICommand);

SendMessage(hWndStmtList, CB_ADDSTRING, 0, (LPARAM)(LPSTR)szBuffer);
SendMessage(hWndStmtList, CB_SELECTSTRING, 0, (LPARAM)(LPSTR)szBuffer);

 wsprintf(szBuffer, PROVIDER_SESSION_COMMAND_FORMAT, (LPSTR)szProvName, pIUnknown, pICommand);

SetWindowText(hWndActiveChild, szBuffer);
nChildCount++;

// Update the Command Combo-Box on the tool bar.
ChangeCurrentICommand(hWndStmtList);
bReturn = TRUE;

error:
// Note that we obtained pIUnknown (Session ptr) from the window.
// So we leave it alone.

// Release ICommand on error only.
// The pointer value has been copied to the window.
if (pICommand && !bReturn)
pICommand->Release();

if (pIDBCreateCommand)
pIDBCreateCommand->Release();

if (pIOpenRowset)
pIOpenRowset->Release();
}

//**********************************************************************
//
// ChangeCurrentCursor
//
// Purpose:
//
//     Change the displayed PROVIDER in the PROVIDER(s) combobox.
//     Also activate the appropriate MDI child window that
//     has the same PROVIDER as the new PROVIDER in the combobox.
//
// Parameters:
//
// HWND hWndCrsrList               - handle to the window
//
// Return Value:
//
// Function Calls:
//     Function                    Location
//
//
//
// Comments:
//
//
//**********************************************************************
void FAR PASCAL ChangeCurrentCursor(HWND hWndCrsrList)
{
ULONGnNewhdbc;// New PROVIDER position
ULONGnConnects;// # of connections
ULONGnCount;// Counter
charszBuffer[MAXBUFLEN+1];// String Buffer
BOOLbChangedFocus;// Activate different MDI child
HWNDhWndChild;// MDI Child window
IDBCreateCommand* pIDBCreate1;// IDBCreateCommand #1
IDBCreateCommand* pIDBCreate2;// IDBCreateCommand #2
ICommand*pICommand;// ICommand Object

// check to see if the current selection in the combo-box
// differs from the previous selection, if it is the same then
// simply return. Check is made by searching a marked string
// in the PROVIDER combobox.
nNewhdbc = (int)SendMessage(hWndCrsrList, CB_GETCURSEL, 0, 0);
nConnects = (int)SendMessage(hWndCrsrList, CB_GETCOUNT, 0, 0);

for(nCount = 0; nCount < nConnects; nCount++)
{
SendMessage(hWndCrsrList, CB_GETLBTEXT, nCount, (LPARAM)(LPSTR)szBuffer);
if (strstr(szBuffer, CUR_MARK))
break;
}

if (nCount == nNewhdbc)
return;

// if there was a current marked hdbc in the combobox, remove the
// mark from the string and replace it in the combobox.
if (nCount != nConnects)
{
SendMessage(hWndCrsrList, CB_GETLBTEXT, nCount, (LPARAM)(LPSTR)szBuffer);
szBuffer[strlen(szBuffer)-2] = '\0';
SendMessage(hWndCrsrList, CB_INSERTSTRING, nCount, (LPARAM)(LPSTR)szBuffer);
SendMessage(hWndCrsrList, CB_DELETESTRING, nCount+1, 0);
}

// Create a new marked string with currently selected hdbc string in
// the combobox and replace it with the original.
SendMessage(hWndCrsrList, CB_GETLBTEXT, nNewhdbc, (LPARAM)(LPSTR)szBuffer);
strcat(szBuffer, CUR_MARK);
SendMessage(hWndCrsrList, CB_INSERTSTRING, nNewhdbc, (LPARAM)(LPSTR)szBuffer);
SendMessage(hWndCrsrList, CB_DELETESTRING, nNewhdbc+1, 0);
SendMessage(hWndCrsrList, CB_SELECTSTRING, 0, (LPARAM)(LPSTR)szBuffer);

// Reset the ICommand combobox. Search through the MDI child windows
// and collect all ICommands from window titles that have the same
// PROVIDER value as the newly selected PROVIDER in the PROVIDER(s) combo-box above.
SendMessage(hWndStmtList, CB_RESETCONTENT, 0, 0);
for (bChangedFocus=FALSE, hWndChild=GetWindow(hWndMDIClient, GW_CHILD);hWndChild; hWndChild = GetWindow(
hWndChild, GW_HWNDNEXT))

{
// Check class name to skip iconized titles or other
// such non MDI Child windows
GetClassName(hWndChild, szBuffer, MAXBUFLEN);
if (strcmp(szBuffer, OLEDBMDICLASS))
continue;

GetWindowText(hWndChild, szBuffer, MAXBUFLEN);
sscanf(szBuffer, SCANSESSIONCOMMAND_FORMAT, &pIDBCreate1, &pICommand);

SendMessage(hWndCrsrList, CB_GETLBTEXT, (WPARAM)nNewhdbc, (LPARAM)(LPSTR)szBuffer);
sscanf(szBuffer, SCANSESSION_FORMAT, &pIDBCreate2);

if (pIDBCreate1 != pIDBCreate2)
continue;

if (!bChangedFocus)
{
            // If the first match is found, change the active window
        // and update the ICommand(s) combobox with a new entry that
        // has ICommand marked with current marker.
bChangedFocus = TRUE;
hWndActiveChild = hWndChild;
SendMessage(hWndMDIClient, WM_MDIACTIVATE, (WPARAM)hWndChild, 0);
wsprintf(szBuffer, CURQUERY_STRING, pICommand);
SendMessage(hWndStmtList, CB_ADDSTRING, 0, (LPARAM)(LPSTR)szBuffer);
SendMessage(hWndStmtList, CB_SELECTSTRING, 0, (LPARAM)(LPSTR)szBuffer);
}
else
{
// simply add the ICommand in the ICommand(s) combobox.
wsprintf(szBuffer, QUERY_STRING, pICommand);
SendMessage(hWndStmtList, CB_ADDSTRING, 0, (LPARAM)(LPSTR)szBuffer);
}
}
}

//**********************************************************************
//
// ChangeCurrentICommand
//
// Purpose:
//
//     Change the current selection in the ICommand(s) combobox.
//     Update the current marker in the combobox and activate
//     proper MDI Child window.
//
// Parameters:
//
// HWND hWndStmtList               - handle to the window
//
// Return Value:
//
// Function Calls:
//     Function                    Location
//
//
//
// Comments:
//
//
//**********************************************************************
void FAR PASCAL ChangeCurrentICommand(HWND hWndStmtList)
{
ULONGnNewICommand;// New Selection in Combo-Box
ULONG nICommands;// # of ICommands
ULONG nCount;// Counter
charszBuffer[MAXBUFLEN+1];// String Buffer
HWNDhWndChild;// MDI Child Window Handle
IDBCreateCommand* pIDBCreate1;// IDBCreateCommand Object #1
IDBCreateCommand* pIDBCreate2;// IDBCreateCommand Object #2
ICommand*pICommand1;// ICommand Object #1
ICommand*pICommand2;// ICommand Object #2

// Find the index of new selection and total number of ICommand(s)
nNewICommand = (int)SendMessage(hWndStmtList, CB_GETCURSEL, 0, 0);
nICommands  = (int)SendMessage(hWndStmtList, CB_GETCOUNT,  0, 0);

// Check if the current selection is same as previous one, if
// so simply return. Check for marker to determine previous selection
for(nCount = 0; nCount < nICommands; nCount++)
{
SendMessage(hWndStmtList, CB_GETLBTEXT, nCount, (LPARAM)(LPSTR)szBuffer);
if (strstr(szBuffer, CUR_MARK))
break;
}

if (nCount == nNewICommand)
return;

// If a previous selection was found, remove current marker
// and update it in the ICommand(s) combobox.
if (nCount != nICommands)
{
SendMessage(hWndStmtList, CB_GETLBTEXT, nCount, (LPARAM)(LPSTR)szBuffer);
szBuffer[strlen(szBuffer)-2] = '\0';
SendMessage(hWndStmtList, CB_INSERTSTRING, nCount, (LPARAM)(LPSTR)szBuffer);
SendMessage(hWndStmtList, CB_DELETESTRING, nCount+1, 0);
}

// Mark the current selection and update it in the ICommand(s) combobox
SendMessage(hWndStmtList, CB_GETLBTEXT, nNewICommand, (LPARAM)(LPSTR)szBuffer);
strcat(szBuffer, CUR_MARK);
SendMessage(hWndStmtList, CB_INSERTSTRING, nNewICommand, (LPARAM)(LPSTR)szBuffer);
SendMessage(hWndStmtList, CB_DELETESTRING, nNewICommand+1, 0);
SendMessage(hWndStmtList, CB_SELECTSTRING, 0, (LPARAM)(LPSTR)szBuffer);

// Scan ICommand value and DSN value from current selection in
// PROVIDER(s) and ICommand(s) comboboxes.
sscanf(szBuffer, QUERY_STRING, &pICommand1);

SendMessage( hWndCrsrList, CB_GETLBTEXT,
(UINT)SendMessage(hWndCrsrList, CB_GETCURSEL, 0, 0),
(LPARAM)(LPSTR)szBuffer);

sscanf(szBuffer, SCANSESSION_FORMAT, &pIDBCreate1);

// Go through list of MDI Child windows and match the ICommand and PROVIDER
// values. If a match if found (must be), activate the window
for (hWndChild=GetWindow(hWndMDIClient, GW_CHILD); hWndChild; hWndChild=GetWindow(hWndChild, GW_HWNDNEXT))
{
// Ignore non MDI child windows
GetClassName(hWndChild, szBuffer, MAXBUFLEN);
if (strcmp(szBuffer, OLEDBMDICLASS))
continue;

GetWindowText(hWndChild, szBuffer, MAXBUFLEN);
sscanf(szBuffer, SCANSESSIONCOMMAND_FORMAT, &pIDBCreate2, &pICommand2);

if (pIDBCreate1 == pIDBCreate2 && pICommand1 == pICommand2)
{
hWndActiveChild = hWndChild;
SendMessage(hWndMDIClient, WM_MDIACTIVATE, (WPARAM)hWndChild, 0);
break;
}
}
}

//**********************************************************************
//
// DisplayNewCrsrAndICommand
//
// Purpose:
//
//     Change the current selection in the ICommand(s) combobox.
//     Update the current marker in the combobox and activate
//     proper MDI Child window.
//
// Parameters:
//
// HWND hWndStmtList               - handle to the window
//
// Return Value:
//
// Function Calls:
//     Function                    Location
//
//
//
// Comments:
//
//
//**********************************************************************
void FAR PASCAL DisplayNewCrsrAndICommand()
{
ULONG nConnects;// # of PROVIDER(s)
ULONG nICommands;// # of ICommand(s)
ULONG nOldPROVIDER;// Prev selected PROVIDER in combobox
ULONGnOldICommand;// Prev selected ICommand in combobox
ULONGnIndex;// Counter
charszBuffer[MAXBUFLEN+1];// String Buffer
HWNDhWndChild;// MDI Child Window
IDBCreateCommand* pIDBCreate1;// IDBCreateCommand Object #1
IDBCreateCommand* pIDBCreate2;// IDBCreateCommand Object #2
ICommand*pICommand1;// ICommand Object #1
ICommand*pICommand2;// ICommand Object #2

// Scan PROVIDER and ICommand values from newly selected window
GetWindowText(hWndActiveChild, szBuffer, MAXBUFLEN);
sscanf(szBuffer, SCANSESSIONCOMMAND_FORMAT, &pIDBCreate1, &pICommand1);

// Search through list of PROVIDER(s) in PROVIDER combobox and find
// matching PROVIDER. remove marker from prev selection and add
// marker to the new selection. Update combobox accordingly.
nConnects = (int)SendMessage(hWndCrsrList, CB_GETCOUNT,  0, 0);
nOldPROVIDER   = (int)SendMessage(hWndCrsrList, CB_GETCURSEL, 0, 0);

for(pIDBCreate2 = (IDBCreateCommand*)(nIndex = 0); pIDBCreate1 != pIDBCreate2; nIndex++)
{
SendMessage(hWndCrsrList, CB_GETLBTEXT, nIndex, (LPARAM)(LPSTR)szBuffer);
sscanf(szBuffer, SCANSESSION_FORMAT, &pIDBCreate2);
}

// Change in PROVIDER combobox required.
if (--nIndex != nOldPROVIDER)
{
SendMessage(hWndCrsrList, CB_GETLBTEXT, nOldPROVIDER, (LPARAM)(LPSTR)szBuffer);
szBuffer[strlen(szBuffer)-2] = '\0';
SendMessage(hWndCrsrList, CB_INSERTSTRING, nOldPROVIDER, (LPARAM)(LPSTR)szBuffer);
SendMessage(hWndCrsrList, CB_DELETESTRING, nOldPROVIDER+1, 0);

SendMessage(hWndCrsrList, CB_GETLBTEXT, nIndex, (LPARAM)(LPSTR)szBuffer);
strcat(szBuffer, CUR_MARK);
SendMessage(hWndCrsrList, CB_INSERTSTRING, nIndex, (LPARAM)(LPSTR)szBuffer);
SendMessage(hWndCrsrList, CB_DELETESTRING, nIndex+1, 0);
SendMessage(hWndCrsrList, CB_SELECTSTRING, 0, (LPARAM)(LPSTR)szBuffer);

// Reset the ICommand(s) combobox, search through the list
// of MDI child windows and find all hstmt(s) associated to
// new PROVIDER. Build the new list of ICommand(s) for the ICommand
// combobox. Mark the one ICommand that matches the currently
// activated MDI child window.
SendMessage(hWndStmtList, CB_RESETCONTENT, 0, 0);
for (hWndChild=GetWindow(hWndMDIClient,GW_CHILD);hWndChild;hWndChild=GetWindow(hWndChild,GW_HWNDNEXT))
{
GetClassName(hWndChild, szBuffer, MAXBUFLEN);
if (strcmp(szBuffer, OLEDBMDICLASS))
continue;

GetWindowText(hWndChild, szBuffer, MAXBUFLEN);
sscanf(szBuffer, SCANSESSIONCOMMAND_FORMAT, &pIDBCreate2, &pICommand2);

if (pIDBCreate1 != pIDBCreate2)
continue;

if (hWndActiveChild == hWndChild)
{
wsprintf(szBuffer, CURQUERY_STRING, pICommand2);
SendMessage(hWndStmtList, CB_ADDSTRING, 0, (LPARAM)(LPSTR)szBuffer);
SendMessage(hWndStmtList, CB_SELECTSTRING, 0, (LPARAM)(LPSTR)szBuffer);
}
else
{
wsprintf(szBuffer, QUERY_STRING, pICommand2);
SendMessage(hWndStmtList, CB_ADDSTRING, 0, (LPARAM)(LPSTR)szBuffer);
}
}
}
// No change in PROVIDER combobox required
else
{
// Go through the list of ICommand(s) in ICommand combobox.
// Find the one that matches the currently activated MDI child window.
nOldICommand = (int)SendMessage(hWndStmtList, CB_GETCURSEL, 0, 0);
nICommands = (int)SendMessage(hWndStmtList, CB_GETCOUNT, 0, 0);

for(pICommand2 = (ICommand*)(nIndex = 0); pICommand1 != pICommand2; nIndex++)
{
SendMessage(hWndStmtList, CB_GETLBTEXT, nIndex, (LPARAM)(LPSTR)szBuffer);
sscanf(szBuffer, QUERY_STRING, &pICommand2);
}

// New index in ICommand differs from previous selection
if (--nIndex != nOldICommand)
{
// Remove the marker from previous selection.
// Add it to the new string and update the combobox display
SendMessage(hWndStmtList, CB_GETLBTEXT, nOldICommand, (LPARAM)(LPSTR)szBuffer);
szBuffer[strlen(szBuffer)-2] = '\0';
SendMessage(hWndStmtList, CB_INSERTSTRING, nOldICommand, (LPARAM)(LPSTR)szBuffer);
SendMessage(hWndStmtList, CB_DELETESTRING, nOldICommand+1, 0);

SendMessage(hWndStmtList, CB_GETLBTEXT, nIndex, (LPARAM)(LPSTR)szBuffer);
strcat(szBuffer, CUR_MARK);
SendMessage(hWndStmtList, CB_INSERTSTRING, nIndex, (LPARAM)(LPSTR)szBuffer);
SendMessage(hWndStmtList, CB_DELETESTRING, nIndex+1, 0);
SendMessage(hWndStmtList, CB_SELECTSTRING, 0, (LPARAM)(LPSTR)szBuffer);
}
}
}

//**********************************************************************
//
// FreeConnect
//
// Purpose:
//
//     Disconnect and Free the currently selected PROVIDER in
//     the PROVIDER listbox in disconnect dialog. Call RELEASE
//     to free the PROVIDER. Close all MDI
//     child windows associated with this PROVIDER. That will
//     automatically free associated ICommand(s).
//
// Parameters:
//
// HWND hWndhdbc               - handle to the window
//
// Return Value:
//
// Function Calls:
//     Function                    Location
//
//     IDBCreateCommand::Release   provider's Command object
//
// Comments:
//
//
//**********************************************************************
void FAR PASCAL FreeConnect(HWND hWndhdbc)
{
int nIndex;// Current Selection in the listbox of disconnect dlg
intnCurrent;// Current Selected PROVIDER(s) combobox
charszBuffer[MAXBUFLEN+1];// String Buffer
charszSelect[MAXBUFLEN+1];// Original Selected PROVIDER(s) combobox
charszProvName[MAXBUFLEN+1];// DSN string
HWNDhWndChild;// MDI Child window
IDBCreateCommand * pIDBCreate1;// IDBCreateCmmand Object #1
IDBCreateCommand * pIDBCreate2;// IDBCreateCmmand Object #2
IErrorInfo *pIErrorInfo;// IErrorInfo Object

// Check current selection in the list box of disconnect dialog.
// Scan PROVIDER value from the current selection.
if ((nIndex = (int)SendMessage(hWndhdbc, LB_GETCURSEL, 0, 0)) == LB_ERR)
return;

SendMessage(hWndhdbc, LB_GETTEXT, (WPARAM)nIndex, (LPARAM)(LPSTR)szBuffer);
sscanf(szBuffer, SCANPROVIDERSESSION_FORMAT, szProvName, &pIDBCreate1);

// Go through the list of MDI child windows and find matching PROVIDER(s)
// close all children who have the same PROVIDER value. Closing them
// automatically frees associated ICommands. See CloseICommandWindow.
for(hWndChild = GetWindow(hWndMDIClient, GW_CHILD); hWndChild; )
{
// Store next window handle before destroying the currentone
HWND hWndTemp = GetWindow(hWndChild, GW_HWNDNEXT);

// Ignore non MDI child windows
GetClassName(hWndChild, szBuffer, MAXBUFLEN);
if (!strcmp(szBuffer, OLEDBMDICLASS))
{
GetWindowText(hWndChild, szBuffer, MAXBUFLEN);
sscanf(szBuffer, SCANSESSION_TITLEFORMAT, &pIDBCreate2);
if (pIDBCreate1 == pIDBCreate2)
{
// Destroy the window and restart search
SendMessage(hWndMDIClient, WM_MDIDESTROY, (WPARAM)hWndChild, 0);
hWndTemp = GetWindow(hWndMDIClient, GW_CHILD);
}
}
hWndChild = hWndTemp;
}

// Release the Command Object & return
if( pIDBCreate1 )
pIDBCreate1->Release();

// Release any remaining error objects.  This is idempotent.
// Otherwise the provider might have an outstanding global-object count.
GetErrorInfo( 0, &pIErrorInfo );
if (pIErrorInfo)
pIErrorInfo->Release();

// Unload any DLL's no longer being used
CoFreeUnusedLibraries();

// Update the PROVIDER(s) combobox display by removing the deleted PROVIDER
// from the list and reselecting the previous selection
wsprintf(szBuffer, PROVIDER_SESSION_FORMAT, (LPSTR)szProvName, pIDBCreate1);
nCurrent = (int)SendMessage(hWndCrsrList, CB_GETCURSEL, 0, 0);
SendMessage(hWndCrsrList, CB_GETLBTEXT, nCurrent, (LPARAM)(LPSTR)szSelect);
nIndex = (int)SendMessage(hWndCrsrList, CB_FINDSTRING, 0, (LPARAM)(LPSTR)szBuffer);
SendMessage(hWndCrsrList, CB_DELETESTRING, (WPARAM)nIndex, 0);
SendMessage(hWndCrsrList, CB_SELECTSTRING, 0, (LPARAM)(LPSTR)szSelect);

// If there is no query window open and the current selected PROVIDER
// was deleted, make sure to make the next available PROVIDER as current
if (SendMessage(hWndCrsrList, CB_GETCOUNT, 0, 0) &&
!GetWindow(hWndMDIClient, GW_CHILD) && (nCurrent == nIndex))
{
if ((nCurrent = (int)SendMessage(hWndCrsrList, CB_GETCURSEL, 0, 0))!=CB_ERR)
return;
SendMessage(hWndCrsrList, CB_GETLBTEXT, 0, (LPARAM)(LPSTR)szSelect);
strcat(szSelect, CUR_MARK);
SendMessage(hWndCrsrList, CB_INSERTSTRING, 0, (LPARAM)(LPSTR)szSelect);
SendMessage(hWndCrsrList, CB_DELETESTRING, 1, 0);
SendMessage(hWndCrsrList, CB_SELECTSTRING, 0, (LPARAM)(LPSTR)szSelect);
}
}

//**********************************************************************
//
// FreeICommand
//
// Purpose:
//
//     Free a ICommand window based upon current selection in
//     ICommand list box in the disconnect dialog.
//
// Parameters:
//
// HWND hWndhstmt               - handle to the window
// HWND hWndhdbc                - handle to the window
//  int             nIndex- Index
//
// Return Value:
//
// Function Calls:
//     Function                    Location
//
//
//
// Comments:
//
//
//**********************************************************************
void FAR PASCAL FreeICommand(HWND hWndhstmt, HWND hWndhdbc, int nIndex)
{
charszBuffer[MAXBUFLEN+1];// display buffer
HWNDhWndChild;// MDI child window
IDBCreateCommand* pIDBCreate1;// IDBCreateCommand Object #1
IDBCreateCommand* pIDBCreate2;// IDBCreateCommand Object #2
ICommand*pICommand1;// ICommand Object #1
ICommand*pICommand2;// ICommand Object #2

// Scan the ICommand and PROVIDER values from the current selections in
// respective listboxes of disconnect dialog box.
SendMessage(hWndhstmt, LB_GETTEXT, nIndex, (LPARAM)(LPSTR)szBuffer);
sscanf(szBuffer, QUERY_STRING, &pICommand1);
SendMessage(hWndhdbc, LB_GETTEXT, (UINT)SendMessage(hWndhdbc, LB_GETCURSEL,0, 0), (LPARAM)(LPSTR)szBuffer);
sscanf(szBuffer, SCANSESSION_FORMAT, &pIDBCreate1);

    // Go through the list of MDI child windows and find matching window
    // that has same values for PROVIDER and ICommand. Destroy the matching window.
    // That will call CloseICommandWindow and free up associated ICommand(s).
for(hWndChild=GetWindow(hWndMDIClient, GW_CHILD); hWndChild; hWndChild=GetWindow(hWndChild, GW_HWNDNEXT))
{
// Ignore non MDI child windows
GetClassName(hWndChild, szBuffer, MAXBUFLEN);
if( strcmp(szBuffer, OLEDBMDICLASS) )
continue;

GetWindowText(hWndChild, szBuffer, MAXBUFLEN);
sscanf(szBuffer, SCANSESSIONCOMMAND_FORMAT, &pIDBCreate2, &pICommand2);
if (pIDBCreate1 == pIDBCreate2 && pICommand1 == pICommand2)
break;
}
SendMessage(hWndMDIClient, WM_MDIDESTROY, (WPARAM)hWndChild, 0);
}

//**********************************************************************
//
// CloseICommandWindow
//
// Purpose:
//
//     Close a ICommand window.
//     Call ICommand->Release() to free associated ICommand(s).
//
// Parameters:
//
// HWND hWnd               - handle to the window
//
// Return Value:
//
// Function Calls:
//     Function                    Location
//
//     ICommand::Release           provider's Command object
//
// Comments:
//
//
//**********************************************************************
void FAR PASCAL CloseICommandWindow(HWND hWnd)
{
charszBuffer[MAXBUFLEN+1];// String buffer
charszSelect[MAXBUFLEN+1];// Current Selected ICommand(s)
ICommand*pICommand;// ICommand Object

// Scan the ICommand value from the window title
GetWindowText(hWnd, szBuffer, MAXBUFLEN);
sscanf(szBuffer, SCANCOMMAND_TITLEFORMAT, &pICommand);

// Release the Command Object & return
if( pICommand )
pICommand->Release();

// Find the matching ICommand in the ICommand(s) combobox and remove it
// from the list. Closure of a MDI child window will cause MDIClient
// to automatically activate a different child window if available.
// That will automatically refresh the ICommand and PROVIDER displays.
// See DisplayNewCrsrAndICommand function.
wsprintf( szBuffer, QUERY_STRING, pICommand );

SendMessage( hWndStmtList, CB_GETLBTEXT,
(WPARAM)SendMessage(hWndStmtList, CB_GETCURSEL, 0, 0),
(LPARAM)(LPSTR)szSelect );

SendMessage( hWndStmtList, CB_DELETESTRING,
(WPARAM)SendMessage(hWndStmtList, CB_FINDSTRING, 0,
(LPARAM)(LPSTR)szBuffer), 0 );

SendMessage( hWndStmtList, CB_SELECTSTRING,
(WPARAM)-1,
(LPARAM)(LPSTR)szSelect );

// Decrement the child window counter.
nChildCount--;
}

//**********************************************************************
//
// ExecuteQuery
//
// Purpose:
//
//     Execute the user typed SQL Statements in the currently
//     active MDI child window. If successful, then prepare
//     the list of results and display it in the child listbox.
//     Display errors in the OLE DB function(s) failed.
//
// Parameters:
//
// Return Value:
//
// Function Calls:
//     Function                    Location
//
//     ExecuteCommand              query.cpp
//     GetDataFromRowset           query.cpp
//
//
// Comments:
//
//
//**********************************************************************
void FAR PASCAL ExecuteQuery()
{
CHARszBuffer[MAXBUFLEN+1];// String Buffer
WCHARwszBuffer[MAXBUFLEN+1];// String Buffer (WCHAR)
signed short intswColLength = MAXDATALEN;// Column Data Length
HCURSORhOldCursor;// Default Cursor Handle
IUnknown*pIUnknown = NULL;// Session Object
ICommand*pICommand = NULL;// ICommand Object
IOpenRowset*pIOpenRowset= NULL;// IOpenRowset Object
IRowset*pIRowset = NULL;// IRowset Object
HRESULThr;// HRESULT

// Check if there is an active window available
if (!hWndActiveChild)
{
MessageBox(hWndFrame, ((SendMessage(hWndCrsrList, CB_GETCOUNT, 0, 0) <=0) ?
MAKECONNECT : OPENWINDOW ), NOCOMMANDERROR, MB_OK | MB_ICONHAND);
return;
}

// Change cursor shape to hour glass
hOldCursor = SetCursor(LoadCursor((HINSTANCE)NULL, IDC_WAIT));

// Scan PROVIDER, session and ICommand values
GetWindowText(hWndActiveChild, (char*)szBuffer, MAXBUFLEN);
sscanf(szBuffer, SCANSESSIONCOMMAND_FORMAT, &pIUnknown, &pICommand);

// Get the user typed SQL
GetWindowText(GetDlgItem((HWND)GetWindowLong(hWndActiveChild, GWLAPP_HDLG), IDTEXT_SQL), szBuffer, MAXBUFLEN);

// Convert ANSI String to WCHARString
MultiByteToWideChar(CP_ACP, 0, szBuffer, -1, wszBuffer, MAXBUFLEN+1);

// Execute the Command Object
hr = ExecuteCommand( pIUnknown, pICommand, wszBuffer, &pIRowset );

if (FAILED(hr))
return;

// Empty IRowset
if( hr == ResultFromScode(S_FALSE) )
{
DumpErrorHResult( ResultFromScode(S_OK), (LPSTR)"ExecuteCommand returned No Rowset!!" );
return;
}

// Get Data from the IRowset Object
GetDataFromRowset( pIRowset );
}

//**********************************************************************
//
// CloseIDBCreateCommand
//
// Purpose:
//
//     Go through all open PROVIDER's (IDBCreateCommand(s) and ICommand(s))
//     and close them one by one.
//
// Parameters:
//
// Return Value:
//
// Function Calls:
//     Function                    Location
//
//     IDBCreateCommand::Release   provider's Command object
//
// Comments:
//
//
//**********************************************************************
BOOL FAR PASCAL CloseIDBCreateCommand()
{
ULONG nIndex;// Index
ULONGnCount;// # of PROVIDER(s)
charszBuffer[MAXBUFLEN+1];// String Buffer
HWNDhWndChild;// MDI Child Window
IDBCreateCommand* pIDBCreate1;// IDBCreateCommand Object #1
IDBCreateCommand* pIDBCreate2;// IDBCreateCommand Object #2

// Get count of connected PROVIDER(s) from the PROVIDER(s) combobox on the toolbar
if (!(nCount = (int)SendMessage(hWndCrsrList, CB_GETCOUNT, 0, 0)))
return (TRUE);

// Go through all available MDI child windows and for each PROVIDER,
// find the matching MDI child window and ask it for closure, thereby
// freeing the associated ICommand (see CloseICommandWindow). Once all
// associated ICommand(s) are freed, free the PROVIDER
for (nIndex = 0; nIndex < nCount; nIndex++)
{
// Scan current indexed PROVIDER from PROVIDER(s) combobox
SendMessage(hWndCrsrList, CB_GETLBTEXT, (WPARAM)nIndex, (LPARAM)(LPSTR)szBuffer);
sscanf(szBuffer, SCANSESSION_FORMAT, &pIDBCreate1);

// Search through the list of MDI Child Windows
for(hWndChild = GetWindow(hWndMDIClient, GW_CHILD); hWndChild; )
{
// Store the next child, before destroying the current
HWND hWndTemp = GetWindow(hWndChild, GW_HWNDNEXT);

// Ignore non MDI child windows
GetClassName(hWndChild, szBuffer, MAXBUFLEN);
if (!strcmp(szBuffer, OLEDBMDICLASS))
{
GetWindowText(hWndChild, szBuffer, MAXBUFLEN);
sscanf(szBuffer, SCANSESSION_TITLEFORMAT, &pIDBCreate2);
if (pIDBCreate1 == pIDBCreate2)
{
// Destroy the window and restart search
SendMessage(hWndMDIClient, WM_MDIDESTROY, (WPARAM)hWndChild, 0);
hWndTemp = GetWindow(hWndMDIClient, GW_CHILD);
}
}
hWndChild = hWndTemp;
}

// Call IDBCreateCommand->Release()
// to free the current IDBCreateCommand resource
if( pIDBCreate1 )
pIDBCreate1->Release();
}

// Reset the PROVIDER(s) combobox display and display all connections
// closed message. Return success to let application exit.
SendMessage(hWndCrsrList, CB_RESETCONTENT, 0, 0);
MessageBox(hWndFrame, CLOSEALLSESSION, LOGOUTINFO,MB_OK | MB_ICONINFORMATION);
return (TRUE);
}

/*
FUNCTION: FreeEnvironment()
COMMENTS: Free the OLE environment.
*/
HRESULT FreeEnvironment()
{
    HRESULThr = S_OK;// HRESULT
IErrorInfo*pIErrorInfo;//IErrorInfo

    // Release the task memory allocator
if( g_pIMalloc )
g_pIMalloc->Release();

// Release any remaining error objects.  This is idempotent.
// Otherwise the provider might have an outstanding global-object count.
GetErrorInfo( 0, &pIErrorInfo );
if (pIErrorInfo)
pIErrorInfo->Release();

// Free the error queues
SetErrorInfo(0,NULL);

// Uninitialize OLE
CoUninitialize();

return hr;
}

//**********************************************************************
//
// ExecuteCommand
//
// Purpose:
//
//    Executes the query command that was previously set on the Command object,
//    and returns a first interface pointer on the resulting Rowset object.
//
// Parameters:
//   IOpenRowset*pIOpenRowset- interface pointer to data provider's
//  IOpenRowset
//   ICommand*pICommand       - interface pointer on data provider's
//              Command object
//   LPWSTRwszBuffer- SQL-style query string to be sent
//              to the Command object or table/file name to
//  be sent to IOpenRowset
//   IRowset**ppIRowset_out   - out pointer through which to return
//              interface pointer on provider's
//          Rowset object
//
// Return Value:
//     S_OK        - Success
//     E_*         - Failure
//
// Function Calls:
//     Function                    Location
//
//     ICommand::Execute           provider's Command object
//     IMalloc::Free               OLE task memory allocator
//
//
//     assert                      c runtime
//
//
//
// Comments:
//
//     The interface pointer returned through ppIRowset_out has been AddRef'ed,
//     it must be Release'd later by the caller.
//
//**********************************************************************
HRESULT ExecuteCommand(
IUnknown*pIUnknown,// Session object
ICommand*pICommand,// Command object
LPWSTR wszBuffer,// SQL String (WCHAR) or Table Name
IRowset**ppIRowset_out
)
{
ICommandText*pICommandText = NULL;// ICommandText Object
IOpenRowset*pIOpenRowset = NULL;
DBIDTableID;// Table ID
HRESULThr = S_OK;// HRESULT

// Asserts
assert(ppIRowset_out != NULL);
    assert(wszBuffer != NULL);

memset(&TableID, 0, sizeof(DBID));

// If no command object use IOpenRowset
if (NULL == pICommand)
{
// if no Command object support, attempt to get the IOpenRowset interface
if( FAILED(hr = pIUnknown->QueryInterface(IID_IOpenRowset, (void**)&pIOpenRowset)) )
{
        GetDetailedErrorInfo(hr, pIUnknown, IID_IUnknown, "QI for IOpenRowset FAILED!!");
goto error;
}
// Pass in table/file name
TableID.eKind = DBKIND_NAME;

TableID.uName.pwszName = wszBuffer;

// From IOpenRowset, get a rowset object
if( FAILED(hr = pIOpenRowset->OpenRowset(
NULL,// pUnkOuter
&TableID,// pTableID
NULL,// pIndexID
IID_IRowset,// refiid
0,// cProperties
NULL,// rgProperties
 (IUnknown**)ppIRowset_out)) )  // IRowset pointer
{
     GetDetailedErrorInfo(hr, pIOpenRowset, IID_IOpenRowset, "IOpenRowset->OpenRowset FAILED!!");
goto error;
}
}
else
{
// QueryInterface for ICommandText::SetCommandText
if( SUCCEEDED(hr = pICommand->QueryInterface(IID_ICommandText,(LPVOID*)&pICommandText)) )
{
// Tell the command object to copy this command text
// The command object will then use this query when we call ICommand::Execute
if( FAILED(hr = pICommandText->SetCommandText(DBGUID_DBSQL, wszBuffer)) )
{
        GetDetailedErrorInfo(hr, pICommandText, IID_ICommandText, "ICommandText->SetCommandText FAILED!!");
goto error;
}
}

// From the command object, get a rowset object by executing command
if( FAILED(hr = pICommand->Execute(
NULL,// pUnkOuter
IID_IRowset,// refiid
NULL,// disp parms
NULL,// rows affected
 (IUnknown**)ppIRowset_out)) )  // IRowset pointer
{
GetDetailedErrorInfo(hr, pICommand, IID_ICommand, "ICommand->Execute FAILED!!");
goto error;
}
}

// NO Rowset Returning Statement
    if( !*ppIRowset_out )
hr = ResultFromScode(S_FALSE);
error:
if( pICommandText )
pICommandText->Release();
if( pIOpenRowset )
pIOpenRowset->Release();

if( FAILED(hr) )
*ppIRowset_out = NULL;

return hr;
}

//**********************************************************************
//
// GetDataFromRowset
//
// Purpose:
//
//     Pulls the data from a Rowset object.
//
// Parameters:
//
// IRowset*pIRowset    -  interface pointer on data provider's
//                             Rowset object
//
// Return Value:
//
//     S_OK        - Success
//     E_*         - Failure
//
// Function Calls:
//     Function                    Location
//
//   IRowset::QueryInterface   Clients Rowset pointer
//     SetupBindings               query.cpp
//     CreateAccessor              query.cpp
//     GetData                     query.cpp
//     CleanupRowset               query.cpp
//
//     IMalloc::Free               OLE task memory allocator
//
//     assert                      c runtime
//
// Comments:
//
//     At a high level, a consumer pulls the data from a Rowset object by:
//
//     1. getting metadata for the Rowset's columns

//     2. using that metadata, along with the consumer's own knowledge of
//        how it wants to recieve the data, to create bindings. Bindings
//        represent how the actual data in the Rowset's columns is
//        actually transferred to the consumer's buffer.
//     3. pass the bindings to the Rowset, and get in return an accessor
//        handle that represents that particular set of bindings
//     4. get the actual data
//     5. clean up the rowset (at a minumum, release the accessor)
//
//     GetDataFromRowset performs these steps by calling GetColumnsInfo,
//     SetupBindings, CreateAccessor, GetData, and CleanupRowset
//
//**********************************************************************
HRESULT GetDataFromRowset(
IRowset*pIRowset
)
{
charszBuffer[MAXBUFLEN+1];// String Buffer
HWNDhList;// Result Listbox Handle
DWORD dwText;// Tab Stop for Listbox
unsigned short intnCount;// Index
signed short intswColLength = MAXDATALEN;// Column Data Length
intcch;// Count of characters
HCURSORhOldCursor;// Default Cursor Handle
HRESULThr = S_OK;// HRESULTS
    ULONGcCol;// # of Columns in Rowset
    DBCOLUMNINFO*pColumnInfo;// Array of DBCOLUMNINFO
    WCHAR*pStringsBuffer;// Storage for strings

// BINDINGS
ULONG cbMaxRowSize;// buffer size for 1 row's data
ULONG cBind;
DBBINDINGrgBind[MAX_COL];

// ACCESSORS
HACCESSORhAccessor= NULL;

// Asserts
assert(pIRowset   != NULL);
assert(g_pIMalloc != NULL);

// Change cursor shape to hour glass
hOldCursor = SetCursor(LoadCursor((HINSTANCE)NULL, IDC_WAIT));

// Get the Columns Info
GetColumnsInfo( pIRowset, &cCol, &pColumnInfo, &pStringsBuffer);

// Call GetColumnsInfo to calculate the number of columns in
// the result set, if more than the MAX_COL (the array boundary)
// limit the number to MAX_COL and display truncation warning.
// if it is 0, the statement probably was a non-SELECT simply return
if (cCol >= MAX_COL)
{
cCol = MAX_COL;
wsprintf(szDispBuffer, COLTRUNC_WARNG, MAX_COL);
MessageBox(hWndFrame, szDispBuffer, TRUNCERR, MB_OK | MB_ICONINFORMATION);
}
else if (cCol == 0)
{
SetCursor(hOldCursor);
goto error;
}

// Reset the display in the list box. Set tabstops to display
// multiple columns in the list box separated by tabs.
hList = GetDlgItem((HWND)GetWindowLong(hWndActiveChild, GWLAPP_HDLG), IDLIST_RSLT);

SendMessage(hList, LB_RESETCONTENT, 0, 0);
SendMessage(hList, WM_SETREDRAW, FALSE, 0);
dwText = LISTTABSTOP;
SendMessage(hList, LB_SETTABSTOPS, (WPARAM)1, (LPARAM)(LPINT)&dwText);

// Display a description of each column in the result set.
// Store the column name in the display buffer and make it
// the first entry in the results list box of the MDI child window.
for(nCount=0, szDispBuffer[0]='\0'; nCount < cCol; nCount++)
{
if (pColumnInfo[nCount].pwszName)
{
cch = WideCharToMultiByte(CP_ACP, 0, pColumnInfo[nCount].pwszName, -1, szBuffer, MAXBUFLEN+1, NULL, NULL);

// Truncate if needed.
// We should use GetTextExtents to determine width, but that's too complicated for now.
szBuffer[MAXCOLNAMELEN] = '\0';

// Might be empty string for column name.
if (cch)
strcat(szDispBuffer, szBuffer);
else
strcat(szDispBuffer, "<EMPTY STRING>");
}
else
{
// Missing the column name; it is unknown.
strcat(szDispBuffer, "<NULL STRING>");
}

dwText = strlen(szDispBuffer);
szDispBuffer[dwText++] = '\t';
szDispBuffer[dwText] = '\0';
}

// NULL Terminate the Display Buffer
if (*szDispBuffer)
szDispBuffer[strlen(szDispBuffer)-1]='\0';

// ADD the Column Info to the Screen
SendMessage(hList, LB_ADDSTRING, 0, (LPARAM)(LPSTR)szDispBuffer);

// SetUp Bindings
if( FAILED(hr = SetupBindings( cCol, pColumnInfo, rgBind, &cBind, &cbMaxRowSize)) )
goto error;

// Create the Accessor
if( FAILED(hr = CreateAccessor( pIRowset, rgBind, cBind, &hAccessor)) )
goto error;

// GetData
if( FAILED(hr = GetData( pIRowset, cbMaxRowSize, hAccessor, rgBind, cBind, pColumnInfo,
cCol, hList, dwText, LB_ADDSTRING)) )
goto error;

// restore the cursor to default value
SetCursor(hOldCursor);

error:
// Release the IColumnsInfo Object
if( pColumnInfo )
g_pIMalloc->Free( pColumnInfo );
if( pStringsBuffer )
g_pIMalloc->Free( pStringsBuffer );

// Clean-up the Rowset
CleanupRowset( pIRowset, hAccessor );

    return hr;
}

//**********************************************************************
//
// GetColumnsInfo
//
// Purpose:
//
//     Get the Columns metadata back for the provider to setup the bundings.
//
// Parameters:
//
//IRowset*pIRowset- IRowset pointer to QI off of
// ULONG cCol                - number of columns in metadata
// DBCOLUMNINFO*pColumnInfo         - pointer to column metadata
// WCHAR*pStringsBuffer      - pointer through which to return
//                                        an array of string data, one
//                                        structure per column bound
//
// Return Value:
//     S_OK         - Success
//     E_*          - Failure
//
// Function Calls:
//     Function                    Location
//
//     assert                      c runtime
//     IRowset::QueryInterface     provider's Rowset object
//     IColumnsInfo::GetColumnInfo provider's ColumnInfo object
//     IColumnsInfo::Release       provider's ColumnInfo object
//
//
// Comments:
//
//
//**********************************************************************
HRESULT GetColumnsInfo
(
IRowset*pIRowset,
ULONG* pcCol,
DBCOLUMNINFO**ppColumnInfo,
WCHAR**ppStringsBuffer
)
{

HRESULThr=NOERROR;// HRESULTS
IColumnsInfo* pIColumnsInfo = NULL;// IColumnsInfo Object

// QI for IColumnsInfo Object
if( FAILED(hr = pIRowset->QueryInterface( IID_IColumnsInfo, (void **) &pIColumnsInfo)) )
{
GetDetailedErrorInfo(hr, pIRowset, IID_IRowset, "QI for IColumnsInfo FAILED!!");
goto error;
}

// Get column information from the command object via IColumnsInfo::GetColumnsInfo
if( FAILED(hr = pIColumnsInfo->GetColumnInfo( pcCol, ppColumnInfo, ppStringsBuffer)) )
{
GetDetailedErrorInfo(hr, pIColumnsInfo, IID_IColumnsInfo, "IColumnsInfo->GetColumnsInfo FAILED!!");
goto error;
}

error:
// Release the IColumnsInfo Object
if( pIColumnsInfo )
pIColumnsInfo->Release();
return hr;

}

//**********************************************************************
//
// SetupBindings
//
// Purpose:
//
//     Creates bindings that map the data in the rowset's columns to
//     slots in the consumer's data buffer.
//
// Parameters:
//
// ULONG cCol                - number of columns in rowset to bind
// DBCOLUMNINFO*pColumnInfo         - pointer to column metadata
// DBBINDING*rgBind_out          - out pointer through which to return
//                                        an array of binding structures, one
//                                        structure per column bound
// ULONG*pcBind_out          - out pointer through which to return
//                                        the number of columns bound (number
//                                        of valid elements in rgBind_out)
// ULONG*pcMaxRowSize_out    - out pointer through which to return
//                                        the buffer size necessary to hold
//                                        the largest row data
//
// Return Value:
//     S_OK         - Success
//     E_*          - Failure
//
// Function Calls:
//     Function                    Location
//
//     assert                      c runtime
//
//
// Comments:
//
//
//**********************************************************************
HRESULT SetupBindings
(
ULONG cCol,
DBCOLUMNINFO*pColumnInfo,
DBBINDING*rgBind_out,
ULONG*pcBind_out,
ULONG*pcMaxRowSize_out
)
{
#define DEFAULT_CBMAXLENGTH 80// cbMaxLength for binding
ULONG dwOffset;// Length of a Row
ULONG iCol;// Column Count
ULONG iBind;// Binding Index

// Asserts
    assert(pColumnInfo!= NULL);
    assert(rgBind_out != NULL);
    assert(pcBind_out != NULL);
    assert(pcMaxRowSize_out != NULL);

   // Create bindings.
// Bind everything as a string just to keep things simple.
dwOffset = 0;
iBind=0;
for (iCol=0; iCol < cCol; iCol++)
{
// Binding Structure
rgBind_out[iBind].dwPart= DBPART_VALUE | DBPART_LENGTH |
  DBPART_STATUS;
rgBind_out[iBind].eParamIO  = DBPARAMIO_NOTPARAM;
rgBind_out[iBind].iOrdinal= pColumnInfo[iCol].iOrdinal;
rgBind_out[iBind].wType     = DBTYPE_WSTR;
rgBind_out[iBind].pTypeInfo = NULL;
rgBind_out[iBind].obValue   = dwOffset + offsetof(COLUMNDATA,bData);
rgBind_out[iBind].obLength  = dwOffset + offsetof(COLUMNDATA,dwLength);
rgBind_out[iBind].obStatus  = dwOffset + offsetof(COLUMNDATA,wStatus);

// If columns information is a STR, double buffer and
// add space for terminator
if( ((pColumnInfo[iCol].wType == DBTYPE_STR) ||
 (pColumnInfo[iCol].wType == DBTYPE_WSTR)) &&
(pColumnInfo[iCol].ulColumnSize != 0xffffffff) )
rgBind_out[iBind].cbMaxLen = (pColumnInfo[iCol].ulColumnSize + sizeof(CHAR)) * 2;
else
rgBind_out[iBind].cbMaxLen = DEFAULT_CBMAXLENGTH;
rgBind_out[iBind].pObject= NULL;
rgBind_out[iBind].pBindExt= NULL;
rgBind_out[iBind].dwFlags= 0;
rgBind_out[iBind].dwMemOwner = DBMEMOWNER_CLIENTOWNED;
rgBind_out[iBind].bPrecision = 0;
rgBind_out[iBind].bScale= 0;

// LONG DATA hack
if(rgBind_out[iBind].cbMaxLen > 1000)
rgBind_out[iBind].cbMaxLen = 1000;

dwOffset += rgBind_out[iBind].cbMaxLen + offsetof( COLUMNDATA, bData );
dwOffset = ROUND_UP( dwOffset, COLUMN_ALIGNVAL );
iBind++;
}

// Return Values
*pcBind_out   = iBind;
*pcMaxRowSize_out = dwOffset;

return S_OK;
}

//**********************************************************************
//
// CreateAccessor
//
// Purpose:
//
//     Passes a set of bindings to the data provider and recieves in return
//     an accessor handle that represents those bindings.
//
// Parameters:
// IRowset*pIRowset        - interface pointer on data provider's Rowset
//                                object
// DBBINDING*rgBind          - array of binding structures
// ULONGcBind           - number of binding structures in rgBind
// HACCESSOR*phAccessor_out  - out pointer through which to return an
//                                accessor handle that represents all the bindings
//                                in rgBind
//
// Return Value:
//     S_OK        - Success
//     E_*         - Failure
//
// Function Calls:
//     Function                    Location
//
//     IRowset::QueryInterface     provider's Rowset object
//     IAccessor::CreateAccessor   provider's Rowset object
//     IAccessor::Release          provider's Rowset object
//
//     assert                      c runtime
//
// Comments:
//
//
//**********************************************************************
HRESULT CreateAccessor
(
IRowset*pIRowset,
DBBINDING*rgBind,
ULONGcBind,
HACCESSOR*phAccessor_out
)
{
IAccessor*pIAccessor = NULL;
HRESULT hr;

// Asserts
assert(pIRowset   != NULL);
    assert(rgBind   != NULL);
    assert(phAccessor_out != NULL);

  // Get an accessor for our bindings from the rowset, via IAccessor
if( FAILED(hr = pIRowset->QueryInterface( IID_IAccessor, (void**)&pIAccessor)) )
{
GetDetailedErrorInfo(hr, pIRowset, IID_IRowset, "QI for IAccessor FAILED!!");
goto error;
}

if( FAILED(hr = pIAccessor->CreateAccessor(
DBACCESSOR_ROWDATA,
cBind,
rgBind,
0,
phAccessor_out,
NULL)) )
{
GetDetailedErrorInfo(hr, pIAccessor, IID_IAccessor, "IAccessor->CreateAccessor FAILED!!");
goto error;
}

hr = S_OK;

error:
if (pIAccessor)
    pIAccessor->Release();

if( FAILED(hr) )
*phAccessor_out = NULL;

return hr;
}

//**********************************************************************
//
// GetData
//
// Purpose:
//
//     Reads the data from a rowset.
//
// Parameters:
//
//    IRowset* pIRowset           - interface pointer on data provider's
//                                    Rowset object
//    ULONG    cMaxRowSize        - size of buffer needed to hold the data
//                                    for the largest row
//     HACCESSOR hAccessor          - accessor handle representing the set
//                                    of desired bindings
//     DBBINDING*rgBind  - needed only for pretty printing
//     ULONGcBind  - for pretty printing
//     DBCOLUMNINFO*pColumnInfo - for pretty printing
//     ULONGcCol- for pretty printing
//
//
// Return Value:
//     S_OK        - Success
//     E_*         - Failure
//
// Function Calls:
//     Function                    Location
//
//     IRowset::GetNextRows        provider's Rowset object
//     IRowset::GetData            provider's Rowset object
//     IRowset::ReleaseRows        provider's Rowset object
//
//
//     malloc, free                c runtime
//     assert                      c runtime
//
//
//
// Comments:
//
//     GetData reads all the rows in the rowset, sequentially.
//
//
//**********************************************************************
HRESULT GetData
(
IRowset*pIRowset,
ULONG   cMaxRowSize,
    HACCESSOR   hAccessor,
    DBBINDING*rgBind,
    ULONGcBind,
    DBCOLUMNINFO*pColumnInfo,
    ULONGcCol,
HWNDhList,
DWORDdwText,
DWORDaddString
)
{
#define NUMROWS_CHUNK       20// Number of Rows to Grab at a Time

ULONG cRowsObtained;// Number of rows obtained
ULONGiRow;// Row Count
BYTE*pRowData = NULL;// Memory for Data
HROW rghRows[NUMROWS_CHUNK];// Row Handles
HROW*pRows = &rghRows[0];// Pointer to the Row Handles
ULONGcMaxColWidth = MAXCOLNDATALENGTH; // Needed for Output
HRESULT hr;// HRESULT

// Asserts
assert(pIRowset != NULL);
    assert(rgBind != NULL);
    assert(pColumnInfo  != NULL);

// Create a buffer for row data, big enough to hold the biggest row
pRowData = (BYTE *) malloc( cMaxRowSize );
if (!pRowData)
        goto error;

    // Process all the rows, NUMROWS_CHUNK rows at a time
while (1)
{
if( FAILED(hr = pIRowset->GetNextRows(
0,// cbChapter
0,// cRowsToSkip
NUMROWS_CHUNK,// cRowsDesired
&cRowsObtained,// cRowsObtained
&pRows)) )// filled in w/ row handles
{
    GetDetailedErrorInfo(hr, pIRowset, IID_IRowset, "IRowset->GetNextRows FAILED!!");
goto error;
}

// All done, no more rows left to get
if ( cRowsObtained == 0 )
break;

// Loop over rows obtained, getting data for each
for ( iRow=0; iRow < cRowsObtained; iRow++ )
{
if( FAILED(hr = pIRowset->GetData(
rghRows[iRow],
hAccessor,
pRowData)) )
{
                GetDetailedErrorInfo(hr, pIRowset, IID_IRowset, "IRowset->GetData FAILED!!");
goto error;
}

// Print to the Screen
            DumpRow( rgBind, cBind, cMaxColWidth, pRowData, hList, dwText, addString );

// See if you are over the limit of rows
if ((iRow + 1) == MAX_ROW)
{
wsprintf(szDispBuffer, ROWTRUNC_WARNG, MAX_ROW);
MessageBox(hWndFrame, szDispBuffer, TRUNCERR, MB_OK | MB_ICONINFORMATION);
break;
}

}

// Release row handles
if( FAILED(hr = pIRowset->ReleaseRows( cRowsObtained, rghRows, NULL, NULL, NULL)) )
{
           GetDetailedErrorInfo(hr, pIRowset, IID_IRowset, "IRowset->ReleaseRows FAILED!!");
   goto error;
}

}// end while

hr = S_OK;
error:
// Set the horizontal scroll extent in the list box and ask for repaint.
SendMessage(hList, LB_SETHORIZONTALEXTENT, (WPARAM)(cBind*LISTHORZSCROLL+LISTHORZEXT), 0);
SendMessage(hList, WM_SETREDRAW, TRUE, 0);

if( cRowsObtained )
pIRowset->ReleaseRows( cRowsObtained, rghRows, NULL, NULL, NULL);

if( pRowData )
free( pRowData );

    return hr;
}

//**********************************************************************
//
// GetSchemaRowset
//
// Purpose:
//
//     Pulls the schema info out of the provider.
//
// Parameters:
//
// GUID  rguidSchema    -  SchemaRowset IID
//
//
// Return Value:
//
// Function Calls:
//     Function                      Location
//
//   IDBCreateCommand::QueryInterface   QI
//   IDBSchemaRowset->GetRowset  Clients Rowset pointer
//     GetDataFromRowset                 query.cpp
//
//     IMalloc::Free                 OLE task memory allocator
//
//     assert                        c runtime
//
// Comments:
//
//
//**********************************************************************
void FAR PASCAL GetSchemaRowset(GUID rguidSchema)
{
charszBuffer[MAXBUFLEN+1];// String Buffer
HCURSORhOldCursor;// Default Cursor Handle
IDBCreateCommand* pIDBCreate  = NULL;// IDBCreateCommand Object
IDBSchemaRowset*pIDBSchemaRowset = NULL;// SchemaRowset Object
ICommand*pICommand = NULL;// Command Object
IRowset*pIRowset = NULL;// Rowset Object
HRESULThr;// HRESULT

// Check if there is an active window available
if( !hWndActiveChild )
{
MessageBox( hWndFrame,
((SendMessage(hWndCrsrList, CB_GETCOUNT, 0, 0) <=0) ?
MAKECONNECT : OPENWINDOW ),
NOCOMMANDERROR, MB_OK | MB_ICONHAND);
return;
}

// Change cursor shape to hour glass
hOldCursor = SetCursor(LoadCursor((HINSTANCE)NULL, IDC_WAIT));

// Scan hdbc, hstmt values
GetWindowText(hWndActiveChild, (char*)szBuffer, MAXBUFLEN);
sscanf(szBuffer, SCANSESSIONCOMMAND_FORMAT, &pIDBCreate, &pICommand);

// QueryInterface for IDBSession::IDBSchemaRowset
hr = pIDBCreate->QueryInterface( IID_IDBSchemaRowset,
(LPVOID*)&pIDBSchemaRowset );

if (FAILED(hr))
{
        GetDetailedErrorInfo(hr, pIDBCreate, IID_IDBCreateCommand, "QI for IDBScemaRowset FAILED!!");
goto error;
}

// Get a SchemaRowset back
hr = pIDBSchemaRowset->GetRowset(
NULL,// punkOuter
rguidSchema,// schema IID
0L,// # of restrictions
NULL,// array of restrictions
IID_IRowset,// rowset interface
0L,// # of properties
NULL,// properties
(IUnknown**)&pIRowset);// rowset pointer

if (FAILED(hr))
{
        GetDetailedErrorInfo(hr, pIDBSchemaRowset, IID_IDBSchemaRowset, "IDBSchemaRowset->GetRowset FAILED!!");
goto error;
}

// Get Data from the IRowset Object
hr = GetDataFromRowset( pIRowset );

if (FAILED(hr))
{
//DumpErrorHResult( hr, (LPSTR)"GetDataFromRowset returned error!!" );
goto error;
}

// Release Object
pIDBSchemaRowset->Release();
    pIDBSchemaRowset = NULL;

return;

error:
if( pIDBSchemaRowset )
pIDBSchemaRowset->Release();

return;
}

//**********************************************************************
//
// CleanupRowset
//
// Purpose:
//
//     Allows the rowset to perform any necessary cleanup.
//
// Parameters:
//
// IRowset*pIRowset    - interface pointer on data provider's Rowset
//                            object
// HACCESSOR hAccessor   - accessor handle to release
//
// Return Value:
//
//     S_OK        - Success
//     E_*         - Failure
//
// Function Calls:
//     Function                    Location
//
//     IRowset::QueryInterface     provider's Rowset object
//     IAccessor::ReleaseAccessor  provider's Rowset object
//     IAccessor::Release          provider's Rowset object
//
//     assert                      c runtime
//
//
// Comments:
//
//     In this sample, the only cleanup that the rowset needs to do is
//     release the accessor handle.
//
//**********************************************************************
HRESULT CleanupRowset
(
IRowset*pIRowset,
HACCESSOR hAccessor
)
{
IAccessor*pIAccessor = NULL;// Pointer to an Accessor
HRESULThr;// HRESULT

// Assert
    assert(pIRowset != NULL);

if( hAccessor )
{
// Tell the rowset object it can release the accessor, via IAccessor
hr = pIRowset->QueryInterface( IID_IAccessor, (void**)&pIAccessor );

if (FAILED(hr))
{
GetDetailedErrorInfo(hr, pIRowset, IID_IRowset, "QI for IAccessor FAILED!!");
goto error;
}

hr = pIAccessor->ReleaseAccessor( hAccessor, NULL );
if (FAILED(hr))
{
GetDetailedErrorInfo(hr, pIAccessor, IID_IAccessor, "IAccessor->ReleaseAccessor FAILED!!");
goto error;
}

pIAccessor->Release();
pIAccessor = NULL;
}

pIRowset->Release();
pIRowset = NULL;

return S_OK;

error:
if( pIAccessor )
    pIAccessor->Release();
if( pIRowset )
pIRowset->Release();

    return hr;
}

//**********************************************************************
//
// DumpRow
//
// Purpose:
//
//     Display the row to the screen.
//
// Parameters:
//
//
// Return Value:
//
//
// Function Calls:
//     Function                    Location
//
//     assert                      c runtime
//
//
// Comments:
//
//
//**********************************************************************
void DumpRow
(
    DBBINDING* rgBind,
    ULONGcBind,
    ULONGcMaxColWidth,
    BYTE* pData,
HWNDhList,
DWORDdwText,
DWORDaddString
    )
{
ULONG iBind;// Binding Count
    COLUMNDATA*pColumn;// Data Structure
intcb;
CHARszTempString[MAXDISPLAYSIZE+1];// Temporary Display Buffer, used to check data length

// Asserts
assert(rgBind);
assert( offsetof(COLUMNDATA, wStatus) == 0);

// Print each column we're bound to.
for (iBind=0, szDispBuffer[0]='\0'; iBind < cBind; iBind++)
{
// Columns are bound differently; not so easy.
// Print out to at least DEFAULT_CBMAXLENGTH width (pretty),
// Limit to first dwLength characters.
pColumn = (COLUMNDATA *) (pData + rgBind[iBind].obStatus);

// Check Status for NULL / OK / CANTCONVERT.
switch (pColumn->wStatus)
{
case DBSTATUS_S_ISNULL:
strcat(szDispBuffer, (LPSTR)"<NULL>");
break;
case DBSTATUS_S_OK:
// Truncate if needed.
// - calculate number of bytes needed
cb = WideCharToMultiByte(CP_ACP, 0, (LPWSTR)pColumn->bData, -1,
NULL, 0, NULL, NULL);
if( cb > MAXCOLNDATALENGTH )
{
cb = WideCharToMultiByte(CP_ACP, 0, (LPWSTR)pColumn->bData, -1,
szTempString, MAXCOLNDATALENGTH, NULL, NULL);

szTempString[MAXCOLNDATALENGTH] = '\0';
strcat(szTempString, "...");
}
else
{
cb = WideCharToMultiByte(CP_ACP, 0, (LPWSTR)pColumn->bData, -1,
szTempString, (MAXDISPLAYSIZE+1), NULL, NULL);
}

strcat(szDispBuffer,szTempString);
        break;

case DBSTATUS_E_CANTCONVERTVALUE:
strcat(szDispBuffer, "<can't convert value>");
break;
default:
{
CHAR szChar[10] = "";
strcat(szDispBuffer, "<unknown status of ");
strcat(szDispBuffer, itoa(pColumn->wStatus, szChar, 10));
strcat(szDispBuffer, ">");
}
break;
}

dwText = strlen(szDispBuffer);
szDispBuffer[dwText++] = '\t';
szDispBuffer[dwText] = '\0';
}
// Take the last \t off the end
szDispBuffer[--dwText] = '\0';
SendMessage(hList, addString, 0, (LPARAM)(LPSTR)szDispBuffer);

}

//**********************************************************************
//
// GetDetailedErrorInfo
//
// Purpose:
//
//    Reads error from the error collection and displays it in messageboxes
//
// Parameters:
//
// HRESULThr- OLE DB HRESULT
// IUnknown*  pBadObject       - Object that caused an error
//  GUIDIID_BadInterface    - Interface that contains method that caused the error
// LPSTR lpStrBuffer- Title Bar text
//
// Return Value:
//
// Comments:
// If any sort of failure is encountered the return values will be NULL
//   strings. Therefore no extended error info will be printed.
//**********************************************************************

HRESULT GetDetailedErrorInfo
    (
HRESULThresult,
IUnknown*pBadObject,
GUIDIID_BadIntereface,
LPSTRlpStrBuffer
)
{
IErrorInfo *        pErrorInfo = NULL;
IErrorInfo *    pErrorInfoRec = NULL;
IErrorRecords *     pErrorRecords = NULL;
ISupportErrorInfo * pSupportErrorInfo = NULL;
HRESULT             hr = S_OK;
ULONG               i,ulNumErrorRecs;
BSTRbstrDescriptionOfError = NULL;
BSTRbstrSourceOfError = NULL;
CHAR                szBuffer [512];  //Error Info Buffer

// Check to see if this interface posts error messages
if( SUCCEEDED( pBadObject->QueryInterface(IID_ISupportErrorInfo,
(LPVOID FAR*)&pSupportErrorInfo)) )
{
if( pSupportErrorInfo->InterfaceSupportsErrorInfo(IID_BadIntereface) == S_OK )
{
//Get Error Object. Return if no object Exists
if( !SUCCEEDED(hr = GetErrorInfo(0,&pErrorInfo)) )
   goto DisplayHResult;

// If returned a NULL error Object, then just display HR
if( pErrorInfo == NULL )
goto DisplayHResult;

//Get the IErrorRecord interface and get the count of error recs.
if( FAILED(hr = pErrorInfo->QueryInterface(IID_IErrorRecords,(LPVOID FAR*)&pErrorRecords)) )
   goto DisplayHResult;

//Retrieve the number of Error Records
if( FAILED(hr = pErrorRecords->GetRecordCount(&ulNumErrorRecs)) )
   goto DisplayHResult;

//Go through and print messages
for(i=0; i<ulNumErrorRecs; i++)
{
if( FAILED(hr = pErrorRecords->GetErrorInfo(i, GetUserDefaultLCID(), &pErrorInfoRec)) )
goto Exit;
if( FAILED(hr = pErrorInfoRec->GetDescription(&bstrDescriptionOfError)) )
goto Exit;
if( FAILED(hr = pErrorInfoRec->GetSource(&bstrSourceOfError)) )
goto Exit;

sprintf(szBuffer, "HResult of 0x%0x (%ld) returned\nError Source: %S\nError Description: %S\n",
(long)hresult,(long)hresult,
bstrSourceOfError ? bstrSourceOfError : L"No Source Description",
bstrDescriptionOfError ? bstrDescriptionOfError : L"No Error Description");

MessageBox( NULL, (LPCTSTR)szBuffer, lpStrBuffer, MB_OK);

if (pErrorInfoRec)
{
pErrorInfoRec->Release();
pErrorInfoRec = NULL;
}
if( bstrSourceOfError )
{
SysFreeString(bstrSourceOfError);
bstrSourceOfError = NULL;
}
if( bstrDescriptionOfError )
{
SysFreeString(bstrDescriptionOfError);
bstrDescriptionOfError = NULL;
}
}

// if we've process all the records we can go to end
goto Exit;

} //S_OK
} //if (SUCCEEDED)

DisplayHResult:
// In some error cases we may not have extended error information and it still would be good to
// post the error HRESULT
sprintf(szBuffer, "HResult of 0x%0x (%ld) returned, no Extended Error Information posted or supported",
(long)hresult,(long)hresult);
MessageBox( NULL, (LPCTSTR)szBuffer, lpStrBuffer, MB_OK);

 Exit :
if (pErrorInfo)
pErrorInfo->Release();
if (pErrorRecords)
pErrorRecords->Release();
if( pSupportErrorInfo )
pSupportErrorInfo->Release();
if( pErrorInfoRec )
pErrorInfoRec->Release();
if( bstrSourceOfError )
SysFreeString(bstrSourceOfError);
if( bstrDescriptionOfError )
SysFreeString(bstrDescriptionOfError);

 return hr;
} //PrintErrorInfo

//**********************************************************************
//
// DumpErrorHResult
//
// Purpose:
//
//     Dump the OLE DB HRESULT in a Message Box with a text Title Bar.
//
// Parameters:
//
// HRESULThr    - OLE DB HRESULT
//
// LPSTR lpStrBuffer - Title Bar text
//
//
// Return Value:
//
//
// Function Calls:
//     Function                    Location
//
//     assert                      c runtime
//
//
// Comments:
//
//
//**********************************************************************
void DumpErrorHResult( HRESULT hr, LPSTR lpStrBuffer )

{
LPVOIDlpMessageBuffer;

if(0 != FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
hr,
GetUserDefaultLCID(),
(LPSTR)&lpMessageBuffer,
0,
NULL))
{
MessageBox( NULL, (LPCTSTR)lpMessageBuffer, lpStrBuffer, MB_OK);
LocalFree(lpMessageBuffer);
}
else
{
lpMessageBuffer = LocalAlloc(LPTR, 256);

if( lpMessageBuffer )
{
sprintf((LPSTR)lpMessageBuffer, "HResult of %ld returned", (long)hr);
MessageBox( NULL, (LPCTSTR)lpMessageBuffer, lpStrBuffer, MB_OK);
LocalFree(lpMessageBuffer);
}
}
}

//**********************************************************************
//
// NewIOpenRowsetWindow
//
// Purpose:
//
//     Create a new ICommand Object on the current IDBCreateCommand.
//
// Parameters:
//
// Return Value:
//
// Function Calls:
//     Function                         Location
//
//   IDBCreateCommand::CreateCommandprovider's Command object
//     ICommand::Release                provider's Command object
//
//
// Comments:
//
//
//**********************************************************************
void FAR PASCAL NewIOpenRowsetWindow()
{
int nCurrenthdbc;// Current PROVIDER
charszBuffer[MAXBUFLEN+1];// String in PROVIDER ComboBox on Toolbar
charszProvName[MAXBUFLEN+1];// DSN String
MDICREATESTRUCTmcs;                    // MDI Child Window Create Struc

IOpenRowset * pIOpenRowset= NULL;// IOpenRowset Object

// check if there is PROVIDER selected in the Combo-Box
if ((nCurrenthdbc = (int)SendMessage(hWndCrsrList, CB_GETCURSEL, 0, 0)) == CB_ERR)
{
MessageBox(hWndFrame, MAKECONNECT, NOSESSIONERROR, MB_OK | MB_ICONHAND);
return;
}

// check if the number of windows exceeds MAXCHILDWNDS
if (nChildCount >= MAXCHILDWNDS)
{
MessageBox(hWndFrame, MAXCHILDEXCEEDED, MAXCHLDERR, MB_OK | MB_ICONHAND);
return;
}

// Scan PROVIDER string and IDBCreateCommand value from the combo-box
SendMessage(hWndCrsrList, CB_GETLBTEXT, (WPARAM)nCurrenthdbc, (LPARAM)(LPSTR)szBuffer);

 sscanf(szBuffer, SCANPROVIDERSESSION_FORMAT, szProvName, &pIOpenRowset);

// create a new MDI client window. maximized, if the previous is so.
mcs.szClass = OLEDBMDICLASS;
mcs.szTitle = UNTITLED;
mcs.hOwner  = hAppInstance;
mcs.style   = hWndActiveChild && IsZoomed(hWndActiveChild) ? WS_MAXIMIZE : 0;
mcs.x = mcs.cx = mcs.y = mcs.cy = CW_USEDEFAULT;
hWndActiveChild = (HWND)(UINT)SendMessage(hWndMDIClient, WM_MDICREATE, 0, (LPARAM)(LPMDICREATESTRUCT)&mcs);

    // check if it was created, if it wasn't free up resource and flag warning
    if (!hWndActiveChild)
    {
MessageBox(hWndFrame, CREATECHILDERR, EXECERROR, MB_OK|MB_ICONEXCLAMATION|MB_TASKMODAL);
return;
    }

// Display the Provider string, PROVIDER and IOpenRowset in the title
// of newly created window. Increment the child window counter
wsprintf(szBuffer, QUERY_STRING, pIOpenRowset);

SendMessage(hWndStmtList, CB_ADDSTRING, 0, (LPARAM)(LPSTR)szBuffer);
SendMessage(hWndStmtList, CB_SELECTSTRING, 0, (LPARAM)(LPSTR)szBuffer);

wsprintf(szBuffer, PROVIDER_SESSION_COMMAND_FORMAT, (LPSTR)szProvName, pIOpenRowset, NULL);

SetWindowText(hWndActiveChild, szBuffer);
nChildCount++;

// Update the Command Combo-Box on the tool bar.
ChangeCurrentICommand(hWndStmtList);
}


QURYDEMO.H

//-----------------------------------------------------------------------------
// Microsoft OLE DB Main Include File
// Copyright (C) 1994 - 1997 By Microsoft Corporation.
//
// @doc
//
// @module main include file
//
//-----------------------------------------------------------------------------

#ifdef RC_INVOKED
#define ID(id) id
#else
#define ID(id) MAKEINTRESOURCE(id)
#endif

/* Resource Identifiers */
#define BMP_NEWQUERYID(10)// new window button bitmap
#define BMP_RUNQUERYID(20)// exec query button bitmap
#defineAPPICONID(30)// application icon
#define QUERYWINDOWICONID(40)// MDI Child window icon
#define QURYDEMOMENUID(50)// Application menu
#define INITDIALOGID(60)// connect modal dialog box
#define DISCONNECTDIALOGID(70)// disconnect modal dialog box
#define ABOUTDIALOGID(80)// about modal dialog box
#define MDICHILDDIALOGID(90)// MDI Child modeless dialog box
#define APPACCELERATORID(100)// Accelerator table for the app

/* Menu Identifiers */
#define IDM_POPUPAPPSYS10// Placeholder, used by status bar
#define IDM_POPUPMDISYS20// Placeholder, used by status bar
#define IDM_POPUPLOGIN100// Placeholder, used by status bar
#define IDM_INITIALIZE110
#define IDM_DISCONNECT120
#define IDM_EXIT130
#define IDM_POPUPQUERY140// Placeholder, used by status bar
#define IDM_QUERY150
#define IDM_NEW160
#define IDM_POPUPSCHEMA170     // Placeholder, used by status bar

#define IDM_POPUPWINDOW210     // Placeholder, used by status bar
#define IDM_TILE220
#define IDM_CASCADE230
#define IDM_ICONS240
#define IDM_CLOSEALL250
#define IDM_POPUPHELP260// Placeholder, used by status bar
#define IDM_APPHELP270
#define IDM_ABOUT280
#define IDM_MDICHILD290

#defineIDM_ASSERTIONS300
#define IDM_CATALOGS310
#define IDM_CHARACTER_SETS320
#define IDM_CHECK_CONSTRAINTS330
#define IDM_COLLATIONS340
#define IDM_COLUMN_DOMAIN_USAGE350
#define IDM_COLUMN_PRIVILEGES360
#define IDM_COLUMNS370
#define IDM_CONSTRAINT_COLUMN_USAGE 380
#define IDM_CONSTRAINT_TABLE_USAGE  390
#define IDM_FOREIGN_KEYS400
#define IDM_INDEXES410
#define IDM_KEY_COLUMN_USAGE420
#define IDM_PRIMARY_KEYS430
#define IDM_PROCEDURE_COLUMNS440
#define IDM_PROCEDURE_PARAMETERS450
#define IDM_PROCEDURES460
#define IDM_PROVIDER_TYPES470
#define IDM_REFERENTIAL_CONSTRAINTS480
#define IDM_SCHEMATA490
#define IDM_SQL_LANGUAGES500
#define IDM_STATISTICS510
#define IDM_TABLE_CONSTRAINTS520
#define IDM_TABLE_PRIVILEGES530
#define IDM_TABLES540
#define IDM_TRANSLATIONS550
#define IDM_USAGE_PRIVILEGES560
#define IDM_VIEW_COLUMN_USAGE570
#define IDM_VIEW_TABLE_USAGE580
#define IDM_VIEWS590  //ADDED

/* child window ids */
#define IDW_TOOLBAR10
#define IDW_STATUSBAR20
#define IDW_MDICLIENT30

#define IDW_CRSRLIST10// toolbar comboboxes
#define IDW_COMMANDLIST20

#define IDCOMBO_PROVIDER10// connect dialog box controls
#define IDCOMBO_NAME20
#define IDTEXT_USERID30
#define IDTEXT_PASSWORD40
#define IDCOMBO_PROMPT45
#define IDENUM              50

#define IDLIST_SESSION10// disconnect dialog box controls
#define IDLIST_COMMAND20
#define IDCLOSE_ACTVTY30
#define IDDISCONNECT40
#define IDICON_WARN50

#define IDTEXT_PRMPT10// MDI child's modeless dialog controls
#define IDTEXT_SQL20
#define IDLIST_RSLT30

/* MDI Child Window Offsets */
#define CBWNDEXTRA4// storage in MDI Child Window
#define GWLAPP_HDLG0// child dialog handle

/* Other Constants */
#define MAXCHILDWNDS    10//maximum child windows allowed
#define MAXBUFLEN256//display buffer size
#define MAX_COL35//maximum column in result set
#define MAX_ROW100//maximum number of rows
#define MAXDATALEN25//maximum data length per column
#define MAXCOLNAMELEN20
#define MAXCOLNDATALENGTH   23
#define MAXDISPLAYSIZEMAX_COL*(MAXDATALEN+1)
#defineMINWIDTH415//minimum width of app window
#defineMINHEIGHT350//minimum height of app window
#define LISTTABSTOP100//tabstop in the display listbox
#define LISTHORZSCROLL(LISTTABSTOP*2)//horizontal scroll for the listbox
#define LISTHORZEXT0//horizontal extent inthe list box
#define WINDOWMENUPLACE2//Window menu place for adding MDI child windows
#define IDT_STATUSTIMER1//timer id
#define TIMERDELAY60000//timer duration
#define BTTNX345//first toolbar button location
#define BTTNY2//button y position
#defineBTTNWIDTH25                      //button width
#define BTTNHEIGHT23                      //button height
#define BTTNMARGIN5//distance between toolbar buttons
#define TOOLBARMARGINY5//fixed margin w/o combobox height
#define STATUSBARMARGINY8//fixed margin w/o textbox height

/* String Constants */
#define POPUPLOGIN"&Login"
#define MENUITEMCONNECT"&Connect... \aCtrl+C"
#define MENUITEMDISCONNECT"&Unitialize... \aCtrl+U"
#defineMENUITEMEXIT"E&xit"
#define POPUPQUERY"&Command"
#define MENUITEMQUERY"&Execute Command Text \aCtrl+E"
#define MENUITEMNEW"&New Command \aCtrl+N"
#define POPUPSCHEMA"&Schema \aCtrl+S"
#define MENUITEMTYPES"&PROVIDER_TYPES \aCtrl+P"
#defineMENUITEMTABLES"T&ABLES \aCtrl+A"
#define MENUITEMCOLUMNS"&COLUMNS \aCtrl+C"
#define POPUPWINDOW"&Window"
#define MENUITEMTILE"&Tile"
#defineMENUITEMCASCADE"&Cascade"
#define MENUITEMICONS"Arrange &Icons"
#defineMENUITEMCLOSEALL"Close &All"
#define POPUPHELP"&Help"
#define MENUITEMAPPHELP"&Help on Qurydemo \aF1"
#defineMENUITEMABOUT"&About Qurydemo..."

#define OLEDBFRAMECLASS"QDEMO32MAIN"

#define OLEDBTOOLCLASS"QDEMOTool"
#define OLEDBSTATUSCLASS"QDEMOStatus"
#define OLEDBMDICLASS"QDEMOMDI"
#define COMBOBOXCLASS"COMBOBOX"
#define MDICLIENTCLASS"MDICLIENT"
#define UNTITLED"Untitled"
#define APPTITLE"Microsoft OLE DB Sample Query Program"
#defineEXECERROR"Execution Error"
#define INITERROR"Failed to initialize Session interface"
#define CLASSERROR"Failed to register application class"
#define CREATEMAINERR"Failed to create application main window"
#define CREATECHILDERR"Failed to create a new Command"
#define ALPHABETS"abcdefghijklmnopqrstuvwxyz"
#define TIMETEXT"MM:MM MM"
#define DATETEXT"MMM-MM"
#define TIMEFORMAT" %I:%M %p"
#define DATEFORMAT" %b-%d"
#define NOPROVIDERERR"Please provide a Data Source"
#define MOREINFO"Incomplete Information"

#define STATUSPOPUPAPPSYS   " Control menu for the application "
#define STATUSPOPUPMDISYS" Control menu for the window "
#define STATUSRESTORE       " Restore window to normal "
#define STATUSMOVE          " Move window "
#define STATUSSIZE          " Size window "
#define STATUSMINIMIZE      " Minimize window "
#define STATUSMAXIMIZE      " Maximize window "
#define STATUSCLOSE         " Close window "
#define STATUSNEXTWINDOW    " Switch to next window "
#define STATUSPREVWINDOW    " Switch to previous window "
#define STATUSTASKLIST      " Bring up task list "
#define STATUSPOPUPLOGIN" Login and logout commands "
#define STATUSCONNECT" Connect with default parameters... "
#define STATUSDISCONNECT" Release open sessions... "
#define STATUSEXIT" Exit application "
#define STATUSPOPUPQUERY" Commands "
#define STATUSQUERY" Execute Command text... "
#define STATUSNEW" New Command on current Session... "
#define STATUSPOPUPSCHEMA" IDBSchema Commands "

#defineSTATUSASSERTIONS  " DBSCHEMA_ASSERTIONS Rowset "
#define STATUSCATALOGS  " DBSCHEMA_CATALOGS Rowset "
#define STATUSCHARACTER_SETS  " DBSCHEMA_CHARACTER_SETS Rowset "
#define STATUSCHECK_CONSTRAINTS  " DBSCHEMA_CHECK_CONSTRAINTS Rowset "
#define STATUSCOLLATIONS  " DBSCHEMA_COLLATIONS Rowset "
#define STATUSCOLUMN_DOMAIN_USAGE  " DBSCHEMA_COLUMN_DOMAIN_USAGE Rowset "
#define STATUSCOLUMN_PRIVILEGES  " DBSCHEMA_COLUMN_PRIVILEGES Rowset "
#define STATUSCOLUMNS  " DBSCHEMA_COLUMNS Rowset "
#define STATUSCONSTRAINT_COLUMN_USAGE " DBSCHEMA_CONSTRAINT_COLUMN_USAGE Rowset "
#define STATUSCONSTRAINT_TABLE_USAGE  " DBSCHEMA_CONSTRAINT_TABLE_USAGE Rowset "
#define STATUSFOREIGN_KEYS  " DBSCHEMA_FOREIGN_KEYS Rowset "
#define STATUSINDEXES  " DBSCHEMA_INDEXES Rowset "
#define STATUSKEY_COLUMN_USAGE  " DBSCHEMA_KEY_COLUMN_USAGE Rowset "
#define STATUSPRIMARY_KEYS  " DBSCHEMA_PRIMARY_KEYS Rowset "
#define STATUSPROCEDURE_COLUMNS  " DBSCHEMA_PROCEDURE_COLUMNS Rowset "
#define STATUSPROCEDURE_PARAMETERS  " DBSCHEMA_PROCEDURE_PARAMETERS Rowset "
#define STATUSPROCEDURES  " DBSCHEMA_PROCEDURES Rowset "
#define STATUSPROVIDER_TYPES  " DBSCHEMA_PROVIDER_TYPES Rowset "
#define STATUSREFERENTIAL_CONSTRAINTS " DBSCHEMA_REFERENTIAL_CONSTRAINTS Rowset "
#define STATUSSCHEMATA  " DBSCHEMA_SCHEMATA Rowset "
#define STATUSSQL_LANGUAGES  " DBSCHEMA_SQL_LANGUAGES Rowset "
#define STATUSSTATISTICS  " DBSCHEMA_STATISTICSRowset "
#define STATUSTABLE_CONSTRAINTS  " DBSCHEMA_TABLE_CONSTRAINTS Rowset "
#define STATUSTABLE_PRIVILEGES  " DBSCHEMA_TABLE_PRIVILEGES Rowset "
#define STATUSTABLES  " DBSCHEMA_TABLES Rowset "
#define STATUSTRANSLATIONS      " DBSCHEMA_TRANSLATIONS Rowset "
#define STATUSUSAGE_PRIVILEGES  " DBSCHEMA_USAGE_PRIVILEGES Rowset "
#define STATUSVIEW_COLUMN_USAGE  " DBSCHEMA_VIEW_COLUMN_USAGE Rowset "
#define STATUSVIEW_TABLE_USAGE  " DBSCHEMA_VIEW_TABLE_USAGE Rowset "
#define STATUSVIEWS  " DBSCHEMA_VIEWS Rowset "//ADDED

#define STATUSPOPUPWINDOW" Window management commands "
#define STATUSTILE" Tile all normal child windows "
#define STATUSCASCADE" Cascade all normal child windows "
#define STATUSICONS" Arrange iconic child windows "
#define STATUSCLOSEALL" Close all child windows "
#define STATUSMDICHILD      " Activate the window marked as %d"
#define STATUSPOPUPHELP" Help commands "
#define STATUSAPPHELP" Display help on application "
#define STATUSABOUT" About application "
#define STATUSDEFAULT" Ready "

#define DEFDBNAME"?"
#define PROVIDER_SESSION_FORMAT"%s!%lX"
#define PROVIDER_SESSION_COMMAND_FORMAT"Provider:=%s!Session=%lX!Command=%lX"
#define SCANPROVIDERSESSION_FORMAT"%[^!]!%lX"
#define SCANSESSION_FORMAT"%*[^!]!%lX"
#define SCANSESSION_TITLEFORMAT"Provider:=%*[^!]!Session=%lX"
#define SCANSESSIONCOMMAND_FORMAT"Provider:=%*[^!]!Session=%lX!Command=%lX"
#define SCANCOMMAND_TITLEFORMAT"Provider:=%*[^!]!Session=%*lX!Command=%lX"
#define CUR_MARK"<-"
#define CURQUERY_STRING"Command!%lX<-"
#define QUERY_STRING"Command!%lX"
#define MAKECONNECT"Please connect to a data source using Login menu."
#defineOPENWINDOW"Please open a window from Query menu."
#define NOSESSIONERROR"No Session Available"
#define NOCOMMANDERROR"No Command Window Available"
#define MAXCHILDEXCEEDED"Please close a child window and try again."
#define MAXCHLDERR"Error - Too many Child Windows"
#define COLTRUNC_WARNG"Number of columns in display truncated to %u"
#define ROWTRUNC_WARNG"Number of rows in display truncated to %u"
#define TRUNCERR"Data Truncation"
#define CLOSEALLSESSION"All open sessions have been released."
#define LOGOUTINFO"Log Out Information"
#define SQLERR_FORMAT"SQL Error State:%s, Native Error Code: %lX, OLEDB Error: %s"
#define SQLERRMSGTITLE"SQL_ERROR results"
#define SQLERRCNTDTITLE"SQL_ERROR results continued"
#define SQLWRNMSGTITLE"SQL_SUCCESS_WITH_INFO results"
#define SQLWRNCNTDTITLE"SQL_SUCCESS_WITH_INFO results continued"
#define NULLDATASTRING"SQL_NULL_DATA"

/* Globals */
//HWNDhWndFrame;      // Main Frame Window handle
//HWNDhWndCrsrList;   // hdbc(s) combobox on the tool bar
//HWNDhWndStmtList;   // hstmt(s) combobox on the tool bar
//HWNDhWndMDIClient;  // MDI Client window handle
//HWNDhWndActiveChild;// Current active MDI Child window
//HINSTANCEhAppInstance;   // Application instance

/* Macros and Function Prototypes */
#ifdef WIN32
#defineACTIVATEWINDOW(h)SetForegroundWindow(h)
#else
#defineACTIVATEWINDOW(h)SetFocus(h)
#endif

#define DRAWBTTNRECT(h,x,y,cx,cy)MoveToEx(h,x+1,y,NULL);\
LineTo(h,x+cx,y);\
MoveToEx(h,x+cx,y+1,NULL);\
LineTo(h,x+cx,y+cy);\
MoveToEx(h,x+cx-1,y+cy,NULL);\
LineTo(h,x,y+cy);\
MoveToEx(h,x,y+cy-1,NULL);\
LineTo(h,x,y);

#define DRAWBTTNLIFT1(h,x,y,cx,cy)MoveToEx(h,x+1,y+cy-1,NULL);\
LineTo(h,x+1,y+1);\
LineTo(h,x+cx-1,y+1);

#defineDRAWBTTNLIFT2(h,x,y,cx,cy)MoveToEx(h,x+1,y+cy-1,NULL);\
LineTo(h,x+cx-1,y+cy-1);\
LineTo(h,x+cx-1,y);\
MoveToEx(h,x+2,y+cy-2,NULL);\
LineTo(h,x+cx-2,y+cy-2);\
LineTo(h,x+cx-2,y+1);
// MAIN.CPP
intPASCALWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow);
longCALLBACKMainWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
longCALLBACKToolbarProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
longCALLBACKStatusbarProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
longCALLBACKMDIChildProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
BOOLCALLBACKConnectDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
BOOLCALLBACKDisconnectDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
BOOLCALLBACKAboutDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
BOOLCALLBACKMDIChildDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
VOIDFAR PASCALDrawBitmap(HDC hDC, int iLeft, int iTop, HBITMAP hBitmap);

// QURYDEMO.CPP
BOOLFAR PASCALInitEnvironment();
voidFAR PASCALDisplayProviders(HWND hWnd);
BOOLFAR PASCAL  ConnectDatabase(HWND hWnd);
voidFAR PASCALDisplayConnections(HWND hWndhdbc);
voidFAR PASCALDisplayICommands(HWND hWndhstmt, HWND hWndhdbc, int nCrsrIndex);
voidFAR PASCALNewICommandWindow();
voidFAR PASCALNewIOpenRowsetWindow();
voidFAR PASCALChangeCurrentICommand(HWND hWndStmtList);
voidFAR PASCALChangeCurrentCursor(HWND hWndCrsrList);
voidFAR PASCALDisplayNewCrsrAndICommand();
voidFAR PASCALFreeConnect(HWND hWndhdbc);
voidFAR PASCALFreeICommand(HWND hWndhstmt, HWND hWndhdbc, int nIndex);
voidFAR PASCALCloseICommandWindow(HWND hWnd);
voidFAR PASCALExecuteQuery();
BOOLFAR PASCALCloseIDBCreateCommand();
HRESULT DisplayDataSource(HWND hWnd);
HRESULTFreeEnvironment();
voidFAR PASCALGetSchemaRowset(GUID rguidSchema);

HRESULT EnumerateProviders ();

HRESULT ExecuteCommand
(
IUnknown*pIUnknown,
ICommand*pICommand,
LPWSTRwszBuffer,
IRowset**ppIRowset_out
);

HRESULT GetDataFromRowset
(
IRowset*pIRowset
);

HRESULT GetColumnsInfo
(
IRowset*pIRowset,
ULONG* pcCol,
DBCOLUMNINFO**ppColumnInfo,
WCHAR**ppStringsBuffer
);

HRESULT SetupBindings
(
ULONG cCol,
DBCOLUMNINFO*pColumnInfo,
DBBINDING*rgBind_out,
ULONG*cBind_out,
    ULONG*          pcMaxRowSize_out
);

HRESULT CreateAccessor
(
IRowset*pIRowset,
DBBINDING*rgBind,
ULONGcBind,
HACCESSOR*phAccessor_out
);

HRESULT GetData
(
IRowset*pIRowset,
ULONG   cMaxRowSize,
    HACCESSORhAccessor,
    DBBINDING*    rgBind,// needed for pretty printing
    ULONG    cBind,    // for pretty printing
    DBCOLUMNINFO*pColumnInfo, // for pretty printing
    ULONGcCol,// for pretty printing
HWNDhList,
DWORDdwText,
DWORDaddString
);

HRESULT CleanupRowset
(
IRowset*pIRowset,
HACCESSOR hAccessor
);

void DumpRow
(
    DBBINDING* rgBind,
    ULONGcBind,
    ULONGcMaxColWidth,
    BYTE* pData,
HWNDhList,
DWORDdwText,
DWORDaddString
    );

HRESULT GetDetailedErrorInfo
      (
   HRESULThr,
   IUnknown * pBadObject,
   GUID IID_BadIntereface,
   LPSTRlpStrBuffer
//       char * szErrorDescription_out,
//   char * szErrorSource_out
  );

/*
void DumpError
     ( HRESULT hr,
   LPSTR lpStrBuffer,
   LPSTR lpSource,
   LPSTR lpDescription
  );
*/

void DumpErrorHResult
(
HRESULT hr,
LPSTR lpStrBuffer
);

// How to lay out each column in memory.
// Note that bData should be DWORD aligned, since it may contain a DWORD.
struct COLUMNDATA
{
DBSTATUSwStatus;// status of column
DWORDdwLength;// length of data (not space allocated)
BYTEbData[1];// data here and beyond
};

/******************************* End of File ********************************/


QURYVER.H

//-----------------------------------------------------------------------------
// Microsoft OLE DB Version File
// Copyright (C) 1994 - 1997 By Microsoft Corporation.
//
// @doc
//
// @module version include file
//
//

// Constants -----------------------------------------------------------------
#ifndef _AXVER_H_
#define _AXVER_H_

#define VER_FILEVERSION 1,50,3221,00
#define VER_FILEVERSION_STR "1.50.3221.00\0"
#define VER_PRODUCTVERSION 1,50,3221,00
#define VER_PRODUCTVERSION_STR "1.50.3221.00\0"

#define VER_FILEFLAGSMASK(VS_FF_DEBUG | VS_FF_PRERELEASE)
#ifdef DEBUG
#define VER_FILEFLAGS(VS_FF_DEBUG)
#else
#define VER_FILEFLAGS(0)
#endif

#define VER_FILEOSVOS_NT_WINDOWS32

#define VER_COMPANYNAME_STR"Microsoft Corporation\0"
#define VER_PRODUCTNAME_STR"Microsoft OLE DB\0"
#define VER_LEGALCOPYRIGHT_STR"Copyright \251 Microsoft Corporation 1994-1997\0"

// Resource DLL Version Resource ID
#define IDS_RESDLL_VER1

#endif
//--------------------


REGISTRY.H

////////////////////////////////////////////////////////////////////////////////
// registry.h
//
// Copyright (C) 1987-1997 By Microsoft Corp. All rights reserved.
// Copyright (C) 1997 Metawise Computing, Inc. All rights reserved.
//
////////////////////////////////////////////////////////////////////////////////

#ifndef _REGISTRY_H_
#define _REGISTRY_H_

#include <winreg.h>
#include "cstring.h"

#define REG_OPTION_NONE0x00000000
#define REG_OPTION_CREATE0x00000001
#define REG_OPTION_RESET0x00000002

extern long RegReadValueString// return ERROR_FILE_NOT_FOUND if subkey does not exist
(
IN HKEY hKey,
IN LPCTSTR lpszSubKey,
IN int nCount,// #elems in the following arrays
IN LPCTSTR ppszNames[],// array of value names whose values are sought
OUT CString pstrValues[],// return values
OUT long pbStatus[]// return status of each value returned (TRUE=>OK)
);

extern long RegWriteValueString
(
IN HKEY hKey,
IN LPCTSTR lpszSubKey,
IN int nCount,// #elems in the following arrays
IN LPCTSTR ppszNames[],// array of value names whose values are to be written
IN LPCTSTR ppszValues[],// values to be written to registry
IN DWORD dwOptions// REG_OPTION_XXX
);

#endif // _REGISTRY_H_


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by qurydemo.rc
//
#define ENUMDIALOG                      102

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        103
#define _APS_NEXT_COMMAND_VALUE         40006
#define _APS_NEXT_CONTROL_VALUE         1000
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif


DIALOGS.H

// Dialog box stuff
#define IDC_CONVSEC505
#define IDC_ALREADYVER506
#define IDE_PATHNAME507
#define IDE_TPNAME508
#define IDC_QUEUED509
#define IDD_FINITE512
#define IDD_INFINITE513
#define IDE_TIMEOUT514
#define IDC_ADD516
#define IDC_EDIT517
#define IDC_DELETE518
#define IDC_LIST519
#define IDC_USERBOX520
#define IDE_USERNAME521
#define IDE_PASSWORD522
#define IDT_PASSWDTEXT523
#define IDE_LOCALLU524

// String ID #s
#define IDS_ERRORTITLE16
#define IDS_UNKNOWN17
#define IDS_OPENSCMFAILED       18
#define IDS_CREATEFAILED19
#define IDS_LOCKFAILED20
#define IDS_SETKEYFAILED        21
#define IDS_OPENKEYFAILED       22
#define IDS_INIWRITEFAILED23
#define IDS_BADPATHNAME24
#define IDS_BADTPNAME25
#define IDS_SERVICEEXISTS26
#define IDS_BADTIMEOUT27
#define IDS_INFOTITLE28
#define IDS_NOMESSAGE29
#define IDS_SUCCESS 30
#define IDS_REGISTRYPATH31
#define IDS_NOUSERNAME          32
#define IDS_NOPASSWORD33
#define IDS_OUTOFMEMORY34
#define IDS_REGISTRYAPPLPATH35
#define IDS_REPLACECAPTION36
#define IDS_REPLACETEXT37
#define IDS_REGISTRYAPPLPATH_WIN95 38
#define IDS_REGISTRYPATH_WIN95     39

#define IDC_PROGRAMTYPE             110
#define IDC_SERVICE                 111
#define IDC_APPLICATION             112


INSTALL.C

/*************************************************************************\
*
* TP Installer
*
* This program brings up a dialog box that prompts for TP configuration
* information.  The information is then placed in the registry under
* Windows NT, and in the WIN.INI file under Windows.  The WIN32 compiler
* flag specifies the NT version, while the WINDOWS flag specifies the
* Windows version.
*
* 6/93 Initial coding    ARK
*
\*************************************************************************/

#define STRICT
#include <windows.h>
#ifdef WIN32
#include <winsvc.h>
#endif

#ifdef        WIN32
        #include <windowsx.h>

BOOL IsWin95Client( void );

#else
        #include <windowsx.h16>
#endif

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include "install.h"

HANDLE  hInst;                       // This program's instance
HWND    hDialog;                     // Global handle to main dialog
HWND    hTimeout;                    // A handle to the "timeout" window
HWND    hList;                       // Handle to the "users" list box

WNDPROC lPrevWndProcInt = NULL,      // Holds original integer edit box window proc
lPrevWndProcAppc = NULL,     // Holds original APPC edit box window proc
        lPrevWndProcInfinite = NULL, // Original window proc for "infinite"
                                     // radio button
        lPrevWndProcConvSec = NULL,  // Same for "conversation security" box
        lPrevWndProcService = NULL;  // Same for "service" box

FARPROC lpfnInt = NULL,              // Pointer to the ValidateFieldInt
                                     // procedure; becomes non-NULL upon
                                     // initialization in ValidateField().
        lpfnAppc = NULL;     // Pointer to ValidateFieldAppc procedure

/*************************************************************************\
*
*  FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int)
*
*  PURPOSE: Creates the dialog box.
*
*  COMMENTS:
*
\*************************************************************************/

int PASCAL WinMain (HINSTANCE hInstance,
                      HINSTANCE hPrevInstance,
                      LPSTR     lpCmdLine,
                      int       nCmdShow)
{
  DWORD retCode;
  FARPROC lpfn;

  hInst   = hInstance;

  lpfn = (FARPROC) MakeProcInstance( (FARPROC)MainDlgProc, hInst);
  retCode = DialogBoxParam ((HANDLE)hInst,
                            (LPCSTR)"MainDlg",
                            NULL,
                            (DLGPROC) lpfn,
                            0);
  FreeProcInstance(lpfn);

  return  (retCode);
}
/************************************************************************/
/*
 *  MainDlgProc: Handle messages to the main dialog.
 *  Note:  Under Windows NT, installation consists of creating a service
 *         and creating some keys in the registry, while under Windows
 *         we instead add slightly different information to the WIN.INI file.
 *         So two different dialogs are necessary for the different operating
 *         systems.  Appropriate sections of the procedure below are
 *         #ifdef'ed to handle parts of the installation that are unique
 *         to a particular operating system.
 */
/************************************************************************/
BOOL CALLBACK MainDlgProc (HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam)
  {
  static   HWND hInfinite;
  char     *lpPathName, *lpTPName, *lpParameters, *lpTimeout, *lpLocalLUName;
  char     *divider;
  FARPROC  lpfn;
  INT      nIndex, cmd;
  BOOL     bService;
  HWND   hwndCntl;
#ifdef WIN32
  DWORD   dwVersion;
  HANDLE   hEvent;
#endif

  switch (wMsg)
    {
      case WM_INITDIALOG:
        hDialog = hDlg;

        // Set maximum pathname to 512 characters
        SendMessage(GetDlgItem(hDlg, IDE_PATHNAME),
                    EM_LIMITTEXT, MAXBINPATHLEN, 0);

        // Set maximum TP name to 128 characters
        SendMessage(GetDlgItem(hDlg, IDE_TPNAME),
                    EM_LIMITTEXT, MAXTPLEN, 0);

        // Default for timeout is "infinite"
        CheckRadioButton(hDlg, IDD_FINITE, IDD_INFINITE, IDD_INFINITE);

        hTimeout = GetDlgItem(hDlg, IDE_TIMEOUT);
        hList = GetDlgItem(hDlg, IDC_LIST);

        // Allow only numbers to be typed into the timeout box.
        ValidateField(hTimeout, VALIDATE_INT);

        // Set maximum length of number to be a few digits
        SendMessage(hTimeout, EM_LIMITTEXT, MAXTIMEOUTLEN, 0);

// Allow only legal characters in "Local LU Alias" box:
ValidateField(GetDlgItem(hDlg, IDE_LOCALLU), VALIDATE_APPC);

        // Set maximum length of LU name to be 8 characters
        SendMessage(GetDlgItem(hDlg, IDE_LOCALLU), EM_LIMITTEXT, MAXLULEN, 0);

        // Put an initial value in the "timeout" box & grey it out
        SetDlgItemInt(hDlg, IDE_TIMEOUT, INIT_TIMEOUT, FALSE);
        EnableWindow(hTimeout, FALSE);

        // Set callback procedure for radiobutton to make the timeout box
        // sensitive only when "finite" is selected.
        InstallCallback(hDlg, IDD_INFINITE, &lPrevWndProcInfinite, (FARPROC)InfiniteWndProc);

        // Set callback for "conversation security" to grey out user group
        // box when it's turned off.
        InstallCallback(hDlg, IDC_CONVSEC, &lPrevWndProcConvSec, (FARPROC)ConvSecWndProc);
        // Uncheck conversation security to force grey out of user group box
        SendMessage(GetDlgItem(hDlg, IDC_CONVSEC), BM_SETCHECK, 0, 0L);

#ifdef WIN32
//
// On Win95 don't allow TP to be configured as an NT service
//
dwVersion = GetVersion();
if( dwVersion < 0x80000000 &&// Windows NT
!IsWin95Client() )// Not Win95 client
{
        // Set callback for "service" to grey out queued
        // box when it's turned on.
        InstallCallback(hDlg, IDC_SERVICE, &lPrevWndProcService, (FARPROC)ServiceWndProc);

        // Default for serviec is "yes"
        CheckDlgButton(hDlg, IDC_SERVICE, 1);
}
else// Non-NT or Win95 client on NT
{
        // Default for service is "no"
        CheckDlgButton(hDlg, IDC_SERVICE, 0);

// Disable the whole service option
    hwndCntl = GetDlgItem( hDialog, IDC_SERVICE );
    EnableWindow( hwndCntl, FALSE );
}
#endif

        // Default for queued is "yes"
        CheckDlgButton(hDlg, IDC_QUEUED, 1);

        return TRUE;

      case WM_COMMAND:
        switch (cmd = GET_WM_COMMAND_ID(wParam, lParam))
          {
            case IDOK:
              // User hits OK; we get relevant info & try to install.

              // If TP name field is blank, error out
              if (ReadString(hDlg, IDE_TPNAME, &lpTPName, MAXTPLEN) == 0)
                {
                  DisplayError(hDlg, IDS_BADTPNAME);
                  free(lpTPName);
                  return TRUE;
                }

              // read path name
              ReadString(hDlg, IDE_PATHNAME, &lpPathName, MAXBINPATHLEN);

      // read Local LU Alias
      ReadString(hDlg, IDE_LOCALLU, &lpLocalLUName, MAXLULEN);

              // Get "timeout" value
              if (IsDlgButtonChecked(hDlg, IDD_INFINITE) == 1)
                lpTimeout = INFINITE_TIMEOUT;
              else
                {
                  // Number must be filled in, or else we complain
                  if (ReadString(hDlg, IDE_TIMEOUT, &lpTimeout, MAXTIMEOUTLEN + 3)
                      == 0)
                    {
                      DisplayError(hDlg, IDS_BADTIMEOUT);
      free(lpPathName);
      free(lpTimeout);
                      free(lpTPName);
                      return TRUE;
                    }
  else
    {
  //
  // convert to milliseconds the hard way
  //
  strcat( lpTimeout, "000" );
    }
                }

              // Split off command-line parameters from executable name.
  // Win95 and NT support spaces in pathnames, it is not safe
  // to assume that first space marks the end of the executable name.
  // It is quite safe to assume that executable programs stil end
  // with extension .exe

  if (divider = strstr(lpPathName, ".exe" ))
  {
              if (divider = strchr(divider, ' '))
              {
                  lpParameters = divider + 1;
                  *divider = '\0';
              }
                else
{
  lpParameters = "";
}
  }
              else lpParameters = "";

#ifdef WIN32
              bService = IsDlgButtonChecked(hDlg, IDC_SERVICE) == 1;

              // Install the new service; if successful, set subkeys
              if (bService && InstallServiceNT(hDlg, lpTPName, lpPathName) != 0)
                {
                  free(lpPathName);
                  free(lpTPName);
                  return TRUE;
                }

              if (CreateKeys(  lpTPName,
   bService,
                       lpLocalLUName,
                               IsDlgButtonChecked(hDlg, IDC_CONVSEC) == 1,
                               IsDlgButtonChecked(hDlg, IDC_ALREADYVER) == 1,
                               IsDlgButtonChecked(hDlg, IDC_QUEUED) == 1,
                               lpTimeout,
   lpParameters,
   lpPathName
                               )
                               != 0)
                {
                  free(lpPathName);
                  free(lpTPName);
                  return TRUE;
                }

  //
  // Now try to open the "SNABASE_NEWTP_EVENT" event and signal
  // the SnaBase to dynamically register this new TP. NOTE this will
  // only work with the WIN95 SnaBase. WinNT SnaBase doesn't create
  // this event.
  //
  hEvent = OpenEvent( EVENT_MODIFY_STATE, FALSE, "SNABASE_NEWTP_EVENT" );
  if( hEvent != NULL )
  {
  SetEvent( hEvent );
  CloseHandle( hEvent );
  }

              DisplayInfo(hDlg, IDS_SUCCESS);

#else

              // Add entries to WIN.INI
              if (InstallWindows(hDlg, lpTPName, lpPathName,
 lpLocalLUName, lpParameters,
                                 IsDlgButtonChecked(hDlg, IDC_CONVSEC) == 1,
                                 IsDlgButtonChecked(hDlg, IDC_ALREADYVER) == 1,
                                 IsDlgButtonChecked(hDlg, IDC_QUEUED) == 1,
                                 lpTimeout
                                 ) != 0)
                {
                  free(lpPathName);
                  free(lpTPName);
                  return TRUE;
                }
              DisplayInfo(hDlg, IDS_SUCCESS);

#endif //ifdef WIN32

              EndDialog(hDlg, TRUE);
              free(lpPathName);
              free(lpTPName);
              return TRUE;

            case IDCANCEL:
              EndDialog(hDlg, FALSE);
              return TRUE;

            case IDC_ADD:
            case IDC_EDIT:
              // Bring up a dialog to add or edit username & password.
              // The last argument to DialogBoxParam is passed to the dialog
              // procedure and used to determine whether the procedure was
              // called from Add or Edit.
              lpfn = (FARPROC) MakeProcInstance( (FARPROC)UserDlgProc, hInst);
              DialogBoxParam (hInst,
                            (LPCSTR)"UserDlg",
                            hDialog,
                            (DLGPROC) lpfn,
                            cmd);
              FreeProcInstance(lpfn);
              return TRUE;

            case IDC_DELETE:
              nIndex = SendMessage(hList, LB_GETCURSEL, 0, 0);
              if (nIndex == LB_ERR)
                break;

              DeleteListItem(nIndex);
              break;

            default:
              return FALSE;
          }

        break;

      case WM_DESTROY:
        PostQuitMessage(0);
        return TRUE;
    }

#ifdef WIN32
  UNREFERENCED_PARAMETER(divider);
  UNREFERENCED_PARAMETER(lpParameters);
#endif

    return FALSE;
}
/*****************************************************************************/
/*
 * UserDlgProc: Dialog procedure for adding or editing a username/password
 *   pair.
 */
/*****************************************************************************/
BOOL CALLBACK UserDlgProc (HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam)
{
  char *szUsername, *szPassword, *szPasswd;
  WORD nIndex;
  INT  nPasswdLen;
  LOCALHANDLE hLocalMem;

  switch(wMsg)
    {
      case WM_INITDIALOG:
        // Set maximum username length
        SendMessage(GetDlgItem(hDlg, IDE_USERNAME),
                    EM_LIMITTEXT, MAXUSERNAMELEN, 0);

        // Set maximum password length
        SendMessage(GetDlgItem(hDlg, IDE_PASSWORD),
                    EM_LIMITTEXT, MAXPASSWORDLEN, 0);

        // If we were called from the Edit button, we should fill in the
        // edit boxes with strings from the current selection
        if (lParam == IDC_EDIT)
          {
            // Make sure something is currently selected
            if ((nIndex = (WORD) SendMessage(hList, LB_GETCURSEL, 0, 0)) != LB_ERR)
              {
                szUsername = (char *) malloc(MAXUSERNAMELEN + 1);
                SendMessage(hList, LB_GETTEXT, (WPARAM) nIndex,
                            (LPARAM)(LPSTR)szUsername);
                SendMessage(GetDlgItem(hDlg, IDE_USERNAME), WM_SETTEXT, 0,
                            (LPARAM)(LPSTR)szUsername);
                free(szUsername);

                // Get pointer to password and then get password
                hLocalMem = (LOCALHANDLE) SendMessage(hList, LB_GETITEMDATA,
                                                      nIndex, 0);
                if (hLocalMem != NULL)
                  {
                    szPasswd = (char *) LocalLock(hLocalMem);
                    SendMessage(GetDlgItem(hDlg, IDE_PASSWORD), WM_SETTEXT, 0,
                                    (LPARAM)(LPSTR)szPasswd);
                    LocalUnlock(hLocalMem);
                  }
              }
          }

        break;

      case WM_COMMAND:
        switch(GET_WM_COMMAND_ID(wParam, lParam))
          {
            case IDOK:
              // Complain if username is blank
              if (ReadString(hDlg, IDE_USERNAME, &szUsername, MAXUSERNAMELEN)
                == 0)
                {
                  DisplayError(hDlg, IDS_NOUSERNAME);
                  free(szUsername);
                  return TRUE;
                }

              // Read password
              nPasswdLen = ReadString(hDlg, IDE_PASSWORD,
                                      &szPassword, MAXPASSWORDLEN);

              // Complain if password is blank
              if (nPasswdLen == 0)
                {
                  DisplayError(hDlg, IDS_NOPASSWORD);
                  free(szUsername);
                  free(szPassword);
                  return TRUE;
                }

              // If user is already listed, replace him by first deleting.
              // This also takes care of the Edit button, which presumably
              // will often have a username that already exists in the list.
              nIndex = (WORD) SendMessage(hList, LB_SELECTSTRING, (WPARAM) -1,
                                  (LPARAM) (LPSTR)szUsername);
              if (nIndex != LB_ERR)
                DeleteListItem(nIndex);

              // Add the new user to the "users" list in the main dialog
              nIndex = (WORD) SendMessage(hList, LB_ADDSTRING, 0,
                                    (LPARAM) (LPSTR) szUsername);

      // Allocate some space for the password & store handle in list
      hLocalMem = LocalAlloc(LHND, nPasswdLen + 1);
      if (hLocalMem == NULL)
{
  DisplayError(hDlg, IDS_OUTOFMEMORY);
  SendMessage(hList, LB_SETITEMDATA, nIndex, (LPARAM) NULL);
        }
      else
{
  szPasswd = (char *) LocalLock(hLocalMem);
  strcpy(szPasswd, szPassword);
  SendMessage(hList, LB_SETITEMDATA, nIndex, (LPARAM)(LPVOID)hLocalMem);
  LocalUnlock(hLocalMem);
                }

              // Force highlight to newly inserted user, & enable Delete & Edit
              SendMessage(hList, LB_SETCURSEL, nIndex, 0L);
              EnableWindow(GetDlgItem(hDialog, IDC_DELETE), TRUE);
              EnableWindow(GetDlgItem(hDialog, IDC_EDIT), TRUE);

              EndDialog(hDlg, TRUE);
              return TRUE;

            case IDCANCEL:
              EndDialog(hDlg, FALSE);
              return TRUE;

            default:
              return FALSE;

          }
        break;
    }
  return FALSE;
}

#ifdef WIN32

/*****************************************************************************/
/*
 * InstallServiceNT( HWND hDlg, LPSTR lpServiceName, LPSTR lpPath )
 *
 * Windows NT version of installation--register a new service.
 *
 * Parameters
 * ----------
 *
 * hDlg: Handle to top level dialog; used for error messages.
 * lpServiceName: A string giving the name of the TP.
 * lpPath: A string giving the full pathname of the TP's executable.
 *
 * Returns
 * -------
 *
 *  Zero upon successful completion, nonzero otherwise.
 *
 * Comments
 * --------
 *  Installs new service if possible.  Puts up appropriate message boxes if
 *  installation fails.
 */
/*****************************************************************************/
int InstallServiceNT(HWND hDlg, LPSTR lpServiceName, LPSTR lpBinaryPath)
{

  SC_HANDLE hSCManager = NULL;
  SC_HANDLE hService   = NULL;
  SC_LOCK   lSCLock    = NULL;

  hSCManager = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );

  if( hSCManager != NULL )
  {
    /*************************************************************************/
    /* Lock the service database                                             */
    /*************************************************************************/
    lSCLock = LockServiceDatabase( hSCManager );
    if ( lSCLock != NULL )
    {
      /***********************************************************************/
      /* Create the service                                                  */
      /***********************************************************************/
      hService = CreateService( hSCManager,
                                lpServiceName,     // Service's name
                                lpServiceName,     // Display name (new for NT)
                                SERVICE_ALL_ACCESS,// Access (allow all)
                                0x10,              // Service type
                                0x3,               // Startup behavior
                                0x1,               // Error control
                                lpBinaryPath,      // Full pathname of binary
                                NULL,              // Load order group
                                NULL,              // Tag ID
                                NULL,              // Dependencies (none)
                                NULL,              // Account name
                                NULL               // Password
                                );
      if ( hService != NULL )
        {
          /*********************************************************************/
          /* Close our handle to the new service                               */
          /*********************************************************************/
          CloseServiceHandle( hService );
        }
      else
        {
          // Try to display the reason for the create failure
          ParseCreateError(hDlg, GetLastError());

          // Must release lock in case we are called again
          UnlockServiceDatabase( lSCLock );

          return 1;
        }

      /***********************************************************************/
      /* Unlock the database                                                 */
      /***********************************************************************/
      UnlockServiceDatabase( lSCLock );
    }
    else
      {
        DisplayError(hDlg, IDS_LOCKFAILED);
        return 1;
      }
    /*************************************************************************/
    /* Free our handle to the service control manager                        */
    /*************************************************************************/
    CloseServiceHandle( hSCManager );
    return 0;
  }

  DisplayError(hDlg, IDS_OPENSCMFAILED);
  return 1;
}

/*****************************************************************************/
/*
 * CreateKeys: Set the required key values for the newly created service.
 *  The keys are:
 *                 Linkage:
 *                        OtherDependencies: REG_MULTI_SZ: SnaBase
 *                Parameters:
 *                        SNAServiceType: REG_DWORD: 0x5
 *                        ConversationSecurity: REG_SZ:  "yes" or "no"
 *                           (depending on setting of radio button in dialog box)
 *  AlreadyVerified: REG_SZ: "yes" or "no"
 * If the timeout isn't infinite, there appears the key:
 *                        Timeout: REG_DWORD: <timeout in seconds>
 *                        Parameters: REG_SZ: cmd line parameters
 * If ConversationSecurity is yes, there are also these keys:
 *                        <User1>: REG_SZ: <Password1>
 *                                ...
 *                        <Usern>: REG_SZ: <Passwordn>
 *
 *  Note that the users and passwords are read from the list box.
 *
 *  Arguments:
 *    lpServiceName is the name of the TP.
 *    lpLUName is the Local LU Alias.
 *    The two integer arguments give the state of the radio buttons on the
 *      dialog box.  They should be zero to indicate that "No" is selected,
 *      nonzero otherwise.
 *    lpTimeout is a string containing the timeout value; if the timeout is
 *      infinite, it holds the value of INFINITE_TIMEOUT.
 *
 *  Returns: 0 on success, nonzero otherwise.
 */
/*****************************************************************************/
INT CreateKeys(LPSTR lpServiceName, BOOL bService, LPSTR lpLUName, int iConvSec,
               int iAlreadyVer, int iQueued, LPSTR lpTimeout, LPSTR lpParameters,
   LPSTR lpExeName)
{
  LPSTR  lpServiceFullName;
  int    bufsize, i, nCount;
  LOCALHANDLE hLocalMem;

  // If any of the buttons says "no", change the key values so that they
  // will be inserted into the registry below.  This is kind of gross, but
  // it keeps the loop below simple.
  if (iConvSec == 0)
    {
      keyinfo[CONVSEC].lpData = "no";
      keyinfo[CONVSEC].iDataSize = 3;
    }
  if (iAlreadyVer == 0)
    {
      keyinfo[ALREADYVER].lpData = "no";
      keyinfo[ALREADYVER].iDataSize = 3;
    }
  if (bService == FALSE && iQueued == 0)
    {
  //
  // the default of 5 is OK for queued support.
  // needs to be redefined to 6 for nonqueued.
  //
      keyinfo[SNASRVTYPE].lpData = "6";
      keyinfo[SNASRVTYPE].iDataSize = 4;
    }

  // Put timeout in structure (it won't be written out if infinite)
  keyinfo[TIMEOUT].lpData = lpTimeout;

  // Put Parameters in structure
  keyinfo[PARAMETERS].lpData = lpParameters;
  keyinfo[PARAMETERS].iDataSize = strlen(lpParameters) + 1;   // Count null

  // Put Local LU Alias in structure; it won't be written out if blank
  keyinfo[LUNAME].lpData = lpLUName;
  keyinfo[LUNAME].iDataSize = strlen(lpLUName) + 1;   // Count null

  // Create full path of TP by concatenating the registry path with the
  // TP's name
  bufsize = MAXREGPATHLEN + 1;
  lpServiceFullName = (LPSTR) malloc(bufsize);

  i = bService ? IDS_REGISTRYPATH : IDS_REGISTRYAPPLPATH ;

#ifdef WIN32
  if( IsWin95Client() )
  {
      i = bService ? IDS_REGISTRYPATH_WIN95 : IDS_REGISTRYAPPLPATH_WIN95;
  }
#endif

  LoadString( hInst, i, lpServiceFullName, bufsize );
  lstrcat(lpServiceFullName, lpServiceName);

  // Write out the basic keys; first key is skipped for non-service tps
  for ( i = bService ? 0 : 1; i < NUMKEYS; i++ )
    {
      if ( WriteKeyNT(lpServiceFullName, keyinfo[i], TRUE ) )
          return 1;
    }

  // if not a service then write out the path name
  if ( bService == FALSE )
    {
      keyinfo[EXENAME].lpData = lpExeName;
      keyinfo[EXENAME].iDataSize = strlen(lpExeName) + 1;   // Count null
      if (WriteKeyNT(lpServiceFullName, keyinfo[EXENAME], TRUE ) )
          return 1;
    }

  // If timeout isn't infinite, write out the "timeout" key
  if (lstrcmp(lpTimeout, INFINITE_TIMEOUT))
    {
      if (WriteKeyNT(lpServiceFullName, keyinfo[TIMEOUT], TRUE ) )
          return 1;
    }

  // If Local LU Alias isn't blank, write out its key
  if (strlen(lpLUName) > 0)
 {
      if (WriteKeyNT(lpServiceFullName, keyinfo[LUNAME], TRUE ) )
          return 1;
    }

  // Only write out user stuff if conversation security is "yes"
  if (iConvSec)
    {
      keyinfo[USER].lpName = (LPSTR) malloc(MAXUSERNAMELEN + 1);
      nCount = SendMessage(hList, LB_GETCOUNT, 0, 0);
      for (i = 0; i < nCount; i++)
        {
          // Get username & make it the name of this key
          SendMessage(hList, LB_GETTEXT, (WPARAM) i,
                                  (LPARAM) keyinfo[USER].lpName);

          // Get pointer to password and then get password
          hLocalMem = (LOCALHANDLE) SendMessage(hList, LB_GETITEMDATA, i, 0);
          keyinfo[USER].lpData = (LPSTR) LocalLock(hLocalMem);

          // Password shouldn't be blank, but just in case...
          if (keyinfo[USER].lpData == NULL)
            keyinfo[USER].iDataSize = 0;
          else
            keyinfo[USER].iDataSize = strlen(keyinfo[USER].lpData);

          if (WriteKeyNT(lpServiceFullName, keyinfo[USER], TRUE ) )
            {
              LocalUnlock(hLocalMem);
              return 1;
            }
          LocalUnlock(hLocalMem);
        }
    }

  free(lpServiceFullName);
  return 0;
}
/*****************************************************************************/
/*
 * WriteKeyNT: Write out the given key to the registry.
 * Return 0 on success, nonzero otherwise.
 */
/*****************************************************************************/
INT WriteKeyNT(LPSTR lpServiceFullName, KEYENTRY keyinfo, BOOL bAppendParent )
{
static BOOLbAskToReplace = TRUE;
  DWORD     dwResult;
  HKEY      hKey;
  LPSTR     lpKeyName;
  DWORDdwTemp;

  lpKeyName = (LPSTR) malloc(MAXREGPATHLEN + 1);

  // Build full pathname of key by appending a backslash and the name of the
  // key to the full pathname of the TP.
  lstrcpy(lpKeyName, lpServiceFullName);
  lstrcat(lpKeyName, TEXT("\\"));
  lstrcat(lpKeyName, keyinfo.lpParent);

  // Now create the subkeys and set their values
  if (RegCreateKeyEx(HKEY_LOCAL_MACHINE,      // Predefined key
      lpKeyName,                      // Our subkey's name
      0,                       // Reserved
      NULL,                    // Class string
      REG_OPTION_NON_VOLATILE, // Option type (value is saved)
      KEY_ALL_ACCESS,          // Access mask
      NULL,                    // Security attributes
      &hKey,                   // Handle to key is put here
      &dwResult)               // Was key opened or created?
      == ERROR_SUCCESS)
        {
          if ( bAskToReplace && dwResult == REG_OPENED_EXISTING_KEY )
    {
char szCaption[64];
char szText[256];
  LoadString( hInst, IDS_REPLACECAPTION, szCaption, sizeof(szCaption) );
  LoadString( hInst, IDS_REPLACETEXT, szText, sizeof(szText) );
  switch( MessageBox( hDialog, szText, szCaption, MB_OKCANCEL|MB_ICONQUESTION ) )
    {
  case IDCANCEL:
  return 1;

    }
    }

  //
  // after first successful pass no need to query
  //
  bAskToReplace = FALSE;

          // We stored all the values as strings, but some are actually numbers.
          // So we have to make a temporary buffer to pass RegSetValueEx the
          // pointer to a DWORD that it wants for DWORD data.
          if ( keyinfo.iDataType == REG_DWORD )
            {
              dwTemp = atol(keyinfo.lpData);
              keyinfo.lpData = (LPSTR) &dwTemp;
              keyinfo.iDataSize = sizeof(DWORD);
            }

          if (RegSetValueEx(hKey,
                        keyinfo.lpName,    // Key name
                        0,                    // Reserved
                        keyinfo.iDataType, // Type of key data
                        keyinfo.lpData,    // Key's value
                        keyinfo.iDataSize  // Length of value (incl. nulls)
                        )
                        != ERROR_SUCCESS)
            {
              DisplayError(hDialog, IDS_SETKEYFAILED);
              return 1;
            }
        }
      else
        {
          DisplayError(hDialog, IDS_OPENKEYFAILED);
          return 1;

}

  free(lpKeyName);
  return 0;
}
/*****************************************************************************/
/*
 * ParseCreateError: Given an error code from a registry operation, attempt
 *   to give an appropriate error message.
 */
/*****************************************************************************/
void ParseCreateError(HWND hDlg, UINT uError)
{
  UINT code;
  switch (uError)
    {
      case ERROR_INVALID_PARAMETER:
        // "Invalid parameter" is a rather generic error name.  Since a blank
        // service name is the most probable way the error arose, say so:
        code = IDS_BADPATHNAME;
        break;

      case ERROR_INVALID_NAME:
        code = IDS_BADTPNAME;
        break;

      case ERROR_SERVICE_EXISTS:
        code = IDS_SERVICEEXISTS;
        break;

      default:
        // Since we don't have a code for this error, the following will bring
        // up the "unknown" error message
        code = uError;
    }
  DisplayError(hDlg, code);
}

#else

/*****************************************************************************/
/*
 * InstallWindows: Windows version of installation; add lines to WIN.INI
 *
 * First we must add an entry under the heading SNAServerAutoTPs.  This entry
 * has the name of the TP and points to another heading that contains TP-
 * specific data.  Sample WIN.INI:
 *
 * [SNAServerAutoTPs]
 * BounceTP = BounceTPParams
 *
 * [BounceTPParams]
 * PathName = c:\sna\bounce.exe
 * Parameters = /t
 * etc....
 *
 * For a list of entries, see the comment at CreateKeys().  The only difference
 * is that WIN.INI also contains an entry for the "queued" toggle.
 *
 *
 * Parameters:
 *    szTPName: The name of the TP
 *    szBinaryPath: The full pathname of the executable
 *    szLUName: Local LU Alias
 *    szParameters: The list of command line parameters
 *    iConvSec: "Conversation security" toggle (1 = yes, 0 = no)
 *    iAlreadyVer: "Already verified" toggle
 *    iQueued: "Queued" toggle
 *    lpTimeout: A string specifying the timeout in seconds, e.g. "2"
 *
 * Returns: zero on success, nonzero otherwise
 */
/*****************************************************************************/
INT InstallWindows(HWND hDlg, char *szTPName, char *szBinaryPath, char *szLUName,
                   char *szParameters,
                   int iConvSec, int iAlreadyVer, int iQueued, char *lpTimeout
                   )
{
  char szNewKeyName[MAXTPLEN + 6];
  int  i, nCount;
  LOCALHANDLE hLocalMem;

  // Make new heading by concatenating TP's name and the string "Params"
  strcpy(szNewKeyName, szTPName);
  strcat(szNewKeyName, "Params");

  // Now add the key to the SNAServerAutoTPs heading
  if (WriteKeyWindows("SNAServerAutoTPs", szTPName, szNewKeyName))
    return 1;

  // Set the data fields of the keys, one at a time
  keyinfo[PATHNAME].data    = szBinaryPath;
  keyinfo[PARAMETERS].data  = szParameters;
  keyinfo[LUNAME].data      = szLUName;
  keyinfo[QUEUED].data      = iQueued ? "yes" : "no";
  keyinfo[TIMEOUT].data     = lpTimeout;
  keyinfo[CONVSEC].data     = iConvSec ? "yes" : "no";
  keyinfo[ALREADYVER].data  = iAlreadyVer ? "yes" : "no";

  // Loop through all the keys and add them under the new "Params" heading
  for (i=0; i < NUMKEYS; i++)
    if (WriteKeyWindows(szNewKeyName, keyinfo[i].name, keyinfo[i].data))
      return 1;

  // Only write out Local LU Alias if it's not blank
  if (strlen(szLUName) > 0)
    if (WriteKeyWindows(szNewKeyName, keyinfo[LUNAME].name, keyinfo[LUNAME].data))
      return 1;

  // If conversation security is on, write out users & passwords
  if (iConvSec)
    {
      keyinfo[USER].name = (char *) malloc(MAXUSERNAMELEN + 1);
      nCount = SendMessage(hList, LB_GETCOUNT, 0, 0);

      // Read usernames and passwords from list box & create keys
      for (i = 0; i < nCount; i++)
        {
          // Get username & make it the name of this key
          SendMessage(hList, LB_GETTEXT, (WPARAM) i,
                                  (LPARAM)(LPSTR)keyinfo[USER].name);

          // Get pointer to password and then get password
          hLocalMem = (LOCALHANDLE) SendMessage(hList, LB_GETITEMDATA, i, 0);
          keyinfo[USER].data = (char *) LocalLock(hLocalMem);

          if (WriteKeyWindows(szNewKeyName, keyinfo[USER].name,
                                keyinfo[USER].data))
            {
              LocalUnlock(hLocalMem);
              return 1;
            }
          LocalUnlock(hLocalMem);
        }
      free(keyinfo[USER].name);

    }

  return 0;
}
/*****************************************************************************/
/*
 * WriteKeyWindows:  Write the given key out to WIN.INI
 * Return 0 on success, nonzero otherwise.
 */
/*****************************************************************************/
INT WriteKeyWindows(char *heading, char *keyname, char *value)
{
  if (WriteProfileString(heading, keyname, value) == FALSE)
    {
      DisplayError(hDialog, IDS_INIWRITEFAILED);
      return 1;
    }
  return 0;
}

#endif //ifdef WIN32

/*****************************************************************************/
/*
 * ValidateField: Install a window procedure that handles messages to the
 *   given edit window.
 *
 * Parameters: hwnd is the edit window, usType an identifier for the window
 *   procuedure to install.  Currently the only allowed type is VALIDATE_INT,
 *   which allows only digits to be typed into the edit box.
 *
 * Returns:  TRUE if window procedure is successfully installed,
 *   FALSE otherwise.
/*****************************************************************************/
BOOL ValidateField( HWND hwnd, USHORT usType)
{
        FARPROC lpfn = NULL;
        LONG        lPrevWndProc;
        LONG FAR *lplNewWndProc;

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

        lPrevWndProc = GetWindowLong( hwnd, GWL_WNDPROC );

        if (lPrevWndProc == 0) {
                return(FALSE);
        }

        // check that the old wndproc is the same as the one we have stored
        // also, get the new wndproc, based on contents of edit field

        // We only need two validation procedures, but if you want others
        // (such as hexadecimal, filenames, etc.) you can add extra cases
        // and procedures below.

        switch (usType) {
          case VALIDATE_INT:
            if (lpfnInt == NULL) {
              lpfnInt = MakeProcInstance( (FARPROC)ValidateFieldInt, hInst );
            }
            lpfn = lpfnInt;

            lplNewWndProc = (LONG FAR *) &lPrevWndProcInt;
            break;

  case VALIDATE_APPC:
            if (lpfnAppc == NULL) {
              lpfnAppc = MakeProcInstance( (FARPROC)ValidateFieldAppc, hInst );
            }
            lpfn = lpfnAppc;

            lplNewWndProc = (LONG FAR *) &lPrevWndProcAppc;
            break;

          default:
            return(FALSE);
          }

        if (*lplNewWndProc == (LONG) NULL) {

                *lplNewWndProc = lPrevWndProc;

        } else {

                if (*lplNewWndProc != lPrevWndProc) {
                        return(FALSE);
                }

                *lplNewWndProc = lPrevWndProc;
        }

        if (lpfn == NULL ||
                SetWindowLong(hwnd, GWL_WNDPROC, (LONG) lpfn) == (LONG) NULL) {

                return(FALSE);
        }
        return(TRUE);
}

/*****************************************************************************/
/*
 * ValidateFieldInt: A window proc that allows only 0 through 9 to be typed to
 *   an edit box.
 */
/*****************************************************************************/
LRESULT CALLBACK ValidateFieldInt( HWND hwnd, WORD msg, WPARAM wParam, LPARAM lParam )
{
        if (msg == WM_CHAR) {

                char ch = LOBYTE(LOWORD(wParam));

                if ((ch < '0' || ch > '9') && ch != VK_BACK )
                  return(0);
        }
        // If character was legal or message wasn't WM_CHAR,
        // pass message on to default window proc.
        return CallWindowProc( lPrevWndProcInt, hwnd, msg, wParam, lParam);
}
/*****************************************************************************/
/*
 * ValidateFieldAppc: A window proc that allows only legal APPC name characters
 *    to be typed to an edit box.
 */
/*****************************************************************************/
LRESULT CALLBACK ValidateFieldAppc( HWND hwnd, WORD msg, WPARAM wParam, LPARAM lParam )
{
if (msg == WM_CHAR) {

char ch = LOBYTE(LOWORD(wParam));

if (!(ch >= '0' && ch <= '9' ||
ch >= 'A' && ch <= 'Z' ||
ch >= 'a' && ch <= 'z' ||
ch == '@'||
ch == '#'||
ch == '$'||
ch == '%'||
ch == VK_BACK ))
    return(0);
}
return CallWindowProc( lPrevWndProcAppc, hwnd, msg, wParam, lParam);
}
/*****************************************************************************/
/*
 * InstallCallback: Install a callback window proc.
 *   Arguments:
 *        hDlg: Handle of the parent dialog
 *      uId: ID of window for which callback will be installed
 *      OldWndProc: Handle for previous window proc
 *      NewWndProc: Pointer to new window proc
 */
/*****************************************************************************/
void InstallCallback(HWND hDlg, UINT uId, WNDPROC *OldWndProc, FARPROC NewWndProc)
{
  HANDLE hTemp;

  hTemp = GetDlgItem(hDlg, uId);
  *OldWndProc = (WNDPROC) GetWindowLong(hTemp, GWL_WNDPROC);
  SetWindowLong(hTemp, GWL_WNDPROC,
               (LONG) MakeProcInstance( (FARPROC)NewWndProc, hInst));
}
/*****************************************************************************/
/*
 * InfiniteWndProc: Disable "timeout" box when the "infinite"
 *   button is pressed, and enable it when "finite" is pressed.
 */
/*****************************************************************************/
LRESULT CALLBACK InfiniteWndProc( HWND hwnd, WORD msg, WPARAM wParam, LPARAM lParam )
{
  if (msg == BM_SETCHECK)
    // wParam is 0 if button is being turned off, 1 if being turned on
    EnableWindow(hTimeout, (wParam == 0));
  return CallWindowProc(lPrevWndProcInfinite, hwnd, msg, wParam, lParam);
}

/*****************************************************************************/
/*
 * ServiceWndProc: Disable "queued" box when the "service" button is pressed
 */
/*****************************************************************************/
LRESULT CALLBACK ServiceWndProc( HWND hwnd, WORD msg, WPARAM wParam, LPARAM lParam )
{
  HWND hwndCntl;

  if (msg == BM_SETCHECK) {
//
    // wParam is 0 if button is being turned off, 1 if being turned on
//
    hwndCntl = GetDlgItem( hDialog, IDC_QUEUED );
    EnableWindow( hwndCntl, wParam == 1 ? FALSE : TRUE );

if ( wParam == 1 ) {
SendMessage( hwndCntl, BM_SETCHECK, 1, 0 );
}
  }

  return CallWindowProc(lPrevWndProcService, hwnd, msg, wParam, lParam);
}

/*****************************************************************************/
/*
 * ConvSecWndProc: Window proc for "conversation security" checkbox.  Grey
 *   out "users" group box when checkbox is off
 */
/*****************************************************************************/
LRESULT CALLBACK ConvSecWndProc( HWND hwnd, WORD msg, WPARAM wParam, LPARAM lParam )
{
  int nItems = SendMessage(hList, LB_GETCOUNT, 0, 0L);
  if (msg == BM_SETCHECK)
    {
      EnableWindow(GetDlgItem(hDialog, IDC_USERBOX), wParam == 1 ? TRUE : FALSE);
      EnableWindow(GetDlgItem(hDialog, IDC_ADD), wParam == 1 ? TRUE : FALSE);
      EnableWindow(hList, wParam == 1 ? TRUE : FALSE);
      // Only turn on Delete & Edit if there's something in the list box
      EnableWindow(GetDlgItem(hDialog, IDC_DELETE), wParam == 1 && nItems);
      EnableWindow(GetDlgItem(hDialog, IDC_EDIT), wParam == 1 && nItems);
    }
  return CallWindowProc(lPrevWndProcConvSec, hwnd, msg, wParam, lParam);
}

/*****************************************************************************/
/*
 * DeleteListItem: Handle all aspects of deleting a user from the list box,
 *   given his index.
 */
/*****************************************************************************/
void DeleteListItem(INT nIndex)
{
  LOCALHANDLE hLocalMem;
  INT nUsers;

  // Free associated password memory, if necessary
  if (hLocalMem = (LOCALHANDLE) SendMessage(hList, LB_GETITEMDATA,
                                            nIndex, 0))
    LocalFree(hLocalMem);

  // Simply remove currently selected user
  SendMessage(hList, LB_DELETESTRING, (WPARAM) nIndex, 0);

  nUsers = SendMessage(hList, LB_GETCOUNT, 0, 0);

  // Disable Delete & Edit buttons if no one is left
  if (0 == nUsers)
    {
      EnableWindow(GetDlgItem(hDialog, IDC_DELETE), FALSE);
      EnableWindow(GetDlgItem(hDialog, IDC_EDIT), FALSE);
      return;
    }

  // Move the highlight:
  // 1) If the deleted user was the last one in the list, go to previous user.
  // 2) Otherwise go to the next user.
  SendMessage(hList, LB_SETCURSEL,
              (nUsers == nIndex) ? (nIndex - 1) : nIndex, 0);
}
/*****************************************************************************/
/*
 * ReadString: Get the text from an edit box and turn it into a C string.
 *
 * Parameters:
 *
 * hDlg:  The dialog box.
 * id: The dialog item id.
 * lpString:  A double pointer to a character.  ReadString reserves space
 *            for the incoming string.
 * maxlen: The maximum number of characters to read from the edit box.
 *
 * Returns:
 *   The number of characters read from the edit box.
 */
/*****************************************************************************/
INT ReadString(HWND hDlg, INT id, char **lpString, INT maxlen)
{
  // Leave space for null character
  *lpString = malloc((maxlen + 1) * sizeof(char));

  return GetDlgItemText(hDlg, id, *lpString, maxlen+1);
}
/*****************************************************************************/
/*
 * DisplayError: Bring up MessageBox with given message code's string.
 */
/*****************************************************************************/
void DisplayError( HWND hwnd, UINT uError)
{
        char        sz[256], szFormat[256];

        if ( LoadString( hInst, uError, sz, sizeof(sz)) == 0 ) {
                LoadString( hInst, IDS_UNKNOWN, szFormat, sizeof(szFormat) );
                sprintf( sz, szFormat, uError );
        }

        LoadString( hInst, IDS_ERRORTITLE, szFormat, sizeof(szFormat) );
        MessageBox( hwnd, sz, szFormat, MB_ICONEXCLAMATION | MB_OK);
}
/*****************************************************************************/
/*
 * DisplayInfo:  Put up an information box with given message string.
 */
/*****************************************************************************/
void DisplayInfo( HWND hwnd, UINT uInfo)
{
        char        sz[256], szFormat[256];

        if ( LoadString( hInst, uInfo, sz, sizeof(sz) ) == 0 ) {
                LoadString( hInst, IDS_NOMESSAGE, szFormat, sizeof(szFormat) );
                sprintf( sz, szFormat, uInfo );
        }

        LoadString( hInst, IDS_INFOTITLE, szFormat, sizeof(szFormat) );
        MessageBox( hwnd, sz, szFormat, MB_ICONINFORMATION | MB_OK);
}

#ifdef WIN32
/*****************************************************************************/
/*
 * IsWin95Client( void ):  If this is the Win95 client, return TRUE
 */
/*****************************************************************************/

BOOL IsWin95Client( void )
{
    HKEY hKey;
    BOOL fRetval = FALSE;
    DWORD dwType=0;
    char  szBuffer[ 30 ];
    DWORD dwDataSize = sizeof( szBuffer );

    if( ERROR_SUCCESS != RegOpenKeyEx( HKEY_LOCAL_MACHINE, "Software\\Microsoft\\SNA Server\\CurrentVersion", 0, KEY_QUERY_VALUE, &hKey ) )
    {
        ; // fall through, return FALSE
    }
    else
    {
        if( ERROR_SUCCESS != RegQueryValueEx( hKey, "ClientType", 0, &dwType, szBuffer, &dwDataSize ) )
        {
            ; // fall through, return FALSE
        }
        else if( REG_SZ != dwType )
        {
            ; // fall through, return FALSE
        }
        else if( stricmp( szBuffer, "Windows 95" ) )
        {
            ; // fall through, return FALSE
        }
        else
        {
            fRetval = TRUE;
        }

        RegCloseKey( hKey );
    }

    return fRetval;

}

#endif


INSTALL.H

#include "dialogs.h"
#define MAXREGPATHLEN1024     // Max length of registry path
#define MAXTPLEN128             // Max length of TP name
#define MAXBINPATHLEN   256             // Max length of file path
#define MAXPARAMLEN  128        // Max length of command-line parameters
#define MAXUSERNAMELEN  32// Max length of username
#define MAXPASSWORDLEN  32// Max length of password
#define MAXLULEN8// Max length of LU name

#define VALIDATE_INT 1
#define VALIDATE_APPC 2

#define INFINITE_TIMEOUT  "-1"     // Value used for infinite timeout
#define INIT_TIMEOUT    5     // Initial value displayed in timeout box
#define MAXTIMEOUTLEN5     // Max digits in timeout

#ifdef WIN32

typedef struct _KeyEntry {
  LPSTR lpParent;                       // Parent key
  LPSTR lpName;                         // Data field name
  int   iDataType;                      // Type of data
  LPSTR lpData;// Data itself
  int   iDataSize;// Length of data, including nulls
} KEYENTRY;

// The following variable defines the subkeys that are placed in the registry.
// The first field gives the key under which to create the new subkey.  The
// second field is the subkey name.  The third field is the data type.
// The fourth field gives the data itself.  This should always be a string,
// HOWEVER, if the third field isn't REG_SZ, some interpretation will go on,
// as follows:
//   If the third field is REG_MULTI_SZ, you should add an extra null character
//   to the end of the string to simulate an array of strings.
//   If the third field is REG_DWORD, you should enter the numeric value as
//   a string in the fourth field, as in "345" if you want the data to be the
//   number 345.
// The fifth field is the length of the fourth field in bytes, INCLUDING nulls.
// This field is set automatically for REG_DWORD data.
KEYENTRY keyinfo[] = {
  { "Linkage",    "OtherDependencies",    REG_MULTI_SZ, "SnaBase\0", 9 },
  { "Parameters", "SNAServiceType",       REG_DWORD,    "5",         4 },
  { "Parameters", "ConversationSecurity", REG_SZ,       "yes",       4 },
  { "Parameters", "AlreadyVerified",      REG_SZ,       "yes",       4 },
  { "Parameters", "Timeout",              REG_DWORD,    "",     4 },
  { "Parameters", "Parameters",           REG_SZ,       "",       4 },
  { "Parameters", "LocalLU",              REG_SZ,       "",     4 },
  { "Parameters", "",                     REG_SZ,       "",          4 },
  { "Parameters", "PathName",             REG_SZ,       "",          4 },
};

// These numbers must correspond to the indices in keyinfo above!
#define SNASRVTYPE 1
#define CONVSEC    2
#define ALREADYVER 3
#define TIMEOUT    4
#define PARAMETERS 5
#define LUNAME     6
#define USER       7  // Blank field for writing out users & passwords
#define EXENAME    8
#define NUMKEYS    6      // Last three keys only written out if needed

int  InstallServiceNT(HWND, LPSTR, LPSTR);
int  CreateKeys(LPSTR, BOOL, LPSTR, int, int, int, LPSTR, LPSTR, LPSTR);
INT  WriteKeyNT(LPSTR, KEYENTRY, BOOL);

#else

#define USHORT unsigned short
#define INT    int
#define SHORT  short

typedef struct _KeyEntry {
  char *name;                        // Data field name
  char *data;     // Data itself
} KEYENTRY;

// If you change the fields below, change the #defines too; they are used
// as indices into the keyinfo structure to set the data fields, which are
// initially all NULL.

KEYENTRY keyinfo[] = {
  { "PathName",     NULL },
  { "Parameters",     NULL },
  { "Queued",     NULL },
  { "Timeout",     NULL },
  { "ConversationSecurity", NULL },
  { "AlreadyVerified",      NULL },
  { "LocalLU",    NULL },
  { "",                     NULL },
};
#define PATHNAME    0
#define PARAMETERS  1
#define QUEUED      2
#define TIMEOUT     3
#define CONVSEC     4
#define ALREADYVER  5
#define LUNAME      6
#define USER        7
#define NUMKEYS     6// Last keys only written out if necessary

int  InstallWindows(HWND, char *, char *, char *, char *, int, int, int, char *);
INT  WriteKeyWindows(char *, char *, char *);

#endif //ifdef WIN32

extern BOOL CALLBACK MainDlgProc (HWND, UINT, WPARAM, LPARAM);
extern BOOL CALLBACK UserDlgProc (HWND, UINT, WPARAM, LPARAM);
BOOL ValidateField(HWND, USHORT);
void InstallCallback(HWND, UINT, WNDPROC *, FARPROC);
extern LRESULT CALLBACK ValidateFieldInt(HWND, WORD, WPARAM, LPARAM);
extern LRESULT CALLBACK ValidateFieldAppc(HWND, WORD, WPARAM, LPARAM);
extern LRESULT CALLBACK InfiniteWndProc(HWND, WORD, WPARAM, LPARAM);
extern LRESULT CALLBACK FiniteWndProc(HWND, WORD, WPARAM, LPARAM);
extern LRESULT CALLBACK ConvSecWndProc(HWND, WORD, WPARAM, LPARAM);
extern LRESULT CALLBACK ServiceWndProc(HWND, WORD, WPARAM, LPARAM);
void ParseCreateError(HWND, UINT);
void DeleteListItem(INT);
INT  ReadString(HWND, INT, char **, INT);
void DisplayError(HWND, UINT);
void DisplayInfo(HWND, UINT);


README.TXT

Simple Network Management Protocol

The SNMP sample has two parts: SNMPUTIL and TESTDLL.

The SNMPUTIL sample demonstrates how to use the SNMP API to build SNMP
manager applications on Windows NT. This is a command-line utility that lets
you retrieve information from any SNMP agent in your network.

The TESTDLL sample demonstrates how to build an extension agent DLL that
works with the extendible SNMP agent. This DLL implements the toaster MIB
and shows how new MIBs can be added to the agent through a DLL.


SNMPUTIL.C

/*++ BUILD Version: 0001    // Increment this if a change has global effects

Copyright 1991 - 1998 Microsoft Corporation

Module Name:

    snmputil.c

Abstract:

    Sample SNMP Management API usage for Windows NT.

    This file is an example of how to code management applications using
    the SNMP Management API for Windows NT.  It is similar in operation to
    the other commonly available SNMP command line utilities.

    Extensive comments have been included to describe its structure and
    operation.  See also "Microsoft Windows/NT SNMP Programmer's Reference".

Created:

    28-Jun-1991

Revision History:

--*/

static char *vcsid = "@(#) $Logfile:   N:/agent/mgmtapi/vcs/snmputil.c_v  $ $Revision:   1.5  $";

// General notes:
//   Microsoft's SNMP Management API for Windows NT is implemented as a DLL
// that is linked with the developer's code.  These APIs (examples follow in
// this file) allow the developer's code to generate SNMP queries and receive
// SNMP traps.  A simple MIB compiler and related APIs are also available to
// allow conversions between OBJECT IDENTIFIERS and OBJECT DESCRIPTORS.

// Necessary includes.

#include <windows.h>

#include <stdio.h>
#include <string.h>
#include <malloc.h>

#include <snmp.h>
#include <mgmtapi.h>

// Constants used in this example.

#define GET     1
#define GETNEXT 2
#define WALK    3
#define TRAP    4

#define TIMEOUT 6000 /* milliseconds */
#define RETRIES 3

// Main program.

INT _CRTAPI1 main(
    IN int  argumentCount,
    IN char *argumentVector[])
    {
    INT                operation;
    LPSTR              agent;
    LPSTR              community;
    RFC1157VarBindList variableBindings;
    LPSNMP_MGR_SESSION session;

    INT        timeout = TIMEOUT;
    INT        retries = RETRIES;

    BYTE       requestType;
    AsnInteger errorStatus;
    AsnInteger errorIndex;
    char        *chkPtr = NULL;

    // Parse command line arguments to determine requested operation.

    // Verify number of arguments...
    if      (argumentCount < 5 && argumentCount != 2)
        {
        printf("Error:  Incorrect number of arguments specified.\n");
        printf(
"\nusage:  snmputil [get|getnext|walk] agent community oid [oid ...]\n");
        printf(
  "        snmputil trap\n");

        return 1;
        }

    // Get/verify operation...
    argumentVector++;
    argumentCount--;
    if      (!strcmp(*argumentVector, "get"))
        operation = GET;
    else if (!strcmp(*argumentVector, "getnext"))
        operation = GETNEXT;
    else if (!strcmp(*argumentVector, "walk"))
        operation = WALK;
    else if (!strcmp(*argumentVector, "trap"))
        operation = TRAP;
    else
        {
        printf("Error:  Invalid operation, '%s', specified.\n",
               *argumentVector);

        return 1;
        }

    if (operation != TRAP)
        {
        if (argumentCount < 4)
            {
            printf("Error:  Incorrect number of arguments specified.\n");
            printf(
"\nusage:  snmputil [get|getnext|walk] agent community oid [oid ...]\n");
            printf(
  "        snmputil trap\n");

            return 1;
            }

        // Get agent address...
        argumentVector++;
        argumentCount--;
        agent = (LPSTR)SNMP_malloc(strlen(*argumentVector) + 1);
        strcpy(agent, *argumentVector);

        // Get agent community...
        argumentVector++;
        argumentCount--;
        community = (LPSTR)SNMP_malloc(strlen(*argumentVector) + 1);
        strcpy(community, *argumentVector);

        // Get oid's...
        variableBindings.list = NULL;
        variableBindings.len = 0;

        while(--argumentCount)
            {
            AsnObjectIdentifier reqObject;

            argumentVector++;

            // Convert the string representation to an internal representation.
            if (!SnmpMgrStrToOid(*argumentVector, &reqObject))
                {
                printf("Error: Invalid oid, %s, specified.\n", *argumentVector);

                return 1;
                }
            else
                {
                // Since sucessfull, add to the variable bindings list.
                variableBindings.len++;
                if ((variableBindings.list = (RFC1157VarBind *)SNMP_realloc(
                    variableBindings.list, sizeof(RFC1157VarBind) *
                    variableBindings.len)) == NULL)
                    {
                    printf("Error: Error allocating oid, %s.\n",
                           *argumentVector);

                    return 1;
                    }

                variableBindings.list[variableBindings.len - 1].name =
                    reqObject; // NOTE!  structure copy
                variableBindings.list[variableBindings.len - 1].value.asnType =
                    ASN_NULL;
                }
            } // end while()

        // Make sure only one variable binding was specified if operation
        // is WALK.
        if (operation == WALK && variableBindings.len != 1)
            {
            printf("Error: Multiple oids specified for WALK.\n");

            return 1;
            }

        // Establish a SNMP session to communicate with the remote agent.  The
        // community, communications timeout, and communications retry count
        // for the session are also required.

        if ((session = SnmpMgrOpen(agent, community, timeout, retries)) == NULL)
            {
            printf("error on SnmpMgrOpen %d\n", GetLastError());

            return 1;
            }

        } // end if(TRAP)

    // Determine and perform the requested operation.

    if      (operation == GET || operation == GETNEXT)
        {
        // Get and GetNext are relatively simple operations to perform.
        // Simply initiate the request and process the result and/or
        // possible error conditions.

        if (operation == GET)
            requestType = ASN_RFC1157_GETREQUEST;
        else
            requestType = ASN_RFC1157_GETNEXTREQUEST;

        // Request that the API carry out the desired operation.

        if (!SnmpMgrRequest(session, requestType, &variableBindings,
                            &errorStatus, &errorIndex))
            {
            // The API is indicating an error.

            printf("error on SnmpMgrRequest %d\n", GetLastError());
            }
        else
            {
            // The API succeeded, errors may be indicated from the remote
            // agent.

            if (errorStatus > 0)
                {
                printf("Error: errorStatus=%d, errorIndex=%d\n",
                       errorStatus, errorIndex);
                }
            else
                {
                // Display the resulting variable bindings.

                UINT i;
                char *string = NULL;

                for(i=0; i < variableBindings.len; i++)
                    {
                    SnmpMgrOidToStr(&variableBindings.list[i].name, &string);
                    printf("Variable = %s\n", string);
                    if (string) SNMP_free(string);

                    printf("Value    = ");
                    SnmpUtilPrintAsnAny(&variableBindings.list[i].value);

                    printf("\n");
                    } // end for()
                }
            }

        // Free the variable bindings that have been allocated.

        SnmpUtilVarBindListFree(&variableBindings);

        }
    else if (operation == WALK)
        {
        // Walk is a common term used to indicate that all MIB variables
        // under a given OID are to be traversed and displayed.  This is
        // a more complex operation requiring tests and looping in addition
        // to the steps for get/getnext above.

        AsnObjectIdentifier root;
        AsnObjectIdentifier tempOid;

        SnmpUtilOidCpy(&root, &variableBindings.list[0].name);

        requestType = ASN_RFC1157_GETNEXTREQUEST;

        while(1)
            {
            if (!SnmpMgrRequest(session, requestType, &variableBindings,
                                &errorStatus, &errorIndex))
                {
                // The API is indicating an error.

                printf("error on SnmpMgrRequest %d\n", GetLastError());

                break;
                }
            else
                {
                // The API succeeded, errors may be indicated from the remote
                // agent.

                // Test for end of subtree or end of MIB.

                if (errorStatus == SNMP_ERRORSTATUS_NOSUCHNAME ||
                    SnmpUtilOidNCmp(&variableBindings.list[0].name,
                                    &root, root.idLength))
                    {
                    printf("End of MIB subtree.\n\n");

                    break;
                    }

                // Test for general error conditions or sucesss.

                if (errorStatus > 0)
                    {
                    printf("Error: errorStatus=%d, errorIndex=%d \n",
                           errorStatus, errorIndex);

                    break;
                    }
                else
                    {
                    // Display resulting variable binding for this iteration.

                    char *string = NULL;

                    SnmpMgrOidToStr(&variableBindings.list[0].name, &string);
                    printf("Variable = %s\n", string);
                    if (string) SNMP_free(string);

                    printf("Value    = ");
                    SnmpUtilPrintAsnAny(&variableBindings.list[0].value);

                    printf("\n");
                    }
                } // end if()

            // Prepare for the next iteration.  Make sure returned oid is
            // preserved and the returned value is freed.

            SnmpUtilOidCpy(&tempOid, &variableBindings.list[0].name);

            SnmpUtilVarBindFree(&variableBindings.list[0]);

            SnmpUtilOidCpy(&variableBindings.list[0].name, &tempOid);
            variableBindings.list[0].value.asnType = ASN_NULL;

            SnmpUtilOidFree(&tempOid);

            } // end while()

        // Free the variable bindings that have been allocated.

        SnmpUtilVarBindListFree(&variableBindings);

        SnmpUtilOidFree(&root);

        }
    else if (operation == TRAP)
        {
        // Trap handling can be done two different ways: event driven or
        // polled.  The following code illustrates the steps to use event
        // driven trap reception in a management application.

        HANDLE hNewTraps = NULL;

        if (!SnmpMgrTrapListen(&hNewTraps))
            {
            printf("error on SnmpMgrTrapListen %d\n", GetLastError());
            }
        else
            {
            printf("snmputil: listening for traps...\n");
            }

        while(1)
            {
            DWORD dwResult;

            if ((dwResult = WaitForSingleObject(hNewTraps, 0xffffffff))
                == 0xffffffff)
                {
                printf("error on WaitForSingleObject %d\n",
                       GetLastError());
                }
            else if (!ResetEvent(hNewTraps))
                {
                printf("error on ResetEvent %d\n", GetLastError());
                }
            else
                {
                AsnObjectIdentifier enterprise;
                AsnNetworkAddress   IPAddress;
                AsnInteger          genericTrap;
                AsnInteger          specificTrap;
                AsnTimeticks        timeStamp;
                RFC1157VarBindList  variableBindings;

                UINT i;
                char *string = NULL;

                while(SnmpMgrGetTrap(&enterprise, &IPAddress, &genericTrap,
                                     &specificTrap, &timeStamp, &variableBindings))
                    {
                    printf("snmputil: trap generic=%d specific=%d\n",
                           genericTrap, specificTrap);
                    if (IPAddress.length == 4) {
                        printf("  from -> %d.%d.%d.%d\n",
                             (int)IPAddress.stream[0], (int)IPAddress.stream[1],
                             (int)IPAddress.stream[2], (int)IPAddress.stream[3]);

                    }
                    if (IPAddress.dynamic) {
                        SNMP_free(IPAddress.stream);
                    }

                    for(i=0; i < variableBindings.len; i++)
                        {
                        SnmpMgrOidToStr(&variableBindings.list[i].name, &string);
                        printf("Variable = %s\n", string);
                        if (string) SNMP_free(string);

                        printf("Value    = ");
                        SnmpUtilPrintAsnAny(&variableBindings.list[i].value);
                        } // end for()
                    printf("\n");

                    SnmpUtilOidFree(&enterprise);

                    SnmpUtilVarBindListFree(&variableBindings);
                    }
                }
            } // end while()

        } // end if(operation)

    if (operation != TRAP)
        {
        // Close SNMP session with the remote agent.

        if (!SnmpMgrClose(session))
            {
            printf("error on SnmpMgrClose %d\n", GetLastError());

            return 1;
            }
        }

    // Let the command interpreter know things went ok.

    return 0;

    } // end main()


TESTDLL.C

/*++ BUILD Version: 0001    // Increment this if a change has global effects

Copyright (c) 1992-1996  Microsoft Corporation

Module Name:

    testdll.c

Abstract:

    Sample SNMP Extension Agent for Windows NT.

    These files (testdll.c, testmib.c, and testmib.h) provide an example of
    how to structure an Extension Agent DLL which works in conjunction with
    the SNMP Extendible Agent for Windows NT.

    Extensive comments have been included to describe its structure and
    operation.  See also "Microsoft Windows NT SNMP Programmer's Reference".

--*/

// General notes:
//
//   Microsoft's Extendible Agent for Windows NT is implemented by dynamically
// linking to Extension Agent DLLs that implement portions of the MIB.  These
// Extension Agents are configured in the Windows NT Registration Database.
// When the Extendible Agent Service is started, it queries the registry to
// determine which Extension Agent DLLs have been installed and need to be
// loaded and initialized.  The Extendible Agent invokes various DLL entry
// points (examples follow in this file) to request MIB queries and obtain
// Extension Agent generated traps.

// Necessary includes.

#include <windows.h>

#include <snmp.h>

// Contains definitions for the table structure describing the MIB.  This
// is used in conjunction with testmib.c where the MIB requests are resolved.

#include "testmib.h"

// Extension Agent DLLs need access to elapsed time agent has been active.
// This is implemented by initializing the Extension Agent with a time zero
// reference, and allowing the agent to compute elapsed time by subtracting
// the time zero reference from the current system time.  This example
// Extension Agent implements this reference with dwTimeZero.

DWORD dwTimeZero = 0;

// Extension Agent DLLs that generate traps must create a Win32 Event object
// to communicate occurence of traps to the Extendible Agent.  The event
// handle is given to the Extendible Agent when the Extension Agent is
// initialized, it should be NULL if traps will not be generated.  This
// example Extension Agent simulates the occurance of traps with hSimulateTrap.

HANDLE hSimulateTrap = NULL;

// This is a standard Win32 DLL entry point.  See the Win32 SDK for more
// information on its arguments and their meanings.  This example DLL does
// not perform any special actions using this mechanism.

BOOL WINAPI DllMain(
    HANDLE hDll,
    DWORD  dwReason,
    LPVOID lpReserved)
    {
    switch(dwReason)
        {
        case DLL_PROCESS_ATTACH:
        case DLL_PROCESS_DETACH:
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        default:
            break;

        } // end switch()

    return TRUE;

    } // end DllEntryPoint()

// Extension Agent DLLs provide the following entry point to coordinate the
// initializations of the Extension Agent and the Extendible Agent.  The
// Extendible Agent provides the Extension Agent with a time zero reference;
// and the Extension Agent provides the Extendible Agent with an Event handle
// for communicating occurence of traps, and an object identifier representing
// the root of the MIB subtree that the Extension Agent supports.

BOOL WINAPI SnmpExtensionInit(
    IN  DWORD               dwTimeZeroReference,
    OUT HANDLE              *hPollForTrapEvent,
    OUT AsnObjectIdentifier *supportedView)
    {

    // Record the time reference provided by the Extendible Agent.

    dwTimeZero = dwTimeZeroReference;

    // Create an Event that will be used to communicate the occurence of traps
    // to the Extendible Agent.  The Extension Agent will assert this Event
    // when a trap has occured.  This is explained further later in this file.

    if ((*hPollForTrapEvent = CreateEvent(NULL, FALSE, FALSE, NULL)) == NULL)
        {
        // Indicate error?, be sure that NULL is returned to Extendible Agent.
        }

    // Indicate the MIB view supported by this Extension Agent, an object
    // identifier representing the sub root of the MIB that is supported.

    *supportedView = MIB_OidPrefix; // NOTE!  structure copy

    // Record the trap Event.  This example Extension Agent simulates traps by
    // generating a trap after every given number of processed requests.

    hSimulateTrap = *hPollForTrapEvent;

    // Indicate that Extension Agent initialization was sucessfull.

    return TRUE;

    } // end SnmpExtensionInit()

// Extension Agent DLLs provide the following entry point to communcate traps
// to the Extendible Agent.  The Extendible Agent will query this entry point
// when the trap Event (supplied at initialization time) is asserted, which
// indicates that zero or more traps may have occured.  The Extendible Agent
// will repetedly call this entry point until FALSE is returned, indicating
// that all outstanding traps have been processed.

BOOL WINAPI SnmpExtensionTrap(
    OUT AsnObjectIdentifier *enterprise,
    OUT AsnInteger          *genericTrap,
    OUT AsnInteger          *specificTrap,
    OUT AsnTimeticks        *timeStamp,
    OUT RFC1157VarBindList  *variableBindings)
    {
    // The body of this routine is an extremely simple example/simulation of
    // the trap functionality.  A real implementation will be more complex.

    // The following define data inserted into the trap below.  The Lan Manager
    // bytesAvailAlert from the Lan Manager Alerts-2 MIB is generated with an
    // empty variable bindings list.

    static UINT OidList[]  = { 1, 3, 6, 1, 4, 1, 77, 2 };
    static UINT OidListLen = 8;

    // The following variable is used for the simulation, it allows a single
    // trap to be generated and then causes FALSE to be returned indicating
    // no more traps.

    static whichTime = 0;

    // The following if/else support the simulation.

    if (whichTime == 0)
        {
        whichTime = 1;    // Supports the simulation.

        // Communicate the trap data to the Extendible Agent.

        enterprise->idLength = OidListLen;
        enterprise->ids = OidList;

        *genericTrap      = SNMP_GENERICTRAP_ENTERSPECIFIC;

        *specificTrap     = 1;                    // the bytesAvailAlert trap

        *timeStamp        = GetCurrentTime() - dwTimeZero;

        variableBindings->list = NULL;
        variableBindings->len  = 0;

        // Indicate that valid trap data exists in the parameters.

        return TRUE;
        }
    else
        {
        whichTime = 0;    // Supports the simulation.

        // Indicate that no more traps are available and parameters do not
        // refer to any valid data.

        return FALSE;
        }

    } // end SnmpExtensionTrap()

// Extension Agent DLLs provide the following entry point to resolve queries
// for MIB variables in their supported MIB view (supplied at initialization
// time).  The requestType is Get/GetNext/Set.

BOOL WINAPI SnmpExtensionQuery(
    IN BYTE                   requestType,
    IN OUT RFC1157VarBindList *variableBindings,
    OUT AsnInteger            *errorStatus,
    OUT AsnInteger            *errorIndex)
    {
    static unsigned long requestCount = 0;  // Supports the trap simulation.
    UINT    I;

    // Iterate through the variable bindings list to resolve individual
    // variable bindings.

    for ( I=0;I < variableBindings->len;I++ )
        {
        *errorStatus = ResolveVarBind( &variableBindings->list[I],
                                       requestType );

        // Test and handle case where Get Next past end of MIB view supported
        // by this Extension Agent occurs.  Special processing is required to
        // communicate this situation to the Extendible Agent so it can take
        // appropriate action, possibly querying other Extension Agents.

        if ( *errorStatus == SNMP_ERRORSTATUS_NOSUCHNAME &&
             requestType == MIB_ACTION_GETNEXT )
           {
           *errorStatus = SNMP_ERRORSTATUS_NOERROR;

           // Modify variable binding of such variables so the OID points
           // just outside the MIB view supported by this Extension Agent.
           // The Extendible Agent tests for this, and takes appropriate
           // action.

           SnmpUtilOidFree( &variableBindings->list[I].name );
           SnmpUtilOidCpy( &variableBindings->list[I].name, &MIB_OidPrefix );
           variableBindings->list[I].name.ids[MIB_PREFIX_LEN-1] ++;
           }

        // If an error was indicated, communicate error status and error
        // index to the Extendible Agent.  The Extendible Agent will ensure
        // that the origional variable bindings are returned in the response
        // packet.

        if ( *errorStatus != SNMP_ERRORSTATUS_NOERROR )
           {
   *errorIndex = I+1;
   goto Exit;
   }
        }

Exit:

    // Supports the trap simulation.

    if (++requestCount % 3 == 0 && hSimulateTrap != NULL)
        SetEvent(hSimulateTrap);

    // Indicate that Extension Agent processing was sucessfull.

    return SNMPAPI_NOERROR;

    } // end SnmpExtensionQuery()

//-------------------------------- END --------------------------------------


TESTMIB.C

/*++ BUILD Version: 0001    // Increment this if a change has global effects

Copyright (c) 1992-1996  Microsoft Corporation

Module Name:

    testmib.c

Abstract:

    Sample SNMP Extension Agent for Windows NT.

    These files (testdll.c, testmib.c, and testmib.h) provide an example of
    how to structure an Extension Agent DLL which works in conjunction with
    the SNMP Extendible Agent for Windows NT.

    Extensive comments have been included to describe its structure and
    operation.  See also "Microsoft Windows NT SNMP Programmer's Reference".

--*/

// This Extension Agent implements the Internet toaster MIB.  It's
// definition follows here:
//
//
//         TOASTER-MIB DEFINITIONS ::= BEGIN
//
//         IMPORTS
//                 enterprises
//                         FROM RFC1155-SMI
//                 OBJECT-TYPE
//                         FROM RFC-1212
//                 DisplayString
//                         FROM RFC-1213;
//
//         epilogue        OBJECT IDENTIFIER ::= { enterprises 12 }
//         toaster         OBJECT IDENTIFIER ::= { epilogue 2 }
//
//         -- toaster MIB
//
//         toasterManufacturer OBJECT-TYPE
//             SYNTAX  DisplayString
//             ACCESS  read-only
//             STATUS  mandatory
//             DESCRIPTION
//                     "The name of the toaster's manufacturer. For instance,
//                      Sunbeam."
//             ::= { toaster 1 }
//
//         toasterModelNumber OBJECT-TYPE
//             SYNTAX  DisplayString
//             ACCESS  read-only
//             STATUS  mandatory
//             DESCRIPTION
//                     "The name of the toaster's model. For instance,
//                      Radiant Automatic."
//             ::= { toaster 2 }
//
//         toasterControl OBJECT-TYPE
//             SYNTAX  INTEGER  {
//                         up(1),
//                         down(2)
//                     }
//             ACCESS  read-write
//             STATUS  mandatory
//             DESCRIPTION
//                     "This variable controls the current state of the
//                      toaster. To begin toasting, set it to down(2). To
//                      abort toasting (perhaps in the event of an
//                      emergency), set it to up(2)."
//             ::= { toaster 3 }
//
//         toasterDoneness OBJECT-TYPE
//             SYNTAX  INTEGER (1..10)
//             ACCESS  read-write
//             STATUS  mandatory
//             DESCRIPTION
//                     "This variable controls how well done ensuing toast
//                      should be on a scale of 1 to 10. Toast made at 10
//                      is generally considered unfit for human consumption;
//                      toast made at 1 is lightly warmed."
//             ::= { toaster 4 }
//
//         toasterToastType OBJECT-TYPE
//             SYNTAX  INTEGER  {
//                         white-bread(1),
//                         wheat-bread(2),
//                         wonder-bread(3),
//                         frozen-waffle(4),
//                         frozen-bagel(5),
//                         hash-brown(6),
//                         other(7)
//                     }
//             ACCESS  read-write
//             STATUS  mandatory
//             DESCRIPTION
//                     "This variable informs the toaster of the type of
//                      material being toasted. The toaster uses this
//                      information combined with toasterToastDoneness to
//                      compute how long the material must be toasted for
//                      to achieve the desired doneness."
//             ::= { toaster 5 }
//
//         END

// Necessary includes.

#include <windows.h>

#include <snmp.h>

// Contains definitions for the table structure describing the MIB.  This
// is used in conjunction with testmib.c where the MIB requests are resolved.

#include "testmib.h"

// If an addition or deletion to the MIB is necessary, there are several
// places in the code that must be checked and possibly changed.
//
// The last field in each MIB entry is used to point to the NEXT
// leaf variable.  If an addition or deletetion is made, these pointers
// may need to be updated to reflect the modification.

// The prefix to all of these MIB variables is 1.3.6.1.4.1.12

UINT OID_Prefix[] = { 1, 3, 6, 1, 4, 1, 12 };
AsnObjectIdentifier MIB_OidPrefix = { OID_SIZEOF(OID_Prefix), OID_Prefix };

//                         //
// OID definitions for MIB //
//                         //

// Definition of the toaster group

UINT MIB_toaster[]  = { 2 };

// Definition of leaf variables under the toaster group
// All leaf variables have a zero appended to their OID to indicate
// that it is the only instance of this variable and it exists.

UINT MIB_toasterManufacturer[]     = { 2, 1, 0 };
UINT MIB_toasterModelNumber[]      = { 2, 2, 0 };
UINT MIB_toasterControl[]          = { 2, 3, 0 };
UINT MIB_toasterDoneness[]         = { 2, 4, 0 };
UINT MIB_toasterToastType[]        = { 2, 5, 0 };

//                             //
// Storage definitions for MIB //
//                             //

char       MIB_toasterManStor[]     = "Microsoft Corporation";
char       MIB_toasterModelStor[]   =
               "Example SNMP Extension Agent for Windows/NT (TOASTER-MIB).";
AsnInteger MIB_toasterControlStor   = 1;
AsnInteger MIB_toasterDonenessStor  = 2;
AsnInteger MIB_toasterToastTypeStor = 3;

// MIB definiton

MIB_ENTRY Mib[] = {
      { { OID_SIZEOF(MIB_toasterManufacturer), MIB_toasterManufacturer },
        &MIB_toasterManStor, ASN_RFC1213_DISPSTRING,
        MIB_ACCESS_READ, MIB_leaf_func, &Mib[1] },

      { { OID_SIZEOF(MIB_toasterModelNumber), MIB_toasterModelNumber },
        &MIB_toasterModelStor, ASN_RFC1213_DISPSTRING,
        MIB_ACCESS_READ, MIB_leaf_func, &Mib[2] },

      { { OID_SIZEOF(MIB_toasterControl), MIB_toasterControl },
        &MIB_toasterControlStor, ASN_INTEGER,
        MIB_ACCESS_READWRITE, MIB_control_func, &Mib[3] },

      { { OID_SIZEOF(MIB_toasterDoneness), MIB_toasterDoneness },
        &MIB_toasterDonenessStor, ASN_INTEGER,
        MIB_ACCESS_READWRITE, MIB_doneness_func, &Mib[4] },

      { { OID_SIZEOF(MIB_toasterToastType), MIB_toasterToastType },
        &MIB_toasterToastTypeStor, ASN_INTEGER,
        MIB_ACCESS_READWRITE, MIB_toasttype_func, NULL }
      };

UINT MIB_num_variables = sizeof Mib / sizeof( MIB_ENTRY );

//
// ResolveVarBind
//    Resolves a single variable binding.  Modifies the variable on a GET
//    or a GET-NEXT.
//
// Notes:
//
// Return Codes:
//    Standard PDU error codes.
//
// Error Codes:
//    None.
//
UINT ResolveVarBind(
        IN OUT RFC1157VarBind *VarBind, // Variable Binding to resolve
IN UINT PduAction               // Action specified in PDU
)

{
MIB_ENTRY            *MibPtr;
AsnObjectIdentifier  TempOid;
int                  CompResult;
UINT                 I;
UINT                 nResult;

   // Search for var bind name in the MIB
   I      = 0;
   MibPtr = NULL;
   while ( MibPtr == NULL && I < MIB_num_variables )
      {
      // Construct OID with complete prefix for comparison purposes
      SnmpUtilOidCpy( &TempOid, &MIB_OidPrefix );
      SnmpUtilOidAppend( &TempOid, &Mib[I].Oid );

      // Check for OID in MIB - On a GET-NEXT the OID does not have to exactly
      // match a variable in the MIB, it must only fall under the MIB root.
      CompResult = SnmpUtilOidCmp( &VarBind->name, &TempOid );
      if ( 0 > CompResult )
 {
 // Since there is not an exact match, the only valid action is GET-NEXT
 if ( MIB_ACTION_GETNEXT != PduAction )
    {
    nResult = SNMP_ERRORSTATUS_NOSUCHNAME;
    goto Exit;
    }

 // Since the match was not exact, but var bind name is within MIB,
 // we are at the NEXT MIB variable down from the one specified.
 PduAction = MIB_ACTION_GET;
 MibPtr = &Mib[I];

         // Replace var bind name with new name
         SnmpUtilOidFree( &VarBind->name );
         SnmpUtilOidCpy( &VarBind->name, &MIB_OidPrefix );
         SnmpUtilOidAppend( &VarBind->name, &MibPtr->Oid );
 }
      else
         {
 // An exact match was found.
         if ( 0 == CompResult )
            {
    MibPtr = &Mib[I];
    }
 }

      // Free OID memory before checking another variable
      SnmpUtilOidFree( &TempOid );

      I++;
      } // while

   // If OID not within scope of MIB, then no such name
   if ( MibPtr == NULL )
      {
      nResult = SNMP_ERRORSTATUS_NOSUCHNAME;
      goto Exit;
      }

   // Call function to process request.  Each MIB entry has a function pointer
   // that knows how to process its MIB variable.
   nResult = (*MibPtr->MibFunc)( PduAction, MibPtr, VarBind );

   // Free temp memory
   SnmpUtilOidFree( &TempOid );

Exit:
   return nResult;
} // ResolveVarBind

//
// MIB_leaf_func
//    Performs generic actions on LEAF variables in the MIB.
//
// Notes:
//
// Return Codes:
//    Standard PDU error codes.
//
// Error Codes:
//    None.
//
UINT MIB_leaf_func(
        IN UINT Action,
IN MIB_ENTRY *MibPtr,
IN RFC1157VarBind *VarBind
)

{
UINT   ErrStat;

   switch ( Action )
      {
      case MIB_ACTION_GETNEXT:
 // If there is no GET-NEXT pointer, this is the end of this MIB
 if ( MibPtr->MibNext == NULL )
    {
    ErrStat = SNMP_ERRORSTATUS_NOSUCHNAME;
    goto Exit;
    }

         // Setup var bind name of NEXT MIB variable
         SnmpUtilOidFree( &VarBind->name );
         SnmpUtilOidCpy( &VarBind->name, &MIB_OidPrefix );
         SnmpUtilOidAppend( &VarBind->name, &MibPtr->MibNext->Oid );

         // Call function to process request.  Each MIB entry has a function
 // pointer that knows how to process its MIB variable.
         ErrStat = (*MibPtr->MibNext->MibFunc)( MIB_ACTION_GET,
                                        MibPtr->MibNext, VarBind );
         break;

      case MIB_ACTION_GET:
         // Make sure that this variable's ACCESS is GET'able
 if ( MibPtr->Access != MIB_ACCESS_READ &&
      MibPtr->Access != MIB_ACCESS_READWRITE )
    {
    ErrStat = SNMP_ERRORSTATUS_NOSUCHNAME;
    goto Exit;
    }

 // Setup varbind's return value
 VarBind->value.asnType = MibPtr->Type;
 switch ( VarBind->value.asnType )
    {
            case ASN_RFC1155_COUNTER:
            case ASN_RFC1155_GAUGE:
            case ASN_INTEGER:
               VarBind->value.asnValue.number = *(AsnInteger *)(MibPtr->Storage);
       break;

            case ASN_OCTETSTRING: // This entails ASN_RFC1213_DISPSTRING also
       VarBind->value.asnValue.string.length =
                                 strlen( (LPSTR)MibPtr->Storage );

       if ( NULL ==
                    (VarBind->value.asnValue.string.stream =
                    SnmpUtilMemAlloc(VarBind->value.asnValue.string.length *
                           sizeof(char))) )
          {
          ErrStat = SNMP_ERRORSTATUS_GENERR;
          goto Exit;
          }

       memcpy( VarBind->value.asnValue.string.stream,
               (LPSTR)MibPtr->Storage,
               VarBind->value.asnValue.string.length );
       VarBind->value.asnValue.string.dynamic = TRUE;

       break;

    default:
       ErrStat = SNMP_ERRORSTATUS_GENERR;
       goto Exit;
    }

 break;

      case MIB_ACTION_SET:
         // Make sure that this variable's ACCESS is SET'able
 if ( MibPtr->Access != MIB_ACCESS_READWRITE &&
      MibPtr->Access != MIB_ACCESS_WRITE )
    {
    ErrStat = SNMP_ERRORSTATUS_NOSUCHNAME;
            goto Exit;
    }

         // Check for proper type before setting
         if ( MibPtr->Type != VarBind->value.asnType )
    {
    ErrStat = SNMP_ERRORSTATUS_BADVALUE;
    goto Exit;
    }

 // Save value in MIB
 switch ( VarBind->value.asnType )
    {
            case ASN_RFC1155_COUNTER:
            case ASN_RFC1155_GAUGE:
            case ASN_INTEGER:
               *(AsnInteger *)(MibPtr->Storage) = VarBind->value.asnValue.number;
       break;

            case ASN_OCTETSTRING: // This entails ASN_RFC1213_DISPSTRING also
               // The storage must be adequate to contain the new string
               // including a NULL terminator.
               memcpy( (LPSTR)MibPtr->Storage,
                       VarBind->value.asnValue.string.stream,
                       VarBind->value.asnValue.string.length );

       ((LPSTR)MibPtr->Storage)[VarBind->value.asnValue.string.length] =
                                                                          '\0';
       break;

    default:
       ErrStat = SNMP_ERRORSTATUS_GENERR;
       goto Exit;
    }

         break;

      default:
 ErrStat = SNMP_ERRORSTATUS_GENERR;
 goto Exit;
      } // switch

   // Signal no error occurred
   ErrStat = SNMP_ERRORSTATUS_NOERROR;

Exit:
   return ErrStat;
} // MIB_leaf_func

//
// MIB_control_func
//    Performs specific actions on the toasterControl MIB variable
//
// Notes:
//
// Return Codes:
//    Standard PDU error codes.
//
// Error Codes:
//    None.
//
UINT MIB_control_func(
        IN UINT Action,
IN MIB_ENTRY *MibPtr,
IN RFC1157VarBind *VarBind
)

{
UINT   ErrStat;

   switch ( Action )
      {
      case MIB_ACTION_SET:
         // Make sure that this variable's ACCESS is SET'able
 if ( MibPtr->Access != MIB_ACCESS_READWRITE &&
      MibPtr->Access != MIB_ACCESS_WRITE )
    {
    ErrStat = SNMP_ERRORSTATUS_NOSUCHNAME;
            goto Exit;
    }

         // Check for proper type before setting
         if ( MibPtr->Type != VarBind->value.asnType )
    {
    ErrStat = SNMP_ERRORSTATUS_BADVALUE;
    goto Exit;
    }

         // Make sure the value is valid
         if ( MIB_TOASTER_UP > VarBind->value.asnValue.number ||
              MIB_TOASTER_DOWN < VarBind->value.asnValue.number )
            {
    ErrStat = SNMP_ERRORSTATUS_BADVALUE;
    goto Exit;
            }

         // Let fall through purposefully for further processing by
         // generic leaf function.

      case MIB_ACTION_GETNEXT:
      case MIB_ACTION_GET:
 // Call the more generic function to perform the action
         ErrStat = MIB_leaf_func( Action, MibPtr, VarBind );
         break;

      default:
 ErrStat = SNMP_ERRORSTATUS_GENERR;
 goto Exit;
      } // switch

Exit:
   return ErrStat;
} // MIB_control_func

//
// MIB_doneness_func
//    Performs specific actions on the toasterDoneness MIB variable
//
// Notes:
//
// Return Codes:
//    Standard PDU error codes.
//
// Error Codes:
//    None.
//
UINT MIB_doneness_func(
        IN UINT Action,
IN MIB_ENTRY *MibPtr,
IN RFC1157VarBind *VarBind
)

{
UINT   ErrStat;

   switch ( Action )
      {
      case MIB_ACTION_SET:
         // Make sure that this variable's ACCESS is SET'able
 if ( MibPtr->Access != MIB_ACCESS_READWRITE &&
      MibPtr->Access != MIB_ACCESS_WRITE )
    {
    ErrStat = SNMP_ERRORSTATUS_NOSUCHNAME;
            goto Exit;
    }

         // Check for proper type before setting
         if ( MibPtr->Type != VarBind->value.asnType )
    {
    ErrStat = SNMP_ERRORSTATUS_BADVALUE;
    goto Exit;
    }

         // Make sure the value is valid
         if ( MIB_TOASTER_LIGHTLYWARM > VarBind->value.asnValue.number ||
              MIB_TOASTER_BURNT < VarBind->value.asnValue.number )
            {
    ErrStat = SNMP_ERRORSTATUS_BADVALUE;
    goto Exit;
            }

         // Let fall through purposefully for further processing by
         // generic leaf function.

      case MIB_ACTION_GETNEXT:
      case MIB_ACTION_GET:
 // Call the more generic function to perform the action
         ErrStat = MIB_leaf_func( Action, MibPtr, VarBind );
         break;

      default:
 ErrStat = SNMP_ERRORSTATUS_GENERR;
 goto Exit;
      } // switch

Exit:
   return ErrStat;
} // MIB_doneness_func

//
// MIB_toasttype_func
//    Performs specific actions on the toasterToastType MIB variable
//
// Notes:
//
// Return Codes:
//    Standard PDU error codes.
//
// Error Codes:
//    None.
//
UINT MIB_toasttype_func(
        IN UINT Action,
IN MIB_ENTRY *MibPtr,
IN RFC1157VarBind *VarBind
)

{
UINT   ErrStat;

   switch ( Action )
      {
      case MIB_ACTION_SET:
         // Make sure that this variable's ACCESS is SET'able
 if ( MibPtr->Access != MIB_ACCESS_READWRITE &&
      MibPtr->Access != MIB_ACCESS_WRITE )
    {
    ErrStat = SNMP_ERRORSTATUS_NOSUCHNAME;
            goto Exit;
    }

         // Check for proper type before setting
         if ( MibPtr->Type != VarBind->value.asnType )
    {
    ErrStat = SNMP_ERRORSTATUS_BADVALUE;
    goto Exit;
    }

         // Make sure the value is valid
         if ( MIB_TOASTER_WHITEBREAD > VarBind->value.asnValue.number ||
              MIB_TOASTER_OTHERBREAD < VarBind->value.asnValue.number )
            {
    ErrStat = SNMP_ERRORSTATUS_BADVALUE;
    goto Exit;
            }

         // Let fall through purposefully for further processing by
         // generic leaf function.

      case MIB_ACTION_GETNEXT:
      case MIB_ACTION_GET:
 // Call the more generic function to perform the action
         ErrStat = MIB_leaf_func( Action, MibPtr, VarBind );
         break;

      default:
 ErrStat = SNMP_ERRORSTATUS_GENERR;
 goto Exit;
      } // switch

Exit:
   return ErrStat;
} // MIB_toasttype_func


TESTMIB.H

/*++ BUILD Version: 0001    // Increment this if a change has global effects

Copyright (c) 1992-1996  Microsoft Corporation

Module Name:

    testmib.h

Abstract:

    Sample SNMP Extension Agent for Windows NT.

    These files (testdll.c, testmib.c, and testmib.h) provide an example of
    how to structure an Extension Agent DLL which works in conjunction with
    the SNMP Extendible Agent for Windows NT.

    Extensive comments have been included to describe its structure and
    operation.  See also "Microsoft Windows NT SNMP Programmer's Reference".

--*/

#ifndef testmib_h
#define testmib_h

// Necessary includes.

#include <snmp.h>

// MIB Specifics.

#define MIB_PREFIX_LEN            MIB_OidPrefix.idLength
#define MAX_STRING_LEN            255

// Ranges and limits for specific MIB variables.

#define MIB_TOASTER_UP            1
#define MIB_TOASTER_DOWN          2

#define MIB_TOASTER_LIGHTLYWARM   1
#define MIB_TOASTER_BURNT         10

#define MIB_TOASTER_WHITEBREAD    1
#define MIB_TOASTER_OTHERBREAD    7

// MIB function actions.

#define MIB_ACTION_GET         ASN_RFC1157_GETREQUEST
#define MIB_ACTION_SET         ASN_RFC1157_SETREQUEST
#define MIB_ACTION_GETNEXT     ASN_RFC1157_GETNEXTREQUEST

// MIB Variable access privileges.

#define MIB_ACCESS_READ        0
#define MIB_ACCESS_WRITE       1
#define MIB_ACCESS_READWRITE   2

// Macro to determine number of sub-oid's in array.

#define OID_SIZEOF( Oid )      ( sizeof Oid / sizeof(UINT) )

// MIB variable ENTRY definition.  This structure defines the format for
// each entry in the MIB.

typedef struct mib_entry
           {
   AsnObjectIdentifier Oid;
   void *              Storage;
   BYTE                Type;
   UINT                Access;
   UINT                (*MibFunc)( UINT, struct mib_entry *,
                                   RFC1157VarBind * );
   struct mib_entry *  MibNext;
   } MIB_ENTRY;

// Internal MIB structure.

extern MIB_ENTRY Mib[];
extern UINT      MIB_num_variables;

// Prefix to every variable in the MIB.

extern AsnObjectIdentifier MIB_OidPrefix;

// Function Prototypes.

UINT ResolveVarBind(
        IN OUT RFC1157VarBind *VarBind, // Variable Binding to resolve
IN UINT PduAction               // Action specified in PDU
);

UINT MIB_leaf_func(
        IN UINT Action,
IN MIB_ENTRY *MibPtr,
IN RFC1157VarBind *VarBind
);

UINT MIB_control_func(
        IN UINT Action,
IN MIB_ENTRY *MibPtr,
IN RFC1157VarBind *VarBind
);

UINT MIB_doneness_func(
        IN UINT Action,
IN MIB_ENTRY *MibPtr,
IN RFC1157VarBind *VarBind
);

UINT MIB_toasttype_func(
        IN UINT Action,
IN MIB_ENTRY *MibPtr,
IN RFC1157VarBind *VarBind
);

#endif /* testmib_h */


README.TXT

TAPI ACD Samples
=======================================================================
ACDSMPL and ACDCLNT implement a simple ACD call center.  The purpose of
these samples is to highlight some of the new call center features of
TAPI 2.0.  This document will give an overview of some of the design
details of these two applications.

ACDSMPL
=======
ACDSMPL is the ACD server application.  It keeps track of groups and
agents, When calls come in on a group s line, ACDSMPL looks for an
agent to transfer the call to.  It also keeps track of agent state information.

Groups
------
In the sample, a group corresponds to a single address on a line
device.  When a call arrives on that address, the ACD server will look
for an agent that is a member of that group to take the call.  A group
has a list of agents that are allowed to log into it.

Agents
------
An agent corresponds to a person that has access to a single line. They
are allowed to use any address on that line. This implementation is
limited, however, because it only has one destination address for each
agent.  That is, when a call is to be transferred to the agent, there
is a single number that it will be transferred to, not a number for
each address.  Most implementations should allow for a destination
address for each address.

UI
--
The UI allows adding of agents and groups.  ACDSMPL automatically
writes out the agent and group information to ACDSMPL.INI when it shuts
down.  The File | Open command will read in the information from that
file.  See below for a description of the ini file.

The default UI view is the Group view.  The Group view shows each
group, and underneath them, every agent that is allowed to log into the
group.  If the agent is logged in, their user name will be in bold.
The Agent view shows each agent.  This view can be used to change
information about the Agent.

Security
--------
There is no security implemented in this sample.  Users are verified by
the user name that TAPI puts in each LINEPROXYREQUEST structure and the
line device.  ACDSMPL compares these with the information entered in
through the ACDSMPL UI.

The new TAPI features that ACDSMPL illustrates are:

New lineOpen functionality
Handling of the LINE_PROXYREQUEST message
New lineInitializeEx functionality.
Unicode

ACDCLNT
=======
ACDCLNT is the client application that works the ACDSMPL.  Using the
model from ACDSMPL, ACDCLNT assumes that the user can only have access
to one line.  Upon startup, it calls lineGetAgentCaps for each device.
When it finds a line device that lineAgentCaps succeeds on, it uses that
line device.

ACDCLNT allows the user to set state, group and activity information.
Also, when a call appears on its line, ACDCLNT will allow the user to
answer that call.

The new TAPI features that ACDCLNT illustrates are:

The new Agent functions:  lineGetAgentList, lineGetAgentStatus,
lineGetAgentCaps, lineGetAgentActivityList, lineSetAgentState,
lineSetAgentActivity, and lineSetAgentGroup.
Unicode
lineInitializeEx - using completion ports for callback mechanism

ACDSMPL.INI format
==================

[GENERAL] section
NumAgents=x
NumGroups=y
x and y are the number of agents and groups in this file

[GROUPS] section
GROUPx=name,perm device id,address id

x starts at 0 and increments for each group
name is the name of the group (cannot contain commas)
permanent device id is the permanent device id of the line device
associated with the group.
address id is the address id of the line that the group uses

[AGENTS] section
AGENTx=name,dest address,perm device id

x starts at 0 and increments for each group
name is the user name of the agent
dest address is the address that acdsmpl would transfer calls to that
agent
permanent device id is the permanent device id if the user's line

[GROUPx] section
each group has a section that indicates which agents are members of that group.  If the agent is in the group, the entries look like
AGENTy=1 where AGENTy corresponds to the agent key in the [AGENTS] section

EXAMPLE:

[Groups]
GROUP0=First Group,786435,0
GROUP1=Second Group,786435,1
GROUP2=Third Group,786436,0

[GROUP0]
AGENT0=1
AGENT1=1

[GROUP1]
AGENT0=1

[Agents]
AGENT0=scooter,3343,786432
AGENT1=frankie,3342,786433

[General]
NumGroups=3
NumAgents=2


ACDCLNT.C

//////////////////////////////////////////////////////////////////////////////
//
//  ACDCLNT.C
//
//  ACDClient app
//
//////////////////////////////////////////////////////////////////////////////
#include <windows.h>
#include <tapi.h>
#include "acdclnt.h"
#include "resource.h"

//////////////////////////////////////////////////////////////////////////////
//  PROTOTYPES
//////////////////////////////////////////////////////////////////////////////
static BOOL CreateMainWindow (int nCmdShow);

static LRESULT CALLBACK MainWndProc (HWND   hwnd,
                                     UINT   uMsg,
                                     WPARAM wParam,
                                     LPARAM lParam);

LRESULT CALLBACK AgentStateDlgProc (HWND   hwnd,
                                    UINT   uMsg,
                                    WPARAM wParam,
                                    LPARAM lParam);

VOID CALLBACK LineCallback (DWORD hDevice,
                            DWORD dwMsg,
                            DWORD dwCallbackInstance,
                            DWORD dwParam1,
                            DWORD dwParam2,
                            DWORD dwParam3);
BOOL InitTapi();

BOOL CloseTapi();

BOOL RedoWindow();

BOOL SetStatusMessage(LPTSTR lpszMessage);

BOOL SetButton(DWORD dwAddress,
               BOOL bAnswer,
               BOOL bEnable);

LRESULT WaitForLineReply();

LONG ThreadRoutine(LPVOID lpv);

//////////////////////////////////////////////////////////////////////////////
//
//  GLOBALS
//
//////////////////////////////////////////////////////////////////////////////
HINSTANCE       ghInstance;                 // main instance
HWND            ghMainWnd;                  // main window
PADDRESSINFO    pAddressInfo = NULL;        // array of info about each address
HLINEAPP        ghLineApp;                  // hlineapp
DWORD           gdwAddresses;               // number of addresses on our line
DWORD           gdwDeviceID;                // our device
HLINE           ghLine;                     // our line

HANDLE          ghCompletionPort;           // tapi message completionport
CRITICAL_SECTION csLineReply;

// using global variables to keep track of line
// replies, since the main thread will only have at most one outstanding
// line reply at a time
BOOL            gbReply;
LONG            glResult;

//////////////////////////////////////////////////////////////////////////////
//
// WinMain()
//
//////////////////////////////////////////////////////////////////////////////
int WINAPI WinMain (HINSTANCE hInstance,
                    HINSTANCE hPrevInstance,
                    LPSTR     lpszCmdLine,
                    int       nCmdShow)
{
    MSG msg;

    ghInstance = hInstance;

    if(!InitTapi())
    {
        MessageBox(NULL,
                   TEXT("Failed to initialize TAPI"),
                   TEXT("Cannot start ACDClient"),
                   MB_OK);

        return 0;
    }

    if (!CreateMainWindow(nCmdShow))
    {
        return 0;
    }

    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return 1;
}

///////////////////////////////////////////////////////////////////////////////
//
// CreateMainWindow()
//
///////////////////////////////////////////////////////////////////////////////
BOOL CreateMainWindow (int nCmdShow)
{

    // main window
    ghMainWnd = CreateDialog(ghInstance,
                             MAKEINTRESOURCE(IDD_MAINDLG),
                             NULL,
                             MainWndProc);

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

    SetStatusMessage(TEXT("Waiting for call"));

    // create buttons
    RedoWindow();

    ShowWindow(ghMainWnd, nCmdShow);

    UpdateWindow(ghMainWnd);

    return TRUE;
}

/////////////////////////////////////////////////////////////////////////////////
//
//  BOOL SetStatusMessage(LPTSTR lpszMessage)
//
//  Sets text in the static control at the bottom of the main window to
//  lpszMessage
//
/////////////////////////////////////////////////////////////////////////////////
BOOL SetStatusMessage(LPTSTR lpszMessage)
{
    return (SetWindowText(GetDlgItem(ghMainWnd,
                                     IDC_STATIC1),
                          lpszMessage));

}

/////////////////////////////////////////////////////////////////////////////////
//
//  BOOL ClearCall(HCALL hCall)
//
//  Called when a CALLSTATE_IDLE message is recieved.  Looks for the call in the
//  global pAddressInfo array.  If it finds it, is clears the appropriate members
//  of the structure
//
/////////////////////////////////////////////////////////////////////////////////
BOOL ClearCall(HCALL hCall)
{
    DWORD       dwCount;

    for (dwCount = 0; dwCount < gdwAddresses; dwCount++)
    {
        if (pAddressInfo[dwCount].hCall == hCall)
        {
            pAddressInfo[dwCount].hCall = NULL;
            pAddressInfo[dwCount].bCall = FALSE;
            SetButton(dwCount,
                      TRUE,
                      FALSE);
            return TRUE;
        }
    }

    return FALSE;
}

//////////////////////////////////////////////////////////////////////////////////
//
//  BOOL SetButton()
//
//  Sets the status and text of the answer/drop button for a specific address
//
//////////////////////////////////////////////////////////////////////////////////
BOOL SetButton(DWORD dwAddress,
               BOOL bAnswer,
               BOOL bEnable)
{
    if (dwAddress >= gdwAddresses)
        return FALSE;

    if (bAnswer)
    {
        SetWindowText(pAddressInfo[dwAddress].hAnswer,
                      TEXT("Answer"));
    }
    else
    {
        SetWindowText(pAddressInfo[dwAddress].hAnswer,
                      TEXT("Hang Up"));
    }

    EnableWindow(pAddressInfo[dwAddress].hAnswer,
                 bEnable);

    return TRUE;
}

///////////////////////////////////////////////////////////////////////////////
//
//  VOID CALLBACK LineCallback ()
//
//  TAPI callback function.  Handles all tapi messages
//
///////////////////////////////////////////////////////////////////////////////
VOID CALLBACK LineCallback (DWORD hDevice,
                            DWORD dwMsg,
                            DWORD dwCallbackInstance,
                            DWORD dwParam1,
                            DWORD dwParam2,
                            DWORD dwParam3)
{
    LPLINECALLINFO          pLCI;
    LPLINECALLSTATUS        pLCS;
    TCHAR                   szBuffer[64];

    switch (dwMsg)
    {
        case LINE_REPLY:
        {
            EnterCriticalSection(&csLineReply);
            if (dwParam1 == (DWORD)glResult)
            {
                gbReply = TRUE;
                glResult = dwParam2;
            }
            LeaveCriticalSection(&csLineReply);
        }
        break;

        case LINE_CALLSTATE:
        {
            if (dwParam1 == LINECALLSTATE_OFFERING)
            {
                // get the call privilege
                // note note note the new LINE_APPNEWCALL
                // give call privilege
                pLCS = LineGetCallStatus((HCALL)hDevice);

                if (!pLCS)
                    return;

                if (!(pLCS->dwCallPrivilege & LINECALLPRIVILEGE_OWNER))
                {
                    // not our call
                    GlobalFree(pLCS);
                    return;
                }

                GlobalFree(pLCS);

                // we're getting offered a call
                // first get the address
                pLCI = LineGetCallInfo((HCALL)hDevice);

                if (!pLCI)
                {
                    // error
                    return;
                }

                // set the status message text
                wsprintf(szBuffer,
                         TEXT("Incoming call on address %lu"),
                         pLCI->dwAddressID);

                pAddressInfo[pLCI->dwAddressID].hCall = (HCALL)hDevice;

                SetStatusMessage(szBuffer);

                // set the button to answer
                SetButton(pLCI->dwAddressID,
                          TRUE,
                          TRUE);

                GlobalFree(pLCI);

                break;
            }

            if (dwParam1 == LINECALLSTATE_IDLE)
            {
                // see if we have this call
                ClearCall((HCALL)hDevice);
                // dealloc no matter what
                lineDeallocateCall((HCALL)hDevice);

                break;
            }
        }

        break;
    }
}

///////////////////////////////////////////////////////////////////////////////
//
//  BOOL GetAddressFromhWnd()
//
///////////////////////////////////////////////////////////////////////////////
BOOL GetAddressFromhWnd(HWND hWnd,
                        LPDWORD pdwAddress,
                        LPBOOL pbStatus)
{
    DWORD       dwAddress;

    // go through the array of addressinfo and see
    // if the hwnd matches
    for (dwAddress = 0; dwAddress < gdwAddresses; dwAddress++)
    {
        if (pAddressInfo[dwAddress].hStatus == hWnd)
        {
            *pdwAddress = dwAddress;
            *pbStatus = TRUE;

            return TRUE;
        }
        if (pAddressInfo[dwAddress].hAnswer == hWnd)
        {
            *pdwAddress = dwAddress;
            *pbStatus = FALSE;

            return TRUE;
        }
    }

    return FALSE;
}

/////////////////////////////////////////////////////////////////////////////
//
//  BOOL DoLineAnswerDrop(DWORD dwAddress)
//
//  Handles what happens when the answer/drop button is pressed
//
/////////////////////////////////////////////////////////////////////////////
BOOL DoLineAnswerDrop(DWORD dwAddress)
{
    // if we have a call, then we want to drop it
    if (pAddressInfo[dwAddress].bCall)
    {

        SetStatusMessage(TEXT("Hanging up call ..."));

        EnterCriticalSection(&csLineReply);
        glResult = lineDrop(pAddressInfo[dwAddress].hCall,
                           NULL,
                           0);

        if (glResult < 0)
        {
            LeaveCriticalSection(&csLineReply);
            // error
        }

        else if (WaitForLineReply())
        {
            // error
        }

        // error or not, deallocate and set button
        lineDeallocateCall(pAddressInfo[dwAddress].hCall);

        SetButton(dwAddress,
                  TRUE,
                  FALSE);

        pAddressInfo[dwAddress].hCall = NULL;
        pAddressInfo[dwAddress].bCall = FALSE;

        SetStatusMessage(TEXT("Waiting for a call"));

    }
    else
    {
        BOOL bError = FALSE;

        // answer
        SetStatusMessage(TEXT("Answering call..."));

        EnterCriticalSection(&csLineReply);
        glResult = lineAnswer(pAddressInfo[dwAddress].hCall,
                             NULL,
                             0);

        if (glResult < 0)
        {
            LeaveCriticalSection(&csLineReply);
            bError = TRUE;
            //error
        }
        else if (WaitForLineReply())
        {
            bError = TRUE;
            // error
        }

        if (bError)
        {
            SetStatusMessage(TEXT("Hanging up call ..."));
            lineDeallocateCall(pAddressInfo[dwAddress].hCall);
            pAddressInfo[dwAddress].hCall = NULL;
            SetButton(dwAddress,
                      TRUE,
                      FALSE);

            SetStatusMessage(TEXT("Waiting for a call"));
            return FALSE;
        }

        SetStatusMessage(TEXT("On a call"));

        pAddressInfo[dwAddress].bCall = TRUE;

        SetButton(dwAddress,
                  FALSE,
                  TRUE);
    }

    return TRUE;
}

//////////////////////////////////////////////////////////////////////
//
//  LRESULT DoCommand(WPARAM wParam,
//                    LPARAM lParam)
//
//  Handles WM_COMMAND messages for the main window
//
//////////////////////////////////////////////////////////////////////
LRESULT DoCommand(WPARAM wParam,
                  LPARAM lParam)
{
    DWORD       dwAddress;
    BOOL        bStatus;

    // check to see if a button is being clicked
    if (HIWORD(wParam) == BN_CLICKED)
    {
        // check to see if it is a button we care about
        if (GetAddressFromhWnd((HWND)lParam,
                               &dwAddress,
                               &bStatus))
        {

            // if it's the status button, display the status
            // dialog
            if (bStatus)
            {
                DialogBoxParam(ghInstance,
                               MAKEINTRESOURCE(IDD_AGENTSTATE),
                               ghMainWnd,
                               AgentStateDlgProc,
                               (LPARAM)dwAddress);
            }
            // else it's the answer/drop button
            else
            {
                DoLineAnswerDrop(dwAddress);
            }
        }

        return 1;

    }

    return 0;
}

////////////////////////////////////////////////////////////////////////////////
//
// MainWndProc()
//
////////////////////////////////////////////////////////////////////////////////
LRESULT CALLBACK MainWndProc (HWND   hwnd,
                              UINT   uMsg,
                              WPARAM wParam,
                              LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_INITDIALOG:

        return 1;

    case WM_COMMAND:

        return DoCommand(wParam,
                         lParam);

        break;

    case WM_CLOSE:
    case WM_DESTROY:
        CloseTapi();
        PostQuitMessage(0);
        return 1;
    }

    return 0;
}

///////////////////////////////////////////////////////////////////////
//
//  BOOL InitTapi()
//
//  Initializes TAPI.  For this sample, we assume that the "agent" (the person
//  logged on) can only have access to _one_ hLine.  Also, they have access to
//  every address on that line.  This may not be true for many ACD situations.
//
//  As soon as we find a device that the agent has access to, we quit
//  looking, and use that device
//
///////////////////////////////////////////////////////////////////////
BOOL InitTapi()
{
    LONG                    lResult;
    LINEINITIALIZEEXPARAMS  exparams;
    LPLINEAGENTCAPS         pLAC;
    LPLINEDEVCAPS           pLDC;
    DWORD                   dwDeviceID, dwNumDevs, dwAPIVersion, dwThreadID;

    // initialize completion port to receive TAPI notifications
    ghCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE,
                                               NULL,
                                               0,
                                               0);

    InitializeCriticalSection(&csLineReply);

    // fill in lineinitex parameters
    exparams.dwTotalSize             = sizeof(LINEINITIALIZEEXPARAMS);
    exparams.dwOptions               = LINEINITIALIZEEXOPTION_USECOMPLETIONPORT;
    exparams.Handles.hCompletionPort = ghCompletionPort;

    dwAPIVersion = TAPI_CURRENT_VERSION;

    lResult = lineInitializeEx(&ghLineApp,
                               ghInstance,
                               NULL,
                               SZAPPNAME,
                               &dwNumDevs,
                               &dwAPIVersion,
                               &exparams);

    if (lResult)
    {
        return FALSE;
    }

    // if no devices, don't continue
    if (dwNumDevs == 0)
    {
        lineShutdown(ghLineApp);
        return FALSE;
    }

    // kick off completion port thread
    CreateThread(NULL,
                 0,
                 (LPTHREAD_START_ROUTINE)ThreadRoutine,
                 NULL,
                 0,
                 &dwThreadID);

    // loop through all devices
    for (dwDeviceID = 0; dwDeviceID < dwNumDevs; dwDeviceID++)
    {
        // Get the Agent Caps.  If this succeedes, this is
        // a device we can use
        pLAC = LineGetAgentCaps(ghLineApp,
                                dwDeviceID,
                                0);

        if (pLAC)
        {
            // this is a device we can use
            gdwDeviceID = dwDeviceID;

            // get the number of addresses
            pLDC = LineGetDevCaps(ghLineApp,
                                  dwDeviceID);

            if (pLDC)
            {
                gdwAddresses = pLDC->dwNumAddresses;
                GlobalFree(pLDC);
            }

            GlobalFree(pLAC);
            break;
        }
    }

    // open the line in owner mode
    lResult = lineOpen(ghLineApp,
                       gdwDeviceID,
                       &ghLine,
                       TAPI_CURRENT_VERSION,
                       0,
                       0,
                       LINECALLPRIVILEGE_OWNER,
                       LINEMEDIAMODE_INTERACTIVEVOICE,
                       NULL);

    // if line failed, don't continue
    if (lResult)
    {
        lineShutdown(ghLineApp);
        return FALSE;
    }

    return TRUE;
}

//////////////////////////////////////////////////////////////////////
//
//  ThreadRoutine
//
//  Thread dedicated to checking completion port for TAPI events
//
//////////////////////////////////////////////////////////////////////
LONG ThreadRoutine(LPVOID lpv)
{
    LPLINEMESSAGE       pMsg;
    DWORD               dwNumBytesTransfered, dwCompletionKey;

    // just wait for tapi notifications
    while (GetQueuedCompletionStatus(ghCompletionPort,
                                     &dwNumBytesTransfered,
                                     &dwCompletionKey,
                                     (LPOVERLAPPED *) &pMsg,
                                     INFINITE))
    {
        if (pMsg)
        {
            // when we get one, call the handling function
            LineCallback(pMsg->hDevice,
                         pMsg->dwMessageID,
                         pMsg->dwCallbackInstance,
                         pMsg->dwParam1,
                         pMsg->dwParam2,
                         pMsg->dwParam3);

            LocalFree (pMsg);
        }
        else
        {
            break;
        }
    }

    ExitThread(0);
    return 0;
}

///////////////////////////////////////////////////////////////////////////
//
//  BOOL CloseTapi()
//
//  Close tapi and free resources
//
///////////////////////////////////////////////////////////////////////////
BOOL CloseTapi()
{
    GlobalFree(pAddressInfo);

    CloseHandle(ghCompletionPort);

    lineClose(ghLine);
    lineShutdown(ghLineApp);

    return TRUE;
}

// static information for the status dialog
static DWORD dwAgentStates[] =
        {
            LINEAGENTSTATE_LOGGEDOFF,
            LINEAGENTSTATE_NOTREADY,
            LINEAGENTSTATE_READY,
            LINEAGENTSTATE_BUSYACD,
            LINEAGENTSTATE_BUSYINCOMING,
            LINEAGENTSTATE_BUSYOUTBOUND,
            LINEAGENTSTATE_BUSYOTHER,
            LINEAGENTSTATE_WORKINGAFTERCALL,
            LINEAGENTSTATE_UNKNOWN,
            LINEAGENTSTATE_UNAVAIL,
            0
        };

static LPTSTR lpszStates[] =
        {
            TEXT("Logged Off"),
            TEXT("Not Ready"),
            TEXT("Ready"),
            TEXT("Busy ACD"),
            TEXT("Busy Incoming"),
            TEXT("Busy Outbound"),
            TEXT("Busy Other"),
            TEXT("Working after call"),
            TEXT("Unknown"),
            TEXT("Unavail"),
            NULL
        };

///////////////////////////////////////////////////////////////////////////
//
//  BOOL InitAgentDlg()
//
//  Handles initialization of the status dialog
//
//  Gets the group list and puts groups in multiselect list box
//      these are the groups that the agent _can_ log into
//      the groups they are logged into will be selected
//  Creates comboboxes of states and nextstates, and select
//      the agent's current state/nextstate
//  Gets the activity list and puts each item into a combobox
//      the current activity will be selected
//
///////////////////////////////////////////////////////////////////////////
BOOL InitAgentDlg(HWND hwnd,
                  DWORD dwAddress,
                  LPLINEAGENTGROUPLIST * ppLAG)
{
    LPLINEAGENTCAPS         pLAC;
    LPLINEAGENTSTATUS       pLAS;
    LPLINEAGENTACTIVITYLIST pLAA;
    LPLINEAGENTGROUPENTRY   pEntry, pLoggedInEntry;
    LPLINEAGENTACTIVITYENTRY     pActivityEntry;
    DWORD                   dwEntries, dwCount;
    LONG                    item;

    // first, get the status
    // this information will be used to know which items to select
    // in each of the listbox/comboboxes
    pLAS = LineGetAgentStatus(ghLine,
                              dwAddress);

    if (!pLAS)
    {
        return FALSE;
    }

    // get the group list
    if (!(*ppLAG = LineGetAgentGroupList(ghLine,
                                         dwAddress)))
    {
        return FALSE;
    }

    // get the first groupentry
    pEntry = (LPLINEAGENTGROUPENTRY)(((LPBYTE)*ppLAG)+(*ppLAG)->dwListOffset);

    // loop through the group entries
    for (dwEntries = 0; dwEntries < (*ppLAG)->dwNumEntries; dwEntries++)
    {
        // add group to list box
        item = SendDlgItemMessage(hwnd,
                                  IDC_GROUPS,
                                  LB_ADDSTRING,
                                  0,
                                  (LPARAM)(LPTSTR)(((LPBYTE)*ppLAG) + pEntry->dwNameOffset));

        // save the entry
        SendDlgItemMessage(hwnd,
                           IDC_GROUPS,
                           LB_SETITEMDATA,
                           (WPARAM)item,
                           (LPARAM)pEntry);

        // now get list of groups currently logged into from the agent status structure
        // loop through them.  if any of them match the group we are currently adding
        // select that group
        pLoggedInEntry = (LPLINEAGENTGROUPENTRY)(((LPBYTE)pLAS) + pLAS->dwGroupListOffset);
        for (dwCount = 0; dwCount < pLAS->dwNumEntries; dwCount++)
        {
            if ((pLoggedInEntry->GroupID.dwGroupID1 == pEntry->GroupID.dwGroupID1) &&
                (pLoggedInEntry->GroupID.dwGroupID2 == pEntry->GroupID.dwGroupID2) &&
                (pLoggedInEntry->GroupID.dwGroupID3 == pEntry->GroupID.dwGroupID3) &&
                (pLoggedInEntry->GroupID.dwGroupID4 == pEntry->GroupID.dwGroupID4))
            {
                SendDlgItemMessage(hwnd,
                                   IDC_GROUPS,
                                   LB_SETSEL,
                                   (WPARAM)TRUE,
                                   (LPARAM)item);
            }

            pLoggedInEntry++;
        }

        pEntry++;
    }

    // get the agent caps
    if (pLAC = LineGetAgentCaps(ghLineApp,
                                gdwDeviceID,
                                dwAddress))
    {
        dwCount = 0;
        // loop through all possbile agent states.  if the agent state
        // is selected in the agent caps, add that state to the list box
        while (dwAgentStates[dwCount])
        {
            if (dwAgentStates[dwCount] & pLAC->dwStates)
            {
                item = SendDlgItemMessage(hwnd,
                                   IDC_STATE,
                                   CB_ADDSTRING,
                                   0,
                                   (LPARAM)lpszStates[dwCount]);
                SendDlgItemMessage(hwnd,
                                   IDC_STATE,
                                   CB_SETITEMDATA,
                                   (WPARAM)item,
                                   (LPARAM)dwAgentStates[dwCount]);

                // if the state matches the current one from the agent status
                // select it
                if (pLAS->dwState == dwAgentStates[dwCount])
                {
                    SendDlgItemMessage(hwnd,
                                       IDC_STATE,
                                       CB_SETCURSEL,
                                       (WPARAM)item,
                                       0);
                }
            }

            dwCount ++;
        }

        dwCount = 0;
        // now do the same for the next states
        while (dwAgentStates[dwCount])
        {
            if (dwAgentStates[dwCount] & pLAC->dwNextStates)
            {
                item = SendDlgItemMessage(hwnd,
                                          IDC_NEXTSTATE,
                                          CB_ADDSTRING,
                                          0,
                                          (LPARAM)lpszStates[dwCount]);
                SendDlgItemMessage(hwnd,
                                   IDC_NEXTSTATE,
                                   CB_SETITEMDATA,
                                   (WPARAM)item,
                                   dwAgentStates[dwCount]);

                if (pLAS->dwNextState == dwAgentStates[dwCount])
                {
                    SendDlgItemMessage(hwnd,
                                       IDC_NEXTSTATE,
                                       CB_SETCURSEL,
                                       (WPARAM)item,
                                       0);
                }
            }

            dwCount++;
        }

        GlobalFree(pLAC);
    }

    // get the activity list
    pLAA = LineGetAgentActivityList(ghLine,
                                    gdwDeviceID,
                                    dwAddress);
    if (pLAA)
    {
        dwCount = pLAA->dwNumEntries;
        pActivityEntry = (LPLINEAGENTACTIVITYENTRY)(((LPBYTE)pLAA) + pLAA->dwListOffset);

        // go through all the possible activities and add them to the list
        while (dwCount)
        {
            item = SendDlgItemMessage(hwnd,
                                      IDC_ACTIVITY,
                                      CB_ADDSTRING,
                                      0,
                                      (LPARAM)(LPTSTR)(((LPBYTE)pLAA) + pActivityEntry->dwNameOffset));

            SendDlgItemMessage(hwnd,
                               IDC_ACTIVITY,
                               CB_SETITEMDATA,
                               (WPARAM)item,
                               (LPARAM)pActivityEntry->dwID);

            // if this is the current activity (from agent status)
            // select it
            if (pLAS->dwActivityID == pActivityEntry->dwID)
            {
                SendDlgItemMessage(hwnd,
                                   IDC_ACTIVITY,
                                   CB_SETCURSEL,
                                   (WPARAM)item,
                                   0);
            }

            dwCount--;
            pActivityEntry++;
        }

        GlobalFree(pLAA);

    }

}

//////////////////////////////////////////////////////////////////////////////////////////////
//
//  BOOL SaveAgentStatus(HWND hwnd)
//
//  Saves information from the status dialog
//
//////////////////////////////////////////////////////////////////////////////////////////////
BOOL SaveAgentStatus(HWND hwnd,
                     DWORD dwAddress)
{
    LPLINEAGENTGROUPENTRY              pGroupEntry, pNewGroupEntry;
    LPLINEAGENTGROUPLIST               pNewLAG;
    DWORD                              dwCount;
    LPINT                              pItems;
    DWORD                              item;
    DWORD                              dwState, dwNextState, dwActivity;

    // get the number of groups selected in the group
    // list box.  each selected group is a group this
    // agent will be logged into
    dwCount = SendDlgItemMessage(hwnd,
                                 IDC_GROUPS,
                                 LB_GETSELCOUNT,
                                 0,

0);

    // allocate an array to hold the selected item's indexes
    pItems = (LPINT)GlobalAlloc(GPTR, sizeof(int) * dwCount);

    // get the item's indexes
    SendDlgItemMessage(hwnd,
                       IDC_GROUPS,
                       LB_GETSELITEMS,
                       dwCount,
                       (LPARAM)pItems);

    // alloc a LINEAGENTGROUP array for groups
    pNewLAG = (LPLINEAGENTGROUPLIST)GlobalAlloc(GPTR,
                                                sizeof(LINEAGENTGROUPLIST) +
                                                dwCount * sizeof(LINEAGENTGROUPENTRY));

    // fill in sizes
    pNewLAG->dwTotalSize = sizeof(LINEAGENTGROUPLIST) + dwCount * sizeof(LINEAGENTGROUPENTRY);
    pNewLAG->dwUsedSize = pNewLAG->dwTotalSize;
    pNewLAG->dwNeededSize = pNewLAG->dwTotalSize;
    pNewLAG->dwListSize = sizeof(LINEAGENTGROUPENTRY) * dwCount;
    pNewLAG->dwListOffset = sizeof(LINEAGENTGROUPLIST);

    // count
    pNewLAG->dwNumEntries = dwCount;

    // get pointer to first entry in array
    pNewGroupEntry = (LPLINEAGENTGROUPENTRY)(((LPBYTE)pNewLAG) + pNewLAG->dwListOffset);
    // loop though all selected item
    while (dwCount)
    {
        // get the item data associated with the item.  this data
        // is a group entry struct
        pGroupEntry = (LPLINEAGENTGROUPENTRY)SendDlgItemMessage(hwnd,
            IDC_GROUPS,
            LB_GETITEMDATA,
            (WPARAM)pItems[dwCount-1],
            0);

        // copy the GroupID to the new array
        CopyMemory(&pNewGroupEntry->GroupID,
                   &pGroupEntry->GroupID,
                   sizeof(pGroupEntry->GroupID));

        // these fields are not used
        pNewGroupEntry->dwNameSize = 0;
        pNewGroupEntry->dwNameOffset = 0;

        // next entry
        pNewGroupEntry++;

        dwCount--;
    }

    // now that we've created the AGENTGROUPLIST, set it
    EnterCriticalSection(&csLineReply);
    glResult = lineSetAgentGroup(ghLine,
                                dwAddress,
                                pNewLAG);

    if (glResult < 0)
    {
        LeaveCriticalSection(&csLineReply);
        //error
    }
    else if (WaitForLineReply())
    {
        //error
    }

    GlobalFree(pNewLAG);

    // now get the current state
    item = SendDlgItemMessage(hwnd,
                              IDC_STATE,
                              CB_GETCURSEL,
                              0,
                              0);

    // get item data.  this is the state flag
    dwState = SendDlgItemMessage(hwnd,
                                 IDC_STATE,
                                 CB_GETITEMDATA,
                                 (WPARAM)item,
                                 0);

    // same for next state
    item = SendDlgItemMessage(hwnd,
                              IDC_NEXTSTATE,
                              CB_GETCURSEL,
                              0,
                              0);

    dwNextState = SendDlgItemMessage(hwnd,
                                     IDC_NEXTSTATE,
                                     CB_GETITEMDATA,
                                     (WPARAM)item,
                                     0);

    // set it
    EnterCriticalSection(&csLineReply);
    glResult = lineSetAgentState(ghLine,
                                dwAddress,
                                dwState,
                                dwNextState);

    if (glResult < 0)
    {
        LeaveCriticalSection(&csLineReply);
        //error
    }
    else if (WaitForLineReply())
    {
        //error
    }

    // get the activity selected
    item = SendDlgItemMessage(hwnd,
                              IDC_ACTIVITY,
                              CB_GETCURSEL,
                              0,
                              0);

    // get the item data.  this is the activity ID
    dwActivity = SendDlgItemMessage(hwnd,
                                    IDC_ACTIVITY,
                                    CB_GETITEMDATA,
                                    (WPARAM)item,
                                    0);

    // set it
    EnterCriticalSection(&csLineReply);
    glResult = lineSetAgentActivity(ghLine,
                                   dwAddress,
                                   dwActivity);

    if (glResult < 0)
    {
        LeaveCriticalSection(&csLineReply);
        //error
    }
    else if (WaitForLineReply())
    {
        //error
    }

    return TRUE;
}

///////////////////////////////////////////////////////////////////////////////
//
//  LRESULT WaitForLineReply()
//
//  waiting for a line reply.
//
//  2 issues:
//      - using global variables for line reply information.  only recommended
//        in the most simple situations
//
//      - using completion ports to demonstrate the completion port mechanism.
//        since this app has ui, the wait loop has a message loop and a sleep()!!
//
///////////////////////////////////////////////////////////////////////////////
LRESULT WaitForLineReply()
{
    MSG     msg;

    gbReply = FALSE;

    LeaveCriticalSection(&csLineReply);

    while (!gbReply)
    {
        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }

        Sleep(5);
    }

    return glResult;

}

///////////////////////////////////////////////////////////////////////////////////
//
//  LRESULT CALLBACK AgentStateDlgProc ()
//
//  Dialog proc for the agent status dialog
//
///////////////////////////////////////////////////////////////////////////////////
LRESULT CALLBACK AgentStateDlgProc (HWND   hwnd,
                                    UINT   uMsg,
                                    WPARAM wParam,
                                    LPARAM lParam)
{
    static DWORD                   dwAddress;
    static LPLINEAGENTGROUPLIST    pLAG;

    switch (uMsg)
    {
        case WM_INITDIALOG:

            dwAddress = (DWORD)lParam;

            InitAgentDlg(hwnd,
                         dwAddress,
                         &pLAG);

            SetFocus(GetDlgItem(hwnd,
                                IDC_GROUPS));
            return 1;

        case WM_COMMAND:
            if (LOWORD(wParam) == IDOK)
            {
                SaveAgentStatus(hwnd,
                                dwAddress);
                GlobalFree(pLAG);
                EndDialog(hwnd,
                          1);
                return 1;
            }

            if (LOWORD(wParam) == IDCANCEL)
            {
                GlobalFree(pLAG);
                EndDialog(hwnd,
                         1);

                return 1;
            }
    }

    return 0;
}

///////////////////////////////////////////////////////////////////////////////
//
//      **************TAPI WRAPPER FUNCTIONS**************
//
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
//
// LineGetAgentGroupList()
//
///////////////////////////////////////////////////////////////////////////////
LINEAGENTGROUPLIST * LineGetAgentGroupList (HLINE    hLine,
                                            DWORD    dwAddressID)
{
    LINEAGENTGROUPLIST *    pLineAgentGroupList;
    static DWORD            dwMaxNeededSize = sizeof(LINEAGENTGROUPLIST);

    // Allocate an initial block of memory for the LINEAGENTGROUPLIST structure,
    // which may or may not be big enough to hold all of the information.
    //
    pLineAgentGroupList = GlobalAlloc(GPTR, dwMaxNeededSize);

    while (TRUE)
    {
        BOOL        bError = FALSE;

        if (pLineAgentGroupList == NULL)
        {
            return NULL;
        }
        pLineAgentGroupList->dwTotalSize = dwMaxNeededSize;

        // Try (or retry) to get the LINEAGENTGROUPLIST information
        //
        EnterCriticalSection(&csLineReply);
        glResult = lineGetAgentGroupList(hLine,
                                        dwAddressID,
                                        pLineAgentGroupList);

        if (glResult < 0)
        {
            LeaveCriticalSection(&csLineReply);
            bError = TRUE;
            //error
        }
        else if (WaitForLineReply())
        {
            bError = TRUE;
            //error
        }

        if (bError)
        {
            GlobalFree((HLOCAL)pLineAgentGroupList);
            return NULL;
        }

        // If the currently allocated LINEAGENTGROUPLIST memory block was big
        // enough, we're all done, else we need to realloc the memory block
        // and try again.
        //
        if (pLineAgentGroupList->dwNeededSize <= dwMaxNeededSize)
        {
            return pLineAgentGroupList;
        }
        else
        {
            dwMaxNeededSize = pLineAgentGroupList->dwNeededSize;
            pLineAgentGroupList = GlobalReAlloc((HLOCAL)pLineAgentGroupList,
                                           dwMaxNeededSize,
                                           GMEM_MOVEABLE);
        }
    }
}

///////////////////////////////////////////////////////////////////////////////
//
// LineGetAgentStatus()
//
///////////////////////////////////////////////////////////////////////////////
LINEAGENTSTATUS * LineGetAgentStatus (HLINE    hLine,
                                      DWORD    dwAddressID)
{
    LINEAGENTSTATUS *   pLineAgentStatus;
    static DWORD        dwMaxNeededSize = sizeof(LINEAGENTSTATUS);

    // Allocate an initial block of memory for the LINEAGENTSTATUS structure,
    // which may or may not be big enough to hold all of the information.
    //
    pLineAgentStatus = GlobalAlloc(GPTR, dwMaxNeededSize);

    while (TRUE)
    {
        BOOL        bError = FALSE;
        if (pLineAgentStatus == NULL)
        {
            return NULL;
        }
        pLineAgentStatus->dwTotalSize = dwMaxNeededSize;

        // Try (or retry) to get the LINEAGENTSTATUS information
        //
        EnterCriticalSection(&csLineReply);
        glResult = lineGetAgentStatus(hLine,
                                     dwAddressID,
                                     pLineAgentStatus);

        if (glResult < 0)
        {
            LeaveCriticalSection(&csLineReply);
            bError = TRUE;
            //error
        }
        else if (WaitForLineReply())
        {
            bError = TRUE;
            //error
        }

        if (bError)
        {
            GlobalFree((HLOCAL)pLineAgentStatus);
            return NULL;
        }

        // If the currently allocated LINEAGENTSTATUS memory block was big
        // enough, we're all done, else we need to realloc the memory block
        // and try again.
        //
        if (pLineAgentStatus->dwNeededSize <= dwMaxNeededSize)
        {
            return pLineAgentStatus;
        }
        else
        {
            dwMaxNeededSize = pLineAgentStatus->dwNeededSize;
            pLineAgentStatus = GlobalReAlloc((HLOCAL)pLineAgentStatus,
                                           dwMaxNeededSize,
                                           GMEM_MOVEABLE);
        }
    }
}

///////////////////////////////////////////////////////////////////////////////
//
// LineGetAgentCaps()
//
///////////////////////////////////////////////////////////////////////////////
LINEAGENTCAPS * LineGetAgentCaps (HLINEAPP hLineApp,
                                  DWORD    dwDeviceID,
                                  DWORD    dwAddressID)
{
    LINEAGENTCAPS *   pLineAgentCaps;
    static DWORD      dwMaxNeededSize = sizeof(LINEAGENTCAPS);

    // Allocate an initial block of memory for the LINEAGENTCAPS structure,
    // which may or may not be big enough to hold all of the information.
    //
    pLineAgentCaps = GlobalAlloc(GPTR, dwMaxNeededSize);

    while (TRUE)
    {
        BOOL            bError = FALSE;

        if (pLineAgentCaps == NULL)
        {
            return NULL;
        }
        pLineAgentCaps->dwTotalSize = dwMaxNeededSize;

        // Try (or retry) to get the LINEAGENTCAPS information
        //
        EnterCriticalSection(&csLineReply);
        glResult = lineGetAgentCaps(hLineApp,
                                   dwDeviceID,
                                   dwAddressID,
                                   TAPI_CURRENT_VERSION,
                                   pLineAgentCaps);

        if (glResult < 0)
        {
            bError = TRUE;
            LeaveCriticalSection(&csLineReply);
            //error
        }
        else if (WaitForLineReply())
        {
            bError = TRUE;
            //error
        }

        if (bError)
        {
            GlobalFree((HLOCAL)pLineAgentCaps);
            return NULL;
        }

        // If the currently allocated LINEAGENTCAPS memory block was big
        // enough, we're all done, else we need to realloc the memory block
        // and try again.
        //
        if (pLineAgentCaps->dwNeededSize <= dwMaxNeededSize)
        {
            return pLineAgentCaps;
        }
        else
        {
            dwMaxNeededSize = pLineAgentCaps->dwNeededSize;
            pLineAgentCaps = GlobalReAlloc((HLOCAL)pLineAgentCaps,
                                           dwMaxNeededSize,
                                           GMEM_MOVEABLE);
        }
    }
}

///////////////////////////////////////////////////////////////////////////////
//
// LineGetAgentActivityList()
//
///////////////////////////////////////////////////////////////////////////////
LPLINEAGENTACTIVITYLIST LineGetAgentActivityList (HLINE    hLine,
                                                  DWORD    dwDeviceID,
                                                  DWORD    dwAddressID)
{
    LINEAGENTACTIVITYLIST * pLineAgentActivityList;
    static DWORD            dwMaxNeededSize = sizeof(LINEAGENTACTIVITYLIST);

    // Allocate an initial block of memory for the LINEAGENTACTIVITYLIST structure,
    // which may or may not be big enough to hold all of the information.
    //
    pLineAgentActivityList = GlobalAlloc(GPTR, dwMaxNeededSize);

    for (;;)
    {
        BOOL        bError = FALSE;
        if (pLineAgentActivityList == NULL)
        {
            return NULL;
        }
        pLineAgentActivityList->dwTotalSize = dwMaxNeededSize;

        // Try (or retry) to get the LINEAGENTACTIVITYLIST information
        //
        EnterCriticalSection(&csLineReply);
        glResult = lineGetAgentActivityList(hLine,
                                           dwAddressID,
                                           pLineAgentActivityList);

        if (glResult < 0)
        {
            LeaveCriticalSection(&csLineReply);
            bError = TRUE;
            //error
        }
        else if (WaitForLineReply())
        {
            bError = TRUE;
            //error
        }

        if (bError)
        {
            GlobalFree((HLOCAL)pLineAgentActivityList);
            return NULL;
        }

        // If the currently allocated LINEAGENTACTIVITYLIST memory block was big
        // enough, we're all done, else we need to realloc the memory block
        // and try again.
        //
        if (pLineAgentActivityList->dwNeededSize <= dwMaxNeededSize)
        {
            return pLineAgentActivityList;
        }
        else
        {
            dwMaxNeededSize = pLineAgentActivityList->dwNeededSize;
            pLineAgentActivityList = GlobalReAlloc((HLOCAL)pLineAgentActivityList,
                                            dwMaxNeededSize,
                                            GMEM_MOVEABLE);
        }
    }
}

///////////////////////////////////////////////////////////////////////////////
//
// LineGetAddressCaps()
//
///////////////////////////////////////////////////////////////////////////////
LINEADDRESSCAPS * LineGetAddressCaps (HLINEAPP hLineApp,
                                      DWORD    dwDeviceID,
                                      DWORD    dwAddressID)
{
    LONG              lRetVal;
    LINEADDRESSCAPS * pLineAddressCaps;
    static DWORD      dwMaxNeededSize = sizeof(LINEADDRESSCAPS);

    // Allocate an initial block of memory for the LINEADDRESSCAPS structure,
    // which may or may not be big enough to hold all of the information.
    //
    pLineAddressCaps = GlobalAlloc(GPTR, dwMaxNeededSize);

    for (;;)
    {
        if (pLineAddressCaps == NULL)
        {
            return NULL;
        }
        pLineAddressCaps->dwTotalSize = dwMaxNeededSize;

        // Try (or retry) to get the LINEADDRESSCAPS information
        //
        lRetVal = lineGetAddressCaps(hLineApp,
                                     dwDeviceID,
                                     dwAddressID,
                                     TAPI_CURRENT_VERSION,
                                     0,
                                     pLineAddressCaps);
        if (lRetVal < 0)
        {
            GlobalFree((HLOCAL)pLineAddressCaps);
            return NULL;
        }

        // If the currently allocated LINEADDRESSCAPS memory block was big
        // enough, we're all done, else we need to realloc the memory block
        // and try again.
        //
        if (pLineAddressCaps->dwNeededSize <= dwMaxNeededSize)
        {
            return pLineAddressCaps;
        }
        else
        {
            dwMaxNeededSize = pLineAddressCaps->dwNeededSize;
            pLineAddressCaps = GlobalReAlloc((HLOCAL)pLineAddressCaps,
                                             dwMaxNeededSize,
                                             GMEM_MOVEABLE);
        }
    }
}

///////////////////////////////////////////////////////////////////////////////
//
// LineGetCallInfo()
//
///////////////////////////////////////////////////////////////////////////////
LINECALLINFO * LineGetCallInfo (HCALL hCall)
{
    LONG           lRetVal;
    LINECALLINFO * pLineCallInfo;
    static DWORD   dwMaxNeededSize = sizeof(LINECALLINFO);

    // Allocate an initial block of memory for the LINECALLINFO structure,
    // which may or may not be big enough to hold all of the information.
    //
    pLineCallInfo = GlobalAlloc(GPTR, dwMaxNeededSize);

    for (;;)
    {
        if (pLineCallInfo == NULL)
        {
            return NULL;
        }
        pLineCallInfo->dwTotalSize = dwMaxNeededSize;

        // Try (or retry) to get the LINECALLINFO information
        //
        lRetVal = lineGetCallInfo(hCall,
                                  pLineCallInfo);
        if (lRetVal < 0)
        {
            GlobalFree((HLOCAL)pLineCallInfo);
            return NULL;
        }

        // If the currently allocated LINECALLINFO memory block was big
        // enough, we're all done, else we need to realloc the memory block
        // and try again.
        //
        if (pLineCallInfo->dwNeededSize <= dwMaxNeededSize)
        {
            return pLineCallInfo;
        }
        else
        {
            dwMaxNeededSize = pLineCallInfo->dwNeededSize;
            pLineCallInfo = GlobalReAlloc((HLOCAL)pLineCallInfo,
                                         dwMaxNeededSize,
                                         GMEM_MOVEABLE);
        }
    }
}

///////////////////////////////////////////////////////////////////////////////
//
// LineGetDevCaps()
//
///////////////////////////////////////////////////////////////////////////////
LINEDEVCAPS * LineGetDevCaps (HLINEAPP hLineApp,
                              DWORD    dwDeviceID)
{
    LONG           lRetVal;
    LINEDEVCAPS  * pLineDevCaps;
    static DWORD   dwMaxNeededSize = sizeof(LINEDEVCAPS);

    pLineDevCaps = GlobalAlloc(GPTR, dwMaxNeededSize);
    for (;;)
    {
        if (pLineDevCaps == NULL)
        {
            return NULL;
        }
        pLineDevCaps->dwTotalSize = dwMaxNeededSize;
        lRetVal = lineGetDevCaps(hLineApp,
                                 dwDeviceID,
                                 TAPI_CURRENT_VERSION,
                                 0,
                                 pLineDevCaps);
        if (lRetVal < 0)
        {
            GlobalFree((HLOCAL)pLineDevCaps);
            return NULL;
        }
        if (pLineDevCaps->dwNeededSize <= dwMaxNeededSize)
        {
            return pLineDevCaps;
        }
        else
        {
            dwMaxNeededSize = pLineDevCaps->dwNeededSize;
            pLineDevCaps = GlobalReAlloc((HLOCAL)pLineDevCaps,
                                        dwMaxNeededSize,
                                        GMEM_MOVEABLE);
        }
    }
}

///////////////////////////////////////////////////////////////////////////////
//
// LineGetID()
//
///////////////////////////////////////////////////////////////////////////////
VARSTRING * LineGetID (HLINE  hLine,
                       DWORD  dwAddressID,
                       HCALL  hCall,
                       DWORD  dwSelect,
                       LPCTSTR lpszDeviceClass)
{
    LONG           lRetVal;
    VARSTRING    * pVarString;
    static DWORD   dwMaxNeededSize = sizeof(VARSTRING);

    // Allocate an initial block of memory for the VARSTRING structure,
    // which may or may not be big enough to hold all of the information.
    //
    pVarString = GlobalAlloc(GPTR, dwMaxNeededSize);

    for (;;)
    {
        if (pVarString == NULL)
        {
            return NULL;
        }
        pVarString->dwTotalSize = dwMaxNeededSize;

        // Try (or retry) to get the VARSTRING information
        //
        lRetVal = lineGetID(hLine,
                            dwAddressID,
                            hCall,
                            dwSelect,
                            pVarString,
                            lpszDeviceClass);
        if (lRetVal < 0)
        {
            GlobalFree((HLOCAL)pVarString);
            return NULL;
        }

        // If the currently allocated VARSTRING memory block was big
        // enough, we're all done, else we need to realloc the memory block
        // and try again.
        //
        if (pVarString->dwNeededSize <= dwMaxNeededSize)
        {
            return pVarString;
        }
        else
        {
            dwMaxNeededSize = pVarString->dwNeededSize;
            pVarString = GlobalReAlloc((HLOCAL)pVarString,
                                      dwMaxNeededSize,
                                      GMEM_MOVEABLE);
        }
    }
}

///////////////////////////////////////////////////////////////////////////////
//
// LineGetCallStatus()
//
///////////////////////////////////////////////////////////////////////////////
LINECALLSTATUS * LineGetCallStatus (HCALL hCall)
{
    LONG                lRetVal;
    LINECALLSTATUS    * pLineCallStatus;
    static DWORD        dwMaxNeededSize = sizeof(LINECALLSTATUS);

    // Allocate an initial block of memory for the LINECALLSTATUS structure,
    // which may or may not be big enough to hold all of the information.
    //
    pLineCallStatus = GlobalAlloc(GPTR, dwMaxNeededSize);

    while (TRUE)
    {
        if (pLineCallStatus == NULL)
        {
            return NULL;
        }
        pLineCallStatus->dwTotalSize = dwMaxNeededSize;

        // Try (or retry) to get the LINECALLSTATUS information
        //
        lRetVal = lineGetCallStatus(hCall,
                                    pLineCallStatus);
        if (lRetVal < 0)
        {
            GlobalFree((HLOCAL)pLineCallStatus);
            return NULL;
        }

        // If the currently allocated LINECALLSTATUS memory block was big
        // enough, we're all done, else we need to realloc the memory block
        // and try again.
        //
        if (pLineCallStatus->dwNeededSize <= dwMaxNeededSize)
        {
            return pLineCallStatus;
        }
        else
        {
            dwMaxNeededSize = pLineCallStatus->dwNeededSize;
            pLineCallStatus = GlobalReAlloc((HLOCAL)pLineCallStatus,
                                            dwMaxNeededSize,
                                            GMEM_MOVEABLE);
        }
    }
}

////////////////////////////////////////////////////////////////////////////
//
//
// constants for creating buttons in the main window
//
#define YSTART              8
#define XSTART              8
#define STATICX             57
#define BUTTONX             50
#define BUTTONGAP           20
#define BUTTONY             14
#define LINEGAP             8

/////////////////////////////////////////////////////////////////////////////
//
//  BOOL RedoWindow()
//
//  Creates the buttons and static controls in the main window
//  For each address on the line, create a static control with the name off
//  the address, a button to get/set status, and a button to answer/drop
//
//  Right now, this should only be done when the app is starting.  It does
//  not check to see if pAddressInfo has already been allocated
//
/////////////////////////////////////////////////////////////////////////////
BOOL RedoWindow()
{
    DWORD                   dwAddress;
    LPLINEADDRESSCAPS       pLAC;
    TCHAR                   szBuffer[64];
    LONG                    lBaseUnits, lxbase, lybase;
    HFONT                   hFont;
    HWND                    hWnd;

    int         x,y,w,h,xstart,ystart,buttonx,buttony,staticx,buttongap,linegap;

    // alloc for address info
    pAddressInfo = (PADDRESSINFO)GlobalAlloc(GPTR, sizeof(ADDRESSINFO) * gdwAddresses);

    if (!pAddressInfo)
    {
        return FALSE;
    }

    // get conversions
    lBaseUnits = GetDialogBaseUnits();
    lxbase = (LONG)LOWORD(lBaseUnits);
    lybase = (LONG)HIWORD(lBaseUnits);

    // convert dialog units to pixels
    xstart = (XSTART * lxbase) / 4;
    ystart = (YSTART * lybase) / 8;
    buttonx = (BUTTONX * lxbase) / 4;
    buttony = (BUTTONY * lybase) / 8;
    staticx = (STATICX * lxbase) / 4;
    buttongap = (BUTTONGAP * lxbase) / 4;
    linegap = (LINEGAP * lybase) / 8;

    // init
    x = xstart;
    y = ystart;
    w = buttonx;
    h = buttony;

    // get the font used by the static control
    hFont = (HFONT)SendDlgItemMessage(ghMainWnd,
                                      IDC_STATIC1,
                                      WM_GETFONT,
                                      0,
                                      0);

    // loop through all addressed
    for (dwAddress = 0; dwAddress < gdwAddresses; dwAddress++)
    {
        // get the name of the address
        pLAC = LineGetAddressCaps(ghLineApp,
                                  gdwDeviceID,
                                  dwAddress);

        if (!pLAC || !pLAC->dwAddressSize)
        {
            wsprintf(szBuffer,
                     TEXT("Address %lu"),
                     dwAddress);
        }
        else
        {
            lstrcpy(szBuffer,
                    (LPTSTR)(((LPBYTE)pLAC)+pLAC->dwAddressOffset));
        }

        if (pLAC)
        {
            GlobalFree(pLAC);
        }

        w = staticx;
        x = xstart;
        // create the static control
        hWnd = CreateWindow(TEXT("STATIC"),
                     szBuffer,
                     WS_CHILD | SS_LEFT | WS_VISIBLE,
                     x,y+(buttony/3),w,h,
                     ghMainWnd,
                     NULL,
                     ghInstance,
                     NULL);

        // set the font
        SendMessage(hWnd,
                    WM_SETFONT,
                    (WPARAM)hFont,
                    0);

        x += staticx;
        w = buttonx;
        // create the status button
        pAddressInfo[dwAddress].hStatus = CreateWindow(TEXT("BUTTON"),
                     TEXT("Set Status..."),
                     WS_CHILD | BS_PUSHBUTTON | WS_VISIBLE,
                     x,y,w,h,
                     ghMainWnd,
                     NULL,
                     ghInstance,
                     NULL);

        // set the font
        SendMessage(pAddressInfo[dwAddress].hStatus,
                    WM_SETFONT,
                    (WPARAM)hFont,

0);

        x += buttonx + buttongap;

        // create the answer/drop button
        pAddressInfo[dwAddress].hAnswer = CreateWindow(TEXT("BUTTON"),
                     TEXT("Answer"),
                     WS_CHILD | WS_DISABLED | BS_PUSHBUTTON  | WS_VISIBLE,
                     x,y,w,h,
                     ghMainWnd,
                     NULL,
                     ghInstance,
                     NULL);

        // set the font
        SendMessage(pAddressInfo[dwAddress].hAnswer,
                    WM_SETFONT,
                    (WPARAM)hFont,
                    0);

        y += buttony + linegap;
    }

    // adjust position of message static control
    SetWindowPos(GetDlgItem(ghMainWnd,
                            IDC_STATIC1),
                  NULL,
                  xstart,y,0,0,
                  SWP_NOZORDER | SWP_NOSIZE);

    // adjust the size of th main window
    SetWindowPos(ghMainWnd,
                 NULL,
                 0,0,xstart+staticx+buttonx+buttonx+buttongap+50,y+50,
                 SWP_NOZORDER | SWP_NOMOVE);

    return TRUE;

}


ACDCLNT.H

/////////////////////////////////////////////////////////////////////////
//
//  ACDCLNT.H
//
/////////////////////////////////////////////////////////////////////////
#include <windows.h>
#include <tapi.h>

#define SZAPPNAME       TEXT("ACDClient")

typedef struct _tagADDRESSINFO
{
    HWND        hStatus;
    HWND        hAnswer;
    HCALL       hCall;
    BOOL        bCall;

} ADDRESSINFO, * PADDRESSINFO;

LINEAGENTGROUPLIST * LineGetAgentGroupList (HLINE    hLine,
                                            DWORD    dwAddressID);

LINEAGENTSTATUS * LineGetAgentStatus (HLINE    hLine,
                                      DWORD    dwAddressID);

LINEAGENTCAPS * LineGetAgentCaps (HLINEAPP hLineApp,
                                  DWORD    dwDeviceID,
                                  DWORD    dwAddressID);

LPLINEAGENTACTIVITYLIST LineGetAgentActivityList (HLINE    hLine,
                                                  DWORD    dwDeviceID,
                                                  DWORD    dwAddressID);

LINEADDRESSCAPS * LineGetAddressCaps (HLINEAPP hLineApp,
                                      DWORD    dwDeviceID,
                                      DWORD    dwAddressID);

LINECALLINFO * LineGetCallInfo (HCALL hCall);

LINEDEVCAPS * LineGetDevCaps (HLINEAPP hLineApp,
                              DWORD    dwDeviceID);

VARSTRING * LineGetID (HLINE  hLine,
                       DWORD  dwAddressID,
                       HCALL  hCall,
                       DWORD  dwSelect,
                       LPCTSTR lpszDeviceClass);

LINECALLSTATUS * LineGetCallStatus (HCALL hCall);


RESOURCE.H

//
#define IDD_MAINDLG                     101
#define IDD_AGENTSTATE                  102
#define IDC_ANSWER1                     1012
#define IDC_STATUS1                     1016
#define IDC_STATE                       1018
#define IDC_GROUPS                      1019
#define IDC_ACTIVITY                    1020
#define IDC_NEXTSTATE                   1021
#define IDC_STATIC1                     1022
#define IDC_STATIC                      -1

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        103
#define _APS_NEXT_COMMAND_VALUE         40001
#define _APS_NEXT_CONTROL_VALUE         1022
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif


ACDSMPL.C

//////////////////////////////////////////////////////////////////////////////
//
//  ACDSMPL.C
//
//  Handles all the UI for ACDSample
//
//////////////////////////////////////////////////////////////////////////////

#include <windows.h>
#include <commctrl.h>
#include <commdlg.h>
#include <tapi.h>
#include <stdlib.h>
#include "resource.h"
#include "acdsmpl.h"

//////////////////////////////////////////////////////////////////////////////
//  PROTOTYPES
//////////////////////////////////////////////////////////////////////////////
static BOOL CreateMainWindow (int nCmdShow);

static LRESULT CALLBACK MainDlgProc (HWND   hwnd,
                                     UINT   uMsg,
                                     WPARAM wParam,
                                     LPARAM lParam);
void MySetWindow(HWND, int);
void MySaveWindow(HWND);
BOOL ResizeWindows(BOOL bSizeBar, DWORD dwBarLocation);
HTREEITEM AddItemToTree(HTREEITEM hParent,
                        LPTSTR lpszName,
                        LPARAM lParam,
                        HTREEITEM * phItem);
BOOL DoPopupMenu(HTREEITEM hItem, POINT pt);
BOOL DeleteLeafAndStruct(HTREEITEM hItem);
BOOL CALLBACK ChangeGroupDlgProc(HWND   hWnd,
                            UINT   uMsg,
                            WPARAM wParam,
                            LPARAM lParam);
BOOL CALLBACK ChangeAgentDlgProc(HWND   hWnd,
                            UINT   uMsg,
                            WPARAM wParam,
                            LPARAM lParam);
BOOL CALLBACK AddGroupDlgProc(HWND   hWnd,
                         UINT   uMsg,
                         WPARAM wParam,
                         LPARAM lParam);
BOOL CALLBACK AddAgentDlgProc(HWND   hWnd,
                         UINT   uMsg,
                         WPARAM wParam,
                         LPARAM lParam);
LRESULT DoCommand(WPARAM wParam, LPARAM lParam);
void AddGroupsToMenu(HTREEITEM hItem,
                     HMENU hMenu);
BOOL CALLBACK GroupAddToListProc(HWND   hWnd,
                            UINT   uMsg,
                            WPARAM wParam,
                            LPARAM lParam);
BOOL CALLBACK AgentAddToListProc(HWND   hWnd,
                            UINT   uMsg,
                            WPARAM wParam,
                            LPARAM lParam);
BOOL BuildLineList(HWND hWnd,
                   DWORD dwDeviceID);
BOOL BuildAddressList(HWND hWnd,
                      HWND hParentWnd,
                      DWORD dwDeviceID);
BOOL InitializeTapi();
BOOL CleanUp();
BOOL UpdateGroupLeaf(PGROUP pGroup);
BOOL DoAgentView();
BOOL DoGroupView();
BOOL ReadInFile();
BOOL WriteToDisk();

//////////////////////////////////////////////////////////////////////////////
//  GLOBALS
//////////////////////////////////////////////////////////////////////////////
ACDGLOBALS      g;

TCHAR gszACDSampleKey[]     = TEXT("Software\\Microsoft\\ACDSample");
TCHAR gszPlacementValue[]   = TEXT("WindowPlacement");
TCHAR gszBarLocation[]      = TEXT("BarLocation");

//////////////////////////////////////////////////////////////////////////////
//
// WinMain()
//
//////////////////////////////////////////////////////////////////////////////
int WINAPI WinMain (HINSTANCE hInstance,
                    HINSTANCE hPrevInstance,
                    LPSTR     lpszCmdLine,
                    int       nCmdShow)
{
    MSG msg;

    // initialize global variables
    g.hInstance = hInstance;
    g.pAgents = NULL;
    g.pGroups = NULL;

    // init tapi stuff
    if (!InitializeTapi())
    {
        MessageBox(NULL,
                   TEXT("TAPI could not be initialized.\nVerify that")
                   TEXT("your machine has TAPI devices installed"),
                   TEXT("Cannot start ACDSMPL"),
                   MB_OK);
    }

    if (!CreateMainWindow(nCmdShow))
    {
        return 0;
    }

    // main message loop
    while (GetMessage(&msg, NULL, 0, 0))
    {
        if (!IsDialogMessage(g.hMainWnd,
                             &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return 1;
}

/////////////////////////////////////////////////////////////////////////////
//
//  CreateMainWindow()
//
//////////////////////////////////////////////////////////////////////////////
BOOL CreateMainWindow (int nCmdShow)
{

    // InitCommonControls for TreeView control
    InitCommonControls();

    // Create the main window
    g.hMainWnd = CreateDialog(g.hInstance,
                             MAKEINTRESOURCE(IDD_MAINDLG),
                             NULL,
                             MainDlgProc);

    if (g.hMainWnd == NULL)
    {
        return FALSE;
    }

    // restore default location
    MySetWindow(g.hMainWnd, nCmdShow);

    // store global hwnds
    g.hTreeWnd = GetDlgItem(g.hMainWnd,
                            IDC_TREEWND);

    g.hLogWnd = GetDlgItem(g.hMainWnd,
                           IDC_EDITWND);

    if ((g.hTreeWnd == FALSE) || (g.hLogWnd == FALSE))
    {
        return FALSE;
    }

    ResizeWindows(FALSE, 0);

    return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
//
//  MainDlgProc()
//
//////////////////////////////////////////////////////////////////////////////
LRESULT CALLBACK MainDlgProc (HWND   hWnd,
                              UINT   uMsg,
                              WPARAM wParam,
                              LPARAM lParam)
{
    static BOOL     bButtonDown = FALSE;

    switch (uMsg)
    {
        case WM_INITDIALOG:
            return TRUE;

        case WM_COMMAND:
        {
            LRESULT lResult;

            lResult = DoCommand(wParam, lParam);
            return lResult;
        }

        // button and mousemove messages tracked to move
        // the bar between the treeview control and the
        // edit control
        case WM_LBUTTONDOWN:
        {
            bButtonDown = TRUE;
            SetCapture(hWnd);
            return 0;
        }

        case WM_LBUTTONUP:
        {
            bButtonDown = FALSE;
            ReleaseCapture();
            return 0;
        }

        case WM_MOUSEMOVE:
        {
            if (bButtonDown)
            {
                ResizeWindows(TRUE, (DWORD)LOWORD(lParam));
                return 1;
            }
            break;
        }

        case WM_SIZE:
        {
            ResizeWindows(FALSE, 0);
            return 1;
        }

        // catch right click in tree view to make
        // popup menu
        case WM_NOTIFY:
        {
            LPNMHDR     pnmhdr;
            POINT       pt;
            HTREEITEM   hItem;
            TV_HITTESTINFO  hittestinfo;
            RECT        rc;

            pnmhdr = (LPNMHDR)lParam;

            // make sure it's a right click and it's in the treeview
            if ((pnmhdr->code != NM_RCLICK) || (pnmhdr->hwndFrom != g.hTreeWnd))
            {
                break;
            }

            GetCursorPos(&pt);
            GetWindowRect(g.hTreeWnd,
                          &rc);

            hittestinfo.pt.x = pt.x - rc.left;
            hittestinfo.pt.y = pt.y - rc.top;

            // hittest to get the tree view item
            hItem = TreeView_HitTest(g.hTreeWnd,
                                     &hittestinfo);

            // only display a menu if the mouse is actually
            // over the item (TVHT_ONITEM)
            if (hItem == NULL || (!(hittestinfo.flags & TVHT_ONITEM)) )
            {
                return TRUE;
            }

            // select that item (right clicking will not select
            // by default
            TreeView_Select(g.hTreeWnd,
                            hItem,
                            TVGN_CARET);

            // create the menu
            DoPopupMenu(hItem, pt);

            return TRUE;

        }

        case WM_CLOSE:

            // save the current window location
            WriteToDisk();
            CleanUp();
            MySaveWindow(hWnd);
            PostQuitMessage(0);
            return 1;

        default:
            break;
    }
    return 0;
}

////////////////////////////////////////////////////////////////////////////////
//
//  ResizeWindows - Handles resizing the two child windows of the
//      main window.  If bSizeBar is true, then the sizing is happening
//      because the user is moving the bar.  if bSizeBar is false, the sizing
//      is happening because of the WM_SIZE or something like that.
//
////////////////////////////////////////////////////////////////////////////////
BOOL ResizeWindows(BOOL bSizeBar, DWORD dwBarLocation)
{
    RECT            rc, rc2;
    int             x;

    // is the user moving the bar?
    if (!bSizeBar)
    {
        dwBarLocation = g.dwBarLocation;
    }

    GetClientRect(g.hMainWnd, &rc);

    // make sure the bar is in a OK location
    if (bSizeBar)
    {
        if ((LONG)dwBarLocation < GetSystemMetrics(SM_CXSCREEN)/WINDOWSCALEFACTOR)
            return FALSE;

        if ((LONG)(rc.right - dwBarLocation) < GetSystemMetrics(SM_CXSCREEN)/WINDOWSCALEFACTOR)
            return FALSE;
    }

    // save the bar location
    g.dwBarLocation = dwBarLocation;

    // get the size of the frame
    x = GetSystemMetrics(SM_CXFRAME);

    // move tree windows
    MoveWindow(g.hTreeWnd,
               0,
               0,
               dwBarLocation,
               rc.bottom,
               TRUE);

    // get the size of the window (in case move window failed
    GetClientRect(g.hTreeWnd, &rc2);

    // move the edit window with respect to the tree window
    MoveWindow(g.hLogWnd,
               rc2.right-rc2.left+x+SIZEBAR,
               0,
               rc.right-(rc2.right-rc2.left)-x-SIZEBAR,
               rc.bottom,
               TRUE);

    return TRUE;
}

//////////////////////////////////////////////////////////////////////////////
//
//  MySetWindow - reads in the window placement from registry
//  and sets the window and bar.
//
//////////////////////////////////////////////////////////////////////////////
void MySetWindow(HWND hWnd, int nCmdShow)
{
    WINDOWPLACEMENT     pwp;
    HKEY                hKey;
    DWORD               dwDataSize;
    DWORD               dwDataType;
    RECT                rc;

    pwp.length = sizeof(WINDOWPLACEMENT);

    // open the key and read in the WINDOWPLACEMENT structure
    RegOpenKeyEx(HKEY_CURRENT_USER,
                 gszACDSampleKey,
                 0,
                 KEY_ALL_ACCESS,
                 &hKey);

    dwDataSize = sizeof(pwp);

    if ( RegQueryValueEx(hKey,
                         gszPlacementValue,
                         0,
                         &dwDataType,
                         (LPBYTE)&pwp,
                         &dwDataSize) )
    {
        // if it fails, default
        ShowWindow(g.hMainWnd, nCmdShow);
        GetWindowRect(g.hMainWnd, &rc);
        g.dwBarLocation = (rc.right - rc.left) / 2;
    }
    else
    {
        // if it succeeds, set the window and bar
        dwDataSize = sizeof(DWORD);

        if (RegQueryValueEx(hKey,
                            gszBarLocation,
                            0,
                            &dwDataType,
                            (LPBYTE)&g.dwBarLocation,
                            &dwDataSize))
        {
            g.dwBarLocation = (pwp.rcNormalPosition.right - pwp.rcNormalPosition.left) / 2;
        }

        SetWindowPlacement(g.hMainWnd, &pwp);
    }

    RegCloseKey( hKey );
}

//////////////////////////////////////////////////////////////////////////////
//
//  MySaveWindow() - save the current window placement and bar
//
//////////////////////////////////////////////////////////////////////////////
void MySaveWindow(HWND hWnd)
{
    WINDOWPLACEMENT     pwp;
    HKEY                hKey;
    DWORD               dwDisposition;

    pwp.length = sizeof(WINDOWPLACEMENT);

    // get and save
    GetWindowPlacement(hWnd, &pwp);

    RegCreateKeyEx(HKEY_CURRENT_USER,
                   gszACDSampleKey,
                   0,
                   TEXT(""),
                   REG_OPTION_NON_VOLATILE,
                   KEY_ALL_ACCESS,
                   0,
                   &hKey,
                   &dwDisposition);

    RegSetValueEx(hKey,
                  gszPlacementValue,
                  0,
                  REG_BINARY,
                  (LPBYTE)&pwp,
                  sizeof(WINDOWPLACEMENT));

    RegSetValueEx(hKey,
                  gszBarLocation,
                  0,
                  REG_DWORD,
                  (LPBYTE)&g.dwBarLocation,
                  sizeof(DWORD));

    RegCloseKey( hKey );

}

//////////////////////////////////////////////////////////////////////////////
//
//  AddItemToTree
//
//    add a new leaf to the tree
//
//////////////////////////////////////////////////////////////////////////////
HTREEITEM AddItemToTree(HTREEITEM hParent,
                        LPTSTR lpszName,
                        LPARAM lParam,
                        HTREEITEM * phItem)
{
    TV_ITEM         tvi;
    TV_INSERTSTRUCT tvins;
    HTREEITEM       hti;

    tvi.mask = TVIF_TEXT | TVIF_PARAM;

    // Set the text of the item.
    tvi.pszText = lpszName;
    tvi.cchTextMax = lstrlen(lpszName) * sizeof(TCHAR);

    // Save the pointer to the buffer
    tvi.lParam = lParam;

    tvins.item = tvi;
    tvins.hInsertAfter = TVI_SORT;

    // Set the parent item
    tvins.hParent = hParent;

    // Add the item to the tree-view control.
    hti = (HTREEITEM) SendMessage(g.hTreeWnd,
                                  TVM_INSERTITEM,
                                  0,
                                  (LPARAM) (LPTV_INSERTSTRUCT) &tvins);

    // save hitem
    if (phItem)
    {
        *phItem = hti;
    }

    // select the item so it has focus
    TreeView_Select(g.hTreeWnd,
                    hti,
                    TVGN_CARET);

    return hti;
}

//////////////////////////////////////////////////////////////////////////////
//
//  DoPopupMenu(HTREEITEM hItem,
//              POINT pt)
//
//      hItem - item to create menu for
//      pt - location of mouse so we can create menu where it is
//
//   creates a popup menu, depending on what kind of item is selected
//
//////////////////////////////////////////////////////////////////////////////
BOOL DoPopupMenu(HTREEITEM hItem, POINT pt)
{

    HMENU       hMenu;
    TV_ITEM     tvi;
    TCHAR       szNewGroup[]        = TEXT("&New Group...");
    TCHAR       szNewAgent[]        = TEXT("New &Agent...");
    TCHAR       szAddAgent[]        = TEXT("A&dd Agent...");
    TCHAR       szGroupProperties[] = TEXT("&Group Properties...");
    TCHAR       szAgentStatus[]     = TEXT("Agent Status...");
    TCHAR       szAddGroup[]        = TEXT("Add Group...");
    TCHAR       szAgentProperties[] = TEXT("Agent Properties...");
    TCHAR       szGroupDelete[]     = TEXT("Group Delete");
    TCHAR       szAgentDelete[]     = TEXT("Agent Delete");
    TCHAR       szSignIn[]          = TEXT("Agent Sign In");
    TCHAR       szSignOut[]         = TEXT("Agent Sign Out");

    // get the selected item
    g.hTreeItemWithMenu = hItem;

    // create the menu
    hMenu = CreatePopupMenu();

    // get the lParam, which is a pointer to the item
    tvi.mask = TVIF_HANDLE | TVIF_PARAM;
    tvi.hItem = hItem;

    TreeView_GetItem(g.hTreeWnd,
                     &tvi);

    if (!tvi.lParam)
    {
        return TRUE;
    }

    switch (((PGROUP)tvi.lParam)->dwKey)
    {
        // root item
        case GROUPROOTKEY:

            AppendMenu(hMenu,
                       MF_ENABLED | MF_STRING,
                       IDM_NEWGROUP,
                       szNewGroup);
            break;

        // root item
        case AGENTROOTKEY:

            AppendMenu(hMenu,
                       MF_ENABLED | MF_STRING,
                       IDM_NEWAGENT,
                       szNewAgent);
            break;
        // group leaf
        case GROUPKEY:

            AppendMenu(hMenu,
                       MF_ENABLED | MF_STRING,
                       (UINT)IDM_GROUPADDTOLIST,
                       szAddAgent);

            AppendMenu(hMenu,
                       MF_ENABLED | MF_STRING,
                       (UINT)IDM_GROUPAGENTSTATUS,
                       szAgentStatus);

            AppendMenu(hMenu,
                       MF_ENABLED | MF_STRING,
                       IDM_GROUPPROPERTIES,
                       szGroupProperties);

            AppendMenu(hMenu,
                       MF_ENABLED | MF_STRING,
                       IDM_GROUPDELETE,
                       szGroupDelete);

            break;
        // agent leaf
        case AGENTKEY:

            AppendMenu(hMenu,
                       MF_ENABLED | MF_STRING,
                       (UINT)IDM_AGENTADDTOLIST,
                       szAddGroup);

            AppendMenu(hMenu,
                       MF_ENABLED | MF_STRING,
                       IDM_AGENTPROPERTIES,
                       szAgentProperties);

            AppendMenu(hMenu,
                       MF_ENABLED | MF_STRING,
                       IDM_AGENTDELETE,
                       szAgentDelete);

            break;

        default:
            break;
    }

    // actually show menu
    TrackPopupMenu(hMenu,
                   TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON,
                   pt.x,
                   pt.y,
                   0,
                   g.hMainWnd,
                   NULL);

    return TRUE;
}

///////////////////////////////////////////////////////////////////////////////
//
//  LRESULT DoCommand(WPARAM wParam, LPARAM lParam)
//      handle WM_COMMAND messages for MainDlgProc
//
///////////////////////////////////////////////////////////////////////////////
LRESULT DoCommand(WPARAM wParam, LPARAM lParam)
{
    switch (LOWORD(wParam))
    {
        // New - create a new tree, so just init
        // the items and return
        case ID_FILE_NEW:
            DoGroupView();
            return 1;

        // Open - read in a file
        case ID_FILE_OPEN:
            ReadInFile();
            return 1;

        case ID_FILE_EXIT:
            // save the current window location
            WriteToDisk();
            CleanUp();
            MySaveWindow(g.hMainWnd);
            PostQuitMessage(0);
            return 1;

        // new group
        case ID_EDIT_ADDGROUP:
        case IDM_NEWGROUP:
            DialogBox(g.hInstance,
                      MAKEINTRESOURCE(IDD_ADD),
                      g.hTreeWnd,
                      AddGroupDlgProc);

            return 1;

        // new agent
        case ID_EDIT_ADDAGENT:
        case IDM_NEWAGENT:
            DialogBox(g.hInstance,
                      MAKEINTRESOURCE(IDD_ADDAGENT),
                      g.hTreeWnd,
                      AddAgentDlgProc);
            return 1;

        // properties
        case IDM_GROUPPROPERTIES:
            DialogBox(g.hInstance,
                      MAKEINTRESOURCE(IDD_ADD),
                      g.hMainWnd,
                      ChangeGroupDlgProc);

            return 1;

        // properties
        case IDM_AGENTPROPERTIES:
            DialogBox(g.hInstance,
                      MAKEINTRESOURCE(IDD_ADDAGENT),
                      g.hMainWnd,
                      ChangeAgentDlgProc);

            return 1;

        // delete
        case IDM_GROUPDELETE:
        case IDM_AGENTDELETE:
        {
            DeleteLeafAndStruct(g.hTreeItemWithMenu);

            return 1;
        }

        // add to list
        case IDM_GROUPADDTOLIST:
            DialogBoxParam(g.hInstance,
                           MAKEINTRESOURCE(IDD_ADDTOLIST),
                           g.hMainWnd,
                           GroupAddToListProc,
                           TRUE);

            return 1;

        // add to list
        case IDM_AGENTADDTOLIST:
            DialogBoxParam(g.hInstance,
                           MAKEINTRESOURCE(IDD_ADDTOLIST),
                           g.hMainWnd,
                           AgentAddToListProc,
                           FALSE);

            return 1;

        case ID_VIEW_GROUP:
            DoGroupView();
            return 1;

        case ID_VIEW_AGENT:
            DoAgentView();
            return 1;

        default:
            break;

    }

    return 0;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////
//
//  AddGroupDlgProc - Window proc for the add agent/group dialog box
//
/////////////////////////////////////////////////////////////////////////////////////////////////////
BOOL CALLBACK AddGroupDlgProc(HWND   hWnd,
                              UINT   uMsg,
                              WPARAM wParam,
                              LPARAM lParam)
{
    switch (uMsg)
    {
        case WM_INITDIALOG:

            // set text appropriately
            SetWindowText(hWnd,
                          TEXT("Add Group"));

            BuildLineList(GetDlgItem(hWnd,
                                     IDC_LINECOMBO),
                          0);

            BuildAddressList(GetDlgItem(hWnd,
                                        IDC_ADDRESSCOMBO),
                             hWnd,
                             0);

            // set focus on first control
            SetFocus(GetDlgItem(hWnd,
                                IDC_NAME));

            return 0;

        case WM_COMMAND:

            switch (LOWORD(wParam))
            {
                case IDC_LINECOMBO:
                {
                    if (HIWORD(wParam) == CBN_SELENDOK)
                    {
                        // need to redo addresses
                        BuildAddressList(GetDlgItem(hWnd,
                                                    IDC_ADDRESSCOMBO),
                                         hWnd,
                                         0);

                        return 1;
                    }

                    return 0;
                }
                case IDOK:
                {
                    TCHAR       szName[128];
                    PGROUP      pGroup;
                    DWORD       dwLine, dwAddress;
                    int         item;

                    // get info
                    SendDlgItemMessage(hWnd,
                                       IDC_NAME,
                                       WM_GETTEXT,
                                       128,
                                       (LPARAM)szName);

                    item = SendDlgItemMessage(hWnd,
                                              IDC_LINECOMBO,
                                              CB_GETCURSEL,
                                              0,
                                              0);

                    dwLine = (DWORD)SendDlgItemMessage(hWnd,
                                                       IDC_LINECOMBO,
                                                       CB_GETITEMDATA,
                                                       item,
                                                       0);

                    dwAddress = (DWORD)SendDlgItemMessage(hWnd,
                                                          IDC_ADDRESSCOMBO,
                                                          CB_GETCURSEL,
                                                          0,
                                                          0);

                    // create a structure
                    pGroup = AddGroup(szName,
                                      dwLine,
                                      dwAddress);

                    if (!pGroup)
                    {
                        return 1;
                    }

                    if (g.bGroupView)
                    {
                        // add it to the tree
                        AddItemToTree(g.hGroupParent,
                                      pGroup->lpszName,
                                      (LPARAM)pGroup,
                                      &pGroup->hItem);
                    }

                    EndDialog(hWnd, 1);
                    return 1;
                }

                case IDCANCEL:
                {
                    EndDialog(hWnd, 0);
                    return 1;
                }

                default:
                    return 0;

            }

    }

    return 0;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////
//
//  AddAgentDlgProc - Window proc for the add agent/group dialog box
//
/////////////////////////////////////////////////////////////////////////////////////////////////////
BOOL CALLBACK AddAgentDlgProc(HWND   hWnd,
                              UINT   uMsg,
                              WPARAM wParam,
                              LPARAM lParam)
{
    switch (uMsg)
    {
        case WM_INITDIALOG:

            BuildLineList(GetDlgItem(hWnd,
                                     IDC_LINECOMBO),
                          0);

            // set focus on first control
            SetFocus(GetDlgItem(hWnd,
                                IDC_NAME));

            return 0;

        case WM_COMMAND:

            switch (LOWORD(wParam))
            {
                case IDOK:
                {
                    TCHAR       szName[128];
                    TCHAR       szNumber[128];
                    PAGENT      pAgent;
                    DWORD       dwLine;
                    int         item;

                    // get info
                    SendDlgItemMessage(hWnd,
                                       IDC_NAME,
                                       WM_GETTEXT,
                                       128,
                                       (LPARAM)szName);

                    SendDlgItemMessage(hWnd,
                                       IDC_DESTADDRESS,
                                       WM_GETTEXT,
                                       128,
                                       (LPARAM)szNumber);

                    item = SendDlgItemMessage(hWnd,
                                              IDC_LINECOMBO,
                                              CB_GETCURSEL,
                                              0,
                                              0);

                    dwLine = (DWORD)SendDlgItemMessage(hWnd,
                                                       IDC_LINECOMBO,
                                                       CB_GETITEMDATA,
                                                       item,
                                                       0);

                    // create a structure
                    pAgent = AddAgent(szName,
                                      szNumber,
                                      dwLine);

                    if (!pAgent)
                    {
                        return 1;
                    }

                    if (!g.bGroupView)
                    {
                    // add it to the tree
                        AddItemToTree(g.hAgentParent,
                                      pAgent->lpszName,
                                      (LPARAM)pAgent,
                                      &pAgent->hItem);
                    }

                    EndDialog(hWnd, 1);
                    return 1;
                }

                case IDCANCEL:
                {
                    EndDialog(hWnd, 0);
                    return 1;
                }

                default:
                    return 0;

}
    }
    return 0;
}

//////////////////////////////////////////////////////////////////////////////////
//
//  ChangeGroupDlgProc - Window proc for the change (properties) dialog box
//     for agents/groups
//
//////////////////////////////////////////////////////////////////////////////////
BOOL CALLBACK ChangeGroupDlgProc(HWND   hWnd,
                                 UINT   uMsg,
                                 WPARAM wParam,
                                 LPARAM lParam)
{
    static TV_ITEM      tvi;

    switch (uMsg)
    {
        case WM_INITDIALOG:

            // set text appropriately
            SetWindowText(hWnd,
                          TEXT("Change Group"));

            // get PGROUP and set edit controls
            tvi.mask    = TVIF_HANDLE | TVIF_PARAM;
            tvi.hItem   = g.hTreeItemWithMenu;

            TreeView_GetItem(g.hTreeWnd,
                             &tvi);

            SendDlgItemMessage(hWnd,
                               IDC_NAME,
                               WM_SETTEXT,
                               0,
                               (LPARAM)((PGROUP)tvi.lParam)->lpszName);

            BuildLineList(GetDlgItem(hWnd,
                                     IDC_LINECOMBO),
                          (((PGROUP)tvi.lParam)->dwDeviceID));

            BuildAddressList(GetDlgItem(hWnd,
                                        IDC_ADDRESSCOMBO),
                             hWnd,
                             (((PGROUP)tvi.lParam)->dwAddress));

            SetFocus(GetDlgItem(hWnd,
                                IDC_NAME));

            return 0;

        case WM_COMMAND:

            switch (LOWORD(wParam))
            {
                case IDC_LINECOMBO:
                {
                    if (HIWORD(wParam) == CBN_SELENDOK)
                    {
                        // need to redo addresses
                        BuildAddressList(GetDlgItem(hWnd,
                                                    IDC_ADDRESSCOMBO),
                                         hWnd,
                                         0);

                        return 1;
                    }

                    return 0;
                }
                case IDOK:
                {
                    TCHAR       szName[128];
                    PGROUP      pGroup;

                    // get info
                    SendDlgItemMessage(hWnd,
                                       IDC_NAME,
                                       WM_GETTEXT,
                                       128,
                                       (LPARAM)szName);

                    // get struct
                    pGroup = (PGROUP)tvi.lParam;

                    /// get device and address
                    pGroup->dwDeviceID = SendDlgItemMessage(hWnd,
                                            IDC_LINECOMBO,
                                            CB_GETCURSEL,
                                            0,
                                            0);

                    pGroup->dwDeviceID = SendDlgItemMessage(hWnd,
                                                            IDC_LINECOMBO,
                                                            CB_GETITEMDATA,
                                                            (WPARAM)pGroup->dwDeviceID,
                                                            0);

                    pGroup->dwAddress = SendDlgItemMessage(hWnd,
                                                           IDC_ADDRESSCOMBO,
                                                           CB_GETCURSEL,
                                                           0,
                                                           0);

                    // save new info and free old info
                    ACDFree(pGroup->lpszName);
                    pGroup->lpszName = ACDAlloc((lstrlen(szName) + 1) * sizeof(TCHAR));
                    lstrcpy(pGroup->lpszName, szName);

                    // update item name
                    tvi.mask = TVIF_TEXT;
                    tvi.pszText = szName;
                    tvi.cchTextMax = lstrlen(szName) * sizeof(TCHAR);
                    TreeView_SetItem(g.hTreeWnd,
                                     &tvi);

                    EndDialog(hWnd, 1);
                    return 1;
                }

                case IDCANCEL:
                {
                    EndDialog(hWnd, 0);
                    return 1;
                }

                default:
                    return 0;

            }
    }
    return 0;
}

//////////////////////////////////////////////////////////////////////////////////
//
//  ChangeGroupDlgProc - Window proc for the change (properties) dialog box
//     for agents/groups
//
//////////////////////////////////////////////////////////////////////////////////
BOOL CALLBACK ChangeAgentDlgProc(HWND   hWnd,
                                 UINT   uMsg,
                                 WPARAM wParam,
                                 LPARAM lParam)
{
    static TV_ITEM      tvi;

    switch (uMsg)
    {
        case WM_INITDIALOG:

            // set text appropriately
            SetWindowText(hWnd,
                          TEXT("Change Agent"));

            // get PGROUP and set edit controls
            tvi.mask    = TVIF_HANDLE | TVIF_PARAM;
            tvi.hItem   = g.hTreeItemWithMenu;

            TreeView_GetItem(g.hTreeWnd,
                             &tvi);

            SendDlgItemMessage(hWnd,
                               IDC_NAME,
                               WM_SETTEXT,
                               0,
                               (LPARAM)((PAGENT)tvi.lParam)->lpszName);

            SendDlgItemMessage(hWnd,
                               IDC_DESTADDRESS,
                               WM_SETTEXT,
                               0,
                               (LPARAM)((PAGENT)tvi.lParam)->lpszNumber);

            BuildLineList(GetDlgItem(hWnd,
                                     IDC_LINECOMBO),
                          (((PAGENT)tvi.lParam)->dwDeviceID));

            SetFocus(GetDlgItem(hWnd,
                                IDC_NAME));

            return 0;

        case WM_COMMAND:

            switch (LOWORD(wParam))
            {
                case IDOK:
                {
                    TCHAR       szName[128];
                    TCHAR       szNumber[128];
                    PAGENT      pAgent;

                    // get info
                    SendDlgItemMessage(hWnd,
                                       IDC_NAME,
                                       WM_GETTEXT,
                                       128,
                                       (LPARAM)szName);

                    SendDlgItemMessage(hWnd,
                                       IDC_DESTADDRESS,
                                       WM_GETTEXT,
                                       128,
                                       (LPARAM)szNumber);

                    // get struct
                    pAgent = (PAGENT)tvi.lParam;

                    /// get device and address
                    pAgent->dwDeviceID = SendDlgItemMessage(hWnd,
                                            IDC_LINECOMBO,
                                            CB_GETCURSEL,
                                            0,
                                            0);

                    pAgent->dwDeviceID = SendDlgItemMessage(hWnd,
                                                            IDC_LINECOMBO,
                                                            CB_GETITEMDATA,
                                                            (WPARAM)pAgent->dwDeviceID,
                                                            0);

                    // save new info and free old info
                    ACDFree(pAgent->lpszName);
                    pAgent->lpszName = ACDAlloc((lstrlen(szName) + 1) * sizeof(TCHAR));
                    lstrcpy(pAgent->lpszName, szName);

                    ACDFree(pAgent->lpszNumber);
                    pAgent->lpszNumber = ACDAlloc((lstrlen(szNumber) + 1) * sizeof(TCHAR));
                    lstrcpy(pAgent->lpszNumber, szNumber);

                    // update item name
                    tvi.mask = TVIF_TEXT;
                    tvi.pszText = szName;
                    tvi.cchTextMax = lstrlen(szName) * sizeof(TCHAR);
                    TreeView_SetItem(g.hTreeWnd,
                                     &tvi);

                    EndDialog(hWnd, 1);
                    return 1;
                }

                case IDCANCEL:
                {
                    EndDialog(hWnd, 0);
                    return 1;
                }

                default:
                    return 0;

            }
    }
    return 0;
}

//////////////////////////////////////////////////////////////////////////////////////
//
//  AddToList() - Window proc for Add Agent To Group and Add Group To Agent
//    dialog box
//
//////////////////////////////////////////////////////////////////////////////////////
BOOL CALLBACK GroupAddToListProc(HWND   hWnd,
                                 UINT   uMsg,
                                 WPARAM wParam,
                                 LPARAM lParam)
{
    static PGROUP           pGroup;
    PAGENT                  pAgent;
    PLISTITEM               pList;
    TV_ITEM                 tvi;
    DWORD                   dwListBox;
    BOOL                    bFound;
    int                     item;
    TCHAR                   szBuffer[128];

    switch (uMsg)
    {
        case WM_INITDIALOG:

            // get the item in question
            tvi.mask    = TVIF_HANDLE | TVIF_PARAM;
            tvi.hItem   = g.hTreeItemWithMenu;

            TreeView_GetItem(g.hTreeWnd,
                             &tvi);

            pGroup = (PGROUP)tvi.lParam;

            // init lists
            if (pGroup)
            {
                // initialize text in dialog
                wsprintf(szBuffer, TEXT("Add To %s"), pGroup->lpszName);

                SetWindowText(hWnd,
                              TEXT("Add To Group"));

                SetDlgItemText(hWnd,
                               IDC_STATICNOTINLIST,
                               TEXT("Not in Group"));
                SetDlgItemText(hWnd,
                               IDC_STATICINLIST,
                               TEXT("Group Members"));

                pAgent = g.pAgents;

                // walk list and initialize list boxes
                while (pAgent)
                {
                    pList = pGroup->pAgentList;

                    bFound = FALSE;

                    while (pList)
                    {
                        if (pList->pAgent == pAgent)
                        {
                            bFound = TRUE;
                            break;
                        }

                        pList = pList->pNext;
                    }

                    // if it was found, it is already a member of
                    // the group
                    if (bFound)
                    {
                        dwListBox = IDC_LIST2;
                    }
                    else
                    {
                        dwListBox = IDC_LIST1;
                    }

                    // add to correct list box
                    item = SendDlgItemMessage(hWnd,
                                              dwListBox,
                                              LB_ADDSTRING,
                                              0,
                                              (LPARAM)pAgent->lpszName);

                    // set the item data to be the item so we can get back it.
                    if (item != LB_ERR)
                    {
                        SendDlgItemMessage(hWnd,
                                           dwListBox,
                                           LB_SETITEMDATA,
                                           (WPARAM)item,
                                           (LPARAM)pAgent);
                    }

                    pAgent = pAgent->pNext;
                }
            }

            return 1;

        case WM_COMMAND:

            switch (LOWORD(wParam))
            {
                case IDC_ADD:
                {
                    // get the item
                    item = SendDlgItemMessage(hWnd,
                                              IDC_LIST1,
                                              LB_GETCURSEL,
                                              0,
                                              0);

                    if (item == 0)
                    {
                        if (!SendDlgItemMessage(hWnd,
                                                IDC_LIST1,
                                                LB_GETSEL,
                                                (WPARAM)item,
                                                0))
                        {
                            item == -1;
                        }
                    }

                    if (item != -1)
                    {
                        // get the PAGENT associated with it
                        pAgent = (PAGENT)SendDlgItemMessage(hWnd,
                                                            IDC_LIST1,
                                                            LB_GETITEMDATA,
                                                            (WPARAM)item,
                                                            0);

                        // delete it from this listbox
                        SendDlgItemMessage(hWnd,
                                           IDC_LIST1,
                                           LB_DELETESTRING,
                                           (WPARAM)item,
                                           0);

                        // add it to this list box
                        item = SendDlgItemMessage(hWnd,
                                                  IDC_LIST2,
                                                  LB_ADDSTRING,
                                                  0,
                                                  (LPARAM)pAgent->lpszName);

                        // set the item data again
                        SendDlgItemMessage(hWnd,
                                           IDC_LIST2,
                                           LB_SETITEMDATA,
                                           item,
                                           (WPARAM)pAgent);

                        // add it to the group's list
                        InsertIntoGroupList(pGroup,
                                            pAgent);

                        return 1;

                    }
                }
                break;

                case IDC_REMOVE:
                {
                    // get the item
                    item = SendDlgItemMessage(hWnd,
                                              IDC_LIST2,
                                              LB_GETCURSEL,
                                              0,
                                              0);

                    if (item == 0)
                    {
                        if (!SendDlgItemMessage(hWnd,
                                                IDC_LIST2,
                                                LB_GETSEL,
                                                (WPARAM)item,
                                                0))
                        {
                            item == -1;
                        }
                    }

                    if (item != -1)
                    {
                        // get the struct associated with it
                        pAgent = (PAGENT)SendDlgItemMessage(hWnd,
                                                                IDC_LIST2,
                                                                LB_GETITEMDATA,
                                                                (WPARAM)item,
                                                                0);

                        // delete it from this list
                        SendDlgItemMessage(hWnd,
                                           IDC_LIST2,
                                           LB_DELETESTRING,
                                           (WPARAM)item,
                                           0);

                        // add it to this list
                        item = SendDlgItemMessage(hWnd,
                                                  IDC_LIST1,
                                                  LB_ADDSTRING,
                                                  0,
                                                  (LPARAM)pAgent->lpszName);

                        // set the item data
                        SendDlgItemMessage(hWnd,
                                           IDC_LIST1,
                                           LB_SETITEMDATA,
                                           item,
                                           (WPARAM)pAgent);

                        // remove it from the lists
                        RemoveFromGroupList(pGroup,
                                            pAgent);

                        return 1;

                    }

                }
                break;

                // bug idcancel doesn't cancel
                case IDOK:
                case IDCANCEL:
                {
                    UpdateGroupLeaf(pGroup);

                    EndDialog(hWnd, 1);
                    return 1;
                }

                default:
                    return 0;

            }

    }

    return 0;
}

BOOL CALLBACK AgentAddToListProc(HWND   hWnd,
                                 UINT   uMsg,
                                 WPARAM wParam,
                                 LPARAM lParam)
{
    static PAGENT           pAgent;
    PGROUP                  pGroup;
    PLISTITEM               pList;
    TV_ITEM                 tvi;
    DWORD                   dwListBox;
    BOOL                    bFound;
    int                     item;
    TCHAR                   szBuffer[128];

    switch (uMsg)
    {
        case WM_INITDIALOG:

            // get the item in question
            tvi.mask    = TVIF_HANDLE | TVIF_PARAM;
            tvi.hItem   = g.hTreeItemWithMenu;

            TreeView_GetItem(g.hTreeWnd,
                             &tvi);

            pAgent = (PAGENT)tvi.lParam;

            // init lists
            if (pAgent)
            {
                // initialize text in dialog
                wsprintf(szBuffer, TEXT("Add To %s"), pAgent->lpszName);

                SetWindowText(hWnd,
                              TEXT("Add To Agent"));

                SetDlgItemText(hWnd,
                               IDC_STATICNOTINLIST,
                               TEXT("Not Member Of"));
                SetDlgItemText(hWnd,
                               IDC_STATICINLIST,
                               TEXT("Member Of"));

                pGroup = g.pGroups;

                // walk list and initialize list boxes
                while (pGroup)
                {
                    pList = pGroup->pAgentList;

                    bFound = FALSE;

                    while (pList)
                    {
                        if (pList->pAgent == pAgent)
                        {
                            bFound = TRUE;
                            break;
                        }

                        pList = pList->pNext;
                    }

                    // if it was found, it is already a member of
                    // the group
                    if (bFound)
                    {
                        dwListBox = IDC_LIST2;
                    }
                    else
                    {
                        dwListBox = IDC_LIST1;
                    }

                    // add to correct list box
                    item = SendDlgItemMessage(hWnd,
                                              dwListBox,
                                              LB_ADDSTRING,
                                              0,
                                              (LPARAM)pGroup->lpszName);

                    // set the item data to be the item so we can get back it.
                    if (item != LB_ERR)
                    {
                        SendDlgItemMessage(hWnd,
                                           dwListBox,
                                           LB_SETITEMDATA,
                                           (WPARAM)item,
                                           (LPARAM)pGroup);
                    }

                    pGroup = pGroup->pNext;
                }
            }

            return 1;

        case WM_COMMAND:

            switch (LOWORD(wParam))
            {
                case IDC_ADD:
                {
                    // get the item
                    item = SendDlgItemMessage(hWnd,
                                              IDC_LIST1,
                                              LB_GETCURSEL,
                                              0,
                                              0);

                    if (item == 0)
                    {
                        if (!SendDlgItemMessage(hWnd,
                                                IDC_LIST1,
                                                LB_GETSEL,
                                                (WPARAM)item,
                                                0))
                        {
                            item == -1;
                        }
                    }

                    if (item != -1)
                    {
                        // get the PGROUP associated with it
                        pGroup = (PGROUP)SendDlgItemMessage(hWnd,
                                                                IDC_LIST1,
                                                                LB_GETITEMDATA,
                                                                (WPARAM)item,
                                                                0);

                        // delete it from this listbox
                        SendDlgItemMessage(hWnd,
                                           IDC_LIST1,
                                           LB_DELETESTRING,
                                           (WPARAM)item,
                                           0);

                        // add it to this list box
                        item = SendDlgItemMessage(hWnd,
                                                  IDC_LIST2,
                                                  LB_ADDSTRING,
                                                  0,
                                                  (LPARAM)pGroup->lpszName);

                        // set the item data again
                        SendDlgItemMessage(hWnd,
                                           IDC_LIST2,
                                           LB_SETITEMDATA,
                                           item,
                                           (WPARAM)pGroup);

                        // add it to the item's list
                        InsertIntoGroupList(pGroup,
                                            pAgent);

                        return 1;

                    }
                }
                break;

                case IDC_REMOVE:
                {
                    // get the item
                    item = SendDlgItemMessage(hWnd,
                                              IDC_LIST2,
                                              LB_GETCURSEL,
                                              0,
                                              0);

                    if (item == 0)
                    {
                        if (!SendDlgItemMessage(hWnd,
                                                IDC_LIST2,
                                                LB_GETSEL,
                                                (WPARAM)item,
                                                0))
                        {
                            item == -1;
                        }
                    }

                    if (item != -1)
                    {
                        // get the struct associated with it
                        pGroup = (PGROUP)SendDlgItemMessage(hWnd,
                                                                IDC_LIST2,
                                                                LB_GETITEMDATA,
                                                                (WPARAM)item,
                                                                0);

                        // delete it from this list
                        SendDlgItemMessage(hWnd,
                                           IDC_LIST2,
                                           LB_DELETESTRING,
                                           (WPARAM)item,
                                           0);

                        // add it to this list
                        item = SendDlgItemMessage(hWnd,
                                                  IDC_LIST1,
                                                  LB_ADDSTRING,
                                                  0,
                                                  (LPARAM)pGroup->lpszName);

                        // set the item data
                        SendDlgItemMessage(hWnd,
                                           IDC_LIST1,
                                           LB_SETITEMDATA,
                                           item,
                                           (WPARAM)pGroup);

                        // remove it from the lists
                        RemoveFromGroupList(pGroup,
                                            pAgent);

                        return 1;

                    }

                }
                break;

                // bug idcancel doesn't cancel
                case IDOK:
                case IDCANCEL:
                {
                    EndDialog(hWnd, 1);
                    return 1;
                }

                default:
                    return 0;
            }
    }
    return 0;
}

//////////////////////////////////////////////////////////////
//
//  BOOL DeleteLeafAndStruct(HTREEITEM hItem)
//    delete hItem from the tree and deleted associated
//    structure
//
//////////////////////////////////////////////////////////////
BOOL DeleteLeafAndStruct(HTREEITEM hItem)
{
    TV_ITEM     tvi;

    // get the item
    tvi.mask = TVIF_PARAM;
    tvi.hItem = hItem;

    TreeView_GetItem(g.hTreeWnd,
                     &tvi);

    // delete the structure
    if (((PGENERICSTRUCT)tvi.lParam)->dwKey == GROUPKEY)
    {
        DeleteGroup((PGROUP)tvi.lParam);
    }
    else
    {
        DeleteAgent((PAGENT)tvi.lParam);
    }

    // remove it from the tree
    TreeView_DeleteItem(g.hTreeWnd,
                        hItem);

    return TRUE;
}

////////////////////////////////////////////////////////////////////
//
//  BOOL BuildLineList(HWND hWnd,
//                     DWORD dwDeviceID)
//
//  Fill in ComboBox with names of all available TAPI
//  devices
//
////////////////////////////////////////////////////////////////////
BOOL BuildLineList(HWND hWnd,
                   DWORD dwDeviceID)
{
    DWORD               dwDev;
    LPLINEDEVCAPS       pLineDevCaps;
    int                 item;
    BOOL                bSet = FALSE;

    // clear dropdown box
    SendMessage(hWnd,
                CB_RESETCONTENT,
                0,
                0);

    // loop through all devices
    for (dwDev = 0; dwDev < g.dwNumDevs; dwDev++)
    {
        pLineDevCaps = LineGetDevCaps(g.hLineApp,
                                      dwDev);

        // add the string to to list
        if (pLineDevCaps == NULL || pLineDevCaps->dwLineNameSize == 0)
        {
            item = SendMessage(hWnd,
                               CB_ADDSTRING,

0,
                               (LPARAM)TEXT("NoName"));
        }
        else
        {
            item = SendMessage(hWnd,
                               CB_ADDSTRING,
                               0,
                               (LPARAM)((LPTSTR)((LPBYTE)pLineDevCaps + pLineDevCaps->dwLineNameOffset)));
        }

        // save the device ID
        SendMessage(hWnd,
                    CB_SETITEMDATA,
                    item,
                    dwDev);

        // if this is the device we are looking for
        // set it to be selected
        if (dwDev == dwDeviceID)
        {
            SendMessage(hWnd,
                        CB_SETCURSEL,
                        (WPARAM)item,
                        0);

            bSet = TRUE;
        }

        if (pLineDevCaps != NULL)
        {
            ACDFree((HLOCAL)pLineDevCaps);
        }
    }

    // if we didn't set the selection, default
    if (!bSet)
    {
        SendMessage(hWnd,
                    CB_SETCURSEL,
                    0,
                    0);
    }

    return TRUE;
}

///////////////////////////////////////////////////////////////////////////////
//
//  BOOL BuildAddressList()
//
//  Fill combo box with list of addresses on selected device ID
//
///////////////////////////////////////////////////////////////////////////////
BOOL BuildAddressList(HWND hWnd,
                      HWND hParentWnd,
                      DWORD dwAddress)
{
    TCHAR               szBuffer[32];
    LPLINEDEVCAPS       pLineDevCaps;
    LPLINEADDRESSCAPS   pLineAddressCaps;
    DWORD               dwCurAddress, dwDeviceID;
    int                 iCurSel;

    // clear box
    SendMessage(hWnd,
                CB_RESETCONTENT,
                0,
                0);

    // get the current selected device
    iCurSel = SendDlgItemMessage(hParentWnd,
                                 IDC_LINECOMBO,
                                 CB_GETCURSEL,
                                 0,
                                 0);

    // get associated deviceid
    dwDeviceID = (DWORD)SendDlgItemMessage(hParentWnd,
                                           IDC_LINECOMBO,
                                           CB_GETITEMDATA,
                                           (WPARAM)iCurSel,
                                           0);

    pLineDevCaps = LineGetDevCaps(g.hLineApp,
                                  dwDeviceID);

    // loop through all addresses
    for (dwCurAddress = 0; dwCurAddress < pLineDevCaps->dwNumAddresses; dwCurAddress++)
    {
        pLineAddressCaps = LineGetAddressCaps(g.hLineApp,
                                              dwDeviceID,
                                              dwCurAddress);

        // add name to list box
        if (pLineAddressCaps == NULL || pLineAddressCaps->dwAddressSize == 0)
        {
            wsprintf(szBuffer, TEXT("Address %d"), dwCurAddress);

            SendMessage(hWnd,
                        CB_ADDSTRING,
                        0,
                        (LPARAM)szBuffer);
        }
        else
        {
            SendMessage(hWnd,
                        CB_ADDSTRING,
                        0,
                        (LPARAM)((LPTSTR)((LPBYTE)pLineAddressCaps + pLineAddressCaps->dwAddressOffset)));
        }

        ACDFree((HLOCAL)pLineAddressCaps);
    }

    SendMessage(hWnd,
                CB_SETCURSEL,
                (WPARAM)dwAddress,
                0);

    ACDFree(pLineDevCaps);

    return TRUE;
}

///////////////////////////////////////////////////////////////////////////
//
//  BOOL UpdateGroupLeaf(PGROUP pStruct)
//
//  Updates a group in the tree view when a new agent is added to that
//  group
//
///////////////////////////////////////////////////////////////////////////
BOOL UpdateGroupLeaf(PGROUP pStruct)
{
    HTREEITEM   hItem;
    PLISTITEM   pItem;
    TV_ITEM     tvi;

    // get the item's first child
    hItem = TreeView_GetChild(g.hTreeWnd,
                              pStruct->hItem);

    while (hItem)
    {
        // delete all childre
        TreeView_DeleteItem(g.hTreeWnd,
                            hItem);
        hItem = TreeView_GetChild(g.hTreeWnd,
                                  pStruct->hItem);

    }

    pItem = pStruct->pAgentList;

    // walk the agent list
    while (pItem)
    {
        // add all the agents
        hItem = AddItemToTree(pStruct->hItem,
                              ((PAGENT)pItem->pAgent)->lpszName,
                              (LPARAM)NULL,
                              (HTREEITEM *)NULL);

        // if currently logged into that group
        /// bold that item
        if (pItem->bLoggedIn)
        {
            tvi.mask = TVIF_STATE | TVIF_HANDLE;
            tvi.hItem = hItem;
            tvi.state = TVIS_BOLD;
            tvi.stateMask = TVIS_BOLD;

            TreeView_SetItem(g.hTreeWnd,
                             &tvi);
        }

        pItem = pItem->pNext;
    }

    return TRUE;
}

///////////////////////////////////////////////////////////////////////
//
//  BOOL DoGroupView()
//
//  Display the tree in a "group view" (show groups, and under the
//  group, the agents that can log into that group)
//
///////////////////////////////////////////////////////////////////////
BOOL DoGroupView()
{
    PGROUP      pGroupParent, pGroup;
    TCHAR       szGroupParentName[] = TEXT("Groups");
    HTREEITEM   hItem;
    TV_ITEM     tvi;

    g.bGroupView = TRUE;

    // get the root
    hItem = TreeView_GetRoot(g.hTreeWnd);

    // free resources allocated for root
    if (hItem)
    {
        tvi.mask = TVIF_PARAM | TVIF_HANDLE;
        tvi.hItem = hItem;
        TreeView_GetItem(g.hTreeWnd,
                         &tvi);

        ACDFree((PAGENT)tvi.lParam);
    }

    // clear tree
    TreeView_DeleteAllItems(g.hTreeWnd);

    // alloc memory for the structure for the Group parent
    pGroupParent = (PGROUP)ACDAlloc(sizeof(GROUP));

    // alloc memory and copy the fixed name
    pGroupParent->lpszName = (LPTSTR)ACDAlloc((lstrlen(szGroupParentName) + 1) * sizeof(TCHAR));
    pGroupParent->dwKey = GROUPROOTKEY;

    lstrcpy(pGroupParent->lpszName, szGroupParentName);

    // add it to the tree
    g.hGroupParent = AddItemToTree(TVI_ROOT,
                                   pGroupParent->lpszName,
                                   (LPARAM)pGroupParent,
                                   &pGroupParent->hItem);

    pGroup = g.pGroups;

    // walk groups and add them to tree
    while (pGroup)
    {
        AddItemToTree(g.hGroupParent,
                      pGroup->lpszName,
                      (LPARAM)pGroup,
                      &pGroup->hItem);

        UpdateGroupLeaf(pGroup);

        pGroup = pGroup->pNext;
    }

    return TRUE;
}

////////////////////////////////////////////////////////////////////
//
//  BOOL DoAgentView()
//
//  Displays the tree in an "agent view"
//
////////////////////////////////////////////////////////////////////
BOOL DoAgentView()
{
    PAGENT      pAgentParent,pAgent;
    TCHAR       szAgentParentName[] = TEXT("Agents");
    HTREEITEM   hItem;
    TV_ITEM     tvi;

    g.bGroupView = TRUE;

    // get root, free resources
    // and clear tree
    hItem = TreeView_GetRoot(g.hTreeWnd);

    if (hItem)
    {
        tvi.mask = TVIF_PARAM | TVIF_HANDLE;
        tvi.hItem = hItem;
        TreeView_GetItem(g.hTreeWnd,
                         &tvi);

        ACDFree((PGROUP)tvi.lParam);
    }

    TreeView_DeleteAllItems(g.hTreeWnd);

    // alloc memory for the structure for the Agent parent
    pAgentParent = (PAGENT)ACDAlloc(sizeof(AGENT));

    // alloc memory and copy the fixed name
    pAgentParent->lpszName = (LPTSTR)ACDAlloc((lstrlen(szAgentParentName) + 1) * sizeof(TCHAR));
    pAgentParent->dwKey = GROUPROOTKEY;

    lstrcpy(pAgentParent->lpszName, szAgentParentName);

    // add it to the tree
    g.hAgentParent = AddItemToTree(TVI_ROOT,
                                   pAgentParent->lpszName,
                                   (LPARAM)pAgentParent,
                                   &pAgentParent->hItem);

    pAgent = g.pAgents;

    // walk agents and add all of them
    while (pAgent)
    {
        AddItemToTree(g.hAgentParent,
                      pAgent->lpszName,
                      (LPARAM)pAgent,
                      &pAgent->hItem);

        pAgent = pAgent->pNext;
    }

    return TRUE;
}

//////////////////////////////////////////////////////////////////////////////////////
//
// FOLLOWING ARE ROUTINES TO SAVE AND RESTORE GROUP / AGENT INFORMATION
//
// An INI file has been used for this implementation.
//
// This format is used to make it easy for users to create an INI file that can be
// read in
//
// However, a real implementation
// may want to use the registry, or a private data file to store more detailed and/or
// secure information
//
//

#define SZGROUPS        TEXT("Groups")
#define SZAGENTS        TEXT("Agents")
#define SZGROUP         TEXT("GROUP")
#define SZAGENT         TEXT("AGENT")
#define SZINIFILE       TEXT("ACDSMPL.INI")
#define SZGENERAL       TEXT("General")
#define SZNUMAGENTS     TEXT("NumAgents")
#define SZNUMGROUPS     TEXT("NumGroups")

//////////////////////////////////////////////////////////////////////////////
//
//  void MakeAgentIndex(PAGENT * ppAgents)
//
//  creates an array of pagents
//
//////////////////////////////////////////////////////////////////////////////
void MakeAgentIndex(PAGENT * ppAgents)
{
    PAGENT pAgent;

    pAgent = g.pAgents;

    while (pAgent)
    {
        *ppAgents = pAgent;
        pAgent = pAgent->pNext;
        ppAgents++;
    }

}

///////////////////////////////////////////////////////////////////////////////
//
// int GetAgentIndex()
//
// retreives agent index
//
///////////////////////////////////////////////////////////////////////////////
int GetAgentIndex(PAGENT * ppAgents,
                  PAGENT pAgent)
{
    DWORD   dwCount;

    for (dwCount = 0; dwCount < g.dwNumAgents; dwCount++)
    {
        if (ppAgents[dwCount] == pAgent)
        {
            return dwCount;
        }
    }

    return -1;

}

///////////////////////////////////////////////////////////////////////////////
//
//  BOOL WriteToDisk()
//
//  save current group/agent config to acdsmpl.ini
//
///////////////////////////////////////////////////////////////////////////////
BOOL WriteToDisk()
{
    int         i;
    PGROUP      pGroup;
    PAGENT      pAgent;
    PLISTITEM   pEntry;
    TCHAR       szGroupName[32], szAgentName[32], szLineBuffer[512];
    PAGENT *    ppAgents;

    // create an index of agents
    ppAgents = (PAGENT *)ACDAlloc(sizeof(PAGENT) * g.dwNumAgents);
    MakeAgentIndex(ppAgents);

    pGroup = g.pGroups;

    i = 0;

    // walk groups
    while (pGroup)
    {
        wsprintf(szGroupName,
                 TEXT("%s%d"),
                 SZGROUP,
                 i);

        wsprintf(szLineBuffer,
                 TEXT("%s,%d,%d"),
                 pGroup->lpszName,
                 g.pdwPermIDs[pGroup->dwDeviceID],
                 pGroup->dwAddress);

        // add group to [groups] section
        WritePrivateProfileString(SZGROUPS,
                                  szGroupName,
                                  szLineBuffer,
                                  SZINIFILE);

        pEntry = pGroup->pAgentList;

        // walk agents in group
        while (pEntry)
        {
            wsprintf(szAgentName,
                     TEXT("%s%d"),
                     SZAGENT,
                     GetAgentIndex(ppAgents,
                                   pEntry->pAgent));

            // write agent index to [groupx] section
            WritePrivateProfileString(szGroupName,
                                      szAgentName,
                                      TEXT("1"),
                                      SZINIFILE);

            pEntry = pEntry->pNext;
        }

        pGroup = pGroup->pNext;

        i++;
    }

    pAgent = g.pAgents;

    i = 0;

    //walk agents
    while (pAgent)
    {
        wsprintf(szAgentName,
                 TEXT("%s%d"),
                 SZAGENT,
                 i);

        wsprintf(szLineBuffer,
                 TEXT("%s,%s,%lu"),
                 pAgent->lpszName,
                 pAgent->lpszNumber,
                 g.pdwPermIDs[pAgent->dwDeviceID]);

        // write agent to [agents] section
        WritePrivateProfileString(SZAGENTS,
                                  szAgentName,
                                  szLineBuffer,
                                  SZINIFILE);

        pAgent = pAgent->pNext;

        i++;

    }

    // save # of agents and groups
    wsprintf(szLineBuffer,
             TEXT("%lu"),
             g.dwNumGroups);

    WritePrivateProfileString(SZGENERAL,
                              SZNUMGROUPS,
                              szLineBuffer,
                              SZINIFILE);

    wsprintf(szLineBuffer,
             TEXT("%lu"),
             g.dwNumAgents);

    WritePrivateProfileString(SZGENERAL,
                              SZNUMAGENTS,
                              szLineBuffer,
                              SZINIFILE);

    ACDFree(ppAgents);

    return TRUE;
}

////////////////////////////////////////////////////////////////////////////////////
//
//  BOOL ReadInFile()
//
//   Read in ACDSMPL.INI
//
////////////////////////////////////////////////////////////////////////////////////
BOOL ReadInFile()
{
    TCHAR       szAgentLabel[32],
                szGroupLabel[32];
    DWORD       dwID, dwAddress, dwNumAgents, dwNumGroups;
    DWORD       dwCount, dwCount2;
    PAGENT *    ppAgents = NULL;
    LPTSTR      lpszName, lpszNumber, lpszDeviceID;
    LPTSTR      lpszHold, szLineBuffer = NULL;
    PGROUP      pGroup;

    dwNumAgents = GetPrivateProfileInt(SZGENERAL,
                                         SZNUMAGENTS,
                                         0,
                                         SZINIFILE);

    dwNumGroups = GetPrivateProfileInt(SZGENERAL,
                                         SZNUMGROUPS,
                                         0,
                                         SZINIFILE);

    ppAgents = (PAGENT *)ACDAlloc(sizeof(PAGENT) * dwNumAgents);
    szLineBuffer = (LPTSTR)ACDAlloc(512 * sizeof(WCHAR));

    if (!ppAgents || !szLineBuffer)
    {
        ACDFree(ppAgents);
        ACDFree(szLineBuffer);
        return FALSE;
    }

    lpszHold = szLineBuffer;

    for (dwCount = 0; dwCount < dwNumAgents; dwCount++)
    {
        wsprintf(szAgentLabel,
                 TEXT("%s%lu"),
                 SZAGENT,
                 dwCount);

        GetPrivateProfileString(SZAGENTS,
                                szAgentLabel,
                                TEXT(""),
                                szLineBuffer,
                                512,
                                SZINIFILE);

        lpszName = (LPTSTR)szLineBuffer;

        while (szLineBuffer && *szLineBuffer)
        {
            if (*szLineBuffer == TEXT(','))
            {
                *szLineBuffer = TEXT('\0');
                szLineBuffer++;
                break;
            }

            szLineBuffer++;
        }

        lpszNumber = (LPTSTR)szLineBuffer;

        while (szLineBuffer && *szLineBuffer)
        {
            if (*szLineBuffer == TEXT(','))
            {
                *szLineBuffer = TEXT('\0');
                szLineBuffer++;
                dwID = _wtol(szLineBuffer);
                dwID = GetDeviceID(dwID);

                ppAgents[dwCount] = AddAgent(lpszName,
                                             lpszNumber,
                                             dwID);

                break;
            }

            szLineBuffer++;
        }

    }

    for (dwCount = 0; dwCount < dwNumGroups; dwCount++)
    {
        wsprintf(szGroupLabel,
                 TEXT("%s%lu"),
                 SZGROUP,
                 dwCount);

        GetPrivateProfileString(SZGROUPS,
                                szGroupLabel,
                                TEXT(""),
                                szLineBuffer,
                                512,
                                SZINIFILE);

        lpszName = (LPTSTR)szLineBuffer;

        while (szLineBuffer && *szLineBuffer)
        {
            if (*szLineBuffer == TEXT(','))
            {
                *szLineBuffer = TEXT('\0');
                szLineBuffer++;
                lpszDeviceID = szLineBuffer;
                break;
            }

            szLineBuffer++;
        }

        while (szLineBuffer && *szLineBuffer)
        {
            if (*szLineBuffer == TEXT(','))
            {
                *szLineBuffer = TEXT('\0');
                szLineBuffer++;

                dwAddress = _wtol(szLineBuffer);

                break;
            }

            szLineBuffer++;
        }

        dwID = _wtol(lpszDeviceID);
        dwID = GetDeviceID(dwID);

        pGroup = AddGroup(lpszName,
                          dwID,
                          dwAddress);

        if (!pGroup)
        {
            continue;
        }

        for (dwCount2 = 0; dwCount2 < dwNumAgents; dwCount2++)
        {
            wsprintf(szAgentLabel,
                     TEXT("%s%lu"),
                     SZAGENT,
                     dwCount2);

            if (GetPrivateProfileString(szGroupLabel,
                                        szAgentLabel,
                                        TEXT(""),
                                        szLineBuffer,
                                        512,
                                        SZINIFILE) != 0)
            {
                InsertIntoGroupList(pGroup,
                                    ppAgents[dwCount2]);
            }

        }  // for dwcount2

    }  // for dwcount

    ACDFree(ppAgents);
    ACDFree(lpszHold);

    DoGroupView();

    return TRUE;
}


ACDSMPL.H

#ifndef _ACDSMPL_
#define _ACDSMPL_

#include <windows.h>
#include <commdlg.h>
#include <commctrl.h>
#include <tapi.h>

///////////////////////////////////////////////////////////////////////////
//
//  STRUCTURES
//
///////////////////////////////////////////////////////////////////////////

typedef struct _tagLISTITEM;

typedef struct
{
    DWORD                   dwState;
    DWORD                   dwNextState;
    DWORD                   dwActivity;

} ADDRESSINFO, * PADDRESSINFO;

typedef struct _tagAGENT
{
    DWORD                   dwKey;
    DWORD                   dwSize;
    struct _tagAGENT *      pNext;
    struct _tagAGENT *      pPrev;
    LPTSTR                  lpszName;
    LPTSTR                  lpszNumber;
    HTREEITEM               hItem;
    DWORD                   dwDeviceID;
    DWORD                   dwPermID;
    HLINE                   hLine;
    DWORD                   dwNumAddresses;
    PADDRESSINFO            pAddressInfo;

}   AGENT, * PAGENT;

typedef struct _tagGROUP
{
    DWORD                   dwKey;
    DWORD                   dwSize;
    struct _tagGROUP *      pNext;
    struct _tagGROUP *      pPrev;
    LPTSTR                  lpszName;
    HTREEITEM               hItem;
    HLINE                   hLine;
    DWORD                   dwDeviceID;
    DWORD                   dwAddress;
    struct _tagLISTITEM *   pAgentList;

}   GROUP, * PGROUP;

typedef struct _tagGENERICSTRUCT
{
    DWORD                       dwKey;
    DWORD                       dwSize;
    struct _tagGENERICSTRUCT *  pNext;
    struct _tagGENERICSTRUCT *  pPrev;

}  GENERICSTRUCT, * PGENERICSTRUCT;

typedef struct _tagLISTITEM
{
    DWORD                   dwKey;
    DWORD                   dwSize;
    struct _tagLISTITEM *   pNext;
    struct _tagLISTITEM *   pPrev;
    PAGENT                  pAgent;
    BOOL                    bLoggedIn;
    DWORD                   dwAddress;

}   LISTITEM, * PLISTITEM;

typedef struct _tagACDGLOBALS
{
    PAGENT         pAgents;
    PGROUP         pGroups;
    DWORD          dwNumAgents;
    DWORD          dwNumGroups;
    LPDWORD        pdwPermIDs;
    HINSTANCE      hInstance;
    HLINEAPP       hLineApp;
    DWORD          dwNumDevs;
    HWND           hMainWnd;
    HWND           hTreeWnd;
    HWND           hLogWnd;
    BOOL           bGroupView;
    DWORD          dwBarLocation;
    HTREEITEM      hAgentParent;
    HTREEITEM      hGroupParent;
    HTREEITEM      hTreeItemWithMenu;

} ACDGLOBALS, * LPACDGLOBALS;

////////////////////////////////////////////////////////////////////////////////////////////////
//
//  PROTOTYPES
//
////////////////////////////////////////////////////////////////////////////////////////////////

// memory
LPVOID ACDAlloc(DWORD dwSize);

void ACDFree(LPVOID pBuf);

LPVOID ACDReAlloc(LPVOID pBuf,
                  DWORD dwSize);

// tapi utils
LINEADDRESSCAPS * LineGetAddressCaps (HLINEAPP hLineApp,
                                      DWORD    dwDeviceID,
                                      DWORD    dwAddressID);

LINECALLINFO * LineGetCallInfo (HCALL hCall);

LINEDEVCAPS * LineGetDevCaps (HLINEAPP hLineApp,
                              DWORD    dwDeviceID);

VARSTRING * LineGetID (HLINE  hLine,
                       DWORD  dwAddressID,
                       HCALL  hCall,
                       DWORD  dwSelect,
                       LPCTSTR lpszDeviceClass);

LINECALLSTATUS * LineGetCallStatus (HCALL hCall);

// list utils
BOOL InsertStruct(PGENERICSTRUCT * ppRoot,
                  PGENERICSTRUCT pStruct);

BOOL DeleteStruct(PGENERICSTRUCT * ppRoot,
                  PGENERICSTRUCT pStruct);

PGROUP AddGroup(LPTSTR lpszName,
                 DWORD dwDeviceID,
                 DWORD dwAddress);

PAGENT AddAgent(LPTSTR lpszName,
                LPTSTR lpszNumber,
                DWORD dwDeviceID);

BOOL DeleteAgent(PAGENT pAgent);

BOOL DeleteGroup(PGROUP pGroup);

BOOL InsertIntoGroupList(PGROUP pGroup,
                         PAGENT pAgent);

BOOL RemoveFromGroupList(PGROUP pGroup,
                         PAGENT pAgent);

DWORD GetDeviceID(DWORD dwPermID);

PAGENT GetAgentFromhLine(HLINE hLine);

PAGENT GetAgentFromName(LPTSTR lpszName);

PLISTITEM IsAgentInList(PLISTITEM pList,
                        PAGENT pAgent);

///////////////////////////////////////////////////////////////////////////
//
//  DEFINES
//
///////////////////////////////////////////////////////////////////////////

#define TOTALACTIVITIES     10
#define NUMGROUPENTRIES     10
#define NAMESIZE            128

// structure keys
#define GROUPROOTKEY        'GRRT'
#define AGENTROOTKEY        'AGRT'
#define AGENTKEY            'AGNT'
#define GROUPKEY            'GROU'
#define LISTKEY             'LIST'

// window control defines
#define SIZEBAR             3
#define WINDOWSCALEFACTOR   15

#define SZAPPNAME           TEXT("ACD Sample")

#endif


ACDTAPI.C

///////////////////////////////////////////////////////////////////////////////////
//
//  ACDTAPI.C
//
//  This file handles all tapi functionality in the ACD sample
//
//
//
////////////////////////////////////////////////////////////////////////////////////
#include <windows.h>
#include <tapi.h>
#include "acdsmpl.h"

VOID CALLBACK LineCallback (DWORD hDevice,
                            DWORD dwMsg,
                            DWORD dwCallbackInstance,
                            DWORD dwParam1,
                            DWORD dwParam2,
                            DWORD dwParam3);

#define LogTapiError(__lResult__, __szString__)
#define LogError(__szString__)

extern ACDGLOBALS       g;

////////////////////////////////////////////////////////////////////////////////////
//
//  BOOL InitializeTapi()
//
//    Whatever is needed to init TAPI for the application.  This is called
//    before the main window is created.
//
////////////////////////////////////////////////////////////////////////////////////
BOOL InitializeTapi()
{
    DWORD                       dwAPIVersion;
    LINEINITIALIZEEXPARAMS      exparams;
    LONG                        lResult;
    DWORD                       i;
    LPLINEDEVCAPS               pLDC;

    // fill in lineinitex parameters
    exparams.dwTotalSize             = sizeof(LINEINITIALIZEEXPARAMS);
    exparams.dwOptions               = LINEINITIALIZEEXOPTION_USEHIDDENWINDOW;

    dwAPIVersion = TAPI_CURRENT_VERSION;

    // line init
    if ((lResult = lineInitializeEx(&g.hLineApp,
                                    g.hInstance,
                                    LineCallback,
                                    SZAPPNAME,
                                    &g.dwNumDevs,
                                    &dwAPIVersion,
                                    &exparams)) < 0)
    {
        LogTapiError(lResult, "lineInitializeEx");
        return FALSE;
    }

    // if there are no tapi devices, should probably
    // not continue
    if (g.dwNumDevs == 0)
    {
        LogError("No TAPI devices installed");
        lineShutdown(g.hLineApp);
        return FALSE;
    }

    // need to get the permanent device IDs to map from
    // an .ini file being read in
    g.pdwPermIDs = (LPDWORD)ACDAlloc(g.dwNumDevs * sizeof(DWORD));

    if (!g.pdwPermIDs)
    {
        return FALSE;
    }

    for (i = 0; i < g.dwNumDevs; i++)
    {
        pLDC = LineGetDevCaps(g.hLineApp,
                              i);

        if (pLDC)
        {
            g.pdwPermIDs[i] = pLDC->dwPermanentLineID;
            ACDFree(pLDC);
        }
    }

    return TRUE;
}

//////////////////////////////////////////////////////////////////////
//
//  BOOL CleanUp()
//
//  Called while shutting down.  free memory, close down tapi
//
//////////////////////////////////////////////////////////////////////
BOOL CleanUp()
{
    PAGENT      pAgent, pAgentNext;
    PGROUP      pGroup, pGroupNext;

    // remove agents
    pAgent = g.pAgents;
    while(pAgent)
    {
        pAgentNext = pAgent->pNext;
        DeleteAgent(pAgent);
        pAgent = pAgentNext;
    }

    // remove groups
    pGroup = g.pGroups;
    while (pGroup)
    {
        pGroupNext = pGroup->pNext;
        DeleteGroup(pGroup);
        pGroup = pGroupNext;
    }

    // free id array
    ACDFree(g.pdwPermIDs);

    // shutdown
    lineShutdown(g.hLineApp);

    return TRUE;

}
////////////////////////////////////////////////////////////////////////////////
//
//  LRESULT MakeGroupList(PAGENT pAgent,
//                        LPLINEAGENTGROUPLIST pGroupList)
//
//    Creates a LINEAGENTGROUPLIST for pAgent - group that the agent
//      is allowed to log into
//    Assumption:  don't care about address for group list
//
////////////////////////////////////////////////////////////////////////////////
LRESULT MakeGroupList(PAGENT pAgent,
                      LPLINEAGENTGROUPLIST pGroupList)
{
    PGROUP                  pGroup;
    DWORD                   dwTotalSizeNeeded, dwNameOffset, dwNumEntries;
    LPLINEAGENTGROUPENTRY   pEntry;
    LPTSTR                  pName;

    pGroup = g.pGroups;
    dwTotalSizeNeeded = sizeof(LINEAGENTGROUPLIST);
    pGroupList->dwNumEntries = 0;
    dwNumEntries = 0;

    // walk list of groups
    while (pGroup)
    {
        if (IsAgentInList(pGroup->pAgentList,
                          pAgent))
        // if found the agent, add the group to the group list
        {
            // incrememt number of entries
            dwNumEntries++;

            // add to total size needed
            dwTotalSizeNeeded += sizeof(LINEAGENTGROUPENTRY);
            dwTotalSizeNeeded += (lstrlen(pGroup->lpszName) + 1) * sizeof(TCHAR);
        }

        pGroup = pGroup->pNext;
    }

    pGroupList->dwNeededSize = dwTotalSizeNeeded;

    if (pGroupList->dwTotalSize < dwTotalSizeNeeded)
    {
        pGroupList->dwUsedSize = sizeof(LINEAGENTGROUPLIST);

        return 0;
//        return LINEERR_STRUCTURETOOSMALL;
    }

    pGroupList->dwNumEntries = dwNumEntries;

    // set the list info
    pGroupList->dwListSize = sizeof(LINEAGENTGROUPENTRY) * pGroupList->dwNumEntries;
    pGroupList->dwListOffset = sizeof(LINEAGENTGROUPLIST);

    // get the first agentgroup entry struct
    pEntry = (LPLINEAGENTGROUPENTRY)(((LPBYTE)pGroupList) + pGroupList->dwListOffset);

    dwNameOffset = pGroupList->dwListOffset + pGroupList->dwListSize;
    pGroup = g.pGroups;

    // loop through the groups again, and fill in the structure
    while (pGroup)
    {
        if (IsAgentInList(pGroup->pAgentList,
                          pAgent))
        {
            // ID is just PGROUP
            pEntry->GroupID.dwGroupID1 = (DWORD)pGroup;
            pEntry->GroupID.dwGroupID2 = 0;
            pEntry->GroupID.dwGroupID3 = 0;
            pEntry->GroupID.dwGroupID4 = 0;

            // set name of group
            pName = (LPTSTR)(((LPBYTE)pGroupList) + dwNameOffset);

            pEntry->dwNameSize = (lstrlen(pGroup->lpszName) + 1) * sizeof(TCHAR);
            pEntry->dwNameOffset = dwNameOffset;
            lstrcpy(pName,
                    pGroup->lpszName);

            dwNameOffset += pEntry->dwNameSize;

            // get next entry
            pEntry++;
        }

        pGroup = pGroup->pNext;
    }

    pGroupList->dwUsedSize = dwTotalSizeNeeded;

    return 0;
}

////////////////////////////////////////////////////////////////////////////
//
//  LRESULT SetGroupList()
//
//   Sets the groups that the agent is logged into.
//     This does not change the groups that the agent _can_ log into
//
////////////////////////////////////////////////////////////////////////////
LRESULT SetGroupList(PAGENT pAgent,
                     DWORD dwAddress,
                     LPLINEAGENTGROUPLIST pGroupList)
{
    LPLINEAGENTGROUPENTRY   pGroupEntry;
    PLISTITEM               pListEntry;
    DWORD                   i;
    PGROUP  *               ppGroups = NULL;
    PGROUP                  pGroup;

    ppGroups = (PGROUP*)ACDAlloc(sizeof(PGROUP) * pGroupList->dwNumEntries);

    // get to the group entry struct
    pGroupEntry = (LPLINEAGENTGROUPENTRY)(((LPBYTE)pGroupList) + pGroupList->dwListOffset);

    // loop through all entries
    for (i = 0; i < pGroupList->dwNumEntries; i++)
    {
        // get the group in entry
        // NOTE! NOTE! NOTE!
        // should protect here against bad pointers !!!
        pGroup = (PGROUP)pGroupEntry->GroupID.dwGroupID1;

        if (pGroup->dwKey != GROUPKEY)
        {
            return LINEERR_INVALAGENTGROUP;
        }

        pListEntry = pGroup->pAgentList;

        // walk list of agents in that group
        if (!IsAgentInList(pGroup->pAgentList,
                           pAgent))
        {
            ACDFree(ppGroups);
            return LINEERR_INVALAGENTGROUP;
        }

        // save group for easy access
        ppGroups[i] = pGroup;

        // get the next entry (after the variable portion of
        // the previous entry struct)
        pGroupEntry++;
    }

    // now we know that the groups to be set are valid
    // walk through the list of groups again, and
    // set the status to logged in/ not logged in
    // for every group that the agent is a member of

    pGroup = g.pGroups;

    // walk list of all groups
    while (pGroup)
    {
        if (pListEntry = IsAgentInList(pGroup->pAgentList,
                                       pAgent))
        {
            // default to not logged in
            pListEntry->bLoggedIn = FALSE;

            // loop through groups being set
            for (i = 0; i < pGroupList->dwNumEntries; i++)
            {
                // if this group is in list, set agent to logged in
                if (pGroup == ppGroups[i])
                {
                    pListEntry->bLoggedIn = TRUE;
                    // assumption:  agent can only log into a group on one address.
                    pListEntry->dwAddress = dwAddress;
                    break;
                }

            } // for

        }  // agent in list

        // next group
        pGroup = pGroup->pNext;

    } // while

    ACDFree(ppGroups);

    return 0;
}

/////////////////////////////////////////////////////////////////////////
//
//  BOOL MakeAgentActivityList()
//
//    Creates a LINEAGENTACTIVITYLIST for pAgent
//
//    for the sample, just generic names are used
//    "Activity 1", "Activity 2"....
//
/////////////////////////////////////////////////////////////////////////
LRESULT MakeAgentActivityList(PAGENT pAgent,
                              LPLINEAGENTACTIVITYLIST pActivityList)
{
    TCHAR                       szBuffer[64];
    DWORD                       dwTotalSize, dwNameOffset, i, dwNumEntries;
    LPTSTR                      pName;
    LPLINEAGENTACTIVITYENTRY    pEntry;

    // init
    dwTotalSize = sizeof(LINEAGENTACTIVITYLIST);
    pActivityList->dwNumEntries = 0;
    dwNumEntries = 0;

    // just a static list of activities
    for (i = 0; i < TOTALACTIVITIES; i++)
    {
        dwNumEntries++;

        // create a name
        wsprintf(szBuffer, TEXT("Activity %lu"), i);

        // determine size of this entry
        dwTotalSize += sizeof(LINEAGENTACTIVITYENTRY);
        dwTotalSize += (lstrlen(szBuffer) + 1) * sizeof(TCHAR);
    }

    pActivityList->dwNeededSize = dwTotalSize;

    // verify size
    if (pActivityList->dwTotalSize < dwTotalSize)
    {
        pActivityList->dwUsedSize = sizeof(LINEAGENTACTIVITYLIST);

        return 0;
//        return LINEERR_STRUCTURETOOSMALL;
    }

    pActivityList->dwNumEntries = dwNumEntries;

    // set list stuff
    pActivityList->dwListSize = sizeof(LINEAGENTACTIVITYENTRY) * pActivityList->dwNumEntries;
    pActivityList->dwListOffset = sizeof(LINEAGENTACTIVITYLIST);

    // get first activityentry
    pEntry = (LPLINEAGENTACTIVITYENTRY)(((LPBYTE)pActivityList) + pActivityList->dwListOffset);
    dwNameOffset = pActivityList->dwListOffset + pActivityList->dwListSize;

    // loop through activities again
    for (i = 0; i < TOTALACTIVITIES; i++)
    {
        // fill in members
        pEntry->dwID = i;

        // create a name
        wsprintf(szBuffer, TEXT("Activity %lu"), i);

        pName = (LPTSTR)(((LPBYTE)pActivityList) + dwNameOffset);

        pEntry->dwNameSize = (lstrlen(szBuffer) + 1) * sizeof(TCHAR);
        pEntry->dwNameOffset = dwNameOffset;
        lstrcpy(pName,
                szBuffer);

        dwNameOffset += pEntry->dwNameSize;

        pEntry++;

    } // for

    // fill in used size
    pActivityList->dwUsedSize = dwTotalSize;

    return 0;
}

#define DWAGENTFEATURES         LINEAGENTFEATURE_SETAGENTGROUP | \
                                LINEAGENTFEATURE_SETAGENTSTATE | \
                                LINEAGENTFEATURE_SETAGENTACTIVITY | \
                                LINEAGENTFEATURE_GETAGENTACTIVITYLIST | \
                                LINEAGENTFEATURE_GETAGENTGROUP

#define DWSTATES                LINEAGENTSTATE_LOGGEDOFF | \
                                LINEAGENTSTATE_NOTREADY | \
                                LINEAGENTSTATE_READY | \
                                LINEAGENTSTATE_BUSYACD | \
                                LINEAGENTSTATE_BUSYINCOMING | \
                                LINEAGENTSTATE_BUSYOUTBOUND | \
                                LINEAGENTSTATE_BUSYOTHER | \
                                LINEAGENTSTATE_WORKINGAFTERCALL | \
                                LINEAGENTSTATE_UNKNOWN | \
                                LINEAGENTSTATE_UNAVAIL

#define DWNEXTSTATES            LINEAGENTSTATE_LOGGEDOFF | \
                                LINEAGENTSTATE_NOTREADY | \
                                LINEAGENTSTATE_READY | \
                                LINEAGENTSTATE_BUSYACD | \
                                LINEAGENTSTATE_BUSYINCOMING | \
                                LINEAGENTSTATE_BUSYOUTBOUND | \
                                LINEAGENTSTATE_BUSYOTHER | \
                                LINEAGENTSTATE_WORKINGAFTERCALL | \
                                LINEAGENTSTATE_UNKNOWN | \
                                LINEAGENTSTATE_UNAVAIL

#define DWSTATUSMESSAGES        LINEAGENTSTATUS_GROUP | \
                                LINEAGENTSTATUS_STATE | \
                                LINEAGENTSTATUS_NEXTSTATE | \
                                LINEAGENTSTATUS_ACTIVITY | \
                                LINEAGENTSTATUS_ACTIVITYLIST | \
                                LINEAGENTSTATUS_GROUPLIST | \
                                LINEAGENTSTATUS_CAPSCHANGE | \
                                LINEAGENTSTATUS_VALIDSTATES | \
                                LINEAGENTSTATUS_VALIDNEXTSTATES

////////////////////////////////////////////////////////////////////
//
//  BOOL IsValidState(DWORD dwState)
//
////////////////////////////////////////////////////////////////////
BOOL IsValidState(DWORD dwState)
{
    if (!dwState)
    {
        return TRUE;
    }

    if ((dwState) & (dwState - 1))
    {
        // more than one bit set
        return FALSE;
    }

    // make sure it's one of the valid states
    return (dwState & DWSTATES);

}

////////////////////////////////////////////////////////////////////
//
//  BOOL IsValidNextState(DWORD dwState)
//
////////////////////////////////////////////////////////////////////
BOOL IsValidNextState(DWORD dwState)
{
    if (!dwState)
    {
        return TRUE;
    }

    if ((dwState) & (dwState - 1))
    {
        // more than one bit set
        return FALSE;
    }

    // make sure it's one of the valid states
    return (dwState & DWNEXTSTATES);

}

///////////////////////////////////////////////////////////////////////
//
//  BOOL IsValidActivityID(DWORD dwActivityID)
//
///////////////////////////////////////////////////////////////////////
BOOL IsValidActivityID(DWORD dwActivityID)
{
    return (dwActivityID <= TOTALACTIVITIES);
}

////////////////////////////////////////////////////////////////////////
//
//  LRESULT MakeAgentCaps(PAGENT pAgent,
//                        LPLINEAGENTCAPS pAgentCaps)
//
//    Creates a LINEAGENTCAPS for pAgent
//    Features/states/messages are hardcoded
//     for this example
//
////////////////////////////////////////////////////////////////////////
LRESULT MakeAgentCaps(PAGENT pAgent,
                      LPLINEAGENTCAPS pAgentCaps)
{
    DWORD       dwStringSize;

    dwStringSize = (lstrlen(SZAPPNAME) + 1) * sizeof(TCHAR);

    pAgentCaps->dwNeededSize = sizeof(LINEAGENTCAPS) + dwStringSize;

    if (pAgentCaps->dwTotalSize < pAgentCaps->dwNeededSize)
    {
        pAgentCaps->dwUsedSize = sizeof(LINEAGENTCAPS);
        return 0;
//        return LINEERR_STRUCTURETOOSMALL;
    }

    pAgentCaps->dwAgentHandlerInfoSize = dwStringSize;
    pAgentCaps->dwAgentHandlerInfoOffset = sizeof(LINEAGENTCAPS);

    pAgentCaps->dwCapsVersion = TAPI_CURRENT_VERSION;

    // these features are hardcoded here.
    // a real implementation may set specific features
    // per agent or line or address
    pAgentCaps->dwFeatures              = DWAGENTFEATURES;
    pAgentCaps->dwStates                = DWSTATES;
    pAgentCaps->dwNextStates            = DWNEXTSTATES;
    pAgentCaps->dwMaxNumGroupEntries    = NUMGROUPENTRIES;
    pAgentCaps->dwAgentStatusMessages   = DWSTATUSMESSAGES;

    // no extensions
    pAgentCaps->dwNumAgentExtensionIDs = 0;
    pAgentCaps->dwAgentExtensionIDListSize = 0;
    pAgentCaps->dwAgentExtensionIDListOffset = 0;

    pAgentCaps->dwUsedSize = pAgentCaps->dwNeededSize;

    return 0;
}

//////////////////////////////////////////////////////////////////////////
//
//  LRESULT GetAgentStatus()
//
//  Creates a LINEAGENTSTATUS for pAgent
//
//////////////////////////////////////////////////////////////////////////
LRESULT GetAgentStatus(PAGENT pAgent,
                       DWORD dwAddress,
                       LPLINEAGENTSTATUS pAgentStatus)
{
    PGROUP                  pGroup;
    LPLINEAGENTGROUPENTRY   pGroupEntry;
    DWORD                   dwTotalSize, dwNameOffset, dwCount;
    TCHAR                   szActivityName[NAMESIZE];
    PGROUP *                ppGroups;
    PLISTITEM               pEntry;

    // init total size
    dwTotalSize = sizeof(LINEAGENTSTATUS);

    if (dwAddress >= pAgent->dwNumAddresses)
    {
        return LINEERR_INVALADDRESSID;
    }

    // set know members
    // for valid states / next states / agent features, just setting it to
    // generic stuff.  a real implementation may want to set these
    // field depending on current state / agent / hline
    pAgentStatus->dwState           = pAgent->pAddressInfo[dwAddress].dwState;
    pAgentStatus->dwNextState       = pAgent->pAddressInfo[dwAddress].dwNextState;
    pAgentStatus->dwActivityID      = pAgent->pAddressInfo[dwAddress].dwActivity;
    pAgentStatus->dwAgentFeatures   = DWAGENTFEATURES;
    pAgentStatus->dwValidStates     = DWSTATES;
    pAgentStatus->dwValidNextStates = DWNEXTSTATES;

    // create the activity name
    wsprintf(szActivityName, TEXT("Activity %lu"), pAgent->pAddressInfo[dwAddress].dwActivity);
    dwTotalSize += (lstrlen(szActivityName) + 1) * sizeof(TCHAR);

    ppGroups = (PGROUP *)ACDAlloc(sizeof(PGROUP) * g.dwNumGroups);

    pGroup = g.pGroups;

    pAgentStatus->dwNumEntries = 0;

    // walk list of groups
    while (pGroup)
    {
        pEntry = pGroup->pAgentList;

        // walk each agent in each group
        while (pEntry)
        {
            if (pEntry->pAgent == pAgent)
            {
                if ((!pEntry->bLoggedIn) ||
                    (pEntry->dwAddress != dwAddress))
                {
                    break;
                }

                // save group
                ppGroups[pAgentStatus->dwNumEntries] = pGroup;

                // adjust total size / entries
                pAgentStatus->dwNumEntries++;
                dwTotalSize += sizeof(LINEAGENTGROUPENTRY);
                dwTotalSize += (lstrlen(pGroup->lpszName) + 1) * sizeof(TCHAR);

                break;
            }

            pEntry = pEntry->pNext;

        }  // while (pEntry)

        pGroup = pGroup->pNext;

    } // while (pGroup)

    // set needed size
    pAgentStatus->dwNeededSize = dwTotalSize;

    // do we have enough room?
    if (pAgentStatus->dwTotalSize < dwTotalSize)
    {
        // if not, return
        pAgentStatus->dwUsedSize = sizeof(LINEAGENTSTATUS);
        ACDFree(ppGroups);

        return 0;
//        return LINEERR_STRUCTURETOOSMALL;
    }

    // set the group entries...

    // first get the offset to the first entry
    pGroupEntry = (LPLINEAGENTGROUPENTRY)(((LPBYTE)pAgentStatus) + sizeof(LINEAGENTSTATUS));
    pAgentStatus->dwGroupListOffset = sizeof(LINEAGENTSTATUS);

    // figure out where the names can go (after all the fixed structures)
    dwNameOffset = sizeof(LINEAGENTSTATUS) +
                   sizeof(LINEAGENTGROUPENTRY) * pAgentStatus->dwNumEntries;

    // loop through all the group that the agent is logged into
    for (dwCount = 0; dwCount < pAgentStatus->dwNumEntries; dwCount++)
    {
        // set the it (just the pGroup)
        pGroupEntry->GroupID.dwGroupID1 = (DWORD)ppGroups[dwCount];
        pGroupEntry->GroupID.dwGroupID2 = 0;
        pGroupEntry->GroupID.dwGroupID3 = 0;
        pGroupEntry->GroupID.dwGroupID4 = 0;

        // set name size and offset
        pGroupEntry->dwNameSize = (lstrlen(ppGroups[dwCount]->lpszName) + 1) * sizeof(TCHAR);
        pGroupEntry->dwNameOffset = dwNameOffset;

        // copy name
        lstrcpy((LPTSTR)(((LPBYTE)pAgentStatus) + dwNameOffset),
                ppGroups[dwCount]->lpszName);

        // fix the name offset
        dwNameOffset += pGroupEntry->dwNameSize;

        // next entry
        pGroupEntry++;

    }

    pAgentStatus->dwGroupListSize = dwNameOffset - pAgentStatus->dwGroupListOffset;

    // put the activity name at the end
    pAgentStatus->dwActivitySize = (lstrlen(szActivityName) + 1) * sizeof(TCHAR);
    pAgentStatus->dwActivityOffset = dwNameOffset;

    lstrcpy((LPTSTR)(((LPBYTE)pAgentStatus) + dwNameOffset),
            szActivityName);

    ACDFree(ppGroups);

    pAgentStatus->dwUsedSize = pAgentStatus->dwNeededSize;
    // return success
    return 0;
}

/////////////////////////////////////////////////////////////////////
//
//  LRESULT SetAgentState()
//
//    Sets the current and next state for pAgent
//    on that specific address
//
//
/////////////////////////////////////////////////////////////////////
LRESULT SetAgentState(PAGENT pAgent,
                      DWORD dwAddress,
                      DWORD dwState,
                      DWORD dwNextState)
{
    // make sure valid
    if ((!IsValidState(dwState)) ||
        (!IsValidNextState(dwNextState)))
    {
        return LINEERR_INVALAGENTSTATE;
    }

    // check address
    if (dwAddress >= pAgent->dwNumAddresses)
    {
        return LINEERR_INVALADDRESSID;
    }

    // set the state if specified
    if (dwState)
    {
        pAgent->pAddressInfo[dwAddress].dwState = dwState;
    }

    if (dwNextState)
    {
        pAgent->pAddressInfo[dwAddress].dwNextState = dwNextState;
    }

    return 0;
}

///////////////////////////////////////////////////////////////////////////////
//
//  LRESULT SetAgentActivity()
//
//   Sets the activity for pAgent
//
///////////////////////////////////////////////////////////////////////////////
LRESULT SetAgentActivity(PAGENT pAgent,
                         DWORD dwAddress,
                         DWORD dwActivityID)
{
    if (dwAddress >= pAgent->dwNumAddresses)
    {
        return LINEERR_INVALADDRESSID;
    }

    if (!IsValidActivityID(dwActivityID))
    {
        return LINEERR_INVALAGENTACTIVITY;
    }

    pAgent->pAddressInfo[dwAddress].dwActivity = dwActivityID;

    return 0;
}

////////////////////////////////////////////////////////////////////////////////
//
//  BOOL HandleOffering(HCALL hCall)
//
//    Handles a LINECALLSTATE_OFFERING message
//      Determines the group associated with the address
//      that the call came in on, finds an available
//      agent in that group, and transfers the call to that
//      agent.
//
////////////////////////////////////////////////////////////////////////////////
BOOL HandleOffering(HCALL hCall)
{
    LPLINECALLINFO      pLCI;
    PGROUP              pGroup;
    PLISTITEM           pEntry;
    LONG                lResult;

    pLCI = LineGetCallInfo(hCall);

    if (!pLCI)
    {
        return FALSE;
    }

    pGroup = g.pGroups;

    // find the group that this call came in on
    // walk all the groups
    while (pGroup)
    {
        // if the line and address match, it's the
        // correct address
        if ((pGroup->hLine == pLCI->hLine) &&
            (pGroup->dwAddress == pLCI->dwAddressID))
        {
            break;
        }

        pGroup = pGroup->pNext;
    }

    // couldn't find the group
    if (!pGroup)
    {
        // error!
        ACDFree(pLCI);
        return FALSE;
    }

    // OK - found the group that this call is for.  Now transfer to
    // an agent that is available.
    pEntry = pGroup->pAgentList;

    while (pEntry)
    {
        if (pEntry->bLoggedIn &&
            (pEntry->pAgent->pAddressInfo[pEntry->dwAddress].dwState ==
                LINEAGENTSTATE_READY))
        {
            // found someone
            // doing a blind transfer here
            // other implementations may need to
            // do lineSetupTransfer / lineDial / lineCompleteTransfer
            if (lResult = lineBlindTransfer(hCall,
                                            (LPCWSTR)pEntry->pAgent->lpszNumber,
                                            0))
            {
                //LogTapiError(TEXT("lineBlindTransfer"), lResult);
                // don't break - try the next agent
            }
            else
            {
                // set the state to reflect that
                // a call is being handled
                SetAgentState(pEntry->pAgent,
                              pEntry->dwAddress,
                              LINEAGENTSTATE_BUSYACD,
                              LINEAGENTSTATE_READY);

                break;
            }

        }

        pEntry = pEntry->pNext;
    }

    if (!pEntry)
    {
        // couldn't find an available agent

        // NOTE! NOTE! NOTE! NOTE! NOTE!
        // something should be done here with this call.  put into
        // a queue on hold or something.  For this sample, we are just
        // ignoring it
    }

    ACDFree(pLCI);

    return TRUE;
}

//////////////////////////////////////////////////////////////////////////
//
//  BOOL HandleIdle(HCALL hCall)
//
//    Handles LINECALLSTATE_IDLE
//     Should always always always deallocate when
//     getting an IDLE message.  Also, determine if this is a call
//     that we know about and set the agent state appropriatly
//
//////////////////////////////////////////////////////////////////////////
BOOL HandleIdle(HCALL hCall)
{
    LPLINECALLINFO      pLCI;
    PAGENT              pAgent;

    pLCI = LineGetCallInfo(hCall);

    // always deallocate the call
    lineDeallocateCall(hCall);

    if (!pLCI)
    {
        return FALSE;
    }

    // get the agent associated with the line
    pAgent = GetAgentFromhLine(pLCI->hLine);

    if (!pAgent)
    {
        ACDFree(pLCI);
        return FALSE;
    }

    // set that agent to their next state
    // Assumption:  only calls that the ACD app know about
    // occur.  For example, if an agent made an outgoing call
    // and it transitioned to idle, this code would still be executed.
    // May make more sense to not handle NextState in the ACD app, and let
    // the client app handle it.
    SetAgentState(pAgent,
                  pLCI->dwAddressID,
                  pAgent->pAddressInfo[pLCI->dwAddressID].dwNextState,
                  0);

    ACDFree(pLCI);

    return TRUE;

}

////////////////////////////////////////////////////////////////////////////
//
//  void HandleLineProxyRequest(HLINE hLine,
//                              LPLINEPROXYREQUEST pProxyRequest)
//
//    Handles LINE_PROXYREQUEST message
//     Just dispatches to appropriate functions
//
////////////////////////////////////////////////////////////////////////////
void HandleLineProxyRequest(HLINE hLine,
                            LPLINEPROXYREQUEST pProxyRequest)
{
    PAGENT       pAgent;
    LRESULT      lResult;

    pAgent = GetAgentFromName((LPTSTR)(((LPBYTE)pProxyRequest) +
                                       pProxyRequest->dwClientUserNameOffset));

    if (!pAgent)
    {
        lineProxyResponse(hLine,
                          pProxyRequest,

LINEERR_INVALAGENTID);

        return;
    }

    switch (pProxyRequest->dwRequestType)
    {
        case LINEPROXYREQUEST_SETAGENTGROUP:

            lResult = SetGroupList(pAgent,
                                   pProxyRequest->SetAgentGroup.dwAddressID,
                                   &pProxyRequest->SetAgentGroup.GroupList);

            lineProxyResponse(hLine,
                              pProxyRequest,
                              lResult);

            return;

        case LINEPROXYREQUEST_SETAGENTSTATE:

            lResult = SetAgentState(pAgent,
                                    pProxyRequest->SetAgentState.dwAddressID,
                                    pProxyRequest->SetAgentState.dwAgentState,
                                    pProxyRequest->SetAgentState.dwNextAgentState);

            lineProxyResponse(hLine,
                              pProxyRequest,
                              lResult);

            break;

        case LINEPROXYREQUEST_SETAGENTACTIVITY:

            lResult = SetAgentActivity(pAgent,
                                       pProxyRequest->SetAgentActivity.dwAddressID,
                                       pProxyRequest->SetAgentActivity.dwActivityID);

            lineProxyResponse(hLine,
                              pProxyRequest,
                              lResult);

            break;

        case LINEPROXYREQUEST_GETAGENTSTATUS:

            lResult = GetAgentStatus(pAgent,
                                     pProxyRequest->GetAgentStatus.dwAddressID,
                                     &pProxyRequest->GetAgentStatus.AgentStatus);

            lineProxyResponse(hLine,
                              pProxyRequest,
                              lResult);

            break;

        case LINEPROXYREQUEST_GETAGENTCAPS:

            if ((hLine == pAgent->hLine) &&
                (pProxyRequest->GetAgentCaps.dwAddressID < pAgent->dwNumAddresses))
            {
                lResult = MakeAgentCaps(pAgent,
                                        &pProxyRequest->GetAgentCaps.AgentCaps);
            }
            else
            {
                lResult = LINEERR_BADDEVICEID;
            }

            lineProxyResponse(hLine,
                              pProxyRequest,
                              lResult);
            break;

        case LINEPROXYREQUEST_GETAGENTACTIVITYLIST:

            lResult = MakeAgentActivityList(pAgent,
                                            &pProxyRequest->GetAgentActivityList.ActivityList);

            lineProxyResponse(hLine,
                              pProxyRequest,
                              lResult);

            break;

        case LINEPROXYREQUEST_GETAGENTGROUPLIST:

            lResult = MakeGroupList(pAgent,
                                    &pProxyRequest->GetAgentGroupList.GroupList);

            lineProxyResponse(hLine,
                              pProxyRequest,
                              lResult);
            return;

    }
    return;
}

/////////////////////////////////////////////////////////////
//
//  void HandleLineCallState(DWORD dwDevice,
//
//  Handles callstate messages we are interested in
//
/////////////////////////////////////////////////////////////
void HandleLineCallState(DWORD dwDevice,
                         DWORD dwParam1,
                         DWORD dwParam2,
                         DWORD dwParam3)
{
    switch (dwParam1)
    {
    case LINECALLSTATE_OFFERING:
    {
        LPLINECALLSTATUS        pLCS;

        // get the call privilege.
        // NOTE:  the new LINE_APPNEWCALL message notifies applications
        // of their priv for new calls not created by app
        pLCS = LineGetCallStatus((HCALL)dwDevice);
        if (!pLCS)
        {
            break;
        }

        if (pLCS->dwCallPrivilege & LINECALLPRIVILEGE_OWNER)
        {
            HandleOffering((HCALL)dwDevice);
        }

        ACDFree(pLCS);

        break;
    }
    case LINECALLSTATE_CONNECTED:
        break;

    case LINECALLSTATE_DISCONNECTED:
        break;

    case LINECALLSTATE_IDLE:

        HandleIdle((HCALL)dwDevice);

        break;

    case LINECALLSTATE_BUSY:
        break;

    default:
        break;
    }
}

///////////////////////////////////////////////////////////////////////
// TAPI message handlers.  For this sample, they don't
// do anything
///////////////////////////////////////////////////////////////////////
void HandleLineDevState(DWORD dwParam1,
                        DWORD dwParam2,
                        DWORD dwParam3)
{
}

void HandleLineReply(DWORD dwParam1,
                     DWORD dwParam2,
                     DWORD dwParam3)
{
}
void HandleLineCallInfo(DWORD dwParam1,
                             DWORD dwParam2,
                             DWORD dwParam3)
{
}

void HandleLineClose(DWORD dwParam1,
                          DWORD dwParam2,
                          DWORD dwParam3)
{
}

//////////////////////////////////////////////////////////////////////////////////
//
// LineCallback() - TAPI callback function
//
//////////////////////////////////////////////////////////////////////////////////
VOID CALLBACK LineCallback (DWORD hDevice,
                            DWORD dwMsg,
                            DWORD dwCallbackInstance,
                            DWORD dwParam1,
                            DWORD dwParam2,
                            DWORD dwParam3)
{
    switch(dwMsg)
    {
      case LINE_PROXYREQUEST:
           HandleLineProxyRequest((HLINE) hDevice,
                                  (LPLINEPROXYREQUEST)dwParam1);
           return;

      case LINE_LINEDEVSTATE:
          HandleLineDevState(dwParam1,
                             dwParam2,
                             dwParam3);
          return;
      case LINE_REPLY:
          HandleLineReply(dwParam1,
                          dwParam2,
                          dwParam3);
          return;
      case LINE_CALLSTATE:
          HandleLineCallState(hDevice,
                              dwParam1,
                              dwParam2,
                              dwParam3);
          return;
      case LINE_CALLINFO:
          HandleLineCallInfo(dwParam1,
                             dwParam2,
                             dwParam3);
          return;
      case LINE_CLOSE:
          HandleLineClose(dwParam1,
                          dwParam2,
                          dwParam3);
          return;

      default:
          return;
   }
}


ACDUTILS.C

//////////////////////////////////////////////////////////////////
//
//  ACDUTILS.C
//
//  Some utility functions used in ACDSMPL
//
//////////////////////////////////////////////////////////////////

#include <windows.h>
#include "acdsmpl.h"

extern ACDGLOBALS   g;

//////////////////////////////////////////////////////////////////
//
//  LPVOID ACDAlloc(DWORD dwSize)
//
//////////////////////////////////////////////////////////////////
LPVOID ACDAlloc(DWORD dwSize)
{
    LPVOID  pBuf;

    pBuf = GlobalAlloc(GPTR, dwSize);

    return pBuf;
}

///////////////////////////////////////////////////////////////////
//
//  void ACDFree(LPVOID pBuf)
//
///////////////////////////////////////////////////////////////////
void ACDFree(LPVOID pBuf)
{
    if (pBuf)
        GlobalFree(pBuf);
}

///////////////////////////////////////////////////////////////////
//
// LPVOID ACDReAlloc()
//
///////////////////////////////////////////////////////////////////
LPVOID ACDReAlloc(LPVOID pBuf,
                  DWORD dwSize)
{
    if (pBuf)
    {
        pBuf = GlobalReAlloc(pBuf,
                             dwSize,
                             GMEM_MOVEABLE);
    }

    return pBuf;
}

///////////////////////////////////////////////////////////////////////////////
//
//  BOOL InsertStruct(PGENERICSTRUCT * ppRoot,
//                    PGENERICSTRUCT pStruct)
//
//    Adds a structure to the end of a linked list
//
///////////////////////////////////////////////////////////////////////////////
BOOL InsertStruct(PGENERICSTRUCT * ppRoot,
                  PGENERICSTRUCT pStruct)
{
    PGENERICSTRUCT      pHold = NULL;

    if (!*ppRoot)
    {
        *ppRoot = pStruct;
    }
    else
    {
        pHold = *ppRoot;
    }

    if (pHold)
    {
        while (pHold->pNext)
        {
            pHold = pHold->pNext;
        }

        pHold->pNext = pStruct;
        pStruct->pPrev = pHold;
    }

    return TRUE;
}

//////////////////////////////////////////////////////////////
//
//  BOOL DeleteStruct
//      Delete structure
//
//   Delete a structure from a linked list and
//   frees memory associated with it.
//
//////////////////////////////////////////////////////////////
BOOL DeleteStruct(PGENERICSTRUCT * ppRoot,
                  PGENERICSTRUCT pStruct)
{
    if (pStruct->pPrev)
    {
        pStruct->pPrev->pNext = pStruct->pNext;
    }
    else
    {
        *ppRoot = pStruct->pNext;
    }

    if (pStruct->pNext)
    {
        pStruct->pNext->pPrev = pStruct->pPrev;
    }

    ACDFree(pStruct);

    return TRUE;
}

//////////////////////////////////////////////////////////////
//
//  PGROUP AddGroup
//
//  Adds a group to the global group list
//
//////////////////////////////////////////////////////////////
PGROUP AddGroup(LPTSTR lpszName,
                 DWORD dwDeviceID,
                 DWORD dwAddress)
{
    PGROUP                  pGroup, pHold = NULL;
    LONG                    lResult;

    // alloc memory
    pGroup = (PGROUP)ACDAlloc(sizeof(GROUP));

    if (!pGroup)
    {
        return NULL;
    }

    pGroup->lpszName = (LPTSTR)ACDAlloc((lstrlen(lpszName) + 1) * sizeof(TCHAR));

    // bail if there is a problem
    if (!pGroup->lpszName)
    {
        ACDFree(pGroup);
        return NULL;
    }

    // init stuff
    lstrcpy(pGroup->lpszName, lpszName);

    pGroup->dwKey = GROUPKEY;
    pGroup->dwSize = sizeof(GROUP);
    pGroup->dwDeviceID = dwDeviceID;

    // open the line in owner mode, so we can
    // get the incoming calls
    lResult = lineOpen(g.hLineApp,
                       dwDeviceID,
                       &pGroup->hLine,
                       TAPI_CURRENT_VERSION,
                       0,
                       0,
                       LINECALLPRIVILEGE_OWNER,
                       LINEMEDIAMODE_INTERACTIVEVOICE,
                       NULL);

    if (lResult < 0)
    {
//      LogTapiError(lResult, "lineOpen line %lu", dwDeviceID);
        ACDFree(pGroup->lpszName);
        ACDFree(pGroup);

        return NULL;
    }

    // insert into global list
    InsertStruct((PGENERICSTRUCT *)&g.pGroups,
                 (PGENERICSTRUCT)pGroup);

    // increment
    g.dwNumGroups++;

    return pGroup;
}

//////////////////////////////////////////////////////////////
//
// PAGENT AddAgent
//
//  Adds an agent to the global agent list
//
//  NOTE:  There is a ton of verification type stuff that can
//  be put in here for a real implementation.   For example,
//  might want to restrict a user to a single line, or restrict
//  a single line to have one person.
//
//////////////////////////////////////////////////////////////
PAGENT AddAgent(LPTSTR lpszName,
                LPTSTR lpszNumber,
                DWORD dwDeviceID)
{
    PAGENT              pAgent, pHold = NULL;
    LPLINEDEVCAPS       pLDC;
    LPLINECALLPARAMS    pLCP;
    LONG                lResult;
    LPDWORD             pdwProxyRequests;

    // alloc memory
    pAgent = (PAGENT)ACDAlloc(sizeof(AGENT));

    if (!pAgent)
    {
        return NULL;
    }

    pAgent->lpszName = (LPTSTR)ACDAlloc((lstrlen(lpszName) + 1) * sizeof(TCHAR));
    pAgent->lpszNumber = (LPTSTR)ACDAlloc((lstrlen(lpszNumber) + 1) * sizeof(TCHAR));

    // bail if there is a problem
    if (!pAgent->lpszName || !pAgent->lpszNumber)
    {
        ACDFree(pAgent);
        return NULL;
    }

    // init stuff
    lstrcpy(pAgent->lpszName, lpszName);
    lstrcpy(pAgent->lpszNumber, lpszNumber);

    pAgent->dwKey       = AGENTKEY;
    pAgent->dwSize      = sizeof(AGENT);
    pAgent->dwDeviceID  = dwDeviceID;
    pAgent->dwPermID    = g.pdwPermIDs[dwDeviceID];

    // insert into global agent list
    InsertStruct((PGENERICSTRUCT *)&g.pAgents,
                 (PGENERICSTRUCT)pAgent);

    // lineOpen is where the application lets TAPI know that it is a Proxy Request handler
    // for this line.  The LINEOPENOPTION_PROXY is added to the privileges.  Also,
    // the dev specific portion of LINECALLPARAMS contains the proxy request constants
    // that indicate which requests this app can handle

    // This sample handles 7 types of proxy requests - all the ones that are defined
    // except for AGENTSPECIFIC
    pLCP = (LPLINECALLPARAMS)ACDAlloc(sizeof(LINECALLPARAMS) + 7*sizeof(DWORD));

    pLCP->dwTotalSize           = sizeof(LINECALLPARAMS) + 7*sizeof(DWORD);
    pLCP->dwDevSpecificOffset   = sizeof(LINECALLPARAMS);
    pLCP->dwDevSpecificSize     = sizeof(DWORD) * 7;

    pdwProxyRequests = (LPDWORD)((LPBYTE)pLCP + sizeof(LINECALLPARAMS));
    // each constant is in a DWORD at the end of LINECALLPARAMS
    *pdwProxyRequests++ = LINEPROXYREQUEST_SETAGENTGROUP;
    *pdwProxyRequests++ = LINEPROXYREQUEST_SETAGENTSTATE;
    *pdwProxyRequests++ = LINEPROXYREQUEST_SETAGENTACTIVITY;
    *pdwProxyRequests++ = LINEPROXYREQUEST_GETAGENTSTATUS;
    *pdwProxyRequests++ = LINEPROXYREQUEST_GETAGENTCAPS;
    *pdwProxyRequests++ = LINEPROXYREQUEST_GETAGENTACTIVITYLIST;
    *pdwProxyRequests   = LINEPROXYREQUEST_GETAGENTGROUPLIST;

    lResult = lineOpen(g.hLineApp,
                       dwDeviceID,
                       &pAgent->hLine,
                       TAPI_CURRENT_VERSION,
                       0,
                       0,
                       LINEOPENOPTION_PROXY | LINECALLPRIVILEGE_MONITOR,
                       LINEMEDIAMODE_INTERACTIVEVOICE,
                       pLCP);

    ACDFree(pLCP);

    if (lResult)
    {
        //
        ACDFree(pAgent->lpszName);
        ACDFree(pAgent);

    }

    pLDC = LineGetDevCaps(g.hLineApp,
                          pAgent->dwDeviceID);

    if (!pLDC)
    {
        return FALSE;
    }

    // alloc memory for address specific info
    pAgent->pAddressInfo = (PADDRESSINFO)ACDAlloc(sizeof(ADDRESSINFO) * pLDC->dwNumAddresses);
    pAgent->dwNumAddresses = pLDC->dwNumAddresses;

    ACDFree(pLDC);

    // increment number of agents
    g.dwNumAgents++;

    return pAgent;
}

//////////////////////////////////////////////////////////////////////////
//
// BOOL DeleteAgent(PAGENT pAgent)
//
//  Frees all memory associated with pAgent, removes
//  agent from group lists, and remove pAgent from
//  global agent list
//
//////////////////////////////////////////////////////////////////////////
BOOL DeleteAgent(PAGENT pAgent)
{
    PGROUP      pGroup;

    lineClose(pAgent->hLine);

    // free name
    ACDFree(pAgent->lpszName);
    ACDFree(pAgent->lpszNumber);

    // free address info
    ACDFree(pAgent->pAddressInfo);

    pGroup = g.pGroups;

    // walk through groups and remove from
    // group list if in group list
    while (pGroup)
    {
        if (IsAgentInList(pGroup->pAgentList,
                          pAgent))
        {
            RemoveFromGroupList(pGroup,
                                pAgent);
        }

        pGroup = pGroup->pNext;

    }

    // finally, remove pAgent from global list
    DeleteStruct((PGENERICSTRUCT *)&g.pAgents,
                 (PGENERICSTRUCT)pAgent);

    return TRUE;
}

//////////////////////////////////////////////////////////////////////////
//
// BOOL DeleteGroup(PGROUP pGroup)
//
//  Frees memory assocated with pGroup, and removes the structure from
//  the global list
//
//////////////////////////////////////////////////////////////////////////
BOOL DeleteGroup(PGROUP pGroup)
{
    PLISTITEM       pList, pListNext;

    lineClose(pGroup->hLine);

    ACDFree(pGroup->lpszName);

    pList = pGroup->pAgentList;

    while (pList)
    {
        pListNext = pList->pNext;
        ACDFree(pList);
        pList = pListNext;
    }

    DeleteStruct((PGENERICSTRUCT *)&g.pGroups,
                 (PGENERICSTRUCT)pGroup);

    return TRUE;
}

//////////////////////////////////////////////////////////////////////////
//
// BOOL InsertIntoGroupList(PGROUP pGroup,
//                          PAGENT pAgent)
//
//  Insert an agent in a group
//
//////////////////////////////////////////////////////////////////////////
BOOL InsertIntoGroupList(PGROUP pGroup,
                         PAGENT pAgent)
{
    PLISTITEM       pListItem;

    pListItem = (PLISTITEM)ACDAlloc(sizeof(LISTITEM));

    pListItem->dwKey = LISTKEY;
    pListItem->dwSize = sizeof(LISTITEM);
    pListItem->pAgent = pAgent;

    InsertStruct((PGENERICSTRUCT *)&pGroup->pAgentList,
                 (PGENERICSTRUCT)pListItem);

    return TRUE;
}

//////////////////////////////////////////////////////////////////////////
//
// BOOL RemoveFromGroupList(PGROUP pGroup,
//
//  remove an agent from a group's list
//
//////////////////////////////////////////////////////////////////////////
BOOL RemoveFromGroupList(PGROUP pGroup,
                         PAGENT pAgent)
{
    PLISTITEM       pList;

    pList = pGroup->pAgentList;

    while (pList)
    {
        if (pList->pAgent == pAgent)
        {
            break;
        }

        pList = pList->pNext;
    }

    if (!pList)
    {
        return FALSE;
    }

    DeleteStruct((PGENERICSTRUCT *)&pGroup->pAgentList,
                 (PGENERICSTRUCT)pList);

    return TRUE;
}

////////////////////////////////////////////////////////////////////////////////
//
//PLISTITEM IsAgentInList(PLISTITEM pList,
//                        PAGENT pAgent)
//
////////////////////////////////////////////////////////////////////////////////
PLISTITEM IsAgentInList(PLISTITEM pList,
                        PAGENT pAgent)
{
    while (pList)
    {
        if (pList->pAgent == pAgent)
        {
            return pList;
        }

        pList = pList->pNext;
    }

    return NULL;
}

////////////////////////////////////////////////////////////////////////////////
//
// PAGENT GetAgentFromName(LPTSTR lpszName)
//
////////////////////////////////////////////////////////////////////////////////
PAGENT GetAgentFromName(LPTSTR lpszName)
{
    PAGENT   pHold;

    pHold = g.pAgents;

    while (pHold)
    {
        if (!lstrcmpi(pHold->lpszName,
                     lpszName))
        {
            return pHold;
        }

        pHold = pHold->pNext;
    }

    return NULL;
}

///////////////////////////////////////////////////////////////////////////////
//
// PAGENT GetAgentFromhLine(HLINE hLine)
//
///////////////////////////////////////////////////////////////////////////////
PAGENT GetAgentFromhLine(HLINE hLine)
{
    PAGENT pAgent;

    pAgent = g.pAgents;

    while (pAgent)
    {
        if (pAgent->hLine == hLine)
        {
            return pAgent;
        }

        pAgent = pAgent->pNext;
    }

    return NULL;
}

///////////////////////////////////////////////////////////////////////////////
//
//  DWORD GetDeviceID(DWORD dwPermID)
//
///////////////////////////////////////////////////////////////////////////////
DWORD GetDeviceID(DWORD dwPermID)
{
    DWORD   dwCount;

    for (dwCount = 0; dwCount < g.dwNumDevs; dwCount++)
    {
        if (g.pdwPermIDs[dwCount] == dwPermID)
        {
            return dwCount;
        }
    }

    return (DWORD)-1;
}

///////////////////////////////////////////////////////////////////////////////
//
//      **************TAPI WRAPPER FUNCTIONS**************
//
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
//
// LineGetAddressCaps()
//
///////////////////////////////////////////////////////////////////////////////
LINEADDRESSCAPS * LineGetAddressCaps (HLINEAPP hLineApp,
                                      DWORD    dwDeviceID,
                                      DWORD    dwAddressID)
{
    LONG              lRetVal;
    LINEADDRESSCAPS * pLineAddressCaps;
    static DWORD      dwMaxNeededSize = sizeof(LINEADDRESSCAPS);

    // Allocate an initial block of memory for the LINEADDRESSCAPS structure,
    // which may or may not be big enough to hold all of the information.
    //
    pLineAddressCaps = ACDAlloc(dwMaxNeededSize);

    for (;;)
    {
        if (pLineAddressCaps == NULL)
        {
            return NULL;
        }
        pLineAddressCaps->dwTotalSize = dwMaxNeededSize;

        // Try (or retry) to get the LINEADDRESSCAPS information
        //
        lRetVal = lineGetAddressCaps(hLineApp,
                                     dwDeviceID,
                                     dwAddressID,
                                     TAPI_CURRENT_VERSION,
                                     0,
                                     pLineAddressCaps);
        if (lRetVal < 0)
        {
            ACDFree((HLOCAL)pLineAddressCaps);
            return NULL;
        }

        // If the currently allocated LINEADDRESSCAPS memory block was big
        // enough, we're all done, else we need to realloc the memory block
        // and try again.
        //
        if (pLineAddressCaps->dwNeededSize <= dwMaxNeededSize)
        {
            return pLineAddressCaps;
        }
        else
        {
            dwMaxNeededSize = pLineAddressCaps->dwNeededSize;
            pLineAddressCaps = ACDReAlloc((HLOCAL)pLineAddressCaps,
                                            dwMaxNeededSize);
        }
    }
}

///////////////////////////////////////////////////////////////////////////////
//
// LineGetCallInfo()
//
///////////////////////////////////////////////////////////////////////////////
LINECALLINFO * LineGetCallInfo (HCALL hCall)
{
    LONG           lRetVal;
    LINECALLINFO * pLineCallInfo;
    static DWORD   dwMaxNeededSize = sizeof(LINECALLINFO);

    // Allocate an initial block of memory for the LINECALLINFO structure,
    // which may or may not be big enough to hold all of the information.
    //
    pLineCallInfo = ACDAlloc(dwMaxNeededSize);

    for (;;)
    {
        if (pLineCallInfo == NULL)
        {
            return NULL;
        }
        pLineCallInfo->dwTotalSize = dwMaxNeededSize;

        // Try (or retry) to get the LINECALLINFO information
        //
        lRetVal = lineGetCallInfo(hCall,
                                  pLineCallInfo);
        if (lRetVal < 0)
        {
            ACDFree((HLOCAL)pLineCallInfo);
            return NULL;
        }

        // If the currently allocated LINECALLINFO memory block was big
        // enough, we're all done, else we need to realloc the memory block
        // and try again.
        //
        if (pLineCallInfo->dwNeededSize <= dwMaxNeededSize)
        {
            return pLineCallInfo;
        }
        else
        {
            dwMaxNeededSize = pLineCallInfo->dwNeededSize;
            pLineCallInfo = ACDReAlloc((HLOCAL)pLineCallInfo,
                                         dwMaxNeededSize);
        }
    }
}

///////////////////////////////////////////////////////////////////////////////
//
// LineGetDevCaps()
//
///////////////////////////////////////////////////////////////////////////////
LINEDEVCAPS * LineGetDevCaps (HLINEAPP hLineApp,
                              DWORD    dwDeviceID)
{
    LONG           lRetVal;
    LINEDEVCAPS  * pLineDevCaps;
    static DWORD   dwMaxNeededSize = sizeof(LINEDEVCAPS);

    pLineDevCaps = ACDAlloc(dwMaxNeededSize);
    for (;;)
    {
        if (pLineDevCaps == NULL)
        {
            return NULL;
        }
        pLineDevCaps->dwTotalSize = dwMaxNeededSize;
        lRetVal = lineGetDevCaps(hLineApp,
                                 dwDeviceID,
                                 TAPI_CURRENT_VERSION,
                                 0,
                                 pLineDevCaps);
        if (lRetVal < 0)
        {
            ACDFree((HLOCAL)pLineDevCaps);
            return NULL;
        }
        if (pLineDevCaps->dwNeededSize <= dwMaxNeededSize)
        {
            return pLineDevCaps;
        }
        else
        {
            dwMaxNeededSize = pLineDevCaps->dwNeededSize;
            pLineDevCaps = ACDReAlloc((HLOCAL)pLineDevCaps,
                                        dwMaxNeededSize);
        }
    }
}

///////////////////////////////////////////////////////////////////////////////
//
// LineGetID()
//
///////////////////////////////////////////////////////////////////////////////
VARSTRING * LineGetID (HLINE  hLine,
                       DWORD  dwAddressID,
                       HCALL  hCall,
                       DWORD  dwSelect,
                       LPCTSTR lpszDeviceClass)
{
    LONG           lRetVal;
    VARSTRING    * pVarString;
    static DWORD   dwMaxNeededSize = sizeof(VARSTRING);

    // Allocate an initial block of memory for the VARSTRING structure,
    // which may or may not be big enough to hold all of the information.
    //
    pVarString = ACDAlloc(dwMaxNeededSize);

    for (;;)
    {
        if (pVarString == NULL)
        {
            return NULL;
        }
        pVarString->dwTotalSize = dwMaxNeededSize;

        // Try (or retry) to get the VARSTRING information
        //
        lRetVal = lineGetID(hLine,
                            dwAddressID,
                            hCall,
                            dwSelect,
                            pVarString,
                            lpszDeviceClass);
        if (lRetVal < 0)
        {
            ACDFree(pVarString);
            return NULL;
        }

        // If the currently allocated VARSTRING memory block was big
        // enough, we're all done, else we need to realloc the memory block
        // and try again.
        //
        if (pVarString->dwNeededSize <= dwMaxNeededSize)
        {
            return pVarString;
        }
        else
        {
            dwMaxNeededSize = pVarString->dwNeededSize;
            pVarString = ACDReAlloc((HLOCAL)pVarString,
                                      dwMaxNeededSize);
        }
    }
}

///////////////////////////////////////////////////////////////////////////////
//
// LineGetCallStatus()
//
///////////////////////////////////////////////////////////////////////////////
LINECALLSTATUS * LineGetCallStatus (HCALL hCall)
{
    LONG                lRetVal;
    LINECALLSTATUS    * pLineCallStatus;
    static DWORD        dwMaxNeededSize = sizeof(LINECALLSTATUS);

    // Allocate an initial block of memory for the LINECALLSTATUS structure,
    // which may or may not be big enough to hold all of the information.
    //
    pLineCallStatus = ACDAlloc(dwMaxNeededSize);

    while (TRUE)
    {
        if (pLineCallStatus == NULL)
        {
            return NULL;
        }
        pLineCallStatus->dwTotalSize = dwMaxNeededSize;

        // Try (or retry) to get the LINECALLSTATUS information
        //
        lRetVal = lineGetCallStatus(hCall,
                                    pLineCallStatus);
        if (lRetVal < 0)
        {
            ACDFree((HLOCAL)pLineCallStatus);
            return NULL;
        }

        // If the currently allocated LINECALLSTATUS memory block was big
        // enough, we're all done, else we need to realloc the memory block
        // and try again.
        //
        if (pLineCallStatus->dwNeededSize <= dwMaxNeededSize)
        {
            return pLineCallStatus;
        }
        else
        {
            dwMaxNeededSize = pLineCallStatus->dwNeededSize;
            pLineCallStatus = ACDReAlloc((HLOCAL)pLineCallStatus,
                                         dwMaxNeededSize);
        }
    }
}


RESOURCE.H

////////////////////////////////////////////////////////////////////////
//
//  resource.h
//
////////////////////////////////////////////////////////////////////////
#define IDD_MAINDLG                     101
#define IDD_ADD                         102
#define IDD_ADDTOLIST                   103
#define IDD_ADDAGENT                    104
#define IDC_TREEWND                     1000
#define IDC_EDITWND                     1001
#define IDM_NEWGROUP                    1002
#define IDM_NEWAGENT                    1003
#define IDM_GROUPPROPERTIES             1004
#define IDM_GROUPDELETE                 1005
#define IDM_AGENTPROPERTIES             1006
#define IDM_AGENTDELETE                 1007
#define IDM_SIGNIN                      1008
#define IDC_NAME                        1009
#define IDC_NUMBER                      1010
#define IDC_LIST1                       1011
#define IDC_LIST2                       1012
#define IDC_ADD                         1013
#define IDC_REMOVE                      1014
#define IDM_GROUPADDTOLIST              1015
#define IDM_AGENTADDTOLIST              1016
#define IDC_STATICNOTINLIST             1017
#define IDC_STATICINLIST                1018
#define IDR_MAINMENU                    1019
#define ID_FILE_OPEN                    1020
#define ID_FILE_EXIT                    1021
#define ID_FILE_NEW                     1022
#define ID_EDIT_ADDAGENT                1023
#define ID_EDIT_ADDGROUP                1024
#define IDC_LINECOMBO                   1025
#define IDC_ADDRESSCOMBO                1026
#define IDM_GROUPAGENTSTATUS            1027
#define ID_VIEW_GROUP                   1028
#define ID_VIEW_AGENT                   1029
#define IDC_DESTADDRESS                 1030

#define IDC_STATIC                      -1


ATSP.C

/*++

Copyright 1995 - 1998 Microsoft Corporation

Module Name:

    atsp.c

Notes:

--*/

#include "atsp.h"

BOOL
WINAPI
DllMain(
    HANDLE  hDLL,
    DWORD   dwReason,
    LPVOID  lpReserved
    )
{
    if (dwReason ==  DLL_PROCESS_ATTACH)
    {
        ghInst = hDLL;

#if DBG
        {
            HKEY    hKey;
            DWORD   dwDataSize, dwDataType;
            char    szAtsp32DebugLevel[] = "Atsp32DebugLevel";

            RegOpenKeyExA(
                HKEY_LOCAL_MACHINE,
                gszAtspKey,
                0,
                KEY_ALL_ACCESS,
                &hKey
                );

            dwDataSize = sizeof (DWORD);
            gdwDebugLevel=0;

            RegQueryValueEx(
                hKey,
                szAtsp32DebugLevel,
                0,
                &dwDataType,
                (LPBYTE) &gdwDebugLevel,
                &dwDataSize
                );

            RegCloseKey (hKey);
        }
#endif

    }

    return TRUE;
}

void
CommThread(
    PDRVLINE    pLine
    )
{
    char            buf[4];
    DWORD           dwThreadID = GetCurrentThreadId(), dwNumBytes;
    HANDLE          hComm = pLine->hComm, hEvent;
    LPOVERLAPPED    pOverlapped = &pLine->Overlapped;

    DBGOUT((
        3,
        "CommThread (id=%d): enter, port=%s",
        dwThreadID,
        pLine->szComm
        ));

    hEvent = pOverlapped->hEvent;
    buf[0] = buf[1] = '.';

    //
    // Loop waiting for i/o to complete (either the Write done in
    // TSPI_lineMakeCall or the Reads done to retrieve status info).
    // Note that TSPI_lineDrop or TSPI_lineCloseCall may set the
    // event to alert us that they're tearing down the call, in
    // which case we just exit.
    //

    for (;;)
    {
        if (WaitForSingleObject (hEvent, ATSP_TIMEOUT) == WAIT_OBJECT_0)
        {
            if (pLine->bDropInProgress == TRUE)
            {
                DBGOUT((2, "CommThread (id=%d): drop in progress"));
                goto CommThread_exit;
            }

            GetOverlappedResult (hComm, pOverlapped, &dwNumBytes, FALSE);
            ResetEvent (hEvent);
        }
        else
        {
            DBGOUT((2, "CommThread (id=%d): wait timeout"));
            SetCallState (pLine, LINECALLSTATE_IDLE, 0);
            goto CommThread_exit;
        }

        buf[1] &= 0x7f; // nuke the parity bit

        DBGOUT((
            3,
            "CommThread (id=%d): read '%c'",
            dwThreadID,
            (buf[1] == '\r' ? '.' : buf[1])
            ));

        switch ((buf[0] << 8) + buf[1])
        {
        case 'CT':  // "CONNECT"
        case 'OK':  // "OK"

            SetCallState (pLine, LINECALLSTATE_CONNECTED, 0);
            goto CommThread_exit;

        case 'SY':  // "BUSY"
        case 'OR':  // "ERROR"
        case 'NO':  // "NO ANSWER", "NO DIALTONE", "NO CARRIER"

            SetCallState (pLine, LINECALLSTATE_IDLE, 0);
            goto CommThread_exit;

        default:

            break;
        }

        buf[0] = buf[1];

        ZeroMemory (pOverlapped, sizeof (OVERLAPPED) - sizeof (HANDLE));
        ReadFile (hComm, &buf[1], 1, &dwNumBytes, pOverlapped);
    }

CommThread_exit:

    CloseHandle (hEvent);
    DBGOUT((3, "CommThread (id=%d): exit", dwThreadID));
    ExitThread (0);
}

//
// We get a slough of C4047 (different levels of indrection) warnings down
// below in the initialization of FUNC_PARAM structs as a result of the
// real func prototypes having params that are types other than DWORDs,
// so since these are known non-interesting warnings just turn them off
//

#pragma warning (disable:4047)

//
// --------------------------- TAPI_lineXxx funcs -----------------------------
//

LONG
TSPIAPI
TSPI_lineClose(
    HDRVLINE    hdLine
    )
{
    LONG        lResult = 0;
#if DBG
    FUNC_PARAM  params[] =
    {
        { gszhdLine, hdLine }
    };
    FUNC_INFO   info =
    {
        "TSPI_lineClose",
        1,
        params,
    };
#endif

    Prolog (&info);
    DrvFree ((PDRVLINE) hdLine);
    return (Epilog (&info, lResult));
}

LONG
TSPIAPI
TSPI_lineCloseCall(
    HDRVCALL    hdCall
    )
{
    PDRVLINE    pLine = (PDRVLINE) hdCall;
#if DBG
    FUNC_PARAM  params[] =
    {
        { gszhdCall, hdCall  }
    };
    FUNC_INFO   info =
    {
        "TSPI_lineCloseCall",
        1,
        params
    };
#endif

    //
    // Note that in TAPI 2.0 TSPI_lineCloseCall can get called
    // without TSPI_lineDrop ever being called, so we need to
    // be prepared for either case.
    //

    Prolog (&info);
    DropActiveCall (pLine);
    pLine->htCall = NULL;
    return (Epilog (&info, 0));
}

LONG
TSPIAPI
TSPI_lineConditionalMediaDetection(
    HDRVLINE            hdLine,
    DWORD               dwMediaModes,
    LPLINECALLPARAMS    const lpCallParams
    )
{
#if DBG
    FUNC_PARAM  params[] =
    {
        { gszhdLine,        hdLine       },
        { "dwMediaModes",   dwMediaModes },
        { gszlpCallParams,  lpCallParams }
    };
    FUNC_INFO   info =
    {
        "TSPI_lineConditionalMediaDetection",
        3,
        params
    };
#endif

    //
    // This func is really a no-op for us, since we don't look
    // for incoming calls (though we do say we support them to
    // make apps happy)
    //

    Prolog (&info);
    return (Epilog (&info, 0));
}

LONG
TSPIAPI
TSPI_lineDrop(
    DRV_REQUESTID   dwRequestID,
    HDRVCALL        hdCall,
    LPCSTR          lpsUserUserInfo,
    DWORD           dwSize
    )
{
    PDRVLINE    pLine = (PDRVLINE) hdCall;
#if DBG
    FUNC_PARAM  params[] =
    {
        { gszdwRequestID,        dwRequestID     },
        { gszhdCall,             hdCall          },
        { "lpsUserUserInfo",    lpsUserUserInfo },
        { gszdwSize,             dwSize          }
    };
    FUNC_INFO   info =
    {
        "TSPI_lineDrop",
        4,
        params
    };
#endif

    Prolog (&info);
    DropActiveCall (pLine);
    SetCallState (pLine, LINECALLSTATE_IDLE, 0);
    (*gpfnCompletionProc)(dwRequestID, 0);
    return (Epilog (&info, dwRequestID));
}

LONG
TSPIAPI
TSPI_lineGetAddressCaps(
    DWORD              dwDeviceID,
    DWORD              dwAddressID,
    DWORD              dwTSPIVersion,
    DWORD              dwExtVersion,
    LPLINEADDRESSCAPS  lpAddressCaps
    )
{

#if DBG
    FUNC_PARAM  params[] =
    {
        { gszdwDeviceID,     dwDeviceID      },
        { "dwAddressID",    dwAddressID     },
        { "dwTSPIVersion",  dwTSPIVersion   },
        { "dwExtVersion",   dwExtVersion    },
        { "lpAddressCaps",  lpAddressCaps   }
    };
    FUNC_INFO   info =
    {
        "TSPI_lineGetAddressCaps",
        5,
        params
    };
#endif

    LONG        lResult = 0;

    Prolog (&info);

    if (dwAddressID != 0)
    {
        lResult = LINEERR_INVALADDRESSID;
    }

    lpAddressCaps->dwNeededSize =
    lpAddressCaps->dwUsedSize   = sizeof(LINEADDRESSCAPS);

    lpAddressCaps->dwLineDeviceID       = dwDeviceID;
    lpAddressCaps->dwAddressSharing     = LINEADDRESSSHARING_PRIVATE;
    lpAddressCaps->dwCallInfoStates     = LINECALLINFOSTATE_MEDIAMODE |
                                          LINECALLINFOSTATE_APPSPECIFIC;
    lpAddressCaps->dwCallerIDFlags      =
    lpAddressCaps->dwCalledIDFlags      =
    lpAddressCaps->dwRedirectionIDFlags =
    lpAddressCaps->dwRedirectingIDFlags = LINECALLPARTYID_UNAVAIL;
    lpAddressCaps->dwCallStates         = LINECALLSTATE_IDLE |
                                          LINECALLSTATE_OFFERING |
                                          LINECALLSTATE_ACCEPTED |
                                          LINECALLSTATE_DIALTONE |
                                          LINECALLSTATE_DIALING |
                                          LINECALLSTATE_CONNECTED |
                                          LINECALLSTATE_PROCEEDING |
                                          LINECALLSTATE_DISCONNECTED |
                                          LINECALLSTATE_UNKNOWN;
    lpAddressCaps->dwDialToneModes      = LINEDIALTONEMODE_UNAVAIL;
    lpAddressCaps->dwBusyModes          = LINEBUSYMODE_UNAVAIL;
    lpAddressCaps->dwSpecialInfo        = LINESPECIALINFO_UNAVAIL;
    lpAddressCaps->dwDisconnectModes    = LINEDISCONNECTMODE_NORMAL |
                                          LINEDISCONNECTMODE_BUSY |
                                          LINEDISCONNECTMODE_NOANSWER |
                                          LINEDISCONNECTMODE_UNAVAIL |
                                          LINEDISCONNECTMODE_NODIALTONE;
    lpAddressCaps->dwMaxNumActiveCalls  = 1;
    lpAddressCaps->dwAddrCapFlags       = LINEADDRCAPFLAGS_DIALED;
    lpAddressCaps->dwCallFeatures       = LINECALLFEATURE_ACCEPT |
                                          LINECALLFEATURE_ANSWER |
                                          LINECALLFEATURE_DROP |
                                          LINECALLFEATURE_SETCALLPARAMS;
    lpAddressCaps->dwAddressFeatures    = LINEADDRFEATURE_MAKECALL;

    return (Epilog (&info, lResult));
}

LONG
TSPIAPI
TSPI_lineGetAddressStatus(
    HDRVLINE            hdLine,
    DWORD               dwAddressID,
    LPLINEADDRESSSTATUS lpAddressStatus
    )
{
#if DBG
    FUNC_PARAM  params[] =
    {
        { gszhdLine,             hdLine         },
        { "dwAddressID",        dwAddressID     },
        { "lpAddressStatus",    lpAddressStatus }
    };
    FUNC_INFO   info =
    {
        "TSPI_lineGetAddressStatus",
        3,
        params
    };
#endif

    LONG        lResult = 0;
    PDRVLINE    pLine = (PDRVLINE) hdLine;

    Prolog (&info);

    lpAddressStatus->dwNeededSize =
    lpAddressStatus->dwUsedSize   = sizeof(LINEADDRESSSTATUS);

    lpAddressStatus->dwNumActiveCalls  = (pLine->htCall ? 1 : 0);
    lpAddressStatus->dwAddressFeatures = LINEADDRFEATURE_MAKECALL;

    return (Epilog (&info, lResult));
}

LONG
TSPIAPI
TSPI_lineGetCallAddressID(
    HDRVCALL            hdCall,
    LPDWORD             lpdwAddressID
    )
{
#if DBG
    FUNC_PARAM  params[] =
    {
        { gszhdCall,        hdCall          },
        { "lpdwAddressID",  lpdwAddressID   }
    };
    FUNC_INFO   info =
    {
        "TSPI_lineGetCallAddressID",
        2,
        params
    };
#endif

    //
    // We only support 1 address (id=0)
    //

    Prolog (&info);
    *lpdwAddressID = 0;
    return (Epilog (&info, 0));
}

LONG
TSPIAPI
TSPI_lineGetCallInfo(
    HDRVCALL        hdCall,
    LPLINECALLINFO  lpLineInfo
    )
{
#if DBG
    FUNC_PARAM  params[] =
    {
        { gszhdCall,     hdCall      },
        { "lpLineInfo", lpLineInfo  }
    };
    FUNC_INFO   info =
    {
        "TSPI_lineGetCallInfo",
        2,
        params
    };
#endif
    LONG        lResult = 0;
    PDRVLINE    pLine = (PDRVLINE) hdCall;

    Prolog (&info);

    lpLineInfo->dwNeededSize =
    lpLineInfo->dwUsedSize   = sizeof(LINECALLINFO);

    lpLineInfo->dwBearerMode         = LINEBEARERMODE_VOICE;
    lpLineInfo->dwMediaMode          = pLine->dwMediaMode;
    lpLineInfo->dwCallStates         = LINECALLSTATE_IDLE |
                                       LINECALLSTATE_DIALTONE |
                                       LINECALLSTATE_DIALING |
                                       LINECALLSTATE_CONNECTED |
                                       LINECALLSTATE_PROCEEDING |
                                       LINECALLSTATE_DISCONNECTED |
                                       LINECALLSTATE_UNKNOWN;
    lpLineInfo->dwOrigin             = LINECALLORIGIN_OUTBOUND;
    lpLineInfo->dwReason             = LINECALLREASON_DIRECT;
    lpLineInfo->dwCallerIDFlags      =
    lpLineInfo->dwCalledIDFlags      =
    lpLineInfo->dwConnectedIDFlags   =
    lpLineInfo->dwRedirectionIDFlags =
    lpLineInfo->dwRedirectingIDFlags = LINECALLPARTYID_UNAVAIL;

    return (Epilog (&info, lResult));
}

LONG
TSPIAPI
TSPI_lineGetCallStatus(
    HDRVCALL            hdCall,
    LPLINECALLSTATUS    lpLineStatus
    )
{
#if DBG
    FUNC_PARAM  params[] =
    {
        { gszhdCall,         hdCall          },
        { "lpLineStatus",   lpLineStatus    }
    };
    FUNC_INFO   info =
    {
        "TSPI_lineGetCallStatus",
        2,
        params
    };
#endif
    LONG        lResult = 0;
    PDRVLINE    pLine = (PDRVLINE) hdCall;

    Prolog (&info);

    lpLineStatus->dwNeededSize =
    lpLineStatus->dwUsedSize   = sizeof(LINECALLSTATUS);

    lpLineStatus->dwCallState  = pLine->dwCallState;

    if (pLine->dwCallState != LINECALLSTATE_IDLE)
    {
        lpLineStatus->dwCallFeatures = LINECALLFEATURE_DROP;
    }

    return (Epilog (&info, lResult));
}

LONG
TSPIAPI
TSPI_lineGetDevCaps(
    DWORD           dwDeviceID,
    DWORD           dwTSPIVersion,
    DWORD           dwExtVersion,
    LPLINEDEVCAPS   lpLineDevCaps
    )
{
#if DBG
    FUNC_PARAM  params[] =
    {
        { gszdwDeviceID,     dwDeviceID      },
        { "dwTSPIVersion",  dwTSPIVersion   },
        { "dwExtVersion",   dwExtVersion    },
        { "lpLineDevCaps",  lpLineDevCaps   }
    };
    FUNC_INFO   info =
    {
        "TSPI_lineGetDevCaps",
        4,
        params
    };
#endif

    LONG            lResult = 0;
    static WCHAR    szProviderInfo[] = L"AT-compatible modem service provider";

    #define PROVIDER_INFO_SIZE (37 * sizeof (WCHAR))

    Prolog (&info);

    lpLineDevCaps->dwNeededSize = sizeof (LINEDEVCAPS) + PROVIDER_INFO_SIZE +
        (MAX_DEV_NAME_LENGTH + 1) * sizeof (WCHAR);

    if (lpLineDevCaps->dwTotalSize >= lpLineDevCaps->dwNeededSize)
    {
        #define LINECONFIG_SIZE   (2 * (MAX_DEV_NAME_LENGTH + 1) + 40)

        char    szLineConfig[LINECONFIG_SIZE], szLineN[16], *p;
        HKEY    hKey;
        DWORD   dwDataSize, dwDataType;

        lpLineDevCaps->dwUsedSize = lpLineDevCaps->dwNeededSize;

        lpLineDevCaps->dwProviderInfoSize   = PROVIDER_INFO_SIZE;
        lpLineDevCaps->dwProviderInfoOffset = sizeof(LINEDEVCAPS);

        My_lstrcpyW ((WCHAR *)(lpLineDevCaps + 1), szProviderInfo);

        RegOpenKeyEx(
            HKEY_LOCAL_MACHINE,
            gszAtspKey,
            0,
            KEY_ALL_ACCESS,
            &hKey
            );

        dwDataSize = LINECONFIG_SIZE;
        wsprintf (szLineN, "Line%d", dwDeviceID - gdwLineDeviceIDBase);
        lstrcpy (szLineConfig, gszDefLineConfigParams);

        RegQueryValueEx(
            hKey,
            szLineN,
            0,
            &dwDataType,
            (LPBYTE) szLineConfig,
            &dwDataSize
            );

        RegCloseKey (hKey);

        for (p = szLineConfig; *p != ','; p++);
        *p = 0;

        lpLineDevCaps->dwLineNameSize   = (lstrlen (szLineConfig) + 1) *
            sizeof (WCHAR);
        lpLineDevCaps->dwLineNameOffset = sizeof(LINEDEVCAPS) +
            PROVIDER_INFO_SIZE;

        MultiByteToWideChar(
            CP_ACP,
            MB_PRECOMPOSED,
            szLineConfig,
            -1,
            (WCHAR *) ((LPBYTE) (lpLineDevCaps + 1) + PROVIDER_INFO_SIZE),
            lpLineDevCaps->dwLineNameSize
            );
    }
    else
    {
        lpLineDevCaps->dwUsedSize = sizeof(LINEDEVCAPS);
    }

    lpLineDevCaps->dwStringFormat      = STRINGFORMAT_ASCII;
    lpLineDevCaps->dwAddressModes      = LINEADDRESSMODE_ADDRESSID;
    lpLineDevCaps->dwNumAddresses      = 1;
    lpLineDevCaps->dwBearerModes       = LINEBEARERMODE_VOICE;
    lpLineDevCaps->dwMaxRate           = 9600;
    lpLineDevCaps->dwMediaModes        = LINEMEDIAMODE_INTERACTIVEVOICE |
                                         LINEMEDIAMODE_DATAMODEM;
    lpLineDevCaps->dwDevCapFlags       = LINEDEVCAPFLAGS_CLOSEDROP |
                                         LINEDEVCAPFLAGS_DIALBILLING |
                                         LINEDEVCAPFLAGS_DIALQUIET |
                                         LINEDEVCAPFLAGS_DIALDIALTONE;
    lpLineDevCaps->dwMaxNumActiveCalls = 1;
    lpLineDevCaps->dwRingModes         = 1;
    lpLineDevCaps->dwLineFeatures      = LINEFEATURE_MAKECALL;

    return (Epilog (&info, lResult));
}

LONG
TSPIAPI
TSPI_lineGetID(
    HDRVLINE    hdLine,
    DWORD       dwAddressID,
    HDRVCALL    hdCall,
    DWORD       dwSelect,
    LPVARSTRING lpDeviceID,
    LPCWSTR     lpszDeviceClass,
    HANDLE      hTargetProcess
    )
{
#if DBG
    FUNC_PARAM  params[] =
    {
        { gszhdLine,             hdLine          },
        { "dwAddressID",        dwAddressID     },
        { gszhdCall,             hdCall          },
        { "dwSelect",           dwSelect        },
        { "lpDeviceID",         lpDeviceID      },
        { "lpszDeviceClass",    lpszDeviceClass },
        { "hTargetProcess",     hTargetProcess  }
    };
    FUNC_INFO   info =
    {
        "TSPI_lineGetID",
        7,
        params
    };
#endif

    DWORD       dwNeededSize = sizeof(VARSTRING) + sizeof (DWORD);
    LONG        lResult = 0;
    PDRVLINE    pLine = (dwSelect == LINECALLSELECT_CALL ?
                    (PDRVLINE) hdCall : (PDRVLINE) hdLine);

    Prolog (&info);

    if (lstrcmpiW (lpszDeviceClass, L"tapi/line") == 0)
    {
        if (lpDeviceID->dwTotalSize < dwNeededSize)
        {
            lpDeviceID->dwUsedSize = 3*sizeof(DWORD);
        }
        else
        {
            lpDeviceID->dwUsedSize = dwNeededSize;

            lpDeviceID->dwStringFormat = STRINGFORMAT_BINARY;
            lpDeviceID->dwStringSize   = sizeof(DWORD);
            lpDeviceID->dwStringOffset = sizeof(VARSTRING);

            *((LPDWORD)(lpDeviceID + 1)) = pLine->dwDeviceID;
        }

        lpDeviceID->dwNeededSize = dwNeededSize;
    }
    else if (lstrcmpiW (lpszDeviceClass, L"comm/datamodem") == 0)
    {
        dwNeededSize += (strlen (pLine->szComm) + 1) * sizeof (WCHAR);

        if (lpDeviceID->dwTotalSize < dwNeededSize)
        {
            lpDeviceID->dwUsedSize = 3 * sizeof(DWORD);
        }
        else
        {
            HANDLE hCommDup = NULL;

            if (!pLine->htCall)
            {
                DBGOUT((1, "TSPI_lineGetID32: error, no active call"));

                lResult = LINEERR_OPERATIONFAILED;

                goto TSPI_lineGetID_epilog;
            }

            if (!DuplicateHandle(
                    GetCurrentProcess(),
                    pLine->hComm,
                    hTargetProcess,
                    &hCommDup,
                    0,
                    TRUE,
                    DUPLICATE_SAME_ACCESS
                    ))
            {
                DBGOUT((
                    1,
                    "TSPI_lineGetID: DupHandle failed, err=%ld",
                    GetLastError()
                    ));

                lResult = LINEERR_OPERATIONFAILED;

                goto TSPI_lineGetID_epilog;
            }

            lpDeviceID->dwUsedSize = dwNeededSize;

            lpDeviceID->dwStringFormat = STRINGFORMAT_BINARY;
            lpDeviceID->dwStringSize   = dwNeededSize - sizeof(VARSTRING);
            lpDeviceID->dwStringOffset = sizeof(VARSTRING);

            *((HANDLE *)(lpDeviceID + 1)) = hCommDup;

            lstrcpy(
                ((char *)(lpDeviceID + 1)) + sizeof (HANDLE),
                pLine->szComm
                );

            MultiByteToWideChar(
                CP_ACP,
                0,
                pLine->szComm,
                -1,
                ((WCHAR *)(lpDeviceID + 1)) + sizeof (HANDLE),
                256
                );
        }

        lpDeviceID->dwNeededSize = dwNeededSize;
    }
    else
    {
        lResult = LINEERR_NODEVICE;
    }

TSPI_lineGetID_epilog:

    return (Epilog (&info, lResult));
}

LONG
TSPIAPI
TSPI_lineGetLineDevStatus(
    HDRVLINE        hdLine,
    LPLINEDEVSTATUS lpLineDevStatus
    )
{
#if DBG
    FUNC_PARAM  params[] =
    {
        { gszhdLine,            hdLine          },
        { "lpLineDevStatus",    lpLineDevStatus }
    };
    FUNC_INFO   info =
    {
        "TSPI_lineGetLineDevStatus",
        2,
        params
    };
#endif

    LONG        lResult = 0;
    PDRVLINE    pLine = (PDRVLINE) hdLine;

    Prolog (&info);

    lpLineDevStatus->dwUsedSize =
    lpLineDevStatus->dwNeededSize = sizeof (LINEDEVSTATUS);

    lpLineDevStatus->dwNumActiveCalls = (pLine->htCall ? 1 : 0);
    //lpLineDevStatus->dwLineFeatures =
    lpLineDevStatus->dwDevStatusFlags = LINEDEVSTATUSFLAGS_CONNECTED |
                                        LINEDEVSTATUSFLAGS_INSERVICE;
    return (Epilog (&info, lResult));
}

LONG
TSPIAPI
TSPI_lineGetNumAddressIDs(
    HDRVLINE    hdLine,
    LPDWORD     lpdwNumAddressIDs
    )
{
#if DBG
    FUNC_PARAM  params[] =
    {
        { gszhdLine,            hdLine            },
        { "lpdwNumAddressIDs",  lpdwNumAddressIDs }
    };
    FUNC_INFO   info =
    {
        "TSPI_lineGetNumAddressIDs",
        2,
        params
    };
#endif

    LONG        lResult = 0;
    PDRVLINE    pLine = (PDRVLINE) hdLine;

    //
    // We only support 1 address (id=0)
    //

    Prolog (&info);
    *lpdwNumAddressIDs = 1;
    return (Epilog (&info, lResult));
}

LONG
TSPIAPI
TSPI_lineMakeCall(
    DRV_REQUESTID       dwRequestID,
    HDRVLINE            hdLine,
    HTAPICALL           htCall,
    LPHDRVCALL          lphdCall,
    LPCWSTR             lpszDestAddress,
    DWORD               dwCountryCode,
    LPLINECALLPARAMS    const lpCallParams
    )
{
    char        szCommands[64], szCommand[64], szDestAddress[128];
    DWORD       dwThreadID, dwNumBytes, dwError;
    PDRVLINE    pLine = (PDRVLINE) hdLine;
#if DBG
    FUNC_PARAM  params[] =
    {
        { gszdwRequestID,       dwRequestID     },
        { gszhdLine,            hdLine          },
        { "htCall",             htCall          },
        { "lphdCall",           lphdCall        },
        { "lpszDestAddress",    szDestAddress   },
        { "dwCountryCode",      dwCountryCode   },
        { gszlpCallParams,      lpCallParams    }
    };
    FUNC_INFO info =
    {
        "TSPI_lineMakeCall",
        7,
        params
    };
#endif

    if (lpszDestAddress)
    {
        WideCharToMultiByte(
            CP_ACP,
            0,
            lpszDestAddress,
            -1,
            (LPSTR) szDestAddress,
            128,
            NULL,
            NULL
            );
    }

    Prolog (&info);

    //
    // Check to see if there's already another call
    //

    if (pLine->htCall)
    {
        (*gpfnCompletionProc)(dwRequestID, LINEERR_CALLUNAVAIL);
        goto TSPI_lineMakeCall_return;
    }

    //
    // Since we don't support TSPI_lineDial, fail if app tries
    // to pass a NULL lpszDestAddress (implying that app just
    // wants to go offhook)
    //

    if (lpszDestAddress == NULL)
    {
        (*gpfnCompletionProc)(dwRequestID, LINEERR_INVALADDRESS);
        goto TSPI_lineMakeCall_return;
    }

    //
    // Get the line's config info
    //

    {
        HKEY    hKey;
        DWORD   dwDataSize, dwDataType;
        char    szLineN[8], *pszConfig, *p, *p2;

        wsprintf(
            szLineN,
            "Line%d",
            ((PDRVLINE) hdLine)->dwDeviceID - gdwLineDeviceIDBase
            );

        dwDataSize = 256;

        pszConfig = DrvAlloc (dwDataSize);

        RegOpenKeyEx(
            HKEY_LOCAL_MACHINE,
            gszAtspKey,
            0,
            KEY_ALL_ACCESS,
            &hKey
            );

        RegQueryValueEx(
            hKey,
            szLineN,
            0,
            &dwDataType,
            (LPBYTE) pszConfig,
            &dwDataSize
            );

        pszConfig[dwDataSize] = '\0';       // *pszConfig = "MyLine,COM1,L0"

        RegCloseKey (hKey);

        //
        // szComm
        //

        for (p = pszConfig; *p != ','; p++);
        p++;                                // *p = "COM1,L0"
        for (p2 = p; *p2 != ','; p2++);
        *p2 = 0;                            // *p = "COM1"

        lstrcpy (pLine->szComm, p);

        //
        // szCommands
        //

        p2++;                               // *p2 = "L0"
        lstrcpy (szCommands, p2);

        DrvFree (pszConfig);
    }

    //
    // Open the port
    //

    if ((pLine->hComm = CreateFile(
            pLine->szComm,
            GENERIC_READ | GENERIC_WRITE,
            0, //FILE_SHARE_READ | FILE_SHARE_WRITE,  // BUGBUG
            NULL, // no security attrs
            OPEN_EXISTING,
            FILE_FLAG_OVERLAPPED,
            NULL  // no template file

            )) == INVALID_HANDLE_VALUE)
    {
        DBGOUT((
            3,
            "TSPI_lineMakeCall: CreateFile(%s) failed, err=%ld",
            pLine->szComm,
            GetLastError()
            ));

        (*gpfnCompletionProc)(dwRequestID, LINEERR_RESOURCEUNAVAIL);
        goto TSPI_lineMakeCall_return;
    }

    //
    // Setup up the modem command string.  If there's an initial 'T'
    // or 'P' (for Tone or Pulse) in the dest address then disregard
    // it.  Also if it's a voice call add the semi colon so we return
    // to cmd mode.
    //

    {
        char *p = (char *) szDestAddress;

        if (*p == 'T'  ||  *p == 'P')
        {
            p++;
        }

        if (lpCallParams &&
            lpCallParams->dwMediaMode != LINEMEDIAMODE_INTERACTIVEVOICE)
        {
            wsprintf (szCommand, "AT%sDT%s\r", szCommands, p);
        }
        else
        {
            wsprintf (szCommand, "AT%sDT%s;\r", szCommands, p);
        }
    }

    //
    // Init the data structure & tell tapi our handle to the call
    //

    pLine->htCall          = htCall;
    pLine->bDropInProgress = FALSE;
    pLine->dwMediaMode     = (lpCallParams ? lpCallParams->dwMediaMode :
        LINEMEDIAMODE_INTERACTIVEVOICE);

    *lphdCall = (HDRVCALL) pLine;

    //
    // Do an overlapped write, the comm thread will deal with the results
    //

    pLine->Overlapped.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL);

    if (!WriteFile(
            pLine->hComm,
            szCommand,
            lstrlen (szCommand),
            &dwNumBytes,
            &pLine->Overlapped
            )

        && (dwError = GetLastError()) != ERROR_IO_PENDING)
    {
        DBGOUT((
            1,
            "TSPI_lineMakeCall: WriteFile(%s) failed, error=%d",
            pLine->szComm,
            dwError
            ));

        pLine->htCall = NULL;
        CloseHandle (pLine->hComm);
        CloseHandle (pLine->Overlapped.hEvent);
        (*gpfnCompletionProc)(dwRequestID, LINEERR_OPERATIONFAILED);
        goto TSPI_lineMakeCall_return;
    }

    //
    // Complete the requests & set the initial call state
    //

    (*gpfnCompletionProc)(dwRequestID, 0);
    SetCallState (pLine, LINECALLSTATE_DIALING, 0);

    //
    // Spin the comm thread to handle the results of the Write above
    //

    {
        HANDLE hCommThread;

        if (!(hCommThread = CreateThread(
                (LPSECURITY_ATTRIBUTES) NULL,
                0,
                (LPTHREAD_START_ROUTINE) CommThread,
                pLine,
                0,
                &dwThreadID
                )))
        {
            DBGOUT((
                1,
                "TSPI_lineMakeCall: CreateThread failed, err=%ld",
                GetLastError()
                ));

            GetOverlappedResult(
                pLine->hComm,

&pLine->Overlapped,
                &dwNumBytes,
                TRUE
                );

            SetCallState (pLine, LINECALLSTATE_IDLE, 0);
            CloseHandle (pLine->hComm);
            CloseHandle (pLine->Overlapped.hEvent);
            goto TSPI_lineMakeCall_return;
        }

        CloseHandle (hCommThread);
    }

TSPI_lineMakeCall_return:

    return (Epilog (&info, dwRequestID));
}

LONG
TSPIAPI
TSPI_lineNegotiateTSPIVersion(
    DWORD   dwDeviceID,
    DWORD   dwLowVersion,
    DWORD   dwHighVersion,
    LPDWORD lpdwTSPIVersion
    )
{
    LONG        lResult = 0;
#if DBG
    FUNC_PARAM  params[] =
    {
        { gszdwDeviceID,        dwDeviceID      },
        { "dwLowVersion",       dwLowVersion    },
        { "dwHighVersion",      dwHighVersion   },
        { "lpdwTSPIVersion",    lpdwTSPIVersion }
    };
    FUNC_INFO   info =
    {
        "TSPI_lineNegotiateTSPIVersion",
        4,
        params
    };
#endif

    Prolog (&info);
    *lpdwTSPIVersion = 0x00020000;
    return (Epilog (&info, lResult));
}

LONG
TSPIAPI
TSPI_lineOpen(
    DWORD       dwDeviceID,
    HTAPILINE   htLine,
    LPHDRVLINE  lphdLine,
    DWORD       dwTSPIVersion,
    LINEEVENT   lpfnEventProc
    )
{
    LONG        lResult;
    PDRVLINE    pLine;
#if DBG
    FUNC_PARAM  params[] =
    {
        { gszdwDeviceID,    dwDeviceID      },
        { "htLine",         htLine          },
        { "lphdLine",       lphdLine        },
        { "dwTSPIVersion",  dwTSPIVersion   },
        { "lpfnEventProc",  lpfnEventProc   }
    };
    FUNC_INFO   info =
    {
        "TSPI_lineOpen",
        5,
        params
    };
#endif

    Prolog (&info);

    if ((pLine = DrvAlloc (sizeof (DRVLINE))))
    {
        pLine->htLine       = htLine;
        pLine->pfnEventProc = lpfnEventProc;
        pLine->dwDeviceID   = dwDeviceID;

        *lphdLine = (HDRVLINE) pLine;

        lResult = 0;
    }
    else
    {
        lResult = LINEERR_NOMEM;
    }

    return (Epilog (&info, lResult));
}

LONG
TSPIAPI
TSPI_lineSetDefaultMediaDetection(
    HDRVLINE    hdLine,
    DWORD       dwMediaModes
    )
{
#if DBG
    FUNC_PARAM  params[] =
    {
        { gszhdLine,        hdLine       },
        { "dwMediaModes",   dwMediaModes }
    };
    FUNC_INFO   info =
    {
        "TSPI_lineSetDefaultMediaDetection",
        2,
        params
    };
#endif

    //
    // This func is really a no-op for us, since we don't look
    // for incoming calls (though we do say we support them to
    // make apps happy)
    //

    Prolog (&info);
    return (Epilog (&info, 0));
}

//
// ------------------------- TSPI_providerXxx funcs ---------------------------
//

LONG
TSPIAPI
TSPI_providerConfig(
    HWND    hwndOwner,
    DWORD   dwPermanentProviderID
    )
{
    //
    // Although this func is never called by TAPI v2.0, we export
    // it so that the Telephony Control Panel Applet knows that it
    // can configure this provider via lineConfigProvider(),
    // otherwise Telephon.cpl will not consider it configurable
    //

    return 0;
}

LONG
TSPIAPI
TSPI_providerGenericDialogData(
    DWORD               dwObjectID,
    DWORD               dwObjectType,
    LPVOID              lpParams,
    DWORD               dwSize
    )
{
    LONG        lResult = 0;
#if DBG
    FUNC_PARAM  params[] =
    {
        { "dwObjectID",     dwObjectID      },
        { "dwObjectType",   dwObjectType    },
        { "lpParams",       lpParams        },
        { "dwSize",         dwSize          }
    };
    FUNC_INFO   info =
    {
        "TSPI_providerGenericDialogData",
        4,
        params
    };
#endif

    Prolog (&info);
    return (Epilog (&info, lResult));
}

LONG
TSPIAPI
TSPI_providerInit(
    DWORD               dwTSPIVersion,
    DWORD               dwPermanentProviderID,
    DWORD               dwLineDeviceIDBase,
    DWORD               dwPhoneDeviceIDBase,
    DWORD               dwNumLines,
    DWORD               dwNumPhones,
    ASYNC_COMPLETION    lpfnCompletionProc,
    LPDWORD             lpdwTSPIOptions
    )
{
    LONG        lResult = 0;
#if DBG
    FUNC_PARAM  params[] =
    {
        { "dwTSPIVersion",          dwTSPIVersion           },
        { gszdwPermanentProviderID, dwPermanentProviderID   },
        { "dwLineDeviceIDBase",     dwLineDeviceIDBase      },
        { "dwPhoneDeviceIDBase",    dwPhoneDeviceIDBase     },
        { "dwNumLines",             dwNumLines              },
        { "dwNumPhones",            dwNumPhones             },
        { "lpfnCompletionProc",     lpfnCompletionProc      }
    };
    FUNC_INFO   info =
    {
        "TSPI_providerInit",
        7,
        params
    };
#endif

    Prolog (&info);
    gdwLineDeviceIDBase = dwLineDeviceIDBase;
    gpfnCompletionProc  = lpfnCompletionProc;
    *lpdwTSPIOptions = LINETSPIOPTION_NONREENTRANT;
    return (Epilog (&info, lResult));
}

LONG
TSPIAPI
TSPI_providerInstall(
    HWND    hwndOwner,
    DWORD   dwPermanentProviderID
    )
{
    //
    // Although this func is never called by TAPI v2.0, we export
    // it so that the Telephony Control Panel Applet knows that it
    // can add this provider via lineAddProvider(), otherwise
    // Telephon.cpl will not consider it installable
    //
    //

    return 0;
}

LONG
TSPIAPI
TSPI_providerRemove(
    HWND    hwndOwner,
    DWORD   dwPermanentProviderID
    )
{
    //
    // Although this func is never called by TAPI v2.0, we export
    // it so that the Telephony Control Panel Applet knows that it
    // can remove this provider via lineRemoveProvider(), otherwise
    // Telephon.cpl will not consider it removable
    //

    return 0;
}

LONG
TSPIAPI
TSPI_providerShutdown(
    DWORD   dwTSPIVersion,
    DWORD   dwPermanentProviderID
    )
{
    LONG        lResult = 0;
#if DBG
    FUNC_PARAM  params[] =
    {
        { "dwTSPIVersion",          dwTSPIVersion },
        { gszdwPermanentProviderID, dwPermanentProviderID   }
    };
    FUNC_INFO   info =
    {
        "TSPI_providerShutdown",
        2,
        params
    };
#endif

    Prolog (&info);

    return (Epilog (&info, lResult));
}

LONG
TSPIAPI
TSPI_providerEnumDevices(
    DWORD       dwPermanentProviderID,
    LPDWORD     lpdwNumLines,
    LPDWORD     lpdwNumPhones,
    HPROVIDER   hProvider,
    LINEEVENT   lpfnLineCreateProc,
    PHONEEVENT  lpfnPhoneCreateProc
    )
{
   HKEY     hKey;
   DWORD    dwNumLines, dwDataType, dwDataSize;

   //
   // Retrieve the number of devices we're
   // configured for from our registry section
   //

   RegOpenKeyEx(
       HKEY_LOCAL_MACHINE,
       gszAtspKey,
       0,
       KEY_ALL_ACCESS,
       &hKey
       );

   dwDataSize = sizeof(dwNumLines);
   dwNumLines = 0;

   RegQueryValueEx(
       hKey,
       gszNumLines,
       0,
       &dwDataType,
       (LPBYTE) &dwNumLines,
       &dwDataSize
       );

   RegCloseKey (hKey);

   *lpdwNumLines  = dwNumLines;
   *lpdwNumPhones = 0;
   return 0;
}

LONG
TSPIAPI
TSPI_providerUIIdentify(
    LPWSTR   lpszUIDLLName
    )
{
    LONG        lResult = 0;
#if DBG
    FUNC_PARAM  params[] =
    {
        { "lpsUIDLLName",  lpszUIDLLName }
    };
    FUNC_INFO   info =
    {
        "TSPI_providerUIIdentify",
        1,
        params
    };
#endif

    Prolog (&info);
    My_lstrcpyW(lpszUIDLLName, L"atsp32.tsp");
    return (Epilog (&info, lResult));
}

//
// ---------------------------- TUISPI_xxx funcs ------------------------------
//

LONG
TSPIAPI
TUISPI_lineConfigDialog(
    TUISPIDLLCALLBACK   lpfnUIDLLCallback,
    DWORD               dwDeviceID,
    HWND                hwndOwner,
    LPCWSTR             lpszDeviceClass
    )
{
    char        szDeviceClass[128];
    LONG        lResult = 0;
#if DBG
    FUNC_PARAM  params[] =
    {
        { "lpfnUIDLLCallback",   lpfnUIDLLCallback },
        { gszdwDeviceID,         dwDeviceID        },
        { gszhwndOwner,          hwndOwner         },
        { "lpszDeviceClass",     szDeviceClass     }
    };
    FUNC_INFO   info =
    {
        "TUISPI_lineConfigDialog",
        4,
        params
    };
#endif

    if (lpszDeviceClass)
    {
        WideCharToMultiByte(
            CP_ACP,
            0,
            lpszDeviceClass,
            -1,
            (LPSTR) szDeviceClass,
            128,
            NULL,
            NULL
            );
    }

    Prolog (&info);

    DialogBoxParam(
        ghInst,
        MAKEINTRESOURCE(IDD_DIALOG1),
        hwndOwner,
        (DLGPROC) ConfigDlgProc,
        0
        );

    return (Epilog (&info, lResult));
}

LONG
TSPIAPI
TUISPI_providerConfig(
    TUISPIDLLCALLBACK   lpfnUIDLLCallback,
    HWND                hwndOwner,
    DWORD               dwPermanentProviderID
    )
{
    LONG        lResult = 0;
#if DBG
    FUNC_PARAM  params[] =
    {
        { "lpfnUIDLLCallback",      lpfnUIDLLCallback },
        { gszhwndOwner,             hwndOwner    },
        { gszdwPermanentProviderID, dwPermanentProviderID   }
    };
    FUNC_INFO   info =
    {
        "TUISPI_providerConfig",
        3,
        params
    };
#endif

    Prolog (&info);

    DialogBoxParam(
        ghInst,
        MAKEINTRESOURCE(IDD_DIALOG1),
        hwndOwner,
        (DLGPROC) ConfigDlgProc,
        0
        );

    return (Epilog (&info, lResult));
}

LONG
TSPIAPI
TUISPI_providerInstall(
    TUISPIDLLCALLBACK   lpfnUIDLLCallback,
    HWND                hwndOwner,
    DWORD               dwPermanentProviderID
    )
{
    LONG    lResult;

    if ((lResult = ProviderInstall ("atsp32.tsp", TRUE)) == 0)
    {
        DialogBoxParam(
            ghInst,
            MAKEINTRESOURCE(IDD_DIALOG1),
            hwndOwner,
            (DLGPROC) ConfigDlgProc,
            0
            );
    }

    return lResult;
}

LONG
TSPIAPI
TUISPI_providerRemove(
    TUISPIDLLCALLBACK   lpfnUIDLLCallback,
    HWND                hwndOwner,
    DWORD               dwPermanentProviderID
    )
{
    HKEY    hKey;
    char    szSoftwareMsft[] = "Software\\Microsoft", szATSP[] = "ATSP";

    //
    // Clean up our registry section
    //

    RegOpenKeyExA(
        HKEY_LOCAL_MACHINE,
        szSoftwareMsft,
        0,
        KEY_ALL_ACCESS,
        &hKey
        );

    RegDeleteKeyA (hKey, szATSP);
    RegCloseKey (hKey);
    return 0;
}

#pragma warning (default:4047)

//
// ---------------------- Misc private support routines -----------------------
//

LPWSTR
PASCAL
My_lstrcpyW(
    WCHAR   *pString1,
    WCHAR   *pString2
    )
{
    WCHAR *p = pString1;

    for (; (*p = *pString2); p++, pString2++);
    return pString1;
}

void
PASCAL
EnableChildren(
    HWND    hwnd,
    BOOL    bEnable
    )
{
    int i;
    static int aiControlIDs[] =
    {
        IDC_DEVICES,
        IDC_NAME,
        IDC_PORT,
        IDC_COMMANDS,
        IDC_REMOVE,
        0
    };

    for (i = 0; aiControlIDs[i]; i++)
    {
        EnableWindow (GetDlgItem (hwnd, aiControlIDs[i]), bEnable);
    }
}

void
PASCAL
SelectDevice(
    HWND    hwnd,
    int     iDevice
    )
{
    SendDlgItemMessage (hwnd, IDC_DEVICES, LB_SETCURSEL, iDevice, 0);
    PostMessage(hwnd, WM_COMMAND, IDC_DEVICES | (LBN_SELCHANGE << 16), 0);
}

BOOL
CALLBACK
ConfigDlgProc(
    HWND    hwnd,
    UINT    msg,
    WPARAM  wParam,
    LPARAM  lParam
    )
{
    static  HKEY    hAtspKey;

    DWORD   dwDataSize;
    DWORD   dwDataType;

    switch (msg)
    {
    case WM_INITDIALOG:
    {
        char   *pBuf;
        DWORD   i, iNumLines;

        //
        // Create or open our configuration key in the registry.  If the
        // create fails it may well be that the current user does not
        // have write access to this portion of the registry, so we'll
        // just show a "read only" dialog and not allow user to make any
        // changes
        //

        {
            LONG    lResult;
            DWORD   dwDisposition;

            if ((lResult = RegCreateKeyEx(
                    HKEY_LOCAL_MACHINE,
                    gszAtspKey,
                    0,
                    "",
                    REG_OPTION_NON_VOLATILE,
                    KEY_ALL_ACCESS,
                    (LPSECURITY_ATTRIBUTES) NULL,
                    &hAtspKey,
                    &dwDisposition

                    )) != ERROR_SUCCESS)
            {
                DBGOUT((
                    3,
                    "RegCreateKeyEx(%s,ALL_ACCESS) failed, err=%d",
                    gszAtspKey,
                    lResult
                    ));

                if ((lResult = RegOpenKeyEx(
                        HKEY_LOCAL_MACHINE,
                        gszAtspKey,
                        0,
                        KEY_QUERY_VALUE,
                        &hAtspKey

                        )) != ERROR_SUCCESS)
                {
                    DBGOUT((
                        3,
                        "RegOpenKeyEx(%s,ALL_ACCESS) failed, err=%d",
                        gszAtspKey,
                        lResult
                        ));

                    EndDialog (hwnd, 0);
                    return FALSE;
                }

                {
                    int i;
                    static int aiControlIDs[] =
                    {
                        IDC_NAME,
                        IDC_PORT,
                        IDC_COMMANDS,
                        IDC_ADD,
                        IDC_REMOVE,
                        IDOK,
                        0
                    };

                    for (i = 0; aiControlIDs[i]; i++)
                    {
                        EnableWindow(
                            GetDlgItem (hwnd, aiControlIDs[i]),
                            FALSE
                            );
                    }
                }
            }
        }

        //
        // Retrieve our configuration info from the registry
        //

        dwDataSize = sizeof(iNumLines);
        iNumLines = 0;

        RegQueryValueEx(
            hAtspKey,
            gszNumLines,
            0,
            &dwDataType,
            (LPBYTE) &iNumLines,
            &dwDataSize
            );

        SendDlgItemMessage(
            hwnd,
            IDC_NAME,
            EM_LIMITTEXT,
            MAX_DEV_NAME_LENGTH,
            0
            );

        SendDlgItemMessage(
            hwnd,
            IDC_COMMANDS,
            EM_LIMITTEXT,
            MAX_DEV_NAME_LENGTH,
            0
            );

        pBuf = DrvAlloc (256);

        for (i = 0; i < iNumLines; i++)
        {
            char           *p, *p2, szLineN[8];
            PDRVLINECONFIG  pLineConfig = DrvAlloc (sizeof(DRVLINECONFIG));
            LONG            lResult;

            wsprintf (szLineN, "Line%d", i);

            dwDataSize = 256;

            lResult = RegQueryValueEx(
                hAtspKey,
                szLineN,
                0,
                &dwDataType,
                (LPBYTE) pBuf,
                &dwDataSize
                );

            //
            // If there was a problem, use the default config
            //

            if (0 != lResult)
            {
               lstrcpy (pBuf, gszDefLineConfigParams);
            }

            for (p = pBuf; *p != ','; p++);
            *p = 0;

            SendDlgItemMessage(
                hwnd,
                IDC_DEVICES,
                LB_ADDSTRING,
                0,
                (LPARAM) pBuf
                );

            SendDlgItemMessage(
                hwnd,
                IDC_DEVICES,
                LB_SETITEMDATA,
                i,
                (LPARAM) pLineConfig
                );

            p++;
            for (p2 = p; *p2 != ','; p2++);
            *p2 = 0;

            lstrcpy (pLineConfig->szPort, p);

            p = p2 + 1;

            lstrcpy (pLineConfig->szCommands, p);
        }

        DrvFree (pBuf);

        //
        // Fill up the various controls with configuration options
        //

        {
            static char *aszPorts[] = { "COM1","COM2","COM3",NULL };

            for (i = 0; aszPorts[i]; i++)
            {
                SendDlgItemMessage(
                    hwnd,
                    IDC_PORT,
                    LB_ADDSTRING,
                    0,
                    (LPARAM) aszPorts[i]
                    );
            }
        }

        if (iNumLines == 0)
        {
            EnableChildren (hwnd, FALSE);
        }
        else
        {
            SelectDevice (hwnd, 0);
        }

        break;
    }
    case WM_COMMAND:
    {
        int             iSelection;
        PDRVLINECONFIG  pLineConfig;

        iSelection = SendDlgItemMessage(
            hwnd,
            IDC_DEVICES,
            LB_GETCURSEL,
            0,
            0
            );

        pLineConfig = (PDRVLINECONFIG) SendDlgItemMessage(
            hwnd,
            IDC_DEVICES,
            LB_GETITEMDATA,
            (WPARAM) iSelection,
            0
            );

        switch (LOWORD((DWORD)wParam))
        {
        case IDC_DEVICES:

            if (HIWORD(wParam) == LBN_SELCHANGE)
            {
                char buf[MAX_DEV_NAME_LENGTH + 1];

                SendDlgItemMessage(
                    hwnd,
                    IDC_DEVICES,
                    LB_GETTEXT,
                    iSelection,
                    (LPARAM) buf
                    );

                SetDlgItemText (hwnd, IDC_NAME, buf);

                SendDlgItemMessage(
                    hwnd,
                    IDC_PORT,
                    LB_SELECTSTRING,
                    (WPARAM) -1,
                    (LPARAM) pLineConfig->szPort
                    );

                SetDlgItemText (hwnd, IDC_COMMANDS, pLineConfig->szCommands);
            }

            break;

        case IDC_NAME:

            if ((HIWORD(wParam) == EN_CHANGE) && (iSelection != LB_ERR))
            {
                char    buf[MAX_DEV_NAME_LENGTH + 1];

                GetDlgItemText (hwnd, IDC_NAME, buf, MAX_DEV_NAME_LENGTH);

                SendDlgItemMessage(
                    hwnd,
                    IDC_DEVICES,
                    LB_DELETESTRING,
                    iSelection,
                    0
                    );

                SendDlgItemMessage(
                    hwnd,
                    IDC_DEVICES,
                    LB_INSERTSTRING,
                    iSelection,
                    (LPARAM) buf
                    );

                SendDlgItemMessage(
                    hwnd,
                    IDC_DEVICES,
                    LB_SETCURSEL,
                    iSelection,
                    0
                    );

                SendDlgItemMessage(
                    hwnd,
                    IDC_DEVICES,
                    LB_SETITEMDATA,
                    iSelection,
                    (LPARAM) pLineConfig
                    );
            }

            break;

        case IDC_PORT:

            if (HIWORD(wParam) == LBN_SELCHANGE)
            {
                iSelection = SendDlgItemMessage(
                    hwnd,
                    IDC_PORT,
                    LB_GETCURSEL,
                    0,
                    0
                    );

                SendDlgItemMessage(
                    hwnd,
                    IDC_PORT,
                    LB_GETTEXT,
                    iSelection,
                    (LPARAM) pLineConfig->szPort
                    );
            }

            break;

        case IDC_COMMANDS:

            if ((HIWORD(wParam) == EN_CHANGE) && (iSelection != LB_ERR))
            {
                GetDlgItemText(
                    hwnd,
                    IDC_COMMANDS,
                    pLineConfig->szCommands,
                    63
                    );
            }

            break;

        case IDC_ADD:
        {
            int             iNumLines, i = 2;
            char            szLineName[32];
            PDRVLINECONFIG  pLineConfig = DrvAlloc (sizeof(DRVLINECONFIG));

            iNumLines = SendDlgItemMessage(
                hwnd,
                IDC_DEVICES,
                LB_GETCOUNT,
                0,
                0
                );

            lstrcpy (pLineConfig->szPort, "COM1");

            lstrcpy (szLineName, "my new line");

find_unique_line_name:

            if (SendDlgItemMessage(
                    hwnd,
                    IDC_DEVICES,
                    LB_FINDSTRING,
                    (WPARAM) -1,
                    (LPARAM) szLineName

                    ) != LB_ERR)
            {
                wsprintf (szLineName, "my new line%d", i++);
                goto find_unique_line_name;
            }

            SendDlgItemMessage(
                hwnd,
                IDC_DEVICES,
                LB_ADDSTRING,
                0,
                (LPARAM) szLineName
                );

            SendDlgItemMessage(
                hwnd,
                IDC_DEVICES,
                LB_SETITEMDATA,
                iNumLines,
                (LPARAM) pLineConfig
                );

            EnableChildren (hwnd, TRUE);

            SelectDevice (hwnd, iNumLines);

            SetFocus (GetDlgItem (hwnd, IDC_NAME));

            SendDlgItemMessage(
                hwnd,
                IDC_NAME,
                EM_SETSEL,
                0,
                (LPARAM) -1
                );

            break;
        }
        case IDC_REMOVE:
        {
            int iNumLines;

            DrvFree (pLineConfig);

            iNumLines = SendDlgItemMessage(
                hwnd,
                IDC_DEVICES,
                LB_DELETESTRING,
                iSelection,
                0
                );

            if (iNumLines == 0)
            {
                SetDlgItemText (hwnd, IDC_NAME, "");
                SetDlgItemText (hwnd, IDC_COMMANDS, "");

                EnableChildren (hwnd, FALSE);
            }
            else
            {
                SelectDevice (hwnd, 0);
            }

            break;
        }
        case IDOK:
        {
            int     i, iNumLines;
            char   *pBuf;

            //
            // Update the num lines & num phones values
            //

            pBuf = DrvAlloc (256);

            iNumLines = SendDlgItemMessage(
                hwnd,
                IDC_DEVICES,
                LB_GETCOUNT,
                0,
                0
                );

            RegSetValueEx(
                hAtspKey,
                gszNumLines,
                0,
                REG_DWORD,
                (LPBYTE) &iNumLines,
                sizeof(DWORD)
                );

            //
            // For each installed device save it's config info
            //

            for (i = 0; i < iNumLines; i++)
            {
                char szLineN[8];
                PDRVLINECONFIG pLineConfig;

                SendDlgItemMessage(
                    hwnd,
                    IDC_DEVICES,
                    LB_GETTEXT,
                    i,
                    (LPARAM) pBuf
                    );

                pLineConfig = (PDRVLINECONFIG) SendDlgItemMessage(
                    hwnd,
                    IDC_DEVICES,
                    LB_GETITEMDATA,
                    i,
                    0
                    );

                wsprintf(
                    pBuf + strlen (pBuf),
                    ",%s,%s",
                    pLineConfig->szPort,
                    pLineConfig->szCommands
                    );

                wsprintf (szLineN, "Line%d", i);

                RegSetValueEx(
                    hAtspKey,
                    szLineN,
                    0,
                    REG_SZ,
                    (LPBYTE) pBuf,
                    lstrlen (pBuf) + 1
                    );

                DrvFree (pLineConfig);
            }

            DrvFree (pBuf);

            // fall thru to EndDialog...
        }
        case IDCANCEL:

            RegCloseKey (hAtspKey);
            EndDialog (hwnd, 0);
            break;

        } // switch (LOWORD((DWORD)wParam))

        break;
    }
    } // switch (msg)

    return FALSE;
}

LPVOID
PASCAL
DrvAlloc(
    DWORD dwSize
    )
{
    return (LocalAlloc (LPTR, dwSize));
}

VOID
PASCAL
DrvFree(
    LPVOID lp
    )
{
    LocalFree (lp);
}

void
PASCAL
SetCallState(
    PDRVLINE    pLine,
    DWORD       dwCallState,
    DWORD       dwCallStateMode
    )
{
    if (dwCallState != pLine->dwCallState)
    {
        pLine->dwCallState     = dwCallState;
        pLine->dwCallStateMode = dwCallStateMode;

        (*pLine->pfnEventProc)(
            pLine->htLine,
            pLine->htCall,
            LINE_CALLSTATE,
            dwCallState,
            dwCallStateMode,
            pLine->dwMediaMode
            );
    }
}

#if DBG

void
PASCAL
Prolog(
    PFUNC_INFO  pInfo
    )
{
    DWORD i;

    DBGOUT((3, "%s: enter", pInfo->lpszFuncName));

    for (i = 0; i < pInfo->dwNumParams; i++)
    {
        if (pInfo->aParams[i].dwVal &&
            pInfo->aParams[i].lpszVal[3] == 'z') // lpszVal = "lpsz..."
        {
            DBGOUT((
                3,
                "%s%s=x%lx, '%s'",
                gszTab,
                pInfo->aParams[i].lpszVal,
                pInfo->aParams[i].dwVal,
                pInfo->aParams[i].dwVal
                ));
        }
        else
        {
            DBGOUT((
                3,
                "%s%s=x%lx",
                gszTab,
                pInfo->aParams[i].lpszVal,
                pInfo->aParams[i].dwVal
                ));
        }
    }
}

LONG
PASCAL
Epilog(
    PFUNC_INFO  pInfo,
    LONG        lResult
    )
{
    DBGOUT((3, "%s: returning x%x", pInfo->lpszFuncName, lResult));

    return lResult;
}

void
CDECL
DebugOutput(
    DWORD   dwDbgLevel,
    LPCSTR  lpszFormat,
    ...
    )
{
    if (dwDbgLevel <= gdwDebugLevel)
    {
        char    buf[128] = "ATSP32: ";
        va_list ap;

        va_start(ap, lpszFormat);

        wvsprintf (&buf[8], lpszFormat, ap);

        lstrcat (buf, "\n");

        OutputDebugString (buf);

        va_end(ap);
    }
}

#endif

LONG
PASCAL
ProviderInstall(
    char   *pszProviderName,
    BOOL    bNoMultipleInstance
    )
{
    LONG    lResult;

    //
    // If only one installation instance of this provider is
    // allowed then we want to check the provider list to see
    // if the provider is already installed
    //

    if (bNoMultipleInstance)
    {
        LONG                (WINAPI *pfnGetProviderList)();
        DWORD               dwTotalSize, i;
        HINSTANCE           hTapi32;

LPLINEPROVIDERLIST  pProviderList;
        LPLINEPROVIDERENTRY pProviderEntry;

        //
        // Load Tapi32.dll & get a pointer to the lineGetProviderList
        // func.  We don't want to statically link because this module
        // plays the part of both core SP & UI DLL, and we don't want
        // to incur the performance hit of automatically loading
        // Tapi32.dll when running as a core SP within Tapisrv.exe's
        // context.
        //

        if (!(hTapi32 = LoadLibrary ("tapi32.dll")))
        {
            DBGOUT((
                1,
                "LoadLibrary(tapi32.dll) failed, err=%d",
                GetLastError()
                ));

            lResult = LINEERR_OPERATIONFAILED;
            goto ProviderInstall_return;
        }

        if (!((FARPROC) pfnGetProviderList = GetProcAddress(
                hTapi32,
                (LPCSTR) "lineGetProviderList"
                )))
        {
            DBGOUT((
                1,
                "GetProcAddr(lineGetProviderList) failed, err=%d",
                GetLastError()
                ));

            lResult = LINEERR_OPERATIONFAILED;
            goto ProviderInstall_unloadTapi32;
        }

        //
        // Loop until we get the full provider list
        //

        dwTotalSize = sizeof (LINEPROVIDERLIST);

        goto ProviderInstall_allocProviderList;

ProviderInstall_getProviderList:

        if ((lResult = (*pfnGetProviderList)(0x00020000, pProviderList)) != 0)
        {
            goto ProviderInstall_freeProviderList;
        }

        if (pProviderList->dwNeededSize > pProviderList->dwTotalSize)
        {
            dwTotalSize = pProviderList->dwNeededSize;

            LocalFree (pProviderList);

ProviderInstall_allocProviderList:

            if (!(pProviderList = LocalAlloc (LPTR, dwTotalSize)))
            {
                lResult = LINEERR_NOMEM;
                goto ProviderInstall_unloadTapi32;
            }

            pProviderList->dwTotalSize = dwTotalSize;

            goto ProviderInstall_getProviderList;
        }

        //
        // Inspect the provider list entries to see if this provider
        // is already installed
        //

        pProviderEntry = (LPLINEPROVIDERENTRY) (((LPBYTE) pProviderList) +
            pProviderList->dwProviderListOffset);

        for (i = 0; i < pProviderList->dwNumProviders; i++)
        {
            char   *pszInstalledProviderName = ((char *) pProviderList) +
                        pProviderEntry->dwProviderFilenameOffset,
                   *p;

            //
            // Make sure pszInstalledProviderName points at <filename>
            // and not <path>\filename by walking backeards thru the
            // string searching for last '\\'
            //

            p = pszInstalledProviderName +
                lstrlen (pszInstalledProviderName) - 1;

            for (; *p != '\\'  &&  p != pszInstalledProviderName; p--);

            pszInstalledProviderName =
                (p == pszInstalledProviderName ? p : p + 1);

            if (lstrcmpiA (pszInstalledProviderName, pszProviderName) == 0)
            {
                lResult = LINEERR_NOMULTIPLEINSTANCE;
                goto ProviderInstall_freeProviderList;
            }

            pProviderEntry++;
        }

        //
        // If here then the provider isn't currently installed,
        // so do whatever configuration stuff is necessary and
        // indicate SUCCESS
        //

        lResult = 0;

ProviderInstall_freeProviderList:

        LocalFree (pProviderList);

ProviderInstall_unloadTapi32:

        FreeLibrary (hTapi32);
    }
    else
    {
        //
        // Do whatever configuration stuff is necessary and return SUCCESS
        //

        lResult = 0;
    }

ProviderInstall_return:

    return lResult;
}

void
PASCAL
DropActiveCall(
    PDRVLINE    pLine
    )
{
    if (pLine->hComm)
    {
        DWORD       dwNumBytes, dwError;
        OVERLAPPED  overlapped;

        pLine->bDropInProgress = TRUE;

        SetEvent (pLine->Overlapped.hEvent);

        ZeroMemory (&overlapped, sizeof (OVERLAPPED));

        overlapped.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL);

        if (pLine->dwMediaMode != LINEMEDIAMODE_INTERACTIVEVOICE)
        {
            if (!WriteFile(
                    pLine->hComm,
                    "+++\r", 4,
                    &dwNumBytes,
                    &overlapped
                    ))
            {
                if ((dwError = GetLastError()) == ERROR_IO_PENDING)
                {
                    GetOverlappedResult(
                        pLine->hComm,
                        &overlapped,
                        &dwNumBytes,
                        TRUE
                        );

                    ResetEvent (overlapped.hEvent);
                }
                else
                {
                }
            }
        }

        if (!WriteFile (pLine->hComm, "ATH\r", 4, &dwNumBytes, &overlapped))
        {
            if ((dwError = GetLastError()) == ERROR_IO_PENDING)
            {
                GetOverlappedResult(
                    pLine->hComm,
                    &overlapped,
                    &dwNumBytes,
                    TRUE
                    );
            }
            else
            {
            }
        }

        CloseHandle (overlapped.hEvent);
        CloseHandle (pLine->hComm);
        pLine->hComm = NULL;
    }
}


ATSP.H

/*++

Copyright 1995 - 1998 Microsoft Corporation

Module Name:

    atsp.h

Notes:

--*/

#include <windows.h>
#include "tapi.h"
#include "tspi.h"
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <string.h>
#include "resource.h"

#define  MAX_DEV_NAME_LENGTH    63
#define  ATSP_TIMEOUT           60000   // milliseconds

typedef struct _DRVLINE
{
    HTAPILINE               htLine;

    LINEEVENT               pfnEventProc;

    DWORD                   dwDeviceID;

    char                    szComm[8];

    HTAPICALL               htCall;

    DWORD                   dwCallState;

    DWORD                   dwCallStateMode;

    DWORD                   dwMediaMode;

    HANDLE                  hComm;

    BOOL                    bDropInProgress;

    OVERLAPPED              Overlapped;

} DRVLINE, FAR *PDRVLINE;

typedef struct _DRVLINECONFIG
{
    char                    szPort[8];

    char                    szCommands[64];

} DRVLINECONFIG, FAR *PDRVLINECONFIG;

typedef struct _ASYNC_REQUEST
{
    DWORD                   dwRequestID;

    DWORD                   dwCommand;

    char                    szCommand[32];

    struct _ASYNC_REQUEST  *pNext;

} ASYNC_REQUEST, *PASYNC_REQUEST;

#if DBG

typedef struct _FUNC_PARAM
{
    char        *lpszVal;

    DWORD       dwVal;

} FUNC_PARAM, *PFUNC_PARAM;

#endif

typedef struct _FUNC_INFO
{

#if DBG

    char        *lpszFuncName;

    DWORD       dwNumParams;

    PFUNC_PARAM aParams;

#endif

    LONG        lResult;

} FUNC_INFO, *PFUNC_INFO;

DWORD               gdwLineDeviceIDBase;
DWORD               gdwPermanentProviderID;
HANDLE              ghInst;
ASYNC_COMPLETION    gpfnCompletionProc;

char gszAtspKey[]      = "Software\\Microsoft\\ATSP";
char gszNumLines[]     = "NumLines";
char gszDefLineConfigParams[] = "my new line,COM1,L0";

#if DBG

char gszTab[]          = "    ";
char gszhdLine[]       = "hdLine";
char gszhdCall[]       = "hdCall";
char gszdwSize[]       = "dwSize";
char gszhwndOwner[]    = "hwndOwner";
char gszdwDeviceID[]   = "dwDeviceID";
char gszdwRequestID[]  = "dwRequestID";
char gszlpCallParams[] = "lpCallParams";
char gszdwPermanentProviderID[] = "dwPermanentProviderID";

DWORD   gdwDebugLevel = 0;

void
CDECL
DebugOutput(
    DWORD   dwLevel,
    LPCSTR  lpszFormat,
    ...
    );

#define DBGOUT(arg) DebugOutput arg

LONG
PASCAL
Epilog(
    PFUNC_INFO  pInfo,
    LONG        lResult
    );

void
PASCAL
Prolog(
    PFUNC_INFO  pInfo
    );

#else

#define DBGOUT(arg)

#define Epilog(pAsyncRequestInfo, lResult) (lResult)

#define Prolog(pAsyncRequestInfo)

#endif

LPWSTR
PASCAL
My_lstrcpyW(
    WCHAR   *pString1,
    WCHAR   *pString2
    );

LPVOID
PASCAL
DrvAlloc(
    DWORD dwSize
    );

VOID
PASCAL
DrvFree(
    LPVOID lp
    );

void
PASCAL
SetCallState(
    PDRVLINE    pLine,
    DWORD       dwCallState,
    DWORD       dwCallStateMode
    );

BOOL
CALLBACK
ConfigDlgProc(
    HWND    hwnd,
    UINT    msg,
    WPARAM  wParam,
    LPARAM  lParam
    );

LONG
PASCAL
ProviderInstall(
    char   *pszProviderName,
    BOOL    bNoMultipleInstance
    );

void
PASCAL
DropActiveCall(
    PDRVLINE    pLine
    );


RESOURCE.H

#define IDD_DIALOG1     101
#define IDC_DEVICES     1001
#define IDC_NAME        1002
#define IDC_PORT        1003
#define IDC_COMMANDS    1004
#define IDC_ADD         1005
#define IDC_REMOVE      1006


README.TXT

Dialer ---- Windows TAPI sample application created as an illustration of
the usage of Windows TAPI

Dialer does the following 3 things :

(1) initiates/drops calls (2) handles simple TAPI request for other
application initiating/dropping calls on their behalf (3) monitors
incoming/outgoing calls and keeps a call log based on the user's request.


DIALER.H

/*++ BUILD Version: 0001    // Increment this if a change has global effects

Copyright 1995 - 1998 Microsoft Corporation

Module Name:

    dialer.h

Abstract:

    Header file for dialer

Author:

    Dan Knudson (DanKn)        05-Apr-1995

Revision History:

    Jeremy Horwitz (t-jereh)   30-May-1995

--*/

#define TAPI_VERSION_1_0 0x00010003
#define TAPI_VERSION_1_4 0x00010004
#define TAPI_VERSION_2_0 0x00020000
#define TAPI_CURRENT_VERSION TAPI_VERSION_2_0

#include <windows.h>
#include "tapi.h"
#include "resource.h"
#include "dialhelp.h"

#define MENU_CHOICE         1 // for Connect Using dialog...
#define INVALID_LINE        2 // if INVALID_LINE, turn off CANCEL
                              // button and add extra text...

#define MAXNUMLENGTH    64
#define MAXBUFSIZE      256
#define NSPEEDDIALS     8 // Dialer supports 8 configurable speed dial entries.
#define NLASTDIALED     20 // Dialer keeps track of the 20 last dialed numbers.

#define ERR_NONE        0
#define ERR_NOVOICELINE 1
#define ERR_LINECLOSE   2
#define ERR_NOLINES     3
#defineERR_911WARN4
#define ERR_NEWDEFAULT5

#define itoa(x,y,z) _itoa(x,y,z)


DIALHELP.H

//
// (c) 1995 Microsoft Corporation.  All Rights Reserved.
//
#define IDH_DIALER_CHANGE_DIAL_HELPER    1000
#define IDH_DIALER_CHANGE_DIGITS         1001
#define IDH_DIALER_CHANGE_OPTIONS        1002
#define IDH_DIALER_CHANGE_REDIAL_BUTTON  1003
#define IDH_DIALER_DIALING_LOGNAME       1004
#define IDH_DIALER_DIALING_STATUS        1005
#define IDH_DIALER_HANGUP                1006
#define IDH_DIALER_LOG_IN                1007
#define IDH_DIALER_LOG_OUT               1008
#define IDH_DIALER_OPTIONS_ADDRESS       1009
#define IDH_DIALER_OPTIONS_LINE          1010
#define IDH_DIALER_OPTIONS_NONVOICE      1011
#define IDH_DIALER_OPTIONS_VOICE         1012
#define IDH_DIALER_PAUSE_CONTINUE        1013
#define IDH_DIALER_SPEED_CHOOSE          1014
#define IDH_DIALER_SPEED_NAME            1015
#define IDH_DIALER_SPEED_NUMBER          1016
#define IDH_DIALER_SPEED_SAVE            1017
#define IDH_DIALER_SPEED_SAVE_DIAL       1018
#define IDH_DIALER_DIAL_NUMBER           1019
#define IDH_DIALER_DIAL_SPEED_CHOOSE     1020
#define IDH_DIALER_DIAL_BUTTON           1021
#define IDH_DIALER_DIAL_KEYPAD           1022
#define IDH_DIALER_LOG                   1023
#define IDH_DIALER_OPTIONS_PROPERTIES    1024
#define IDH_DIALER_BUTTONS               1025


DIALER.C

/*++

Copyright 1996 - 1997 Microsoft Corporation

Module Name:

    dialer.c

--*/

#include "dialer.h"
#include "string.h"
#include "stdlib.h"
#include "shellapi.h"

#defineISDIGIT(x)(((x) - '0') >= 0) && (((x) - '0') <= 9)
enum NumberTypes
{
LOCAL_NUMBER = 7,
EXTENDED_LOCAL_NUMBER,
LONG_DISTANCE_NUMBER = 10,
EXTENDED_LONG_DISTANCE_NUMBER
};

// structs
typedef struct tagLINEINFO
{
    DWORD nAddr;                    // Number of avail. addresses on the line
    BOOL  fVoiceLine;               // Is this a voice line?
    DWORD dwAPIVersion;             // API version which the line supports
    HLINE hLine;                    // line handle returned by lineOpen
    DWORD dwPermanentLineID;        // Permanent line ID retreived from devcaps
    char  szLineName[MAXBUFSIZE];   // the line's name

} LINEINFO, *LPLINEINFO;

// Global variables

// window/instance variables
HWND        ghWndMain;
HWND        ghWndDialing = NULL;
HINSTANCE   ghInst = 0;

// file name vars.
static TCHAR gszAppName[64];
static TCHAR gszINIfilename [] = "DIALER.INI";
static TCHAR gszHELPfilename [] = "DIALER.HLP";
static TCHAR gszDialerClassName[] = "DialerClass";
TCHAR const gszNULL[] = "";

// window item variables
HFONT       ghFontBtn;               // handle to 1, 2, ..., 0's font
HFONT       ghFontBtnText;           // handle to number button's text font
HFONT       ghFontBtnStar;           // handle to * and # button's font
HLINEAPP    ghLineApp = NULL;        // Dialer's usage handle (regist. w/TAPI)
HCALL       ghCall = NULL;           // call handle for Dialer's call

LPSTR       gszCurrentNumber = NULL; // number of destination of current call
LPSTR       gszCurrentName = NULL; // name of destination of current call

BOOL        gfRegistered;            // was lineRegisterRequestRecipient()
                                     // successful?

//BOOL        gfDropping = FALSE;      // is Dialer currently dropping a call?
BOOL        gfNeedToReinit = FALSE;  // does Dialer need to re-initialize?

BOOL        gfCallRequest = FALSE;   // Does a Simple TAPI app want a call?
BOOL        gfCurrentLineAvail = TRUE; // Simple TAPI requests are only carried
                                       // out if the current chosen line is avail.
BOOLgfMakeCallReplyPending = FALSE;

LONG        gMakeCallRequestID = 0;      // request ID returned by async TAPI fns.
LONG        gDropCallRequestID = 0;      // request ID returned by async TAPI fns.

DWORD       gnAvailDevices = 0;      // # of line devices avail. to Dialer
LINEINFO    gCurrentLineInfo;
DWORD       * gnAddr;

// global to remember where the cursor is in the edit control
DWORD       gdwStartSel;
DWORD       gdwEndSel;

DWORD       * gdwPLID;               // current line's permanent line ID
DWORD       giCurrentLine = (DWORD)-1;       // the line selected by the user
DWORD       giCurrentAddress = 0;    // the address selected by the user

// + 1 so we can work 1-based rather than 0-based (for convenience only)
// global varibles to hold the names and address of the
TCHAR       gszSDNumber[ NSPEEDDIALS + 1 ][ TAPIMAXDESTADDRESSSIZE ];

// Function declarations

// button related functions
VOID ButtonFontSetup(VOID);
VOID DrawButton(HDC hdc, RECT rcBtn, BOOL fHighlighted);
VOID DrawButtonText(HDC hdc, RECT rcBtn, BOOL fHighlighted, UINT IDD_Btn);
VOID DisableDialButtons(BOOL fDisable);
VOID FitTextToButton( HWND, INT, LPSTR );

// Callback functions
BOOL CALLBACK MainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
BOOL CALLBACK DialingProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
BOOL CALLBACK AboutProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
BOOL CALLBACK ConnectUsingProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
BOOL CALLBACK LineInUseProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
BOOL CALLBACK SpeedDial1Proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
BOOL CALLBACK SpeedDial2Proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
VOID CALLBACK tapiCallback (
DWORD hDevice, DWORD dwMsg,
DWORD dwCallbackInstance,
DWORD dwParam1, DWORD dwParam2,
DWORD dwParam3
    );

// tapi related functions
VOID ManageAssistedTelephony(VOID);
VOID InitiateCall(LPCSTR szNumber, LPCSTR szName);

VOID DialerLineClose(VOID);
VOID DialerCleanup(VOID);
VOID CloseTAPI(VOID);

DWORD GetLineInfo(DWORD iLine, LPLINEINFO lpLineInfo);
VOID GetLineInfoFailed (
DWORD iLine, LPLINEDEVCAPS lpDevCaps,
LPLINEINFO lpLineInfo
    );
LPSTR GetAddressName(DWORD iLine, DWORD iAddress);
BOOL MakeCanonicalNumber( LPCSTR szName, LPSTR szCanNumber );

// misc. helper functions
VOID ReadINI(VOID);
int errString(HWND hWnd, UINT errCode, UINT uFlags);
VOID AddToRedialList(LPCSTR szNumber);
BOOL InitializeLineBox(HWND hwndLineBox);
BOOL InitializeAddressBox(HWND hwndLineBox, HWND hwndAddressBox);
BOOL Is911 ( LPLINETRANSLATEOUTPUT lpTransOut );
VOID AmpersandCompensate( LPCSTR lpszSrc, LPSTR lpszDst );
VOID AmpersandDeCompensate( LPCSTR lpszSrc, LPSTR lpszDst );

// Dialer memory management functions
LPVOID DialerAlloc(size_t cbToAlloc);
LPVOID DialerFree(LPVOID lpMem);

// Function definitions

//***************************************************************************
//***************************************************************************
//***************************************************************************
DWORD InitializeTAPI (VOID)
{
INT cvLine;

DWORD iLine;
DWORD dwPreferredPLID, dwID = (DWORD) -1;

MSG msg;

LPLINEINFO lpLineInfo = NULL;// LINEINFO for each available line

    DWORD errCode;
    DWORD tc = GetTickCount();
    DWORD dwReturn = ERR_NONE;

char szBuffer[MAXBUFSIZE];// to read in dwPreferredPLID as a string first

errCode = lineInitialize (
&ghLineApp,
ghInst,
(LINECALLBACK) tapiCallback,
gszAppName,
&gnAvailDevices
 );
if ( errCode == LINEERR_REINIT )
{
// take away dialer functionality
EnableWindow( ghWndMain, FALSE );
DisableDialButtons(TRUE);

// keep trying until the user cancels
// or we stop getting LINEERR_REINIT
while ( ( errCode = lineInitialize (
&ghLineApp,
ghInst,
(LINECALLBACK)tapiCallback,
gszAppName,
&gnAvailDevices
    ) )
 == LINEERR_REINIT )
{
// flush queue & yield
while ( PeekMessage( &msg, 0, 0, 0, PM_REMOVE ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}

// bring up the box if 5 seconds have passed since
if(GetTickCount() > 5000 + tc)
{
if ( errString( ghWndMain, ikszWarningTapiReInit, MB_RETRYCANCEL )
 == IDCANCEL )
{
break;
}
// reset the relative counter
tc = GetTickCount();
}
}

// give back dialer functionality
DisableDialButtons( FALSE );
EnableWindow( ghWndMain, TRUE );
}

if ( errCode )
    {
        dwReturn = errCode;
        goto tapiinit_exit;
    }

// retrieve preferred line info from INI file
GetPrivateProfileString (
"Preference",
"Preferred Line",
gszNULL,
szBuffer,
MAXBUFSIZE,
gszINIfilename
);

// if szBuffer is not empty.
if ( lstrcmp ( gszNULL, szBuffer ) )
{
dwPreferredPLID = (DWORD) atoi( szBuffer );
}
else
{
dwPreferredPLID = (DWORD) -1;
}

// -1 default - tells us if it ever gets set
giCurrentLine = (DWORD) -1;

// allocate buffer for storing LINEINFO for all of the available lines
// always allocate space for at least one line
if ( gnAvailDevices == 0 )
{
gnAddr = (DWORD *) DialerAlloc( sizeof( DWORD ) );
gdwPLID = (DWORD *) DialerAlloc( sizeof( DWORD ) );
lpLineInfo = (LPLINEINFO) DialerAlloc( sizeof( LINEINFO ) );
}
else
{
gnAddr = (DWORD *) DialerAlloc( sizeof( DWORD ) * (int)gnAvailDevices);
gdwPLID = (DWORD *) DialerAlloc( sizeof( DWORD ) * (int)gnAvailDevices);
lpLineInfo = (LPLINEINFO) DialerAlloc( sizeof( LINEINFO ) * (int)gnAvailDevices );
}

// if no space was set aside...
if ( lpLineInfo == NULL || gnAddr == NULL )
    {
        dwReturn = LINEERR_NOMEM;
        goto tapiinit_exit;
    }

// fill lpLineInfo[] and open each line
for ( iLine = 0, cvLine = 0; iLine < gnAvailDevices; ++iLine )
{
// skip remaining processing for this if it didn't open
if ( GetLineInfo( iLine, &lpLineInfo[iLine] ) != ERR_NONE )
continue;

gnAddr [ iLine ] = lpLineInfo[iLine].nAddr;
gdwPLID[ iLine ] = lpLineInfo[iLine].dwPermanentLineID;

if ( lpLineInfo[iLine].dwPermanentLineID == dwPreferredPLID )
giCurrentLine = iLine;

// note number of lines with Interactive voice caps.
// used to select a preferred line by default
if ( lpLineInfo [ iLine ].fVoiceLine )
{
cvLine++;
dwID = iLine;
}
}

// if we couldn't find the preferred line,
// try and assign one by default
// else bring up connect using dialog
if (  giCurrentLine == (DWORD)-1 )
{
// check if there is only one line
// that has interactive voice caps,
// make it default line
if ( cvLine == 1 )
{
giCurrentLine = dwID;

// if the preferred address read from the INI file
// was different i.e we are changing setting, inform
// the user
if ( dwPreferredPLID != -1 )
{
errString( ghWndMain, ERR_NEWDEFAULT, MB_ICONEXCLAMATION | MB_OK );
}
}
else
{
gCurrentLineInfo = lpLineInfo[0];
if ( DialogBoxParam (
ghInst,
MAKEINTRESOURCE(IDD_CONNECTUSING),
ghWndMain,
(DLGPROC)ConnectUsingProc,
INVALID_LINE
)
 == -1)
{
                dwReturn = (DWORD) -1;
}
else
{
                dwReturn = ERR_NONE;
}

            goto tapiinit_exit;
}
}
gCurrentLineInfo = lpLineInfo[ giCurrentLine ];

// select default address
giCurrentAddress = 0;

// get the name of the preferred address from ini file
GetPrivateProfileString (
"Preference",
"Preferred Address",
gszNULL,
szBuffer,
MAXBUFSIZE,
gszINIfilename
);

// if preferred address read from INI file
if ( lstrcmp( gszNULL, szBuffer ) )
{
giCurrentAddress = (DWORD) atoi( szBuffer );

// if the address is invalid, set default
if ( giCurrentAddress >= gCurrentLineInfo.nAddr )
giCurrentAddress = 0;
}

tapiinit_exit:

    if (lpLineInfo)
    {
        DialerFree(lpLineInfo);
    }

return dwReturn;;
}

//***************************************************************************
//***************************************************************************
//***************************************************************************
int WINAPI WinMain (
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow
   )
{
HACCEL hAccel;
MSG msg;
DWORD errCode;
HANDLE hImHere;

ghInst = GetModuleHandle( NULL );
LoadString( ghInst, ikszAppFriendlyName, gszAppName, sizeof(gszAppName)/sizeof(TCHAR) );

//
// Now, let's see if we've already got an instance of ourself
hImHere = CreateMutex(NULL, TRUE, "DialersIveBeenStartedMutex");

//
// Is there another one of us already here?
if ( ERROR_ALREADY_EXISTS == GetLastError() )
{
        HWND        hDialerWnd;

        hDialerWnd = FindWindow(gszDialerClassName,
                                NULL);

        SetForegroundWindow(hDialerWnd);

   CloseHandle( hImHere );
   return 0;
}

{
WNDCLASS wc;
wc.style = CS_DBLCLKS | CS_SAVEBITS | CS_BYTEALIGNWINDOW;
wc.lpfnWndProc = DefDlgProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = DLGWINDOWEXTRA;
wc.hInstance = ghInst;
wc.hIcon = LoadIcon(ghInst, MAKEINTRESOURCE(IDI_DIALER) );
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = GetStockObject (COLOR_WINDOW + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = gszDialerClassName;
RegisterClass(&wc);
}

// create the dialog box and set it with info
// from the .INI file
ghWndMain = CreateDialog (
ghInst,
(LPCSTR)MAKEINTRESOURCE(IDD_DIALER),
(HWND)NULL,
MainWndProc
 );

ReadINI();
ButtonFontSetup();

ShowWindow(ghWndMain, SW_SHOW);
UpdateWindow(ghWndMain);

// limit text in Number field to TAPIMAXDESTADDRESSSIZE
SendDlgItemMessage (
ghWndMain,
IDD_DCOMBO,
CB_LIMITTEXT,
(WPARAM)TAPIMAXDESTADDRESSSIZE,
0
   );

// 0 (ERR_NONE) error code registers success - otherwise terminate
errCode = InitializeTAPI();
if(errCode)
{
errString(ghWndMain, errCode, MB_APPLMODAL | MB_ICONEXCLAMATION );

DialerCleanup();
return errCode;
}

errCode = lineRegisterRequestRecipient (
ghLineApp,
0, // registration instance
LINEREQUESTMODE_MAKECALL,
TRUE
   );

if(errCode)
{
gfRegistered = FALSE;
errString(ghWndMain, errCode, MB_ICONEXCLAMATION | MB_OK );
}
else
{
gfRegistered = TRUE;
}

hAccel = LoadAccelerators(ghInst, gszAppName);

while ( GetMessage( &msg, NULL, 0, 0 ) )
{
if ( ghWndMain == NULL || !IsDialogMessage( ghWndMain, &msg ) )
{
if(!TranslateAccelerator(ghWndMain, hAccel, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}

// If: 1) Dialer is a call manager (if not, ignore requests)
//     2) the currently chosen line is available
//     3) there is a Simple TAPI request
// Then: process the request
if ( gfCurrentLineAvail && gfCallRequest )
{
ManageAssistedTelephony();
}
}

DialerCleanup();

CloseHandle( hImHere );

return msg.wParam;
}

//***************************************************************************
//***************************************************************************
//***************************************************************************
LPVOID DialerAlloc(size_t cbToAlloc)
{
return LocalAlloc(LPTR, cbToAlloc);
}

LPVOID DialerFree(LPVOID lpMem)
{
return LocalFree( lpMem );
}

//***************************************************************************
//***************************************************************************
//***************************************************************************
VOID ReadINI( VOID )
{
    WORD cSDEntry, cLastDialed;
    DWORD cComma;

    POINT ptLeftTop;

    char szName[ TAPIMAXCALLEDPARTYSIZE ];
    char szTemp[ TAPIMAXCALLEDPARTYSIZE ];

    char szNum[MAXBUFSIZE];
    char szFieldName[MAXBUFSIZE];
    char szPt[MAXBUFSIZE];

    // get speed dial settings from INI file
    for(cSDEntry = 1; cSDEntry <= NSPEEDDIALS; ++cSDEntry)
    {

        wsprintf(szFieldName, "Name%d", cSDEntry);
        GetPrivateProfileString (
"Speed Dial Settings",
szFieldName,
gszNULL,
szName,
TAPIMAXCALLEDPARTYSIZE - 1,
gszINIfilename
);

        wsprintf(szFieldName, "Number%d", cSDEntry);
        GetPrivateProfileString (
"Speed Dial Settings",
szFieldName,
gszNULL,
gszSDNumber[cSDEntry],
TAPIMAXDESTADDRESSSIZE - 1,
gszINIfilename
);

if ( !lstrcmp( gszNULL, szName ) )
{
lstrcpy( szName, gszSDNumber[ cSDEntry ] );
}

FitTextToButton( ghWndMain, IDD_DSPEEDDIAL1 + cSDEntry - 1, szName );

AmpersandCompensate( szName, szTemp );
SetDlgItemText (
ghWndMain,
IDD_DSPEEDDIAL1 + cSDEntry - 1,
(LPCSTR)szTemp
   ); // Label the speed dial button
}

// set up last dialed numbers in combo box (read from INI)
for(cLastDialed = 1; cLastDialed <= NLASTDIALED; ++cLastDialed)
{
wsprintf(szFieldName, "Last dialed %d", cLastDialed);
GetPrivateProfileString (
"Last dialed numbers",
szFieldName,
gszNULL,
szNum,
MAXBUFSIZE - 1,
gszINIfilename
);
if ( szNum[0] ) // i.e. if szNum isn't simply "" - if we read something
// from the INI - put it in the combo box
SendDlgItemMessage(
ghWndMain,
IDD_DCOMBO,
CB_ADDSTRING,
0,
(LPARAM)(LPCSTR)szNum
  );
}

// set defaults
ptLeftTop.x = 100;
ptLeftTop.y = 100;

// set the window position based on the INI data
GetPrivateProfileString (
"Preference",
"Main Window Left/Top",
gszNULL,
szPt,
MAXBUFSIZE - 1,
gszINIfilename
);

if ( szPt[0] )
{
cComma = strcspn(szPt, ",");
szPt[cComma] = 0;
ptLeftTop.x = atoi(szPt);

// a possibly absurd check to see that the string
// wasn't akin to "320," with no second entry
if ( *(szPt + cComma + 1 ) )
ptLeftTop.y = atoi( szPt + cComma + 1 );

// check to see that the box is on the screen - the upper left
// must be on the screen, along with a 50x50 box below and to
// the right of it
if ( ptLeftTop.x < 0
|| ptLeftTop.x + 50 >= GetSystemMetrics(SM_CXSCREEN)
|| ptLeftTop.y < 0
|| ptLeftTop.y + 50 >= GetSystemMetrics(SM_CYSCREEN)
   )
{
ptLeftTop.x = 100; // set defaults if the box is off of the screen
ptLeftTop.y = 100; // set defaults if the box is off of the screen
}
}

SetWindowPos (
ghWndMain,
NULL,
ptLeftTop.x,
ptLeftTop.y,
0,
0,
SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOZORDER
 );
}

//***************************************************************************
//***************************************************************************
//***************************************************************************
VOID ButtonFontSetup(VOID)
    {
    HDC hdc;
    int IDD;

    // define the fonts for the buttons
    hdc = GetDC(GetDesktopWindow());

    ghFontBtn = CreateFont(
        (-10)*GetDeviceCaps(hdc, LOGPIXELSY)/72,
        0,
        0,
        0,
        FW_BOLD,
        FALSE,
        FALSE,
        FALSE,
        ANSI_CHARSET,
        OUT_DEFAULT_PRECIS,
        CLIP_DEFAULT_PRECIS,
        PROOF_QUALITY,
        VARIABLE_PITCH | FF_SWISS,
        (LPSTR)"Arial"
        );

    ghFontBtnText = CreateFont(
        (-6)*GetDeviceCaps(hdc, LOGPIXELSY)/72,
        0,
        0,
        0,
        FW_NORMAL,
        FALSE,
        FALSE,
        FALSE,
        ANSI_CHARSET,
        OUT_DEFAULT_PRECIS,
        CLIP_DEFAULT_PRECIS,
        PROOF_QUALITY,
        VARIABLE_PITCH | FF_SWISS,
        NULL
        );

    ghFontBtnStar = CreateFont(
        (-18)*GetDeviceCaps(hdc, LOGPIXELSY)/72,
        0,
        0,
        0,
        FW_BOLD,
        FALSE,
        FALSE,
        FALSE,
        SYMBOL_CHARSET,
        OUT_TT_PRECIS,
        CLIP_DEFAULT_PRECIS,
        PROOF_QUALITY,
        VARIABLE_PITCH | FF_DONTCARE,
        (LPSTR)"Symbol"
        );

    ReleaseDC(GetDesktopWindow(), hdc);

    // set the fonts for the buttons
    if(ghFontBtn)
        {
        // set fonts on number buttons
            for(IDD = IDD_DBUTTON1; IDD <= IDD_DBUTTON0; ++IDD)
            // the order is IDD_DBUTTON1, 2, 3, ..., 0 (thus from 1 to 0)
            SendMessage(
                GetDlgItem(ghWndMain, IDD),
                WM_SETFONT,
                (WPARAM)ghFontBtn,
                0L
                );

        // set fonts on * and # buttons
        SendMessage(
            GetDlgItem(ghWndMain, IDD_DBUTTONSTAR),
            WM_SETFONT,
            (WPARAM)ghFontBtnStar,
            0L
            );
        SendMessage(
                    GetDlgItem(ghWndMain, IDD_DBUTTONPOUND),
            WM_SETFONT,
            (WPARAM)ghFontBtnStar,
            0L
            );
        }
    }

//***************************************************************************
//***************************************************************************
//***************************************************************************
// Draws a 3D button within rcBtn on hDC
VOID DrawButton(HDC hDC, RECT rcBtn, BOOL fHighlighted)
    {
    HPEN hPenPrev, hPenShadow, hPenHighlight, hPenBlack;
    HBRUSH hBrushPrev, hBrushFace;
    int RopPrev;

    --rcBtn.right;
    --rcBtn.bottom;

    // set up pens/brush
    hPenShadow = CreatePen(PS_SOLID,0,GetSysColor(COLOR_3DSHADOW));
    hPenHighlight = CreatePen(PS_SOLID,0,GetSysColor(COLOR_3DHILIGHT));
    hPenBlack = GetStockObject(BLACK_PEN);
    hBrushFace = GetSysColorBrush(COLOR_3DFACE);

    // get current state so we can put it back at the end of DrawButton
    hPenPrev = SelectObject(hDC, hPenBlack);
    RopPrev = SetROP2(hDC, R2_COPYPEN);
    hBrushPrev = SelectObject(hDC, hBrushFace);

    PatBlt(
        hDC,
        rcBtn.left + 1,
        rcBtn.top + 1,
        rcBtn.right - rcBtn.left - 1,
        rcBtn.bottom - rcBtn.top - 1,
        PATCOPY
        );

    if(fHighlighted)
        {
        SelectObject(hDC, hPenBlack);
        MoveToEx(hDC, rcBtn.left, rcBtn.bottom - 1, NULL);
        LineTo(hDC, rcBtn.left, rcBtn.top);  //  _
        LineTo(hDC, rcBtn.right, rcBtn.top); // |

        SelectObject(hDC, hPenHighlight);
        MoveToEx(hDC, rcBtn.right, rcBtn.top, NULL);
        LineTo(hDC, rcBtn.right, rcBtn.bottom);
        LineTo(hDC, rcBtn.left - 1, rcBtn.bottom); // _|

        SelectObject(hDC, hPenShadow);
        MoveToEx(hDC, rcBtn.left + 1, rcBtn.bottom - 2, NULL);
        LineTo(hDC, rcBtn.left + 1, rcBtn.top + 1);
        LineTo(hDC, rcBtn.right - 1, rcBtn.top + 1);
        }
    else
        {
        SelectObject(hDC, hPenHighlight);
        MoveToEx(hDC, rcBtn.left, rcBtn.bottom - 1, NULL);
        LineTo(hDC, rcBtn.left, rcBtn.top);  //  _
        LineTo(hDC, rcBtn.right, rcBtn.top); // |

        SelectObject(hDC, hPenBlack);
        MoveToEx(hDC, rcBtn.right, rcBtn.top, NULL);
        LineTo(hDC, rcBtn.right, rcBtn.bottom);
        LineTo(hDC, rcBtn.left - 1, rcBtn.bottom); // _|

        SelectObject(hDC, hPenShadow);
        MoveToEx(hDC, rcBtn.left + 1, rcBtn.bottom - 1, NULL);
        LineTo(hDC, rcBtn.right - 1, rcBtn.bottom - 1);
        LineTo(hDC, rcBtn.right - 1, rcBtn.top);
        }

    // put everything back how it was
    SetROP2(hDC, RopPrev);
    SelectObject(hDC, hBrushPrev);
    SelectObject(hDC, hPenPrev);

    DeleteObject(hPenBlack);
    DeleteObject(hPenShadow);
    DeleteObject(hPenHighlight);
    DeleteObject(hBrushFace);
    }

//***************************************************************************
//***************************************************************************
//***************************************************************************
VOID DrawButtonText(HDC hDC, RECT rcBtn, BOOL fHighlighted, UINT IDD_Btn)
    {
    char szLine1[MAXBUFSIZE]; // text in button up to '\n'
    LPSTR pszLine2; // text in button after '\n'
    int BkModePrev;
    HFONT hFontPrev = NULL;
    TEXTMETRIC tm;
    RECT rcText = rcBtn;

    BkModePrev = SetBkMode(hDC, TRANSPARENT);

    GetDlgItemText(ghWndMain, IDD_Btn, szLine1, MAXBUFSIZE);
    pszLine2 = strstr(szLine1, "\n");
    if(pszLine2)
        {
        *pszLine2 = 0; // 1 -> "TEST1 \n TEST2" becomes "TEST 1 \0"
        ++pszLine2; // now 2 -> " TEST 2"
        }

    // now szLine1 points to the null terminated first string and
    // pszLine2 points to either the null terminated second string or NULL
    // if there was no second string

    if(szLine1[0])
        {
        if(ghFontBtnText && pszLine2)
            hFontPrev = SelectObject(hDC, ghFontBtnText);

        GetTextMetrics(hDC, &tm);
        rcText.bottom = (rcBtn.bottom + rcBtn.top)/2 - 2;
        rcText.top = rcText.bottom - (tm.tmHeight - 1);

        if(pszLine2 == NULL)
            OffsetRect(&rcText, 0, (rcText.bottom - rcText.top)/2);

        if(fHighlighted)
            OffsetRect(&rcText, 1, 1);

        DrawText(hDC, szLine1, -1, &rcText, DT_SINGLELINE | DT_CENTER);

        if(hFontPrev)
            SelectObject(hDC, hFontPrev);
        }
    if(pszLine2) // recall that pszLine2 == NULL to represent no second string
        {
        GetTextMetrics(hDC, &tm);
        if(IDD_Btn == IDD_DBUTTONSTAR || IDD_Btn == IDD_DBUTTONPOUND)
            rcText.top = (rcBtn.bottom + rcBtn.top)/2 - (tm.tmHeight)/2;
        else
            rcText.top = (rcBtn.bottom + rcBtn.top)/2 - 2;
        rcText.bottom = rcText.top + tm.tmHeight;

        if(fHighlighted)
            OffsetRect(&rcText, 1, 1);

        DrawText(hDC, pszLine2, -1, &rcText, DT_SINGLELINE | DT_CENTER);
        }

    SetBkMode(hDC, BkModePrev);
    }

//***************************************************************************
//***************************************************************************
//***************************************************************************
VOID DisableDialButtons(BOOL fDisable)
{
int IDD;

// Disable/enable Dial button
EnableWindow( GetDlgItem( ghWndMain, IDD_DDIAL ),!fDisable) ;

// Disable/enable Speed dial buttons
for ( IDD = IDD_DSPEEDDIAL1; IDD <= IDD_DSPEEDDIAL8; ++IDD )
{
EnableWindow(GetDlgItem(ghWndMain, IDD),!fDisable);
}
}

//***************************************************************************
//***************************************************************************
//***************************************************************************
VOID DialerCleanup(VOID)
    {
    RECT rc;
    WORD cItem; // count of numbers in combo box
    DWORD cLastDialed;
    char szPt[MAXBUFSIZE];
    char szNumber[TAPIMAXDESTADDRESSSIZE];
    char szFieldName[MAXBUFSIZE];

    CloseTAPI(); // unregister and line close

    if(!IsIconic(ghWndMain)) // if the window is not minimized, record position
        {
        GetWindowRect(ghWndMain, &rc);
        wsprintf(szPt, "%ld, %ld", rc.left, rc.top);
        WritePrivateProfileString(
            "Preference",
            "Main Window Left/Top",
            szPt,
            gszINIfilename
            );
        }

    cItem = (WORD)SendDlgItemMessage(ghWndMain, IDD_DCOMBO, CB_GETCOUNT, 0, 0);

    // write out last dialed numbers from combo box (write to INI)
    for(cLastDialed = 1; cLastDialed <= NLASTDIALED; ++cLastDialed)
        {
        if(cLastDialed <= cItem)
            SendDlgItemMessage(
                ghWndMain,
                IDD_DCOMBO,
                CB_GETLBTEXT,
                cLastDialed - 1, // it's a zero-based count
                (LPARAM)(LPCSTR)szNumber);

        else
            szNumber[0] = 0;

        wsprintf(szFieldName, "Last dialed %d", cLastDialed);
        WritePrivateProfileString(
            "Last dialed numbers",
            szFieldName,
            szNumber,
            gszINIfilename
            );

        }

    WinHelp(ghWndMain, gszHELPfilename, HELP_QUIT, 0); // unload help

    DestroyWindow(ghWndMain);
    ghWndMain = NULL;

    DeleteObject(ghFontBtn);
    DeleteObject(ghFontBtnText);
    DeleteObject(ghFontBtnStar);
    }

//***************************************************************************
//***************************************************************************
//***************************************************************************
// unregister and line close
VOID CloseTAPI(VOID)
{

// unregister as call manager
lineRegisterRequestRecipient (
ghLineApp,
0, // registration instance
LINEREQUESTMODE_MAKECALL,
FALSE
 );

if ( gCurrentLineInfo.hLine )
{
lineClose ( gCurrentLineInfo.hLine );
gfCurrentLineAvail = FALSE;
gCurrentLineInfo.hLine = NULL;
}

lineShutdown(ghLineApp);
}

//***************************************************************************
//***************************************************************************
//***************************************************************************
BOOL CALLBACK MainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)

{
static HICON hIcon;
static const DWORD aMenuHelpIDs[] =
{
        IDD_DSPEEDDIALGRP,   (DWORD)-1,
IDD_DNUMTODIAL,      IDH_DIALER_DIAL_NUMBER,
IDD_DCOMBO,          IDH_DIALER_DIAL_NUMBER,
IDD_DDIAL,           IDH_DIALER_DIAL_BUTTON,
IDD_DSPEEDDIAL1,     IDH_DIALER_DIAL_SPEED_CHOOSE,
IDD_DSPEEDDIAL2,     IDH_DIALER_DIAL_SPEED_CHOOSE,
IDD_DSPEEDDIAL3,     IDH_DIALER_DIAL_SPEED_CHOOSE,
IDD_DSPEEDDIAL4,     IDH_DIALER_DIAL_SPEED_CHOOSE,
IDD_DSPEEDDIAL5,     IDH_DIALER_DIAL_SPEED_CHOOSE,
IDD_DSPEEDDIAL6,     IDH_DIALER_DIAL_SPEED_CHOOSE,
IDD_DSPEEDDIAL7,     IDH_DIALER_DIAL_SPEED_CHOOSE,
IDD_DSPEEDDIAL8,     IDH_DIALER_DIAL_SPEED_CHOOSE,
IDD_DSPEEDDIALTEXT1, IDH_DIALER_DIAL_SPEED_CHOOSE,
IDD_DSPEEDDIALTEXT2, IDH_DIALER_DIAL_SPEED_CHOOSE,
IDD_DSPEEDDIALTEXT3, IDH_DIALER_DIAL_SPEED_CHOOSE,
IDD_DSPEEDDIALTEXT4, IDH_DIALER_DIAL_SPEED_CHOOSE,
IDD_DSPEEDDIALTEXT5, IDH_DIALER_DIAL_SPEED_CHOOSE,
IDD_DSPEEDDIALTEXT6, IDH_DIALER_DIAL_SPEED_CHOOSE,
IDD_DSPEEDDIALTEXT7, IDH_DIALER_DIAL_SPEED_CHOOSE,
IDD_DSPEEDDIALTEXT8, IDH_DIALER_DIAL_SPEED_CHOOSE,
IDD_DBUTTON1,        IDH_DIALER_DIAL_KEYPAD,
IDD_DBUTTON2,        IDH_DIALER_DIAL_KEYPAD,
IDD_DBUTTON3,        IDH_DIALER_DIAL_KEYPAD,
IDD_DBUTTON4,        IDH_DIALER_DIAL_KEYPAD,
IDD_DBUTTON5,        IDH_DIALER_DIAL_KEYPAD,
IDD_DBUTTON6,        IDH_DIALER_DIAL_KEYPAD,
IDD_DBUTTON7,        IDH_DIALER_DIAL_KEYPAD,
IDD_DBUTTON8,        IDH_DIALER_DIAL_KEYPAD,
IDD_DBUTTON9,        IDH_DIALER_DIAL_KEYPAD,
IDD_DBUTTONSTAR,     IDH_DIALER_DIAL_KEYPAD,
IDD_DBUTTON0,        IDH_DIALER_DIAL_KEYPAD,
IDD_DBUTTONPOUND,    IDH_DIALER_DIAL_KEYPAD,
0,                   0
};

        switch (msg)
        {
            case WM_INITDIALOG:
                hIcon = LoadIcon( ghInst, (LPCSTR) MAKEINTRESOURCE( IDI_DIALER ) );
                return TRUE;

            case WM_SYSCOMMAND:
                switch( (DWORD) wParam )
                {
                    case SC_CLOSE:
                        PostQuitMessage(0);
                }
                break;

                // processes clicks on controls when
                // context mode help is selected
            case WM_HELP:
                WinHelp (
                         ( (LPHELPINFO) lParam)->hItemHandle,
                         gszHELPfilename,
                         HELP_WM_HELP,
                         (DWORD)(LPVOID) aMenuHelpIDs
                        );
                return TRUE;

                // processes right-clicks on controls
            case WM_CONTEXTMENU:
                WinHelp (
                         (HWND)wParam,
                         gszHELPfilename,
                         HELP_CONTEXTMENU,
                         (DWORD)(LPVOID)aMenuHelpIDs
                        );
                return TRUE;

            case WM_INITMENUPOPUP:
                // if edit menu
                if ( LOWORD(lParam) == 1 )
                {
                    UINT wEnable;

                    if ( GetParent( GetFocus() ) != GetDlgItem( ghWndMain, IDD_DCOMBO ) )
                    {
                        wEnable = MF_GRAYED;
                    }
                    else
                    {
                        LONG lSelect = SendDlgItemMessage (
                            ghWndMain,
                            IDD_DCOMBO,
                            CB_GETEDITSEL,
                            0,
                            0
                            );

                        if ( HIWORD( lSelect ) != LOWORD( lSelect ) )
                            wEnable = MF_ENABLED;
                        else
                            wEnable = MF_GRAYED;
                    }

                    EnableMenuItem((HMENU)wParam, IDM_EDIT_CUT, wEnable);
                    EnableMenuItem((HMENU)wParam, IDM_EDIT_COPY, wEnable);
                    EnableMenuItem((HMENU)wParam, IDM_EDIT_DELETE, wEnable);

                    // enable paste option is there is data
                    // in the clipboard
                    if ( IsClipboardFormatAvailable( CF_TEXT ) )
                    {
                        if ( GetClipboardData ( CF_TEXT ) )
                        {
                            wEnable = MF_ENABLED;
                        }
                        else
                        {
                            wEnable = MF_GRAYED;
                        }
                    }
                    else
                    {
                        wEnable = MF_GRAYED;
                    }

                }
                break;

            case WM_COMMAND:
            {
                char szName[TAPIMAXCALLEDPARTYSIZE] = {'\0'};
                char szNumber[TAPIMAXDESTADDRESSSIZE] = {'\0'};

                switch( LOWORD( (DWORD)wParam ) )
                {
                    // FILE menu
                    case IDM_EXIT:
                        PostQuitMessage(0);
                        return TRUE;

                        // EDIT menu
                    case IDM_EDIT_CUT:
                        SendDlgItemMessage(ghWndMain, IDD_DCOMBO, WM_CUT, 0, 0);
                        return TRUE;

                    case IDM_EDIT_COPY:
                        SendDlgItemMessage(ghWndMain, IDD_DCOMBO, WM_COPY, 0, 0);
                        return TRUE;

                    case IDM_EDIT_PASTE:
                        SendDlgItemMessage(ghWndMain, IDD_DCOMBO, WM_PASTE, 0, 0);
                        return TRUE;

                    case IDM_EDIT_DELETE:
                        SendDlgItemMessage(ghWndMain, IDD_DCOMBO, WM_CLEAR, 0, 0);
                        return TRUE;

                    case IDM_EDIT_SPEEDDIAL:
                        DialogBoxParam (
                                        ghInst,
                                        MAKEINTRESOURCE(IDD_SD1),
                                        ghWndMain,
                                        (DLGPROC)SpeedDial1Proc,
                                        0
                                       );
                        SetFocus(GetDlgItem(ghWndMain, IDD_DDIAL));
                        return TRUE;

                        // TOOLS menu
                    case IDM_CONNECTUSING:
                        DialogBoxParam (
                                        ghInst,
                                        MAKEINTRESOURCE(IDD_CONNECTUSING),
                                        ghWndMain,
                                        (DLGPROC)ConnectUsingProc,
                                        MENU_CHOICE
                                       );
                        return TRUE;

                    case IDM_LOCATION:
                    {
                        char szCanNumber[ TAPIMAXDESTADDRESSSIZE ] = "";

                        // fetch the number to be dialed
                        if ( GetDlgItemText (
                                              ghWndMain,
                                              IDD_DCOMBO,
                                              szNumber,
                                              TAPIMAXDESTADDRESSSIZE
                                            )
                           )
                        {
                            // if a number exists, convert it to
                            // its canonical form.
                            if ( !MakeCanonicalNumber ( szNumber, szCanNumber ) )
                            {
                                lstrcpy( szCanNumber, szNumber );
                            }
                        }

                        lineTranslateDialog (
                                             ghLineApp,
                                             0,
                                             TAPI_CURRENT_VERSION,
                                             ghWndMain,
                                             szCanNumber
                                            );
                        return TRUE;

                    }
                    // HELP menu
                    case IDM_HELP_CONTENTS:
                        WinHelp(ghWndMain, gszHELPfilename, HELP_CONTENTS, 0);
                        return TRUE;

                    case IDM_HELP_WHATSTHIS:
                        PostMessage(ghWndMain, WM_SYSCOMMAND, SC_CONTEXTHELP, 0);
                        return TRUE;

                    case IDM_ABOUT:
                        DialogBoxParam(
                                       ghInst,
                                       MAKEINTRESOURCE(IDD_ABOUT),
                                       ghWndMain,
                                       (DLGPROC)AboutProc,
                                       0
                                      );

                        return TRUE;

                        // Accelerator processing
                    case IDM_ACCEL_NUMTODIAL:
                        if(GetActiveWindow() == ghWndMain)
                            SetFocus(GetDlgItem(ghWndMain, IDD_DCOMBO));
                        return TRUE;

                        // Buttons
                    case IDD_DDIAL:

                    {
                        DWORD cSDEntry;
                        char szSDNumber[TAPIMAXDESTADDRESSSIZE];
                        char szFieldName[MAXBUFSIZE];

                        // check if number entered is dialable
                        if ( SendMessage (
                                          GetDlgItem(ghWndMain, IDD_DCOMBO),
                                          WM_GETTEXTLENGTH,
                                          0,
                                          0
                                         ) > 0
                           )
                        {
                            // get the number to be dialed
                            GetDlgItemText (
                                            ghWndMain,
                                            IDD_DCOMBO,
                                            (LPSTR)szNumber,
                                            TAPIMAXDESTADDRESSSIZE
                                           );

                            // check if it is a speed dial number.
                            // If so choose the name to be displayed.
                            for( cSDEntry = 1; cSDEntry <= NSPEEDDIALS; ++cSDEntry)
                            {
                                wsprintf(szFieldName, "Number%d", cSDEntry);
                                GetPrivateProfileString (
                                    "Speed Dial Settings",
                                    szFieldName,
                                    gszNULL,
                                    szSDNumber,
                                    TAPIMAXCALLEDPARTYSIZE - 1,
                                    gszINIfilename
                                    );

                                // if the number matches, get the name
                                if ( lstrcmp(szSDNumber, szNumber) == 0 )
                                {
                                    wsprintf( szFieldName, "Name%d", cSDEntry);
                                    GetPrivateProfileString (
                                        "Speed Dial Settings",
                                        szFieldName,
                                        gszNULL,
                                        szName,
                                        TAPIMAXCALLEDPARTYSIZE - 1,
                                        gszINIfilename
                                        );
                                    break;
                                }
                            }

                            SetFocus( GetDlgItem( ghWndMain, IDD_DDIAL ) );

                            // once the currentline has been set
                            // using the connect proc
                            // the user must hit dial again
                            if ( giCurrentLine == (DWORD)-1 )
                            {
                                DialogBoxParam (
                                                ghInst,
                                                MAKEINTRESOURCE(IDD_CONNECTUSING),
                                                ghWndMain,
                                                (DLGPROC)ConnectUsingProc,
                                                INVALID_LINE
                                               );
                            }
                            else
                            {
                                AddToRedialList(szNumber);
                                InitiateCall(szNumber, szName);
                            }
                        }
                        return TRUE;
                    }

                    case IDD_DBUTTON1:
                    case IDD_DBUTTON2:
                    case IDD_DBUTTON3:
                    case IDD_DBUTTON4:
                    case IDD_DBUTTON5:
                    case IDD_DBUTTON6:
                    case IDD_DBUTTON7:
                    case IDD_DBUTTON8:
                    case IDD_DBUTTON9:
                    case IDD_DBUTTON0:
                    case IDD_DBUTTONSTAR:
                    case IDD_DBUTTONPOUND:
                    {
                        int     i;
                        TCHAR   szBuffer[TAPIMAXDESTADDRESSSIZE+1];

                        static const char digits[] = { '1', '2', '3', '4',
                        '5', '6', '7', '8',
                        '9', '0', '*', '#' };

                        i = SendDlgItemMessage(ghWndMain,
                                               IDD_DCOMBO,
                                               WM_GETTEXT,
                                               (WPARAM)TAPIMAXDESTADDRESSSIZE+1,
                                               (LPARAM)szBuffer);

                        if (i < TAPIMAXDESTADDRESSSIZE)
                        {
                            MoveMemory(szBuffer+gdwStartSel+1,
                                       szBuffer+gdwEndSel,
                                       i - ( gdwEndSel ) + 1 );

                            szBuffer[gdwStartSel] = digits[LOWORD(wParam) - IDD_DBUTTON1];

                            SendDlgItemMessage(ghWndMain,
                                               IDD_DCOMBO,
                                               WM_SETTEXT,
                                               0,
                                               (LPARAM)szBuffer);

                            gdwStartSel++;
                            gdwEndSel = gdwStartSel;
                        }

                        SetFocus(GetDlgItem(ghWndMain, IDD_DDIAL));
                        EnableWindow(GetDlgItem(ghWndMain, IDD_DDIAL), TRUE);

                        return TRUE;
                    }

                    case IDD_DCOMBO:

                        if (HIWORD(wParam) == CBN_SELENDOK)
                        {
                            EnableWindow( GetDlgItem(ghWndMain, IDD_DDIAL), TRUE );
                        }

                        if ((HIWORD(wParam) == CBN_SELENDOK) ||
                            (HIWORD(wParam) == CBN_SELENDCANCEL))
                        {

                            (DWORD)SendDlgItemMessage(ghWndMain,
                                IDD_DCOMBO,
                                CB_GETEDITSEL,
                                (WPARAM)&gdwStartSel,
                                (LPARAM)&gdwEndSel);
                            return FALSE;
                        }

                        if ( HIWORD( wParam ) == CBN_EDITCHANGE )
                        {
                            EnableWindow (
                                          GetDlgItem( ghWndMain, IDD_DDIAL ),
                                          (BOOL) GetWindowTextLength (
                                GetDlgItem (
                                            ghWndMain,
                                            IDD_DCOMBO
                                           )
                                )
                                         );
                            return TRUE;
                        }

                        break;

                    case IDD_DSPEEDDIAL1:
                    case IDD_DSPEEDDIAL2:
                    case IDD_DSPEEDDIAL3:
                    case IDD_DSPEEDDIAL4:
                    case IDD_DSPEEDDIAL5:
                    case IDD_DSPEEDDIAL6:
                    case IDD_DSPEEDDIAL7:
                    case IDD_DSPEEDDIAL8:
                    {
                        DWORD cSDEntry = LOWORD( (DWORD) wParam)  - IDD_DSPEEDDIAL1 + 1;
                        char szFieldName [MAXBUFSIZE];

                        // get information for the speed dial button
                        // from the INI file
                        wsprintf(szFieldName, "Name%d", cSDEntry);
                        GetPrivateProfileString (
                            "Speed Dial Settings",
                            szFieldName,
                            gszNULL,
                            szName,
                            TAPIMAXCALLEDPARTYSIZE - 1,
                            gszINIfilename
                                                );

                        wsprintf(szFieldName, "%s%d", "Number", cSDEntry);
                        GetPrivateProfileString (
                            "Speed Dial Settings",
                            szFieldName,
                            gszNULL,
                            gszSDNumber[cSDEntry],
                            TAPIMAXDESTADDRESSSIZE - 1,
                            gszINIfilename
                                                );

                        // entry not set yet
                        if( gszSDNumber[cSDEntry][0] == 0 )
                        {
                            DialogBoxParam (
                                            ghInst,
                                            MAKEINTRESOURCE(IDD_SD2),
                                            ghWndMain,
                                            (DLGPROC)SpeedDial2Proc,
                                            MAKELPARAM(wParam,0)
                                           );
                        }

                        // no line open
                        // once the currentline has been set
                        // using the connect proc
                        // the user must hit dial again
                        else if ( giCurrentLine == (DWORD)-1)
                        {
                            DialogBoxParam (
                                            ghInst,
                                            MAKEINTRESOURCE(IDD_CONNECTUSING),
                                            ghWndMain,
                                            (DLGPROC)ConnectUsingProc,
                                            INVALID_LINE
                                           );
                        }
                        // entry is set and valid voice line is open
                        else
                        {
                            // add number to list box combo.
                            AddToRedialList( gszSDNumber[cSDEntry] );
                            InitiateCall( gszSDNumber[cSDEntry], szName );
                        }
                        break;
                    }
                           } // end switch (LOWORD((DWORD)wParam)) { ... }

                            break; // end case WM_COMMAND
                            }

                                            case WM_PAINT:
                                            {
                                                PAINTSTRUCT ps;

                                                BeginPaint(ghWndMain, &ps);

                                                if(IsIconic(ghWndMain))
                                                    DrawIcon(ps.hdc, 0, 0, hIcon);
                                                else
                                                {
                                                    HBRUSH hBrush;

                                                    hBrush = GetSysColorBrush( COLOR_3DFACE );
                                                    //                FillRect(ps.hdc, &ps.rcPaint, GetStockObject(LTGRAY_BRUSH));
                                                    FillRect(ps.hdc, &ps.rcPaint, hBrush);
                                                }

                                                EndPaint(ghWndMain, &ps);

                                                return TRUE;
                                            }

                                            case WM_DRAWITEM:
                                            {
                                                LPDRAWITEMSTRUCT lpdis = (LPDRAWITEMSTRUCT)lParam;
                                                BOOL fHighlighted = (SendDlgItemMessage(
                                                    ghWndMain,
                                                    lpdis->CtlID,
                                                    BM_GETSTATE,
                                                    0,
                                                    0
                                                    ) & 0x0004);

                                                DrawButton(lpdis->hDC, lpdis->rcItem, fHighlighted);
                                                DrawButtonText(
                                                    lpdis->hDC,
                                                    lpdis->rcItem,
                                                    fHighlighted,
                                                    lpdis->CtlID
                                                    );
                                                return TRUE;
                                            }

                                            case WM_CTLCOLORLISTBOX:
                                            case WM_CTLCOLORBTN:
                                            case WM_CTLCOLORSTATIC:
                                                SetBkColor((HDC)wParam, GetSysColor(COLOR_BTNFACE));
                                                return (BOOL)GetSysColorBrush( COLOR_3DFACE );

                                            default:
                                                ;
                                                //            return DefDlgProc( hwnd, msg, wParam, lParam );
                                                //            return DefWindowProc( hwnd, msg, wParam, lParam );

                    } // switch (msg) { ... }

return FALSE;
}

//***************************************************************************
//***************************************************************************
//***************************************************************************
VOID AddToRedialList( LPCSTR szNumber )
{
// NLASTDIALED == 10
WORD cNum;
HWND hWndCombo = GetDlgItem(ghWndMain, IDD_DCOMBO);
DWORD nMatch;

// if valid number
if ( szNumber[0] )
{
// if list box has entries, check if this number
// is already present.  If so delete old entry
cNum = (WORD) SendMessage(hWndCombo, CB_GETCOUNT, 0, 0);
if ( cNum != 0 )
{
nMatch = SendMessage ( hWndCombo, CB_FINDSTRING, 0, (LPARAM)szNumber );
if ( nMatch != CB_ERR )
{
SendMessage(hWndCombo, CB_DELETESTRING, nMatch, 0);
}
else
{
// if the list is full, remove oldest
if ( cNum == NLASTDIALED )
{
SendMessage( hWndCombo, CB_DELETESTRING, NLASTDIALED - 1, 0 );
}
}
}
SendMessage(hWndCombo, CB_INSERTSTRING, 0, (LPARAM)szNumber);
SendMessage(hWndCombo, CB_SETCURSEL, 0, 0L);
EnableWindow ( GetDlgItem( ghWndMain, IDD_DDIAL ), TRUE );
}
}

//***************************************************************************
//***************************************************************************
//***************************************************************************
VOID InitiateCall ( LPCSTR szNumber, LPCSTR szName )
{
HLINE hLine = NULL;

DWORD errCode;

// struct size info
DWORD dwLTPSize= sizeof ( LINETRANSLATEOUTPUT );
DWORD dwNameLen= lstrlen( szName ) + 1;
DWORD dwLCPSize= sizeof( LINECALLPARAMS );

LPLINETRANSLATEOUTPUT lpTransOut = NULL;
LPLINECALLPARAMS lpLineCallParams = NULL;

char szCanNumber[ TAPIMAXDESTADDRESSSIZE ];

// Open a line
errCode = lineOpen (
ghLineApp,
giCurrentLine,
&hLine,
gCurrentLineInfo.dwAPIVersion,
0,
0,
LINECALLPRIVILEGE_NONE,
0,
NULL
   );
if (errCode)
{
errString ( ghWndMain, errCode, MB_ICONEXCLAMATION | MB_OK );
goto error;
}

// call translate address before dialing
do
{
lpTransOut = (LPLINETRANSLATEOUTPUT) DialerAlloc( dwLTPSize );
if ( !lpTransOut )
{
errString( ghWndMain, LINEERR_NOMEM, MB_ICONSTOP | MB_OK );
goto error;
}
lpTransOut-> dwTotalSize = dwLTPSize;

if ( !MakeCanonicalNumber( szNumber, szCanNumber ) )
{
lstrcpy( szCanNumber, szNumber );
}

errCode = lineTranslateAddress (
ghLineApp,
giCurrentLine,
gCurrentLineInfo.dwAPIVersion,
szCanNumber,
0,
0,
lpTransOut
   );
if ( ((LONG)errCode) < 0 )
{
errString( ghWndMain, errCode, MB_ICONEXCLAMATION | MB_OK );
goto error;
}

if ( lpTransOut-> dwNeededSize <= lpTransOut->dwTotalSize  )
{
// ok we are done
break;
}
else
{
dwLTPSize = lpTransOut-> dwNeededSize;
DialerFree ( lpTransOut );
lpTransOut = NULL;
}

} while ( TRUE );

// if number dialed is 911, bring up a warning
if ( Is911( lpTransOut) )
{
INT nRes = errString ( ghWndMain, ERR_911WARN, MB_ICONSTOP | MB_YESNO );
if ( nRes == IDNO )
{
goto error;
}
}

// set call parameters
dwLCPSize += dwNameLen + lpTransOut-> dwDisplayableStringSize;

lpLineCallParams = (LPLINECALLPARAMS) DialerAlloc( dwLCPSize );
if ( !lpLineCallParams )
{
errString( ghWndMain, LINEERR_NOMEM, MB_ICONSTOP | MB_OK );
goto error;
}

lpLineCallParams->dwTotalSize = dwLCPSize;
lpLineCallParams->dwBearerMode = LINEBEARERMODE_VOICE;
lpLineCallParams->dwMediaMode = LINEMEDIAMODE_INTERACTIVEVOICE;
lpLineCallParams->dwCallParamFlags = LINECALLPARAMFLAGS_IDLE;
lpLineCallParams->dwAddressMode = LINEADDRESSMODE_ADDRESSID;
lpLineCallParams->dwAddressID = giCurrentAddress;

if ( szName[ 0 ] )
{
lpLineCallParams->dwCalledPartySize = dwNameLen;
lpLineCallParams->dwCalledPartyOffset = sizeof( LINECALLPARAMS );
lstrcpy (
(LPSTR) lpLineCallParams + sizeof(LINECALLPARAMS),
szName
    );
}

lpLineCallParams-> dwDisplayableAddressSize = lpTransOut-> dwDisplayableStringSize;
lpLineCallParams-> dwDisplayableAddressOffset = sizeof( LINECALLPARAMS ) + dwNameLen;

lstrcpy (
(LPSTR) lpLineCallParams + sizeof(LINECALLPARAMS) + dwNameLen,
(LPSTR) lpTransOut + lpTransOut-> dwDisplayableStringOffset
);

// save dialing information
// Free old allocs.
if ( gszCurrentName )
{
DialerFree ( gszCurrentName );
}

if ( gszCurrentNumber )
{
DialerFree ( gszCurrentNumber );
}

// save new stuff
gszCurrentName = (LPSTR) DialerAlloc( dwNameLen );
if ( !gszCurrentName )
{
errString( ghWndMain, LINEERR_NOMEM, MB_ICONSTOP | MB_OK );
goto error;
}
lstrcpy ( gszCurrentName, szName );

gszCurrentNumber = (LPSTR) DialerAlloc( lpTransOut-> dwDisplayableStringSize );
if ( !gszCurrentNumber )
{
errString( ghWndMain, LINEERR_NOMEM, MB_ICONSTOP | MB_OK );
goto error;
}
lstrcpy (
 gszCurrentNumber,
 (LPSTR) lpTransOut + lpTransOut-> dwDisplayableStringOffset
);

gCurrentLineInfo.hLine = hLine;
ghCall = NULL;

// finally make the call.
gMakeCallRequestID = 0;

gMakeCallRequestID = lineMakeCall (
hLine,
&ghCall,
(LPSTR) lpTransOut + lpTransOut-> dwDialableStringOffset,
0,
lpLineCallParams
  );

// async request ID
// - the call is going out
if ( (LONG) gMakeCallRequestID > 0 )
{
gfCurrentLineAvail = FALSE;
gfMakeCallReplyPending = TRUE;
DialogBoxParam (
ghInst,
MAKEINTRESOURCE(IDD_DIALING),
ghWndMain,
(DLGPROC)DialingProc,
0
   );

}

else
{
if ( gMakeCallRequestID == LINEERR_CALLUNAVAIL )
{
DialogBoxParam (
ghInst,
MAKEINTRESOURCE(IDD_CALLFAILED),
ghWndMain,

(DLGPROC)LineInUseProc,
0
   );
}

else
{
errString( ghWndMain, gMakeCallRequestID, MB_ICONEXCLAMATION | MB_OK );
}

DialerLineClose();
gfCurrentLineAvail = TRUE;
}

error :
if ( lpLineCallParams )
{
DialerFree( lpLineCallParams );
}

if ( lpTransOut )
{
DialerFree( lpTransOut );
}

// if makecall did not succeed but line
// was opened, close it.
if ( ( gMakeCallRequestID <= 0 ) && ( gCurrentLineInfo.hLine ) )
{
DialerLineClose ();
gfCurrentLineAvail = TRUE;
}

SetFocus( GetDlgItem( ghWndMain, IDD_DCOMBO ) );

return;

}

//***************************************************************************
//***************************************************************************
//***************************************************************************
DWORD GetLineInfo ( DWORD iLine, LPLINEINFO lpLineInfo )
{
DWORD errCode = 0;
DWORD dwNeededSize = 0;
LINEEXTENSIONID ExtensionID;

LPSTR pszLineName = NULL;
LPLINEDEVCAPS lpDevCaps = NULL;

errCode = lineNegotiateAPIVersion (
ghLineApp,
iLine,
TAPI_VERSION_1_0,
TAPI_CURRENT_VERSION,
&( lpLineInfo->dwAPIVersion ),
&ExtensionID
  );
if ( errCode )
{
GetLineInfoFailed( iLine, lpDevCaps, lpLineInfo );
goto error;
}

dwNeededSize = sizeof( LINEDEVCAPS );
do
{
lpDevCaps = ( LPLINEDEVCAPS ) DialerAlloc( dwNeededSize );
if ( !lpDevCaps )
{
GetLineInfoFailed( iLine, lpDevCaps, lpLineInfo );
errCode = LINEERR_NOMEM;
goto error;
}

lpDevCaps->dwTotalSize = dwNeededSize;
errCode = lineGetDevCaps (
ghLineApp,
iLine,
lpLineInfo->dwAPIVersion,
0,
lpDevCaps
 );
if ( errCode )
{
GetLineInfoFailed( iLine, lpDevCaps, lpLineInfo );
goto error;
}

if ( lpDevCaps-> dwNeededSize <= lpDevCaps-> dwTotalSize )
{
break;
}

dwNeededSize = lpDevCaps->dwNeededSize;
DialerFree( lpDevCaps );
lpDevCaps = NULL;

} while ( TRUE );

lpLineInfo->nAddr = lpDevCaps->dwNumAddresses;
lpLineInfo->fVoiceLine =
( (lpDevCaps->dwMediaModes & LINEMEDIAMODE_INTERACTIVEVOICE) != 0 );

pszLineName = (LPSTR) DialerAlloc( MAXBUFSIZE );
if ( !pszLineName )
{
errCode = LINEERR_NOMEM;
goto error;
}

if ( lpDevCaps->dwLineNameSize > 0 )
{
if ( lpDevCaps-> dwLineNameSize > (MAXBUFSIZE - 1) )
{
strncpy (
 pszLineName,
 (LPSTR) lpDevCaps + lpDevCaps->dwLineNameOffset,
 MAXBUFSIZE - 1
);
pszLineName[ MAXBUFSIZE - 1 ] = '\0';
}
else
{
lstrcpy( pszLineName, (LPSTR) lpDevCaps + lpDevCaps-> dwLineNameOffset );
}
}
else
{
wsprintf ( pszLineName, "Line %d", iLine );
}

lstrcpy( lpLineInfo->szLineName, pszLineName );
lpLineInfo->dwPermanentLineID = lpDevCaps->dwPermanentLineID;

error:
if ( lpDevCaps )
DialerFree( lpDevCaps );

if ( pszLineName )
DialerFree( pszLineName );

return errCode;
}

//***************************************************************************
//***************************************************************************
//***************************************************************************
VOID GetLineInfoFailed ( DWORD iLine, LPLINEDEVCAPS lpDevCaps, LPLINEINFO lpLineInfo )
{
if ( lpDevCaps )
DialerFree(lpDevCaps);

lpLineInfo->nAddr = 0;
lpLineInfo->fVoiceLine = FALSE;
lpLineInfo->dwAPIVersion = 0;
lpLineInfo->hLine = (HLINE)0;
lpLineInfo->dwPermanentLineID = 0;
lpLineInfo->szLineName[0] = 0;
}

//***************************************************************************
//***************************************************************************
//***************************************************************************
LPSTR GetAddressName(DWORD iLine, DWORD iAddress)
{
DWORD errCode = 0;
DWORD dwNeededSize = 0;
LPSTR pszAddressName = NULL;
LPLINEADDRESSCAPS lpAddressCaps = NULL;

// allocate space for lineGetAddressCaps data
dwNeededSize = sizeof( LINEADDRESSCAPS );

do
{
lpAddressCaps = ( LPLINEADDRESSCAPS )DialerAlloc( dwNeededSize );
if ( !lpAddressCaps )
{
goto error;
}

lpAddressCaps->dwTotalSize = dwNeededSize;
errCode = lineGetAddressCaps (
ghLineApp,
iLine,
iAddress,
gCurrentLineInfo.dwAPIVersion,
0,
lpAddressCaps
 );
if ( errCode )
{
errString( ghWndMain, errCode, MB_ICONSTOP | MB_OK );
goto error;
}

if ( lpAddressCaps-> dwNeededSize <= lpAddressCaps-> dwTotalSize )
{
break;
}

dwNeededSize = lpAddressCaps->dwNeededSize;
DialerFree( lpAddressCaps );
lpAddressCaps = NULL;

} while( TRUE );

// get the address name
pszAddressName = DialerAlloc( MAXBUFSIZE );
if ( !pszAddressName )
{
goto error;
}

if ( lpAddressCaps-> dwAddressSize > 0 )
{
// keep string length bounded
if ( lpAddressCaps-> dwAddressSize > (MAXBUFSIZE - 1 ) )
{
strncpy(
pszAddressName,
(LPSTR) lpAddressCaps + lpAddressCaps->dwAddressOffset,
MAXBUFSIZE - 1
   );
pszAddressName[ MAXBUFSIZE - 1] = '\0';
}
else
{
lstrcpy (
 pszAddressName,
 (LPSTR) lpAddressCaps + lpAddressCaps->dwAddressOffset
    );
}
}
else
// use default name
{
wsprintf(pszAddressName, "Address %d", iAddress);
}

error:
if ( lpAddressCaps )
{
DialerFree( lpAddressCaps );
}

return pszAddressName;
}

//***************************************************************************
//***************************************************************************
//***************************************************************************
BOOL CALLBACK DialingProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{

switch(msg)
{
char szTemp[ TAPIMAXCALLEDPARTYSIZE ];

case WM_INITDIALOG:
// set global handle to window
ghWndDialing = hwnd;

AmpersandCompensate( gszCurrentName, szTemp );

SetDlgItemText(hwnd, IDD_DGNUMBERTEXT, gszCurrentNumber);
SetDlgItemText(hwnd, IDD_DGNAMETEXT, szTemp );
break;

case WM_COMMAND:
switch ( LOWORD( (DWORD)wParam ) )
{
// hang up
case IDCANCEL:
//gfDropping = TRUE;

// if lineMakeCall has completed
// only then drop call.
if ( !gfMakeCallReplyPending && ghCall )
{
if ( ( gDropCallRequestID = lineDrop ( ghCall, NULL, 0 ) ) < 0 )
{
errString ( ghWndDialing, gDropCallRequestID, MB_ICONSTOP | MB_OK );
}
}
else
{
DialerLineClose();
gfCurrentLineAvail = TRUE;
gfMakeCallReplyPending = FALSE;
}

ghWndDialing = NULL;
EndDialog(hwnd, FALSE);

return TRUE;

// something else terminated the call
// all we have to do is terminate this dialog box
case IDOK:
ghWndDialing = NULL;
EndDialog(hwnd, TRUE);

return TRUE;
}
break;

default:
  ;
}
return FALSE;
}

//***************************************************************************
//***************************************************************************
//***************************************************************************
BOOL CALLBACK AboutProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch(msg)
    {
        case WM_INITDIALOG:
        {
            return TRUE;
        }

        case WM_CLOSE:
            EndDialog(hwnd, TRUE);
            return TRUE;

        case WM_COMMAND:
            if(LOWORD((DWORD)wParam) == IDOK)
            {
                EndDialog(hwnd, TRUE);
                return TRUE;
            }

            break;
    }

    return FALSE;

}

//***************************************************************************
//***************************************************************************
//***************************************************************************
BOOL CALLBACK ConnectUsingProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
static const DWORD aMenuHelpIDs[] =
{
IDD_CUTEXTLINE,         IDH_DIALER_OPTIONS_LINE,
IDD_CULISTLINE,         IDH_DIALER_OPTIONS_LINE,
IDD_CUTEXTADDRESS,      IDH_DIALER_OPTIONS_ADDRESS,
IDD_CULISTADDRESS,      IDH_DIALER_OPTIONS_ADDRESS,
IDD_CUSIMPLETAPICHKBOX, IDH_DIALER_OPTIONS_VOICE,
IDD_CUPROPERTIES,       IDH_DIALER_OPTIONS_PROPERTIES,
0,                      0
};

switch(msg)
{
case WM_HELP:
// processes clicks on controls when
// context mode help is selected
WinHelp (
((LPHELPINFO)lParam)->hItemHandle,
gszHELPfilename,
HELP_WM_HELP,
(DWORD)(LPVOID)aMenuHelpIDs
);
return TRUE;

case WM_CONTEXTMENU:
// processes right-clicks on controls
WinHelp (
(HWND)wParam,
gszHELPfilename,
HELP_CONTEXTMENU,
(DWORD)(LPVOID)aMenuHelpIDs
);
return TRUE;

case WM_INITDIALOG:
{
BOOL fEnable;
DWORD dwPriority;

//
// Is there any point in even showing this dialog box?
if ( gnAvailDevices == 0 )
{
// Nope.  Let's tell the user what we don't like.
errString ( ghWndMain, ERR_NOLINES, MB_ICONEXCLAMATION | MB_OK );

EndDialog(hwnd, FALSE);
return TRUE;
}

// if not brought up by InitializeTAPI()
if ( lParam != INVALID_LINE )
{
// hide error text
EnableWindow( GetDlgItem( hwnd, IDD_CUERRORTEXT ), FALSE );
}

// get list of lines into the line list box.
fEnable = InitializeLineBox( GetDlgItem(hwnd, IDD_CULISTLINE) );
EnableWindow( GetDlgItem( hwnd, IDD_CULISTLINE ), fEnable);

// get list of addresses into the address list box.
fEnable =fEnable &&
InitializeAddressBox (
GetDlgItem(hwnd, IDD_CULISTLINE),
GetDlgItem(hwnd, IDD_CULISTADDRESS)
 );
EnableWindow( GetDlgItem( hwnd, IDD_CULISTADDRESS ), fEnable );
EnableWindow( GetDlgItem( hwnd, IDOK ), fEnable );

EnableWindow( GetDlgItem( hwnd, IDD_CUPROPERTIES ), fEnable );

lineGetAppPriority (
"DIALER.EXE",
0, // checking app priority for Assisted Telephony requests
NULL,
LINEREQUESTMODE_MAKECALL,
NULL,
&dwPriority
   );
CheckDlgButton(hwnd, IDD_CUSIMPLETAPICHKBOX, (dwPriority == 1));

// if dwPriority == 1, we're supporting Assisted Telephony AND
// have the highest priority.
EnableWindow (
GetDlgItem(hwnd, IDD_CUSIMPLETAPICHKBOX),
gfRegistered
 );

return FALSE;
}

case WM_COMMAND:
{
switch ( LOWORD( (DWORD)wParam ) )
{
case IDD_CULISTLINE:
if ( HIWORD( wParam ) == CBN_SELENDOK )
// update address box
InitializeAddressBox (
GetDlgItem(hwnd, IDD_CULISTLINE),
GetDlgItem(hwnd, IDD_CULISTADDRESS)
 );
break;

case IDD_CUPROPERTIES:
{
HWND hW = GetDlgItem(hwnd, IDD_CULISTLINE);

lineConfigDialog (
// device ID
(DWORD) SendMessage (
hW,
CB_GETITEMDATA,
(WORD) SendMessage(hW, CB_GETCURSEL, 0, 0),
0
    ),
hwnd,
NULL
 );
break;
}

case IDOK:
{
HWND hwndBox;
char szBuffer[MAXBUFSIZE];
DWORD dwPriority;

// Update line
hwndBox = GetDlgItem( hwnd, IDD_CULISTLINE );
giCurrentLine = SendMessage (
hwndBox,
CB_GETITEMDATA,
SendMessage( hwndBox, CB_GETCURSEL, 0, 0 ),
0
);

// base 10
itoa( gdwPLID[giCurrentLine], szBuffer, 10 );
WritePrivateProfileString (
"Preference",
"Preferred Line",
szBuffer,
gszINIfilename
  );

// Update address
hwndBox = GetDlgItem( hwnd, IDD_CULISTADDRESS );
giCurrentAddress = SendMessage (
hwndBox,
CB_GETITEMDATA,
SendMessage(hwndBox, CB_GETCURSEL, 0, 0),
0
   );

itoa( giCurrentAddress, szBuffer, 10 );
WritePrivateProfileString (
"Preference",
"Preferred Address",
szBuffer,
gszINIfilename
  );

// Update application priority
if ( SendDlgItemMessage (
hwnd,
IDD_CUSIMPLETAPICHKBOX,
BM_GETCHECK,
0,
0L
)
  == 0)
{
dwPriority = 0;
}
else
{
dwPriority = 1;
}

lineSetAppPriority (
"DIALER.EXE",
0,
NULL,
LINEREQUESTMODE_MAKECALL,
NULL,
dwPriority
   );

EndDialog(hwnd, TRUE);
return TRUE;
}

case IDCANCEL:
EndDialog(hwnd, FALSE);
return TRUE;
}
}

default:
  ;

}

return FALSE;
}

//***************************************************************************
//***************************************************************************
//***************************************************************************
BOOL CALLBACK LineInUseProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {
    LPARAM lNewParam = lParam;
    PTSTR  ptStr;

    switch(msg)
        {
        case WM_INITDIALOG:
            {
            switch(lParam)
                {
                case LINEDISCONNECTMODE_REJECT:
                    lNewParam = ikszDisconnectedReject;
                    break;

                case LINEDISCONNECTMODE_BUSY:
                    lNewParam = ikszDisconnectedBusy;
                    break;

                case LINEDISCONNECTMODE_NOANSWER:
                    lNewParam = ikszDisconnectedNoAnswer;
                    break;

                case LINEDISCONNECTMODE_CONGESTION:
                    lNewParam = ikszDisconnectedNetwork;
                    break;

                case LINEDISCONNECTMODE_INCOMPATIBLE:
                    lNewParam = ikszDisconnectedIncompatible;
                    break;

                case LINEDISCONNECTMODE_NODIALTONE:
                    lNewParam = ikszDisconnectedNoDialTone;
                    break;

                default:
                    lNewParam = ikszDisconnectedCantDo;
                    break;
                }
            return TRUE;
            }

        case WM_COMMAND:
            if(LOWORD((DWORD)wParam) == IDOK)
                {
                EndDialog(hwnd, TRUE);
                return TRUE;
                }
            break;

        default:
              ;
//            return DefDlgProc( hwnd, msg, wParam, lParam );

        }

    ptStr = DialerAlloc( MAXBUFSIZE );

    LoadString( ghInst, lNewParam, ptStr, MAXBUFSIZE );

    SetDlgItemText(
        hwnd,
        IDD_CFTEXT,
        ptStr
        );

    DialerFree( ptStr );

    return FALSE;
    }

//***************************************************************************
//***************************************************************************
//***************************************************************************
BOOL CALLBACK SpeedDial1Proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static DWORD nCurrentSpeedDial;

static const DWORD aMenuHelpIDs[] =
{
IDOK,                   IDH_DIALER_SPEED_SAVE,
IDD_SD1SPEEDDIAL1,      IDH_DIALER_BUTTONS,
IDD_SD1SPEEDDIAL2,      IDH_DIALER_BUTTONS,
IDD_SD1SPEEDDIAL3,      IDH_DIALER_BUTTONS,
IDD_SD1SPEEDDIAL4,      IDH_DIALER_BUTTONS,
IDD_SD1SPEEDDIAL5,      IDH_DIALER_BUTTONS,
IDD_SD1SPEEDDIAL6,      IDH_DIALER_BUTTONS,
IDD_SD1SPEEDDIAL7,      IDH_DIALER_BUTTONS,
IDD_SD1SPEEDDIAL8,      IDH_DIALER_BUTTONS,
IDD_SD1SPEEDDIALTEXT1,  IDH_DIALER_BUTTONS,
IDD_SD1SPEEDDIALTEXT2,  IDH_DIALER_BUTTONS,
IDD_SD1SPEEDDIALTEXT3,  IDH_DIALER_BUTTONS,
IDD_SD1SPEEDDIALTEXT4,  IDH_DIALER_BUTTONS,
IDD_SD1SPEEDDIALTEXT5,  IDH_DIALER_BUTTONS,
IDD_SD1SPEEDDIALTEXT6,  IDH_DIALER_BUTTONS,
IDD_SD1SPEEDDIALTEXT7,  IDH_DIALER_BUTTONS,
IDD_SD1SPEEDDIALTEXT8,  IDH_DIALER_BUTTONS,
IDD_SD1TEXTNAME,        IDH_DIALER_SPEED_NAME,
IDD_SD1EDITNAME,        IDH_DIALER_SPEED_NAME,
IDD_SD1TEXTNUMBER,      IDH_DIALER_SPEED_NUMBER,
IDD_SD1EDITNUMBER,      IDH_DIALER_SPEED_NUMBER,
IDD_SD1TEXTCHOOSE,      (DWORD)-1,
IDD_SD1TEXTENTER,       (DWORD)-1,
0,                      0
};

// buffer to store speed dial names till they are saved.
static TCHAR szSDName[NSPEEDDIALS + 1][TAPIMAXDESTADDRESSSIZE];

switch(msg)
{
case WM_HELP:
// processes clicks on controls when
// context mode help is selected
WinHelp(
((LPHELPINFO)lParam)->hItemHandle,
gszHELPfilename,
HELP_WM_HELP,
(DWORD)(LPVOID)aMenuHelpIDs
);
return TRUE;

case WM_CONTEXTMENU: // processes right-clicks on controls
WinHelp(
(HWND)wParam,
gszHELPfilename,
HELP_CONTEXTMENU,
(DWORD)(LPVOID)aMenuHelpIDs
);
return TRUE;

case WM_INITDIALOG:
{
DWORD cSDEntry;
DWORD idFirstEmpty = (DWORD) -1;

char szName[TAPIMAXCALLEDPARTYSIZE];
char szTemp[TAPIMAXCALLEDPARTYSIZE];
char szFieldName[MAXBUFSIZE];

// Retrieve speed dial info from INI file
for(cSDEntry = 1; cSDEntry <= NSPEEDDIALS; ++cSDEntry)
{
wsprintf(szFieldName, "Name%d", cSDEntry);
GetPrivateProfileString (
"Speed Dial Settings",
szFieldName,
gszNULL,
szSDName[ cSDEntry ],
TAPIMAXCALLEDPARTYSIZE - 1,
gszINIfilename
);

// set the first empty speed dial button
if ( idFirstEmpty == -1 &&
 szSDName[ cSDEntry ][0] == '\0' &&
 gszSDNumber[ cSDEntry ][ 0 ] == '\0' )
idFirstEmpty = cSDEntry;

wsprintf(szFieldName, "Number%d", cSDEntry);
GetPrivateProfileString (
"Speed Dial Settings",
szFieldName,
gszNULL,
gszSDNumber[cSDEntry],
MAXBUFSIZE - 1,
gszINIfilename
);

// get a copy of the name for editing
// if name is empty, use the number as the
// name.
if ( lstrcmp( gszNULL, szSDName[ cSDEntry ] ) )
{
lstrcpy( szName, szSDName[ cSDEntry] );
}
else
{
lstrcpy( szName, gszSDNumber[ cSDEntry ] );
}

FitTextToButton( hwnd, IDD_SD1SPEEDDIAL1 + cSDEntry - 1, szName );
AmpersandCompensate( szName, szTemp );

SetDlgItemText (
hwnd,
IDD_SD1SPEEDDIAL1 + cSDEntry - 1,
(LPCSTR) szTemp
   );

}

// for the edit speed dial dialog
// limit the lengths of text
SendDlgItemMessage (
hwnd,
IDD_SD1EDITNAME,
EM_LIMITTEXT,
(WPARAM)(TAPIMAXCALLEDPARTYSIZE - 1),
0
   );

SendDlgItemMessage (
hwnd,
IDD_SD1EDITNUMBER,
EM_LIMITTEXT,
(WPARAM)(TAPIMAXDESTADDRESSSIZE - 1),
0
   );

// select the first empty button
// nothing empty, then edit #1
if ( -1 == idFirstEmpty )
{
nCurrentSpeedDial = 1;
SetDlgItemText(
hwnd,
IDD_SD1EDITNAME,
(LPCSTR) szSDName[ 1 ]
  );

SetDlgItemText(
hwnd,
IDD_SD1EDITNUMBER,
(LPCSTR) gszSDNumber[ 1 ]
  );
}
else
{
nCurrentSpeedDial = idFirstEmpty;
}

SetFocus( GetDlgItem( hwnd, IDD_SD1EDITNAME ) );
return FALSE;
}

case WM_COMMAND:
{
char szName[TAPIMAXCALLEDPARTYSIZE];
char szTemp[ TAPIMAXCALLEDPARTYSIZE ];

switch( LOWORD( (DWORD) wParam ) )
{
case IDOK:
{
DWORD cSDEntry;
char szFieldName[MAXBUFSIZE];

// save new speed dial settings
for ( cSDEntry = 1; cSDEntry <= NSPEEDDIALS; ++cSDEntry )
{
wsprintf(szFieldName, "Name%d", cSDEntry);
WritePrivateProfileString (
"Speed Dial Settings",
szFieldName,
szSDName [cSDEntry],
gszINIfilename
  );

wsprintf(szFieldName, "Number%d", cSDEntry);
WritePrivateProfileString (
"Speed Dial Settings",
szFieldName,
gszSDNumber[cSDEntry],
gszINIfilename
  );

// set the text for the corresponding
// main window button
if ( szSDName[ cSDEntry ][ 0 ] == '\0' )
{
lstrcpy( szName, gszSDNumber[ cSDEntry ] );
}
else
{
lstrcpy( szName, szSDName[ cSDEntry ] );
}

FitTextToButton(
ghWndMain,
IDD_DSPEEDDIAL1 + cSDEntry - 1,
szName
   );

AmpersandCompensate( szName, szTemp );
SetDlgItemText (
ghWndMain,
IDD_DSPEEDDIAL1 + cSDEntry - 1,
(LPCSTR) szTemp
   );
}

EndDialog(hwnd, TRUE);
return TRUE;
}

case IDCANCEL:
EndDialog(hwnd, FALSE);
return TRUE;

case IDD_SD1SPEEDDIAL1:
case IDD_SD1SPEEDDIAL2:
case IDD_SD1SPEEDDIAL3:
case IDD_SD1SPEEDDIAL4:
case IDD_SD1SPEEDDIAL5:
case IDD_SD1SPEEDDIAL6:
case IDD_SD1SPEEDDIAL7:
case IDD_SD1SPEEDDIAL8:

nCurrentSpeedDial =LOWORD( (DWORD) wParam ) - IDD_SD1SPEEDDIAL1 + 1;

SetDlgItemText (
hwnd,
IDD_SD1EDITNAME,
szSDName [ nCurrentSpeedDial ]
   );
SetDlgItemText (
hwnd,
IDD_SD1EDITNUMBER,
gszSDNumber[nCurrentSpeedDial]
   );

SetFocus( GetDlgItem( hwnd, IDD_SD1EDITNAME ) );
SendDlgItemMessage(
hwnd,
IDD_SD1EDITNAME,
EM_SETSEL,
0,
MAKELPARAM(0, -1)
);
break;

case IDD_SD1EDITNAME:
if ( HIWORD( wParam ) == EN_CHANGE )
{

GetDlgItemText (
hwnd,
IDD_SD1EDITNAME,
szName,
TAPIMAXCALLEDPARTYSIZE
   );

// if there is no name, label the button with
// the number
if ( szName[ 0 ] == '\0' )
{
szSDName[ nCurrentSpeedDial ][ 0 ] = '\0';
lstrcpy( szName, gszSDNumber[ nCurrentSpeedDial ] );
}
else
{
lstrcpy( szSDName[ nCurrentSpeedDial ], szName );
}

FitTextToButton (
hwnd,
IDD_SD1SPEEDDIAL1 + nCurrentSpeedDial - 1,
szName
);
AmpersandCompensate( szName, szTemp );

SetDlgItemText (
hwnd,
IDD_SD1SPEEDDIAL1 + nCurrentSpeedDial - 1,
szTemp
   );
}
break;

case IDD_SD1EDITNUMBER:
if ( HIWORD( wParam ) == EN_CHANGE )
{
GetDlgItemText (
hwnd,
IDD_SD1EDITNUMBER,
gszSDNumber[nCurrentSpeedDial],
TAPIMAXDESTADDRESSSIZE
   );

if ( gszSDNumber[ nCurrentSpeedDial ][ 0 ] == '\0' )
{
GetDlgItemText (
hwnd,
IDD_SD1EDITNAME,
szName,
TAPIMAXDESTADDRESSSIZE
   );

if ( szName[ 0 ] == '\0' )
{
SetDlgItemText (
hwnd,
IDD_SD1SPEEDDIAL1 + nCurrentSpeedDial - 1,
szName
   );

}
}
}
break;
} // switch(LOWORD((DWORD)wParam))
break;

} // case WM_COMMAND:

default:
  ;

} // switch(msg)

return FALSE;
}

//***************************************************************************
//***************************************************************************
//***************************************************************************
BOOL CALLBACK SpeedDial2Proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static DWORD nCurrentSpeedDial;

static const DWORD aMenuHelpIDs[] =
{
IDOK,                   IDH_DIALER_SPEED_SAVE,
IDD_SD2SAVEANDDIAL,     IDH_DIALER_SPEED_SAVE_DIAL,
IDD_SD2TEXTNAME,        IDH_DIALER_SPEED_NAME,
IDD_SD2EDITNAME,        IDH_DIALER_SPEED_NAME,
IDD_SD2TEXTNUMBER,      IDH_DIALER_SPEED_NUMBER,
IDD_SD2EDITNUMBER,      IDH_DIALER_SPEED_NUMBER,
0,                      0
};

switch(msg)
{
case WM_HELP:
// processes clicks on controls when
// context mode help is selected
WinHelp (
((LPHELPINFO)lParam)->hItemHandle,
gszHELPfilename,
HELP_WM_HELP,
(DWORD)(LPVOID)aMenuHelpIDs
);
return TRUE;

case WM_CONTEXTMENU:
// processes right-clicks on controls
WinHelp (
(HWND)wParam,
gszHELPfilename,
HELP_CONTEXTMENU,
(DWORD)(LPVOID)aMenuHelpIDs
);
return TRUE;

case WM_INITDIALOG:
{
char szFieldName [MAXBUFSIZE];
char szName [TAPIMAXCALLEDPARTYSIZE];

nCurrentSpeedDial = LOWORD( lParam ) - IDD_DSPEEDDIAL1 + 1;

// retrieve speed dial button info
wsprintf(szFieldName, "Name%d", nCurrentSpeedDial);
GetPrivateProfileString (
"Speed Dial Settings",
szFieldName,
gszNULL,
szName,
TAPIMAXCALLEDPARTYSIZE - 1,
gszINIfilename
);
SetDlgItemText (
hwnd,
IDD_SD2EDITNAME,
szName
   );

SetDlgItemText (
hwnd,
IDD_SD2EDITNUMBER,
gszSDNumber[nCurrentSpeedDial]
   );

// limit the lengths of the texts
SendDlgItemMessage (
hwnd,
IDD_SD2EDITNAME,
EM_LIMITTEXT,
(WPARAM)(TAPIMAXCALLEDPARTYSIZE - 1),
0
   );

SendDlgItemMessage (
hwnd,
IDD_SD2EDITNUMBER,
EM_LIMITTEXT,
(WPARAM)(TAPIMAXDESTADDRESSSIZE - 1),
0
   );

SetFocus( GetDlgItem( hwnd, IDD_SD2EDITNAME ) );
SendDlgItemMessage (
hwnd,
IDD_SD2EDITNAME,
EM_SETSEL,
0,
MAKELPARAM(0, -1)
   );

return FALSE;
}

case WM_COMMAND:
{
char szName[ TAPIMAXCALLEDPARTYSIZE ];
char szTemp[ TAPIMAXCALLEDPARTYSIZE ];
char szFieldName[MAXBUFSIZE];

switch ( LOWORD( (DWORD) wParam ) )
{
case IDOK:
case IDD_SD2SAVEANDDIAL:
{
GetDlgItemText (
hwnd,
IDD_SD2EDITNAME,
(LPTSTR) szName,
TAPIMAXCALLEDPARTYSIZE
   );

GetDlgItemText (
hwnd,
IDD_SD2EDITNUMBER,
(LPTSTR) gszSDNumber[nCurrentSpeedDial],
TAPIMAXCALLEDPARTYSIZE
   );

wsprintf ( szFieldName, "Name%d", nCurrentSpeedDial );

WritePrivateProfileString (
"Speed Dial Settings",
szFieldName,
szName,
gszINIfilename
  );

wsprintf ( szFieldName, "Number%d", nCurrentSpeedDial );
WritePrivateProfileString (
"Speed Dial Settings",
szFieldName,
gszSDNumber[nCurrentSpeedDial],
gszINIfilename
  );

// update main window buttons
// is only number has been entered, label button with it.
if ( szName[ 0 ] == '\0' )
{
lstrcpy( szName, gszSDNumber[ nCurrentSpeedDial ] );
}

FitTextToButton (
ghWndMain,
IDD_DSPEEDDIAL1 + nCurrentSpeedDial - 1,
(LPSTR) szName
);

AmpersandCompensate( szName, szTemp );

SetDlgItemText (
ghWndMain,
IDD_DSPEEDDIAL1 + nCurrentSpeedDial - 1,
(LPCSTR)szTemp
   );

// if save and dial, then post dial message to main window
if ( LOWORD( (DWORD) wParam ) == IDD_SD2SAVEANDDIAL )
{
PostMessage (

ghWndMain,
WM_COMMAND,
MAKEWPARAM (
nCurrentSpeedDial + IDD_DSPEEDDIAL1 - 1,
BN_CLICKED
   ),
(LPARAM) GetDlgItem (
ghWndMain,
nCurrentSpeedDial + IDD_DSPEEDDIAL1 - 1
)
);
}
EndDialog(hwnd, TRUE);
return TRUE;
}

case IDCANCEL:
EndDialog(hwnd, FALSE);
return TRUE;

case IDD_SD2EDITNAME:
case IDD_SD2EDITNUMBER:
if ( HIWORD( wParam ) == EN_CHANGE)
{
EnableWindow (
GetDlgItem( hwnd, IDD_SD2SAVEANDDIAL ),
GetWindowTextLength ( GetDlgItem( hwnd, IDD_SD2EDITNUMBER ) ) > 0
 );
}
break;

} // switch(LOWORD((DWORD)wParam))
break;
}

default:
  ;

} // switch(msg)

return FALSE;
}

//***************************************************************************
//***************************************************************************
//***************************************************************************
VOID CALLBACK
tapiCallback (
DWORD   hDevice,
DWORD   dwMsg,
DWORD   dwCBInstance,
DWORD   dwParam1,
DWORD   dwParam2,
DWORD   dwParam3
    )
{
switch (dwMsg)
{
INT errCode;

case LINE_ADDRESSSTATE:
break;

case LINE_CALLINFO:
break;

case LINE_CALLSTATE:
if ( (HCALL)hDevice != ghCall )
return;

switch ( dwParam1 ) // new state
{
case LINECALLSTATE_IDLE:

// tell "Dialing" window to terminate
if ( ghWndDialing )
{
SendMessage (
ghWndDialing,
WM_COMMAND,
MAKEWPARAM( IDOK, 0 ),
0
);
}

// tapi call cleanup
if ( !gfMakeCallReplyPending && ghCall )
{
if ( ( errCode = lineDeallocateCall( ghCall ) ) < 0 )
{
errString ( ghWndMain, errCode, MB_ICONSTOP | MB_OK );
}
ghCall = NULL;
}
DialerLineClose();
gfCurrentLineAvail = TRUE;

// update main window
DisableDialButtons( FALSE );
break;

case LINECALLSTATE_BUSY:
tapiCallback (
hDevice,
dwMsg,
dwCBInstance,
LINECALLSTATE_DISCONNECTED,
LINEDISCONNECTMODE_BUSY,
dwParam3
 );
break;

case LINECALLSTATE_SPECIALINFO:
tapiCallback (
hDevice,
dwMsg,
dwCBInstance,
LINECALLSTATE_DISCONNECTED,
LINEDISCONNECTMODE_UNREACHABLE,
dwParam3
 );
break;

case LINECALLSTATE_DISCONNECTED:
{
BOOL fCallOK;
DWORD LineDisconnectMode;

if ( dwParam2 == 0 )
LineDisconnectMode = LINEDISCONNECTMODE_NORMAL;
else
LineDisconnectMode = dwParam2;

fCallOK = ( LineDisconnectMode == LINEDISCONNECTMODE_NORMAL||
LineDisconnectMode == LINEDISCONNECTMODE_UNKNOWN||
LineDisconnectMode == LINEDISCONNECTMODE_PICKUP||
LineDisconnectMode == LINEDISCONNECTMODE_FORWARDED||
LineDisconnectMode == LINEDISCONNECTMODE_UNAVAIL
  );

if ( !gfMakeCallReplyPending && ghCall )
{
//gfDropping = TRUE;
if ( ( gDropCallRequestID = lineDrop ( ghCall, NULL, 0 ) ) < 0 )
{
errString ( ghWndMain, gDropCallRequestID, MB_ICONSTOP | MB_OK );
}
}

if ( !fCallOK )
DialogBoxParam (
ghInst,
MAKEINTRESOURCE(IDD_CALLFAILED),
ghWndMain,
(DLGPROC)LineInUseProc,
LineDisconnectMode
   );
break;
}
}
break;

case LINE_CLOSE:
if ( gCurrentLineInfo.hLine == (HLINE)hDevice )
{
errString(ghWndMain, ERR_LINECLOSE, MB_ICONEXCLAMATION | MB_OK );
gCurrentLineInfo.hLine = NULL;
gfCurrentLineAvail = FALSE;
DisableDialButtons(FALSE);
}
break;

case LINE_CREATE:
// dwParam1 is the new device's ID
if ( dwParam1 >= gnAvailDevices )
{
DWORD* gnAddrTemp;
DWORD iLine;
LINEINFO LineInfo;

// we record new device's address count.

                // we are assuming here that we're just adding a new
                // line and it's sequential and it's the last one

                gnAvailDevices = dwParam1 + 1;

gnAddrTemp = (DWORD *) DialerAlloc ( sizeof(DWORD) * (int)(gnAvailDevices) );

for ( iLine = 0; iLine < (gnAvailDevices-1); ++iLine )
gnAddrTemp[iLine] = gnAddr[iLine];

DialerFree( gnAddr );

// we have effectively added one more
// space in the gnAddr array
gnAddr = gnAddrTemp;

if ( GetLineInfo( dwParam1, &LineInfo ) != ERR_NONE )
break;

 gnAddr[dwParam1] = LineInfo.nAddr;
}
break;

case LINE_DEVSPECIFIC:
break;

case LINE_DEVSPECIFICFEATURE:
break;

case LINE_GATHERDIGITS:
break;

case LINE_GENERATE:
break;

case LINE_LINEDEVSTATE:
if ( dwParam1 & LINEDEVSTATE_REINIT )
{
if(dwParam2 != 0)
{
// this is another msg translated into REINIT
tapiCallback( hDevice, dwParam2, dwCBInstance, dwParam3, 0, 0 );
}
else
{
// Re-initialize TAPI
gfNeedToReinit = TRUE;
}
}

if ( dwParam1 & LINEDEVSTATE_REMOVED )
{
DialerLineClose();
tapiCallback(hDevice, LINE_CLOSE, dwCBInstance, 0, 0, 0); // is this needed?
}
break;

case LINE_MONITORDIGITS:
break;

case LINE_MONITORMEDIA:
break;

case LINE_MONITORTONE:
break;

// async reply from lineMakeCall() or lineDrop()
case LINE_REPLY:

// reply for lineMakeCall
if ( (LONG) dwParam1 == gMakeCallRequestID )
{
// error on make call
if ( dwParam2 != ERR_NONE )
{
// Get rid of the Dialing Dialog box if it's up
if ( ghWndDialing )
{
   SendMessage(
ghWndDialing,
WM_COMMAND,
MAKEWPARAM(IDOK,0),
0
  );
}

if ( dwParam2 == LINEERR_CALLUNAVAIL )
{
DialogBoxParam (
ghInst,
MAKEINTRESOURCE(IDD_CALLFAILED),
ghWndMain,
(DLGPROC)LineInUseProc,
0
   );
}
else
{
errString ( ghWndMain, dwParam2, MB_ICONEXCLAMATION | MB_OK );
}

ghCall = NULL;
DialerLineClose();
gfCurrentLineAvail = TRUE;
}

gfMakeCallReplyPending = FALSE;
}

// reply from lineDrop()
if ( (LONG) dwParam1 == gDropCallRequestID )
{
//gfDropping = FALSE;

// tell "Dialing" window to terminate
if ( ghWndDialing )
{
SendMessage (
ghWndDialing,
WM_COMMAND,
MAKEWPARAM( IDOK,0 ),
0
);
}

// tapi call cleanup
if ( dwParam2 == ERR_NONE )
{
if ( !gfMakeCallReplyPending && ghCall )
{
if ( ( errCode = lineDeallocateCall( ghCall ) ) < 0 )
{
errString ( ghWndMain, errCode, MB_ICONSTOP | MB_OK );
}
ghCall = NULL;
}
}
DialerLineClose ();
gfCurrentLineAvail = TRUE;
}

break;

case LINE_REQUEST:
// Simple TAPI request
if ( dwParam1 == LINEREQUESTMODE_MAKECALL )
{
gfCallRequest = TRUE;
}
break;
}
}

//***************************************************************************
//***************************************************************************
//***************************************************************************
BOOL InitializeLineBox(HWND hwndLineBox)
{

DWORD iLine, iItem, iItemCurrent = (DWORD)-1;
DWORD errCode;

LPLINEINFO lpLineInfo = NULL;

// allocate buffer for storing LINEINFO for all of
// the available lines.  Always allocate space for
// at least one line
if ( gnAvailDevices == 0 )
{
lpLineInfo = (LPLINEINFO) DialerAlloc( sizeof(LINEINFO) );
}
else
{
lpLineInfo = (LPLINEINFO) DialerAlloc ( sizeof(LINEINFO) * (int)gnAvailDevices );
}

// if no space was set aside...
if ( lpLineInfo == NULL )
return LINEERR_NOMEM;

// fill lpLineInfo[] and open each line
for ( iLine = 0; iLine < gnAvailDevices; ++iLine )
{
// skip remaining processing for this line if it didn't open
if ( GetLineInfo( iLine, &lpLineInfo[iLine] ) != ERR_NONE )
{
continue;
}

iItem = SendMessage (
hwndLineBox,
CB_ADDSTRING,
0,
(LPARAM)(LPCSTR)(lpLineInfo[iLine].szLineName)
);

// error, bail out.
if ( iItem == CB_ERR || iItem == CB_ERRSPACE )
{
            if (lpLineInfo)
            {
                DialerFree(lpLineInfo);
            }

return FALSE;
}

errCode = SendMessage (
hwndLineBox,
CB_SETITEMDATA,
(WPARAM)iItem,
(LPARAM)iLine
  );

if ( iLine == giCurrentLine )
{
iItemCurrent = iItem;
}
else if ( iItemCurrent != -1 && iItem <= iItemCurrent )
{
// if the item we are putting is before the
// "current" item, we must increment iItemCurrent
// to reflect that something is being placed before
// it, due to sorting
++iItemCurrent;
}
}

if ( iItemCurrent == (DWORD)-1 )
iItemCurrent = 0;

if ( SendMessage( hwndLineBox, CB_GETCOUNT, 0, 0) != 0 )
{
SendMessage( hwndLineBox, CB_SETCURSEL, (WPARAM)iItemCurrent, 0 );
return TRUE;
}

    DialerFree(lpLineInfo);
return FALSE;
}

//***************************************************************************
//***************************************************************************
//***************************************************************************
BOOL InitializeAddressBox( HWND hwndLineBox, HWND hwndAddressBox )
{
DWORD errCode;
DWORD iAddress, iItem, iItemCurrent = (DWORD)-1;
DWORD iLineBoxCurrent;
LPSTR pszAddressName;

if ( SendMessage( hwndLineBox, CB_GETCOUNT, 0, 0 ) == 0 )
{
return FALSE;
}

// select current entry in line box
iLineBoxCurrent = SendMessage (
hwndLineBox,
CB_GETITEMDATA,
SendMessage( hwndLineBox, CB_GETCURSEL, 0, 0 ),
0
  );
// empty address list box
SendMessage ( hwndAddressBox, CB_RESETCONTENT, 0, 0);

// get all the address for this line
for ( iAddress = 0; iAddress < gnAddr[iLineBoxCurrent]; ++iAddress )
{
pszAddressName = GetAddressName( iLineBoxCurrent, iAddress );

// if this address if fails, try the next one
if ( !pszAddressName )
continue;

iItem = SendMessage (
hwndAddressBox,
CB_ADDSTRING,
0,
(LPARAM) (LPCSTR) (pszAddressName)
);

// error, bail out
if ( iItem == CB_ERR || iItem == CB_ERRSPACE )
return FALSE;

errCode = SendMessage (
hwndAddressBox,
CB_SETITEMDATA,
(WPARAM) iItem,
(LPARAM) iAddress
  );

if ( iLineBoxCurrent == giCurrentLine )
{
if(iAddress == giCurrentAddress)
{
iItemCurrent = iItem;
}
else
{
// if the item we are putting is before the
// "current" item, we must increment iItemCur
// to reflect that something is being placed
// before it, due to sorting
if ( iItemCurrent != -1 && iItem <= iItemCurrent )
{
++iItemCurrent;
}
}
}

DialerFree( pszAddressName );
}

if ( iLineBoxCurrent != giCurrentLine )
{
// if we're not looking at the current line
// then highlight address 0
iItemCurrent = 0;
}

SendMessage (
hwndAddressBox,
CB_SETCURSEL,
iItemCurrent,
0
);
return TRUE;
}

//***************************************************************************
//***************************************************************************
//***************************************************************************
VOID ManageAssistedTelephony(VOID)
{
DWORD errCode;
LPLINEREQMAKECALL lpRequestBuffer;

lpRequestBuffer = (LPLINEREQMAKECALL) DialerAlloc( sizeof( LINEREQMAKECALL ) );
if ( !lpRequestBuffer )
{
goto error;
}

    // bring window to front
    SetForegroundWindow(ghWndMain);

// get next queued request.
errCode = lineGetRequest (
ghLineApp,
LINEREQUESTMODE_MAKECALL,
lpRequestBuffer

 );
if ( errCode )
{
// if no more call requests pending, reset flag.
if ( errCode == LINEERR_NOREQUEST )
{
gfCallRequest = FALSE;
}
else
{
errString ( ghWndMain, errCode, MB_ICONEXCLAMATION | MB_OK );
}
goto error;
}

// if a line has not been selected
if ( giCurrentLine == (DWORD)-1 )
{
if (!DialogBoxParam (
ghInst,
MAKEINTRESOURCE(IDD_CONNECTUSING),
ghWndMain,
(DLGPROC) ConnectUsingProc,
INVALID_LINE
  ))
        {
            // failed to get a line
            goto error;
        }
}

// make the reuested call.
InitiateCall (
lpRequestBuffer->szDestAddress,
lpRequestBuffer->szCalledParty
 );

error :
if ( lpRequestBuffer )
{
DialerFree( lpRequestBuffer );
}
return;
}

//***************************************************************************
//***************************************************************************
//***************************************************************************
VOID DialerLineClose()
{
DWORD errCode;

if ( gCurrentLineInfo.hLine )
{
if ( errCode = lineClose ( gCurrentLineInfo.hLine ) )
{
errString ( ghWndMain, errCode, MB_ICONSTOP | MB_OK );
}
gCurrentLineInfo.hLine = NULL;
}

// re-initialize TAPI if it needs to be re-initialized
if ( gfNeedToReinit )
{
CloseTAPI();

errCode = InitializeTAPI();
if(errCode)
{
errString(ghWndMain, errCode, MB_APPLMODAL | MB_ICONEXCLAMATION );
DialerCleanup(); // terminate program if we can't init
return;
}

errCode = lineRegisterRequestRecipient (
ghLineApp,
0,
LINEREQUESTMODE_MAKECALL,
TRUE
   );
if (errCode)
{
errString(ghWndMain, errCode, MB_ICONEXCLAMATION | MB_OK );
}

gfNeedToReinit = FALSE;
}
}

//***************************************************************************
//***************************************************************************
//***************************************************************************
int errString( HWND hWndOwner, UINT errCode, UINT uFlags )
{
PTSTR ptStrTitle;
PTSTR ptStrError;
int   nResult;
    BOOL  bDefault = FALSE;

ptStrTitle = DialerAlloc( MAXBUFSIZE );
if ( NULL == ptStrTitle )
{
   // Now, _this_ is a problem.
   return 0;
}

ptStrError = DialerAlloc( MAXBUFSIZE );
if ( NULL == ptStrError )
{
   // Now, _this_ is a problem.
   DialerFree( ptStrTitle);
   return 0;
}

switch(errCode)
{
case ERR_NOLINES:
errCode = ikszErrNoVoiceLine;
break;

case ERR_NOVOICELINE:
errCode = ikszErrNoVoiceLine;
break;

case ERR_LINECLOSE:
errCode = ikszErrLineClose;
break;

case ERR_911WARN:
errCode = ikszWarningFor911;
break;

case ERR_NEWDEFAULT:
errCode = ikszWarningNewDefault;
break;

case LINEERR_NODRIVER:
errCode = ikszErrLineInitNoDriver;
break;

case LINEERR_NODEVICE:
errCode = ikszErrLineInitNoDevice;
break;

case LINEERR_INIFILECORRUPT:
errCode = ikszErrLineInitBadIniFile ;
break;

case LINEERR_NOMEM:
errCode = ikszErrOOM;
break;

case LINEERR_INCOMPATIBLEAPIVERSION:
errCode = ikszErrLineInitWrongDrivers ;
break;

case LINEERR_OPERATIONFAILED:
errCode = ikszErrTAPI;
break;

case LINEERR_INVALADDRESS:
errCode = ikszErrInvalAddress;
break;

case LINEERR_ADDRESSBLOCKED:
errCode = ikszErrAddrBlocked;
break;

case LINEERR_BILLINGREJECTED:
errCode = ikszErrBillingRejected;
break;

case LINEERR_RESOURCEUNAVAIL:
case LINEERR_ALLOCATED:
case LINEERR_INUSE:
errCode = ikszErrResUnavail;
break;

case LINEERR_NOMULTIPLEINSTANCE:
errCode = ikszErrNoMultipleInstance;
break;

case LINEERR_INVALCALLSTATE:
errCode = ikszErrInvalCallState;
break;

        case LINEERR_INVALCOUNTRYCODE:
            errCode = ikszErrInvalidCountryCode;
            break;

default:
            bDefault = TRUE;
            break;

}

    if (bDefault)
    {
        // if using default error, get TAPI's
        // error message from FormatError()
        if (!FormatMessage(FORMAT_MESSAGE_FROM_HMODULE,
                           (LPCVOID)GetModuleHandle(TEXT("TAPI32.DLL")),
                           (DWORD)TAPIERROR_FORMATMESSAGE(errCode),
                           0,
                           (LPTSTR)ptStrError,
                           MAXBUFSIZE,
                           NULL))
        {
            // if this fails, fall back on default
            LoadString( ghInst, ikszErrDefault, ptStrError, MAXBUFSIZE);
        }

    }
    else    // not the default error message
    {

        if ( 0 == LoadString( ghInst, errCode, ptStrError, MAXBUFSIZE ) )
        {
#if DBG
            TCHAR buf[200];
            wsprintf( buf, "Cannot load string: hinst:0x%08lx errcode: %ld", (DWORD)ghInst,(DWORD)errCode);
            OutputDebugString( buf );
            MessageBox( NULL, buf, "Dialer", MB_OK);
#endif

            LoadString( ghInst, ikszErrDefault, ptStrError, MAXBUFSIZE );
        }
    }

    LoadString( ghInst, ikszWarningTitle, ptStrTitle, MAXBUFSIZE );

nResult = MessageBox( hWndOwner, ptStrError, ptStrTitle, uFlags );

DialerFree( ptStrTitle );
DialerFree( ptStrError );

return nResult;
}

/*
 *Name :
 *FitTextToButton
 *
 *Arguements :
 *hDlg handle for the dialog in which this button is embedded
 *nButtonID button id of this button
 *szNameName to fit on the button. Max size TAPIMAXCALLEDPARTYSIZE
 *
 *Return :
 *None
 *
 *Comments :
 *Function first checks to see if the button text specified fits in the
 *button.  If it does not it truncates it appropriately and adds trailing
 *ellipses.
 */
VOID FitTextToButton ( HWND hDlg, INT nButtonID, LPSTR szName )
{

HDC hDC;
HFONT hFont, hOldFont;
HWND hWnd;

do
{
// calculate number of chars. that can fit on
// the button
intnLen;
RECT rect;
SIZE size;
POINT pt;
char buf [TAPIMAXCALLEDPARTYSIZE + 1];

// get button dimensions
hWnd = GetDlgItem( hDlg, nButtonID );
if ( hWnd == NULL )
break;

if ( !GetClientRect( hWnd, &rect ) )
break;

// get character dimensions
hDC = GetDC( hWnd );
if ( hDC == NULL )
break;

hFont = (HFONT) SendMessage( hWnd, WM_GETFONT, 0, 0 );
if ( hFont == NULL )
hOldFont = SelectObject( hDC, GetStockObject( SYSTEM_FONT ) );
else
hOldFont = SelectObject( hDC, hFont );

// add an extra char at the end to compensate for
// leading space,
lstrcpy ( buf, szName );
nLen = lstrlen( buf );
buf [ nLen ] = 'X';
buf [ nLen + 1 ] = '\0';

if ( !GetTextExtentPoint32( hDC, buf, nLen + 1, &size ) )
break;

pt.x = size.cx;
if ( !LPtoDP( hDC, &pt, 1 ) )
break;

// check if name fits on button
if (  pt.x > rect.right )
{
// find how much of the name fits
int i = 0;

nLen = lstrlen( szName );
for ( i = 0; i < nLen; i++ )
{
buf[ i ] = szName[ i ];
// an extra char is stuffed to compensate for the
// leading space left by the left alignment
buf [ i + 1 ] = 'X';
buf [ i + 2 ] = '\0';

// break out in cases of error condition
if ( !GetTextExtentPoint32( hDC, buf, i + 2, &size ) )
{
i = nLen;
break;
}

pt.x = size.cx;
if ( !LPtoDP( hDC, &pt, 1 ) )
{
i = nLen;
break;
}

if ( pt.x > rect.right )
break;
}

            // error
if ( i >= nLen )
break;

// name is too long. truncate and add ellipses
szName [i - 3] = '\0';
lstrcat( szName, "..." );
}

} while( FALSE );

if ( hDC )
{
SelectObject( hDC, hOldFont );
ReleaseDC( hWnd, hDC );
}

return;
}

/*
 *Name :
 *Is911
 *
 *Arguements :
 *lpTransOut -Translated address contained the dialable string
 *
 *Returns
 *TRUE -If number to be dialed (in the US) is prefixed by 911
 *FALSE -Otherwise
 *
 * Comments
 *
 */
BOOL Is911 ( LPLINETRANSLATEOUTPUT lpTransOut )
{

DWORD i = 0, j = 0;
LPSTR lpDialDigits = (LPSTR)lpTransOut + lpTransOut-> dwDialableStringOffset;
char sz3Pref [ 4 ] = "";

// if this is not the US
if ( lpTransOut-> dwCurrentCountry != 1 )
return FALSE;

// skip non digit characters and extract
// the first 3 digits in the dialable number
for ( i = 0, j = 0; i < lpTransOut-> dwDialableStringSize ; i++ )
{
if ( ISDIGIT( lpDialDigits[i] ) )
{
sz3Pref[ j++ ] = lpDialDigits [ i ];
sz3Pref[ j ] = '\0';
if ( j == 3 )
break;
}
}

if ( !lstrcmp( sz3Pref, "911" ) )
{
return TRUE;
}

return FALSE;
}

/*
 *Name :
 *MakeCanonicalNumber
 *
 *Arguements :
 *szNumber Number to convert into canonical form. Max size TAPIMAXDESTADDRESSSIZE
 *szCanNumberCanonical representation of number specified in szNumber
 *
 *Return :
 *TRUEIf the conversion was successful.
 *FALSEotherwise
 *
 *Comments :
 *Function first checks if given number is already in canonical form.
 *If it is, it returns.  If it is not, then it performs the conversion.
 */

BOOL MakeCanonicalNumber ( LPCSTR szNumber, LPSTR szCanNumber )
{
char szDigits [ TAPIMAXDESTADDRESSSIZE ];
char szPref [ TAPIMAXDESTADDRESSSIZE ];

BOOL bRes = FALSE;

INT errCode = -1;
INT nLenPref, nLenDigits, cPos, i;

DWORD dwSize = 0;
DWORD dwInd = 0;

LPLINETRANSLATEOUTPUT lpTransOut = NULL;
LPLINETRANSLATECAPS lpTransCaps = NULL;

dwSize = sizeof ( LINETRANSLATEOUTPUT );
do
{
lpTransOut = ( LPLINETRANSLATEOUTPUT ) DialerAlloc ( dwSize );
if ( !lpTransOut )
{
errString( ghWndMain, LINEERR_NOMEM, MB_ICONSTOP | MB_OK );
goto error;
}

lpTransOut-> dwTotalSize = dwSize;
errCode = lineTranslateAddress (
ghLineApp,
giCurrentLine,
gCurrentLineInfo.dwAPIVersion,
szNumber,
0,
0,
lpTransOut
   );
if ( errCode )
{
goto error;
}

if ( lpTransOut-> dwNeededSize <= lpTransOut-> dwTotalSize )
break;

dwSize = lpTransOut-> dwNeededSize;
DialerFree( lpTransOut );

} while( TRUE );

// check if input number is already in
// canonical form.
if ( lpTransOut-> dwTranslateResults & LINETRANSLATERESULT_CANONICAL )
goto error;

// ensure country is the USA.
if ( lpTransOut-> dwCurrentCountry != 1 )
goto error;

// Extract the digits from given string
// allowed formatting characters that are ignored are
// space, (, ), -, .
// presence of other characters will render the string invalid.

// find the prefix of the address upto the | mark.
// the rest of the string can be ignored
nLenPref = strcspn ( szNumber, "|" );
strncpy( szPref, szNumber, nLenPref );
szPref[ nLenPref ] = '\0';

// if string is not composed entirely of digits
// and allowable formating characters, quit conversion
if ( strspn( szPref, " 0123456789()-." ) != (size_t) nLenPref )
goto error;

// collect digits ignoring formating characters.
szDigits[ 0 ] = '\0';
for ( i = 0, nLenDigits = 0; i < nLenPref; i++ )
{
if ( ISDIGIT( szNumber[ i ] ) )
{
szDigits[ nLenDigits++ ] = szNumber[ i ];
}
}
szDigits[ nLenDigits ] = '\0';

// if "internal" number
if ( nLenDigits < LOCAL_NUMBER )
goto error;

switch ( nLenDigits )
{
// Local number ( 7 digits) preceeded by a 0/1
// Strip leading 0/1 and treat as a local number
case EXTENDED_LOCAL_NUMBER:
if ( szDigits[ 0 ] == '0' || szDigits[ 0 ] == '1' )
{
nLenDigits--;
memmove( szDigits, &(szDigits[1]), nLenDigits );
szDigits[ nLenDigits ] = '\0';

cPos = strcspn( szPref, "01" );
nLenPref--;
memmove( &(szPref[ cPos ]), &(szPref[ cPos + 1 ]), nLenPref - cPos );
szPref[ nLenPref ] = '\0';
}
else
{
goto error;
}

case LOCAL_NUMBER :
{
LPLINELOCATIONENTRY lpLocLst;

// if leading digit is 0 or 1, it is
// illegal in the US
if ( szDigits[ 0 ] == '0' || szDigits[ 0 ] == '1' )
{
goto error;
}

// get area code nformation for local number
dwSize = sizeof( LINETRANSLATECAPS );
do
{
lpTransCaps = (LPLINETRANSLATECAPS) DialerAlloc( dwSize );
if ( !lpTransCaps )
{
errString( ghWndMain, LINEERR_NOMEM, MB_ICONSTOP | MB_OK );
goto error;
}

lpTransCaps-> dwTotalSize = dwSize;
errCode = lineGetTranslateCaps (
ghLineApp,
gCurrentLineInfo.dwAPIVersion,
lpTransCaps
   );
if ( errCode )
{
errString( ghWndMain, errCode, MB_ICONSTOP | MB_OK );
goto error;
}

if ( lpTransCaps-> dwNeededSize <= lpTransCaps-> dwTotalSize )
{
break;
}

dwSize = lpTransCaps-> dwNeededSize;
DialerFree( lpTransCaps );

} while ( TRUE );

// skip entries till you locate information for current location
dwSize = sizeof( LINELOCATIONENTRY );
lpLocLst = (LPLINELOCATIONENTRY) ( (LPSTR) lpTransCaps +
lpTransCaps-> dwLocationListOffset );

for ( dwInd = 0; dwInd < lpTransCaps-> dwNumLocations ; dwInd++ )
{
if ( lpLocLst[ dwInd ].dwPermanentLocationID == lpTransCaps-> dwCurrentLocationID )
break;
}

// current location no found ?????
// login error
if ( dwInd == lpTransCaps-> dwNumLocations )
{
goto error;
}

// construct canonical form as
szCanNumber[ 0 ]= '\0';
lstrcat( szCanNumber, "+1 (" );
lstrcat( szCanNumber, (LPSTR) lpTransCaps + lpLocLst[ dwInd ].dwCityCodeOffset );
lstrcat( szCanNumber, ") " );
lstrcat( szCanNumber, szDigits );

cPos = strcspn( szNumber, "|" );
if ( cPos != lstrlen( szNumber ) )
{
lstrcat( szCanNumber, &(szNumber[ cPos ]) );
}

bRes = TRUE;
break;
}

case EXTENDED_LONG_DISTANCE_NUMBER:
{
// Long distance number ( 10 digits) preceeded by a 0/1
// Strip leading 0/1 and treat as a long distance number
if ( szDigits[ 0 ] == '0' || szDigits[ 0 ] == '1' )
{
nLenDigits--;
memmove( szDigits, &(szDigits[1]), nLenDigits );
szDigits[ nLenDigits ] = '\0';

cPos = strcspn( szPref, "01" );
nLenPref--;
memmove( &(szPref[ cPos ]), &(szPref[ cPos + 1 ]), nLenPref - cPos );
szPref[ nLenPref ] = '\0';
}
else
{
goto error;
}

}

case LONG_DISTANCE_NUMBER:
{
// if first or fourth digit is 0/1, illegal number
if ( szDigits[ 0 ] == '0' || szDigits[ 0 ] == '1' ||
 szDigits[ 3 ] == '0' || szDigits[ 3 ] == '1' )
{
goto error;
}

szCanNumber[ 0 ] = '\0';
lstrcat( szCanNumber, "+1 (" );
strncat( szCanNumber, szDigits, 3 );
lstrcat( szCanNumber, ") " );

lstrcat( szCanNumber, &(szDigits[ 3 ]) );

bRes = TRUE;
}
break;

default :
goto error;
}

error:
if ( lpTransOut )
DialerFree( lpTransOut );

if ( lpTransCaps )
DialerFree( lpTransCaps );

return bRes;
}

/*
 *Name :
 *AmpersandCompensate
 *
 *Arguements :
 *lpszSrc:Src string containing &s

*lpszDst:Dest string
 *
 *Return :
 *
 *Comments :
 *Copies string pointed to by lpszSrc to lpszDst character by
 *character.  If an & is encountered in this process in lpszSrc
 *it is copied as && into lpszDst.
 *Assumes lpszDst and lpszSrc are of size TAPIMAXCALLEDPARTYSIZE
 */
VOID AmpersandCompensate ( LPCSTR lpszSrc, LPSTR lpszDst )
{
// check if the name has an & in it.  If so replace
// it with &&.
INT cCnt, cInd;

for ( cCnt = 0, cInd = 0;
  cInd < TAPIMAXCALLEDPARTYSIZE;
  cInd++, cCnt++ )
{
if ( lpszSrc[ cCnt ] == '&' )
{
lpszDst[ cInd++ ] = '&';
}
lpszDst[ cInd ] = lpszSrc[ cCnt ];

if ( lpszSrc[ cCnt ] == '\0' )
break;
}

// make sure string is null terminated.
lpszDst[ TAPIMAXCALLEDPARTYSIZE - 1 ] = '\0';

return;
}

 /*
 *Name :
 *AmpersandDeCompensate
 *
 *Arguements :
 *lpszSrc:Src string containing &s
 *lpszDst:Dest string
 *
 *Return :
 *
 *Comments :
 *Copies string pointed to by lpszSrc to lpszDst character by
 *character.  If an && is encountered in this process in lpszSrc
 *it is copied as & into lpszDst.
 *Assumes lpszDst and lpszSrc are of size TAPIMAXCALLEDPARTYSIZE
 */
 VOID AmpersandDeCompensate ( LPCSTR lpszSrc, LPSTR lpszDst )
 {
// check if the name has an & in it.  If so replace
// it with &&.
INT cCnt, cInd;

for ( cCnt = 0, cInd = 0;
  cInd < TAPIMAXCALLEDPARTYSIZE;
  cInd++, cCnt++ )
{
if  ( ( lpszSrc[ cInd ] == '&' ) &&
  ( lpszSrc[ cInd + 1 ] == '&' ) )
{
cInd++;
}
lpszDst[ cCnt ] = lpszSrc[ cInd ] ;

if ( lpszSrc [ cInd ] == '\0' )
{
break;
}
}

lpszDst[ TAPIMAXCALLEDPARTYSIZE - 1 ] = '\0';

return;
 }


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by dialer.RC
//
#define IDI_DIALER                      1
#define IDD_DDIAL                       1
#define IDI_LINEBUSY                    116
#define IDA_DIALER                      117
#define IDM_MENU                        118
#define IDD_DIALER                      200
#define IDD_DCOMBO                      201
#define IDD_DBUTTON1                    202
#define IDD_DBUTTON2                    203
#define IDD_DBUTTON3                    204
#define IDD_DBUTTON4                    205
#define IDD_DBUTTON5                    206
#define IDD_DBUTTON6                    207
#define IDD_DBUTTON7                    208
#define IDD_DBUTTON8                    209
#define IDD_DBUTTON9                    210
#define IDD_DBUTTON0                    211
#define IDD_DBUTTONSTAR                 212
#define IDD_DBUTTONPOUND                213
#define IDD_DSPEEDDIAL1                 214
#define IDD_DSPEEDDIAL2                 215
#define IDD_DSPEEDDIAL3                 216
#define IDD_DSPEEDDIAL4                 217
#define IDD_DSPEEDDIAL5                 218
#define IDD_DSPEEDDIAL6                 219
#define IDD_DSPEEDDIAL7                 220
#define IDD_DSPEEDDIAL8                 221
#define IDD_DSPEEDDIALGRP               222
#define IDD_DNUMTODIAL                  223
#define IDD_DRECTSEPARATOR              224
#define IDD_DSPEEDDIALTEXT1             225
#define IDD_DSPEEDDIALTEXT2             226
#define IDD_DSPEEDDIALTEXT3             227
#define IDD_DSPEEDDIALTEXT4             228
#define IDD_DSPEEDDIALTEXT5             229
#define IDD_DSPEEDDIALTEXT6             230
#define IDD_DSPEEDDIALTEXT7             231
#define IDD_DSPEEDDIALTEXT8             232
#define IDD_SD1                         300
#define IDD_SD1SPEEDDIAL1               301
#define IDD_SD1SPEEDDIAL2               302
#define IDD_SD1SPEEDDIAL3               303
#define IDD_SD1SPEEDDIAL4               304
#define IDD_SD1SPEEDDIAL5               305
#define IDD_SD1SPEEDDIAL6               306
#define IDD_SD1SPEEDDIAL7               307
#define IDD_SD1SPEEDDIAL8               308
#define IDD_SD1EDITNAME                 317
#define IDD_SD1EDITNUMBER               318
#define IDD_SD1TEXTCHOOSE               319
#define IDD_SD1TEXTENTER                320
#define IDD_SD1TEXTNAME                 321
#define IDD_SD1TEXTNUMBER               322
#define IDD_SD1RECTSEPARATOR            323
#define IDD_SD1SPEEDDIALTEXT1           324
#define IDD_SD1SPEEDDIALTEXT2           325
#define IDD_SD1SPEEDDIALTEXT3           326
#define IDD_SD1SPEEDDIALTEXT4           327
#define IDD_SD1SPEEDDIALTEXT5           328
#define IDD_SD1SPEEDDIALTEXT6           329
#define IDD_SD1SPEEDDIALTEXT7           330
#define IDD_SD1SPEEDDIALTEXT8           331
#define IDD_SD2                         400
#define IDD_SD2SAVEANDDIAL              401
#define IDD_SD2EDITNAME                 402
#define IDD_SD2EDITNUMBER               403
#define IDD_SD2TEXTENTER                404
#define IDD_SD2TEXTNAME                 405
#define IDD_SD2TEXTNUMBER               406
#define IDD_CONNECTUSING                500
#define IDD_CULISTLINE                  501
#define IDD_CULISTADDRESS               502
#define IDD_CUSIMPLETAPICHKBOX          503
#define IDD_CUTEXTLINE                  504
#define IDD_CUTEXTADDRESS               505
#define IDD_CUPROPERTIES                506
#define IDD_ABOUT                       700
#define IDD_AICON                       701
#define IDD_ATEXTTITLE                  702
#define IDD_ATEXTVERSION                703
#define IDD_ATEXTWINMODE                704
#define IDD_ATEXTFREEMEM                705
#define IDD_ATEXTRESOURCE               706
#define IDD_ATEXTCOPYRIGHT              707
#define IDD_INUSE                       900
#define IDD_IUICON                      901
#define ikszAppName                     901
#define IDD_IUTEXT1                     902
#define ikszAppFriendlyName             902
#define IDD_IUTEXT2                     903
#define ikszErrDefault                  940
#define ikszErrOOM                      941
#define ikszErrTAPI                     942
#define ikszErrNoVoiceLine              943
#define ikszErrInvalAddress             945
#define ikszErrLineInitWrongDrivers     949
#define ikszErrAddrBlocked              950
#define ikszErrBillingRejected          952
#define ikszErrResUnavail               956
#define ikszWarningTitle                977
#define ikszWarningRegisterSTapi        978
#define ikszWarningFor911               978
#define ikszWarningNewDefault           979
#define IDM_EXIT                        1000
#define IDM_EDIT_CUT                    1001
#define IDM_EDIT_COPY                   1002
#define IDD_DGNUMBERTEXT                1002
#define IDM_EDIT_PASTE                  1003
#define IDM_EDIT_DELETE                 1004
#define IDD_DGNAMETEXT                  1004
#define IDM_EDIT_SPEEDDIAL              1005
#define IDM_CONNECTUSING                1006
#define IDM_LOCATION                    1008
#define IDM_HELP_CONTENTS               1010
#define IDM_ABOUT                       1011
#define IDM_ACCEL_NUMTODIAL             1012
#define ikszErrLineInitBadIniFile       1013
#define IDM_ACCEL_HELP                  1013
#define ikszErrLineInitNoDriver         1014
#define ikszErrBadTAPIAddr              1015
#define IDM_HELP_WHATSTHIS              1015
#define ikszErrLineInitNoDevice         1016
#define ikszErrNoMultipleInstance       1020
#define IDD_CALLFAILED                  1300
#define IDD_CFTEXT                      1301
#define ikszWarning911                  1544
#define ikszDisconnectedReject          1545
#define ikszDisconnectedBusy            1546
#define ikszDisconnectedNoAnswer        1547
#define ikszDisconnectedCantDo          1548
#define ikszDisconnectedNetwork         1549
#define ikszDisconnectedIncompatible    1550
#define ikszErrInvalCallState           1551
#define ikszWarningTapiReInit           1552
#define ikszErrLineClose                1553
#define ikszDisconnectedNoDialTone      1554
#define ikszErrInvalidCountryCode       1555
#define IDD_DIALING                     9115
#define IDD_CUERRORTEXT                 -1
#define IDD_DGNUMBER                    -1

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        9116
#define _APS_NEXT_COMMAND_VALUE         40001
#define _APS_NEXT_CONTROL_VALUE         1005
#define _APS_NEXT_SYMED_VALUE           9101
#endif
#endif


README.TXT

Telephony API (TAPI) Sample

The TapiComm sample uses both the Telephony API and the Win32 Communications
API to demonstrate one way to implement a very simple TTY application. While
the user interface and TTY emulation are very rudimentary, the TAPI and comm
modules are fairly complete.

For more information, see the tapicomm.doc or tapicomm.rtf file included in
the TapiComm sample directory.


COMMCODE.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.
//
//  MODULE: CommCode.c
//
//  PURPOSE: Handles all the COMM routines for TapiComm.
//
//  EXPORTED FUNCTIONS:  These functions are for use by other modules.
//    StartComm        - Start communications.
//    StopComm         - Stop Communications.
//    WriteCommString  - Write a string to the Comm port.
//
//  INTERNAL FUNCTION:  These functions are for this module only.
//    CloseReadThread  - Close the Read Thread.
//    CloseWriteThread - Close the Write Thread.
//
//    StartReadThreadProc    - Starting function for the Read Thread.
//    StartWriteThreadProc   - Starting function for the Write Thread.
//
//    - Write Thread helper function
//    HandleWriteData - Actually does the work of writing a string to comm.
//
//    - Read Thread helper functions
//    SetupReadEvent  - Sets up the overlapped ReadFile
//    HandleReadEvent - Gets the results from the overlapped ReadFile
//    HandleReadData  - Handles data returned from the ReadFile
//
//    HandleCommEvent - Sets up the CommEvent event.
//    SetupCommEvent  - Handles CommEvent events (if they occur).
//

#include <windows.h>
#include <string.h>
#include "TapiCode.h"
#include "CommCode.h"
#include "globals.h"
#include "TapiInfo.h"
#include "EditCtls.h"

// This is the message posted to the WriteThread
// When we have something to write.
#define PWM_COMMWRITE   WM_USER+1

// Default size of the Input Buffer used by this code.
#define INPUTBUFFERSIZE 2048

//*****************************************
// Global variables.
//*****************************************

HANDLE g_hCommFile = NULL;

DWORD g_dwReadThreadID  = 0;
DWORD g_dwWriteThreadID = 0;
HANDLE g_hReadThread  = NULL;
HANDLE g_hWriteThread = NULL;

HANDLE g_hCloseEvent = NULL;

//*****************************************
// CommCode internal Function Prototypes
//*****************************************

void CloseReadThread();
void CloseWriteThread();

DWORD WINAPI StartReadThreadProc(LPVOID lpvParam);
DWORD WINAPI StartWriteThreadProc(LPVOID lpvParam);

BOOL HandleWriteData(LPOVERLAPPED lpOverlappedWrite,
        LPCSTR lpszStringToWrite, DWORD dwNumberOfBytesToWrite);

BOOL SetupReadEvent(LPOVERLAPPED lpOverlappedRead,
        LPSTR lpszInputBuffer, DWORD dwSizeofBuffer,
        LPDWORD lpnNumberOfBytesRead);
BOOL HandleReadEvent(LPOVERLAPPED lpOverlappedRead,
        LPSTR lpszInputBuffer, DWORD dwSizeofBuffer,
        LPDWORD lpnNumberOfBytesRead);
BOOL HandleReadData(LPCSTR lpszInputBuffer, DWORD dwSizeofBuffer);

BOOL HandleCommEvent(LPOVERLAPPED lpOverlappedCommEvent,
        LPDWORD lpfdwEvtMask, BOOL fRetrieveEvent);
BOOL SetupCommEvent(LPOVERLAPPED lpOverlappedCommEvent,
        LPDWORD lpfdwEvtMask);

//*****************************************
// Functions exported for use by other modules
//*****************************************

//
//  FUNCTION: StartComm(HANDLE)
//
//  PURPOSE: Starts communications over the comm port.
//
//  PARAMETERS:
//    hNewCommFile - This is the COMM File handle to communicate with.
//                   This handle is obtained from TAPI.
//
//  RETURN VALUE:
//    TRUE if able to setup the communications.
//
//  COMMENTS:
//
//    StartComm makes sure there isn't communication in progress already,
//    the hNewCommFile is valid, and all the threads can be created.  It
//    also configures the hNewCommFile for the appropriate COMM settings.
//
//    If StartComm fails for any reason, it's up to the calling application
//    to close the Comm file handle.
//
//

BOOL StartComm(HANDLE hNewCommFile)
{
    // Is this a valid comm handle?
    if (GetFileType(hNewCommFile) != FILE_TYPE_CHAR)
    {
        OutputDebugString("File handle is not a comm handle.\n");
        return FALSE;
    }

    // Are we already doing comm?
    if (g_hCommFile != NULL)
    {
        OutputDebugString("Already have a comm file open\n");
        return FALSE;
    }

    // Its ok to continue.

    g_hCommFile = hNewCommFile;

    // Setting and querying the comm port configurations.

    { // Configure the comm settings.
        COMMTIMEOUTS commtimeouts;
        DCB dcb;
        COMMPROP commprop;
        DWORD fdwEvtMask;

        // These are here just so you can set a breakpoint
        // and see what the comm settings are.  Most Comm settings
        // are already set through TAPI.
        GetCommState(hNewCommFile, &dcb);
        GetCommProperties(hNewCommFile, &commprop);
        GetCommMask(g_hCommFile, &fdwEvtMask);
        GetCommTimeouts(g_hCommFile, &commtimeouts);

        // The CommTimeout numbers will very likely change if you are
        // coding to meet some kind of specification where
        // you need to reply within a certain amount of time after
        // recieving the last byte.  However,  If 1/4th of a second
        // goes by between recieving two characters, its a good
        // indication that the transmitting end has finished, even
        // assuming a 1200 baud modem.

        commtimeouts.ReadIntervalTimeout         = 250;
        commtimeouts.ReadTotalTimeoutMultiplier  = 0;
        commtimeouts.ReadTotalTimeoutConstant    = 0;
        commtimeouts.WriteTotalTimeoutMultiplier = 0;
        commtimeouts.WriteTotalTimeoutConstant   = 0;

        SetCommTimeouts(g_hCommFile, &commtimeouts);

        // fAbortOnError is the only DCB dependancy in TapiComm.
        // Can't guarentee that the SP will set this to what we expect.
        dcb.fAbortOnError = FALSE;
        SetCommState(hNewCommFile, &dcb);
    }

    // Create the event that will signal the threads to close.
    g_hCloseEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

    if (g_hCloseEvent == NULL)
    {
        OutputDebugLastError(GetLastError(), "Unable to CreateEvent: ");
        g_hCommFile = NULL;
        return FALSE;
    }

    // Create the Read thread.
    g_hReadThread =
        CreateThread(NULL, 0, StartReadThreadProc, 0, 0, &g_dwReadThreadID);

    if (g_hReadThread == NULL)
    {
        OutputDebugLastError(GetLastError(),"Unable to create Read thread");

        g_dwReadThreadID = 0;
        g_hCommFile = 0;
        return FALSE;
    }

    // Comm threads should to have a higher base priority than the UI thread.
    // If they don't, then any temporary priority boost the UI thread gains
    // could cause the COMM threads to loose data.
    SetThreadPriority(g_hReadThread, THREAD_PRIORITY_HIGHEST);

    // Create the Write thread.
    g_hWriteThread =
        CreateThread(NULL, 0, StartWriteThreadProc, 0, 0, &g_dwWriteThreadID);

    if (g_hWriteThread == NULL)
    {
        OutputDebugLastError(GetLastError(),"Unable to create Write thread");

        CloseReadThread();
        g_dwWriteThreadID = 0;
        g_hCommFile = 0;
        return FALSE;
    }

    SetThreadPriority(g_hWriteThread, THREAD_PRIORITY_ABOVE_NORMAL);

    // Everything was created ok.  Ready to go!
    return TRUE;
}

//
//  FUNCTION: StopComm
//
//  PURPOSE: Stop and end all communication threads.
//
//  PARAMETERS:
//    none
//
//  RETURN VALUE:
//    none
//
//  COMMENTS:
//
//    Tries to gracefully signal all communication threads to
//    close, but terminates them if it has to.
//
//

void StopComm()
{
    // No need to continue if we're not communicating.
    if (g_hCommFile == NULL)
        return;

    OutputDebugString("Stopping the Comm\n");

    // Close the threads.
    CloseReadThread();
    CloseWriteThread();

    // Not needed anymore.
    CloseHandle(g_hCloseEvent);

    // Now close the comm port handle.
    CloseHandle(g_hCommFile);
    g_hCommFile = NULL;
}

//
//  FUNCTION: WriteCommString(LPCSTR, DWORD)
//
//  PURPOSE: Send a String to the Write Thread to be written to the Comm.
//
//  PARAMETERS:
//    pszStringToWrite     - String to Write to Comm port.
//    nSizeofStringToWrite - length of pszStringToWrite.
//
//  RETURN VALUE:
//    Returns TRUE if the PostMessage is successful.
//    Returns FALSE if PostMessage fails or Write thread doesn't exist.
//
//  COMMENTS:
//
//    This is a wrapper function so that other modules don't care that
//    Comm writing is done via PostMessage to a Write thread.  Note that
//    using PostMessage speeds up response to the UI (very little delay to
//    'write' a string) and provides a natural buffer if the comm is slow
//    (ie:  the messages just pile up in the message queue).
//
//    Note that it is assumed that pszStringToWrite is allocated with
//    LocalAlloc, and that if WriteCommString succeeds, its the job of the
//    Write thread to LocalFree it.  If WriteCommString fails, then its
//    the job of the calling function to free the string.
//
//

BOOL WriteCommString(LPCSTR lpszStringToWrite, DWORD dwSizeofStringToWrite)
{
    if (g_hWriteThread)
    {
        if (PostThreadMessage(g_dwWriteThreadID, PWM_COMMWRITE,
                (WPARAM) dwSizeofStringToWrite, (LPARAM) lpszStringToWrite))
        {
            return TRUE;
        }
        else
            OutputDebugString("Failed to Post to Write thread.\n");
    }
    else
        OutputDebugString("Write thread not created\n");

    return FALSE;
}

//*****************************************
// The rest of the functions are intended for use
// only within the CommCode module.
//*****************************************

//
//  FUNCTION: CloseReadThread
//
//  PURPOSE: Close the Read Thread.
//
//  PARAMETERS:
//    none
//
//  RETURN VALUE:
//    none
//
//  COMMENTS:
//
//    Closes the Read thread by signaling the CloseEvent.
//    Purges any outstanding reads on the comm port.
//
//    Note that terminating a thread leaks memory (read the docs).
//    Besides the normal leak incurred, there is an event object
//    that doesn't get closed.  This isn't worth worrying about
//    since it shouldn't happen anyway.
//
//

void CloseReadThread()
{
    // If it exists...
    if (g_hReadThread)
    {
        OutputDebugString("Closing Read Thread\n");

        // Signal the event to close the worker threads.
        SetEvent(g_hCloseEvent);

        // Purge all outstanding reads
        PurgeComm(g_hCommFile, PURGE_RXABORT | PURGE_RXCLEAR);

        // Wait 10 seconds for it to exit.  Shouldn't happen.
        if (WaitForSingleObject(g_hReadThread, 10000) == WAIT_TIMEOUT)
        {
            OutputDebugString("Read thread not exiting.  Terminating it.\n");

            TerminateThread(g_hReadThread, 0);

            // The ReadThread cleans up these itself if it terminates
            // normally.
            CloseHandle(g_hReadThread);
            g_hReadThread = 0;
            g_dwReadThreadID = 0;
        }
    }
}

//
//  FUNCTION: CloseWriteThread
//
//  PURPOSE: Closes the Write Thread.
//
//  PARAMETERS:
//    none
//
//  RETURN VALUE:
//    none
//
//  COMMENTS:
//
//    Closes the write thread by signaling the CloseEvent.
//    Purges any outstanding writes on the comm port.
//
//    Note that terminating a thread leaks memory (read the docs).
//    Besides the normal leak incurred, there is an event object
//    that doesn't get closed.  This isn't worth worrying about
//    since it shouldn't happen anyway.
//
//

void CloseWriteThread()
{
    // If it exists...
    if (g_hWriteThread)
    {
        OutputDebugString("Closing Write Thread\n");

        // Signal the event to close the worker threads.
        SetEvent(g_hCloseEvent);

        // Purge all outstanding writes.
        PurgeComm(g_hCommFile, PURGE_TXABORT | PURGE_TXCLEAR);

        // Wait 10 seconds for it to exit.  Shouldn't happen.
        if (WaitForSingleObject(g_hWriteThread, 10000) == WAIT_TIMEOUT)
        {
            OutputDebugString("Write thread not exiting.  Terminating it.\n");

            TerminateThread(g_hWriteThread, 0);

            // The WriteThread cleans up these itself if it terminates
            // normally.
            CloseHandle(g_hWriteThread);
            g_hWriteThread = 0;
            g_dwWriteThreadID = 0;

        }
    }
}

//
//  FUNCTION: StartWriteThreadProc(LPVOID)
//
//  PURPOSE: The starting point for the Write thread.
//
//  PARAMETERS:
//    lpvParam - unused.
//
//  RETURN VALUE:
//    DWORD - unused.
//
//  COMMENTS:
//
//    The Write thread uses a PeekMessage loop to wait for a string to write,
//    and when it gets one, it writes it to the Comm port.  If the CloseEvent
//    object is signaled, then it exits.  The use of messages to tell the
//    Write thread what to write provides a natural desynchronization between
//    the UI and the Write thread.
//
//

DWORD WINAPI StartWriteThreadProc(LPVOID lpvParam)
{
    MSG msg;
    DWORD dwHandleSignaled;

    // Needed for overlapped I/O.
    OVERLAPPED overlappedWrite = {0, 0, 0, 0, NULL};

    overlappedWrite.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
    if (overlappedWrite.hEvent == NULL)
    {
        OutputDebugLastError(GetLastError(), "Unable to CreateEvent: ");
        PostHangupCall();
        goto EndWriteThread;
    }

    // This is the main loop.  Loop until we break out.
    while (TRUE)
    {
        if (!PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            // If there are no messages pending, wait for a message or
            // the CloseEvent.
            dwHandleSignaled =
                MsgWaitForMultipleObjects(1, &g_hCloseEvent, FALSE,
                    INFINITE, QS_ALLINPUT);

            switch(dwHandleSignaled)
            {
                case WAIT_OBJECT_0:     // CloseEvent signaled!
                {
                    // Time to exit.
                    goto EndWriteThread;
                }

                case WAIT_OBJECT_0 + 1: // New message was received.
                {
                    // Get the message that woke us up by looping again.
                    continue;
                }

                case WAIT_FAILED:       // Wait failed.  Shouldn't happen.
                {
                    OutputDebugLastError(GetLastError(),"Write WAIT_FAILED: ");
                    PostHangupCall();
                    goto EndWriteThread;
                }

                default:                // This case should never occur.
                {
                    OutputDebugPrintf("Unexpected Wait return value '%lx'",
                        dwHandleSignaled);
                    PostHangupCall();
                    goto EndWriteThread;
                }

            }
        }

        // Make sure the CloseEvent isn't signaled while retrieving messages.
        if (WAIT_TIMEOUT != WaitForSingleObject(g_hCloseEvent,0))
            goto EndWriteThread;

        // Process the message.

        // This could happen if a dialog is created on this thread.
        // This doesn't occur in this sample, but might if modified.
        if (msg.hwnd != NULL)
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);

            continue;
        }

        // Handle the message.
        switch(msg.message)
        {
            case PWM_COMMWRITE:  // New string to write to Comm port.
            {
                OutputDebugString("Writing to comm port\n");

                // Write the string to the comm port.  HandleWriteData
                // does not return until the whole string has been written,
                // an error occurs or until the CloseEvent is signaled.
                if (!HandleWriteData(&overlappedWrite,
                        (LPSTR) msg.lParam, (DWORD) msg.wParam))
                {
                    // If it failed, either we got a signal to end or there
                    // really was a failure.

                    LocalFree((HLOCAL) msg.lParam);
                    goto EndWriteThread;
                }

                // Data was sent in a LocalAlloc()d buffer.  Must free it.
                LocalFree((HLOCAL) msg.lParam);
                break;
            }

            // What other messages could the thread get?
            default:
            {
                char Output[256];

                wsprintf(Output,
                    "Unexpected message posted to Write thread: %ui\n",
                    msg.message );

                OutputDebugString(Output);
                break;
            }
        } // End of switch(message)

    } // End of main loop.

    // Thats the end.  Now clean up.
  EndWriteThread:

    OutputDebugString("Write thread shutting down\n");

    PurgeComm(g_hCommFile, PURGE_TXABORT | PURGE_TXCLEAR);

    CloseHandle(overlappedWrite.hEvent);

    g_dwWriteThreadID = 0;
    CloseHandle(g_hWriteThread);
    g_hWriteThread = 0;

    return 0;
}

//
//  FUNCTION: HandleWriteData(LPOVERLAPPED, LPCSTR, DWORD)
//
//  PURPOSE: Writes a given string to the comm file handle.
//
//  PARAMETERS:
//    lpOverlappedWrite      - Overlapped structure to use in WriteFile
//    lpszStringToWrite      - String to write.
//    dwNumberOfBytesToWrite - Length of String to write.
//
//  RETURN VALUE:
//    TRUE if all bytes were written.  False if there was a failure to
//    write the whole string.
//
//  COMMENTS:
//
//    This function is a helper function for the Write Thread.  It
//    is this call that actually writes a string to the comm file.
//    Note that this call blocks and waits for the Write to complete
//    or for the CloseEvent object to signal that the thread should end.
//    Another possible reason for returning FALSE is if the comm port
//    is closed by the service provider.
//
//

BOOL HandleWriteData(LPOVERLAPPED lpOverlappedWrite,
    LPCSTR lpszStringToWrite, DWORD dwNumberOfBytesToWrite)
{
    DWORD dwLastError;

    DWORD dwNumberOfBytesWritten = 0;
    DWORD dwWhereToStartWriting = 0; // Start at the beginning.

    DWORD dwHandleSignaled;
    HANDLE HandlesToWaitFor[2];

    HandlesToWaitFor[0] = g_hCloseEvent;
    HandlesToWaitFor[1] = lpOverlappedWrite -> hEvent;

    // Keep looping until all characters have been written.
    do
    {
        // Start the overlapped I/O.
        if (!WriteFile(g_hCommFile,
                &lpszStringToWrite[ dwWhereToStartWriting ],
                dwNumberOfBytesToWrite, &dwNumberOfBytesWritten,
                lpOverlappedWrite))
        {
            // WriteFile failed.  Expected; lets handle it.
            dwLastError = GetLastError();

            // Its possible for this error to occur if the
            // service provider has closed the port.  Time to end.
            if (dwLastError == ERROR_INVALID_HANDLE)
            {
                OutputDebugString("ERROR_INVALID_HANDLE, "
                    "Likely that the Service Provider has closed the port.\n");
                return FALSE;
            }

            // Unexpected error.  No idea what.
            if (dwLastError != ERROR_IO_PENDING)
            {
                OutputDebugLastError(dwLastError,
                    "Error to writing to CommFile");

                OutputDebugString("Closing TAPI\n");
                PostHangupCall();
                return FALSE;
            }

            // This is the expected ERROR_IO_PENDING case.

            // Wait for either overlapped I/O completion,
            // or for the CloseEvent to get signaled.
            dwHandleSignaled =
                WaitForMultipleObjects(2, HandlesToWaitFor,
                    FALSE, INFINITE);

            switch(dwHandleSignaled)
            {
                case WAIT_OBJECT_0:     // CloseEvent signaled!
                {
                    // Time to exit.
                    return FALSE;
                }

                case WAIT_OBJECT_0 + 1: // Wait finished.
                {
                    // Time to get the results of the WriteFile
                    break;
                }

                case WAIT_FAILED: // Wait failed.  Shouldn't happen.
                {
                    OutputDebugLastError(GetLastError(),
                        "Write WAIT_FAILED: ");
                    PostHangupCall();
                    return FALSE;
                }

                default: // This case should never occur.
                {
                    OutputDebugPrintf(
                        "Unexpected Wait return value '%lx'",
                        dwHandleSignaled);
                    PostHangupCall();
                    return FALSE;
                }
            }

            if (!GetOverlappedResult(g_hCommFile,
                     lpOverlappedWrite,
                     &dwNumberOfBytesWritten, TRUE))
            {
                dwLastError = GetLastError();

                // Its possible for this error to occur if the
                // service provider has closed the port.
                if (dwLastError == ERROR_INVALID_HANDLE)
                {
                    OutputDebugString("ERROR_INVALID_HANDLE, "
                        "Likely that the Service Provider has closed the port.\n");
                    return FALSE;
                }

                // No idea what could cause another error.
                OutputDebugLastError(dwLastError,
                    "Error writing to CommFile while waiting");
                OutputDebugString("Closing TAPI\n");
                PostHangupCall();
                return FALSE;
            }
        }

        // Some data was written.  Make sure it all got written.

        dwNumberOfBytesToWrite -= dwNumberOfBytesWritten;
        dwWhereToStartWriting += dwNumberOfBytesWritten;
    }
    while(dwNumberOfBytesToWrite > 0);  // Write the whole thing!

    // Wrote the whole string.
    return TRUE;
}

//
//  FUNCTION: StartReadThreadProc(LPVOID)
//
//  PURPOSE: This is the starting point for the Read Thread.
//
//  PARAMETERS:
//    lpvParam - unused.
//
//  RETURN VALUE:
//    DWORD - unused.
//
//  COMMENTS:
//
//    The Read Thread uses overlapped ReadFile and sends any strings
//    read from the comm port to the UI to be printed.  This is
//    eventually done through a PostMessage so that the Read Thread
//    is never away from the comm port very long.  This also provides
//    natural desynchronization between the Read thread and the UI.
//
//    If the CloseEvent object is signaled, the Read Thread exits.
//
//    Note that there is absolutely *no* interpretation of the data,
//    which means no terminal emulation.  It basically means that this
//    sample is pretty useless as a TTY program.
//
//  Separating the Read and Write threads is natural for a application
//    like this sample where there is no need for synchronization between
//    reading and writing.  However, if there is such a need (for example,
//    most file transfer algorithms synchronize the reading and writing),
//    then it would make a lot more sense to have a single thread to handle
//    both reading and writing.
//
//

DWORD WINAPI StartReadThreadProc(LPVOID lpvParam)
{
    char szInputBuffer[INPUTBUFFERSIZE];
    DWORD nNumberOfBytesRead;

    HANDLE HandlesToWaitFor[3];
    DWORD dwHandleSignaled;

    DWORD fdwEvtMask;

    // Needed for overlapped I/O (ReadFile)
    OVERLAPPED overlappedRead  = {0, 0, 0, 0, NULL};

    // Needed for overlapped Comm Event handling.
    OVERLAPPED overlappedCommEvent = {0, 0, 0, 0, NULL};

    // Lets put an event in the Read overlapped structure.
    overlappedRead.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
    if (overlappedRead.hEvent == NULL)
    {
        OutputDebugLastError(GetLastError(), "Unable to CreateEvent: ");
        PostHangupCall();
        goto EndReadThread;
    }

    // And an event for the CommEvent overlapped structure.
    overlappedCommEvent.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
    if (overlappedCommEvent.hEvent == NULL)
    {
        OutputDebugLastError(GetLastError(), "Unable to CreateEvent: ");
        PostHangupCall();
        goto EndReadThread;
    }

    // We will be waiting on these objects.
    HandlesToWaitFor[0] = g_hCloseEvent;
    HandlesToWaitFor[1] = overlappedCommEvent.hEvent;
    HandlesToWaitFor[2] = overlappedRead.hEvent;

    // Setup CommEvent handling.

    // Set the comm mask so we receive error signals.
    if (!SetCommMask(g_hCommFile, EV_ERR))
    {
        OutputDebugLastError(GetLastError(),"Unable to SetCommMask: ");
        PostHangupCall();
        goto EndReadThread;
    }

    // Start waiting for CommEvents (Errors)
    if (!SetupCommEvent(&overlappedCommEvent, &fdwEvtMask))
    {
        PostHangupCall();
        goto EndReadThread;
    }

    // Start waiting for Read events.
    if (!SetupReadEvent(&overlappedRead,
                szInputBuffer, INPUTBUFFERSIZE,
                &nNumberOfBytesRead))
    {
        PostHangupCall();
        goto EndReadThread;
    }

    // Keep looping until we break out.
    while (TRUE)
    {
        // Wait until some event occurs (data to read; error; stopping).
        dwHandleSignaled =
            WaitForMultipleObjects(3, HandlesToWaitFor,
                FALSE, INFINITE);

        // Which event occured?
        switch(dwHandleSignaled)
        {
            case WAIT_OBJECT_0:     // Signal to end the thread.
            {
                // Time to exit.
                goto EndReadThread;
            }

            case WAIT_OBJECT_0 + 1: // CommEvent signaled.
            {
                // Handle the CommEvent.
                if (!HandleCommEvent(&overlappedCommEvent, &fdwEvtMask, TRUE))
                {
                    PostHangupCall();
                    goto EndReadThread;
                }

                // Start waiting for the next CommEvent.
                if (!SetupCommEvent(&overlappedCommEvent, &fdwEvtMask))
                {
                    PostHangupCall();
                    goto EndReadThread;
                }
                break;
            }

            case WAIT_OBJECT_0 + 2: // Read Event signaled.
            {
                // Get the new data!
                if (!HandleReadEvent(&overlappedRead,
                            szInputBuffer, INPUTBUFFERSIZE,
                            &nNumberOfBytesRead))
                {
                    PostHangupCall();
                    goto EndReadThread;
                }

                // Wait for more new data.
                if (!SetupReadEvent(&overlappedRead,
                            szInputBuffer, INPUTBUFFERSIZE,
                            &nNumberOfBytesRead))
                {
                    PostHangupCall();
                    goto EndReadThread;
                }
                break;
            }

            case WAIT_FAILED:       // Wait failed.  Shouldn't happen.
            {
                OutputDebugLastError(GetLastError(),"Read WAIT_FAILED: ");
                PostHangupCall();
                goto EndReadThread;
            }

            default:    // This case should never occur.
            {
                OutputDebugPrintf("Unexpected Wait return value '%lx'",
                    dwHandleSignaled);
                PostHangupCall();
                goto EndReadThread;
            }
        } // End of switch(dwHandleSignaled).

    } // End of while(TRUE) loop.

    // Time to clean up Read Thread.
  EndReadThread:

    OutputDebugString("Read thread shutting down\n");
    PurgeComm(g_hCommFile, PURGE_RXABORT | PURGE_RXCLEAR);
    CloseHandle(overlappedRead.hEvent);
    CloseHandle(overlappedCommEvent.hEvent);
    g_dwReadThreadID = 0;
    CloseHandle(g_hReadThread);
    g_hReadThread = 0;
    return 0;
}

//
//  FUNCTION: SetupReadEvent(LPOVERLAPPED, LPSTR, DWORD, LPDWORD)
//
//  PURPOSE: Sets up an overlapped ReadFile
//
//  PARAMETERS:
//    lpOverlappedRead      - address of overlapped structure to use.
//    lpszInputBuffer       - Buffer to place incoming bytes.
//    dwSizeofBuffer        - size of lpszInputBuffer.

//    lpnNumberOfBytesRead  - address of DWORD to place the number of read bytes.
//
//  RETURN VALUE:
//    TRUE if able to successfully setup the ReadFile.  FALSE if there
//    was a failure setting up or if the CloseEvent object was signaled.
//
//  COMMENTS:
//
//    This function is a helper function for the Read Thread.  This
//    function sets up the overlapped ReadFile so that it can later
//    be waited on (or more appropriatly, so the event in the overlapped
//    structure can be waited upon).  If there is data waiting, it is
//    handled and the next ReadFile is initiated.
//    Another possible reason for returning FALSE is if the comm port
//    is closed by the service provider.
//
//
//

BOOL SetupReadEvent(LPOVERLAPPED lpOverlappedRead,
    LPSTR lpszInputBuffer, DWORD dwSizeofBuffer,
    LPDWORD lpnNumberOfBytesRead)
{
    DWORD dwLastError;

  StartSetupReadEvent:

    // Make sure the CloseEvent hasn't been signaled yet.
    // Check is needed because this function is potentially recursive.
    if (WAIT_TIMEOUT != WaitForSingleObject(g_hCloseEvent,0))
        return FALSE;

    // Start the overlapped ReadFile.
    if (ReadFile(g_hCommFile,
            lpszInputBuffer, dwSizeofBuffer,
            lpnNumberOfBytesRead, lpOverlappedRead))
    {
        // This would only happen if there was data waiting to be read.

        OutputDebugString("Data waiting for ReadFile.\n");

        // Handle the data.
        if (!HandleReadData(lpszInputBuffer, *lpnNumberOfBytesRead))
        {
            return FALSE;
        }

        // Start waiting for more data.
        goto StartSetupReadEvent;
    }

    // ReadFile failed.  Expected because of overlapped I/O.
    dwLastError = GetLastError();

    // LastError was ERROR_IO_PENDING, as expected.
    if (dwLastError == ERROR_IO_PENDING)
    {
        OutputDebugString("Waiting for data from comm connection.\n");
        return TRUE;
    }

    // Its possible for this error to occur if the
    // service provider has closed the port.  Time to end.
    if (dwLastError == ERROR_INVALID_HANDLE)
    {
        OutputDebugString("ERROR_INVALID_HANDLE, "
            "Likely that the Service Provider has closed the port.\n");
        return FALSE;
    }

    // Unexpected error. No idea what could cause this to happen.
    OutputDebugLastError(dwLastError,"Unexpected ReadFile error: ");

    PostHangupCall();
    return FALSE;
}

//
//  FUNCTION: HandleReadEvent(LPOVERLAPPED, LPSTR, DWORD, LPDWORD)
//
//  PURPOSE: Retrieves and handles data when there is data ready.
//
//  PARAMETERS:
//    lpOverlappedRead      - address of overlapped structure to use.
//    lpszInputBuffer       - Buffer to place incoming bytes.
//    dwSizeofBuffer        - size of lpszInputBuffer.
//    lpnNumberOfBytesRead  - address of DWORD to place the number of read bytes.
//
//  RETURN VALUE:
//    TRUE if able to successfully retrieve and handle the available data.
//    FALSE if unable to retrieve or handle the data.
//
//  COMMENTS:
//
//    This function is another helper function for the Read Thread.  This
//    is the function that is called when there is data available after
//    an overlapped ReadFile has been setup.  It retrieves the data and
//    handles it.
//
//

BOOL HandleReadEvent(LPOVERLAPPED lpOverlappedRead,
    LPSTR lpszInputBuffer, DWORD dwSizeofBuffer,
    LPDWORD lpnNumberOfBytesRead)
{
    DWORD dwLastError;

    if (GetOverlappedResult(g_hCommFile,
            lpOverlappedRead, lpnNumberOfBytesRead, FALSE))
    {
        return HandleReadData(lpszInputBuffer, *lpnNumberOfBytesRead);
    }

    // Error in GetOverlappedResult; handle it.

    dwLastError = GetLastError();

    // Its possible for this error to occur if the
    // service provider has closed the port.  Time to end.
    if (dwLastError == ERROR_INVALID_HANDLE)
    {
        OutputDebugString("ERROR_INVALID_HANDLE, "
            "Likely that the Service Provider has closed the port.\n");
        return FALSE;
    }

    OutputDebugLastError(dwLastError,
        "Unexpected GetOverlappedResult Read Error: ");

    PostHangupCall();
    return FALSE;
}

//
//  FUNCTION: HandleReadData(LPCSTR, DWORD)
//
//  PURPOSE: Deals with data after its been read from the comm file.
//
//  PARAMETERS:
//    lpszInputBuffer  - Buffer to place incoming bytes.
//    dwSizeofBuffer   - size of lpszInputBuffer.
//
//  RETURN VALUE:
//    TRUE if able to successfully handle the data.
//    FALSE if unable to allocate memory or handle the data.
//
//  COMMENTS:
//
//    This function is yet another helper function for the Read Thread.
//    It LocalAlloc()s a buffer, copies the new data to this buffer and
//    calls PostWriteToDisplayCtl to let the EditCtls module deal with
//    the data.  Its assumed that PostWriteToDisplayCtl posts the message
//    rather than dealing with it right away so that the Read Thread
//    is free to get right back to waiting for data.  Its also assumed
//    that the EditCtls module is responsible for LocalFree()ing the
//    pointer that is passed on.
//
//

BOOL HandleReadData(LPCSTR lpszInputBuffer, DWORD dwSizeofBuffer)
{
    // If we got data and didn't just time out empty...
    if (dwSizeofBuffer)
    {
        LPSTR lpszPostedBytes;

        // Do something with the bytes read.
        OutputDebugString("Got something from Comm port!!!\n");

        lpszPostedBytes = LocalAlloc(LPTR,dwSizeofBuffer+1);
        if (lpszPostedBytes == NULL)
        {
            OutputDebugLastError(GetLastError(), "LocalAlloc: ");
            return FALSE;
        }

        memcpy(lpszPostedBytes, lpszInputBuffer, dwSizeofBuffer);
        lpszPostedBytes[dwSizeofBuffer] = '\0';

        return PostWriteToDisplayCtl(lpszPostedBytes, dwSizeofBuffer);
    }

}

//
//  FUNCTION: SetupCommEvent(LPOVERLAPPED, LPDWORD)
//
//  PURPOSE: Sets up the overlapped WaitCommEvent call.
//
//  PARAMETERS:
//    lpOverlappedCommEvent - Pointer to the overlapped structure to use.
//    lpfdwEvtMask          - Pointer to DWORD to received Event data.
//
//  RETURN VALUE:
//    TRUE if able to successfully setup the WaitCommEvent.
//    FALSE if unable to setup WaitCommEvent, unable to handle
//    an existing outstanding event or if the CloseEvent has been signaled.
//
//  COMMENTS:
//
//    This function is a helper function for the Read Thread that sets up
//    the WaitCommEvent so we can deal with comm events (like Comm errors)
//    if they occur.
//
//

BOOL SetupCommEvent(LPOVERLAPPED lpOverlappedCommEvent,
    LPDWORD lpfdwEvtMask)
{
    DWORD dwLastError;

  StartSetupCommEvent:

    // Make sure the CloseEvent hasn't been signaled yet.
    // Check is needed because this function is potentially recursive.
    if (WAIT_TIMEOUT != WaitForSingleObject(g_hCloseEvent,0))
        return FALSE;

    // Start waiting for Comm Errors.
    if (WaitCommEvent(g_hCommFile, lpfdwEvtMask, lpOverlappedCommEvent))
    {
        // This could happen if there was an error waiting on the
        // comm port.  Lets try and handle it.

        OutputDebugString("Event (Error) waiting before WaitCommEvent.\n");

        if (!HandleCommEvent(NULL, lpfdwEvtMask, FALSE))
            return FALSE;

        // What could cause infinite recursion at this point?
        goto StartSetupCommEvent;
    }

    // We expect ERROR_IO_PENDING returned from WaitCommEvent
    // because we are waiting with an overlapped structure.

    dwLastError = GetLastError();

    // LastError was ERROR_IO_PENDING, as expected.
    if (dwLastError == ERROR_IO_PENDING)
    {
        OutputDebugString("Waiting for a CommEvent (Error) to occur.\n");
        return TRUE;
    }

    // Its possible for this error to occur if the
    // service provider has closed the port.  Time to end.
    if (dwLastError == ERROR_INVALID_HANDLE)
    {
        OutputDebugString("ERROR_INVALID_HANDLE, "
            "Likely that the Service Provider has closed the port.\n");
        return FALSE;
    }

    // Unexpected error. No idea what could cause this to happen.
    OutputDebugLastError(dwLastError, "Unexpected WaitCommEvent error: ");
    return FALSE;
}

//
//  FUNCTION: HandleCommEvent(LPOVERLAPPED, LPDWORD, BOOL)
//
//  PURPOSE: Handle an outstanding Comm Event.
//
//  PARAMETERS:
//    lpOverlappedCommEvent - Pointer to the overlapped structure to use.
//    lpfdwEvtMask          - Pointer to DWORD to received Event data.
//     fRetrieveEvent       - Flag to signal if the event needs to be
//                            retrieved, or has already been retrieved.
//
//  RETURN VALUE:
//    TRUE if able to handle a Comm Event.
//    FALSE if unable to setup WaitCommEvent, unable to handle
//    an existing outstanding event or if the CloseEvent has been signaled.
//
//  COMMENTS:
//
//    This function is a helper function for the Read Thread that (if
//    fRetrieveEvent == TRUE) retrieves an outstanding CommEvent and
//    deals with it.  The only event that should occur is an EV_ERR event,
//    signalling that there has been an error on the comm port.
//
//    Normally, comm errors would not be put into the normal data stream
//    as this sample is demonstrating.  Putting it in a status bar would
//    be more appropriate for a real application.
//
//

BOOL HandleCommEvent(LPOVERLAPPED lpOverlappedCommEvent,
    LPDWORD lpfdwEvtMask, BOOL fRetrieveEvent)
{
    DWORD dwDummy;
    LPSTR lpszOutput;
    char szError[128] = "";
    DWORD dwErrors;
    DWORD nOutput;
    DWORD dwLastError;

    lpszOutput = LocalAlloc(LPTR,256);
    if (lpszOutput == NULL)
    {
        OutputDebugLastError(GetLastError(), "LocalAlloc: ");
        return FALSE;
    }

    // If this fails, it could be because the file was closed (and I/O is
    // finished) or because the overlapped I/O is still in progress.  In
    // either case (or any others) its a bug and return FALSE.
    if (fRetrieveEvent)
        if (!GetOverlappedResult(g_hCommFile,
                lpOverlappedCommEvent, &dwDummy, FALSE))
        {
            dwLastError = GetLastError();

            // Its possible for this error to occur if the
            // service provider has closed the port.  Time to end.
            if (dwLastError == ERROR_INVALID_HANDLE)
            {
                OutputDebugString("ERROR_INVALID_HANDLE, "
                    "Likely that the Service Provider has closed the port.\n");
                return FALSE;
            }

            OutputDebugLastError(dwLastError,
                "Unexpected GetOverlappedResult for WaitCommEvent: ");
            return FALSE;
        }

    // Was the event an error?
    if (*lpfdwEvtMask & EV_ERR)
    {
        // Which error was it?
        if (!ClearCommError(g_hCommFile, &dwErrors, NULL))
        {
            dwLastError = GetLastError();

            // Its possible for this error to occur if the
            // service provider has closed the port.  Time to end.
            if (dwLastError == ERROR_INVALID_HANDLE)
            {
                OutputDebugString("ERROR_INVALID_HANDLE, "
                    "Likely that the Service Provider has closed the port.\n");
                return FALSE;
            }

            OutputDebugLastError(GetLastError(),"ClearCommError: ");
            return FALSE;
        }

        // Its possible that multiple errors occured and were handled
        // in the last ClearCommError.  Because all errors were signaled
        // individually, but cleared all at once, pending comm events
        // can yield EV_ERR while dwErrors equals 0.  Ignore this event.
        if (dwErrors == 0)
        {
            strcat(szError, "NULL Error");
        }

        if (dwErrors & CE_FRAME)
        {
            if (szError[0])
                strcat(szError," and ");

            strcat(szError,"CE_FRAME");
        }

        if (dwErrors & CE_OVERRUN)
        {
            if (szError[0])
                strcat(szError," and ");

            strcat(szError,"CE_OVERRUN");
        }

        if (dwErrors & CE_RXPARITY)
        {
            if (szError[0])
                strcat(szError," and ");

            strcat(szError,"CE_RXPARITY");
        }

        if (dwErrors & ~ (CE_FRAME | CE_OVERRUN | CE_RXPARITY))
        {
            if (szError[0])
                strcat(szError," and ");

            strcat(szError,"EV_ERR Unknown EvtMask");
        }

        nOutput = wsprintf(lpszOutput,
            "Comm Event: '%s', EvtMask = '%lx'\n",
            szError, dwErrors);

        PostWriteToDisplayCtl(lpszOutput, nOutput);
        return TRUE;

    }

    // Should not have gotten here.  Only interested in ERR conditions.

    OutputDebugPrintf("Unexpected comm event %lx",*lpfdwEvtMask);
    return FALSE;
}


COMMCODE.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 1995 - 1998 Microsoft Corporation.  All Rights Reserved.
//
// PURPOSE:
//    Contains public declarations for the CommCode module.
//

BOOL StartComm(HANDLE hNewCommFile);
void StopComm();
BOOL WriteCommString(LPCSTR pszStringToWrite, DWORD nSizeofStringToWrite);


DISPATCH.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.
//
//  MODULE:   dispatch.c
//
//  PURPOSE:  Implement the generic message and command dispatchers.
//
//
//  FUNCTIONS:
//    DispMessage - Call the function associated with a message.
//    DispCommand - Call the function associated with a command.
//    DispDefault - Call the appropriate default window procedure.
//
//  COMMENTS:
//

#include <windows.h>            // required for all Windows applications
#include <windowsx.h>
#include "globals.h"            // prototypes specific to this application

LRESULT DispDefault(EDWP, HWND, UINT, WPARAM, LPARAM);

//
//  FUNCTION: DispMessage(LPMSDI, HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Call the function associated with a message.
//
//  PARAMETERS:
//    lpmsdi - Structure containing the message dispatch information.
//    hwnd - The window handle
//    uMessage - The message number
//    wparam - Message specific data
//    lparam - Message specific data
//
//  RETURN VALUE:
//    The value returned by the message function that was called.
//
//  COMMENTS:
//    Runs the table of messages stored in lpmsdi->rgmsd searching
//    for a message number that matches uMessage.  If a match is found,
//    call the associated function.  Otherwise, call DispDefault to
//    call the default function, if any, associated with the message
//    structure.  In either case, return the value recieved from the
//    message or default function.
//

LRESULT DispMessage(LPMSDI lpmsdi,
                    HWND   hwnd,
                    UINT   uMessage,
                    WPARAM wparam,
                    LPARAM lparam)
{
    int  imsd = 0;

    MSD *rgmsd = lpmsdi->rgmsd;
    int  cmsd  = lpmsdi->cmsd;

    for (imsd = 0; imsd < cmsd; imsd++)
    {
        if (rgmsd[imsd].uMessage == uMessage)
            return rgmsd[imsd].pfnmsg(hwnd, uMessage, wparam, lparam);
    }

    return DispDefault(lpmsdi->edwp, hwnd, uMessage, wparam, lparam);
}

//
//  FUNCTION: DispCommand(LPCMDI, HWND, WPARAM, LPARAM)
//
//  PURPOSE: Call the function associated with a command.
//
//  PARAMETERS:
//    lpcmdi - Structure containing the command dispatch information.
//    hwnd - The window handle
//    GET_WM_COMMAND_ID(wparam, lparam) - Identifier of the menu item,
//      control, or accelerator.
//    GET_WM_COMMAND_CMD(wparam, lparam) - Notification code.
//    GET_WM_COMMAND_HWND(wparam, lparam) - The control handle or NULL.
//
//  RETURN VALUE:
//    The value returned by the command function that was called.
//
//  COMMENTS:
//    Runs the table of commands stored in lpcmdi->rgcmd searching
//    for a command number that matches wCommand.  If a match is found,
//    call the associated function.  Otherwise, call DispDefault to
//    call the default function, if any, associated with the command
//    structure.  In either case, return the value recieved from the
//    command or default function.
//

LRESULT DispCommand(LPCMDI lpcmdi,
                    HWND   hwnd,
                    WPARAM wparam,
                    LPARAM lparam)
{
    LRESULT lRet = 0;
    WORD    wCommand = GET_WM_COMMAND_ID(wparam, lparam);
    int     icmd;

    CMD    *rgcmd = lpcmdi->rgcmd;
    int     ccmd  = lpcmdi->ccmd;

    // Message packing of wparam and lparam have changed for Win32,
    // so use the GET_WM_COMMAND macro to unpack the commnad

    for (icmd = 0; icmd < ccmd; icmd++)
    {
        if (rgcmd[icmd].wCommand == wCommand)
        {
            return rgcmd[icmd].pfncmd(hwnd,
                                      wCommand,
                                      GET_WM_COMMAND_CMD(wparam, lparam),
                                      GET_WM_COMMAND_HWND(wparam, lparam));
        }
    }

    return DispDefault(lpcmdi->edwp, hwnd, WM_COMMAND, wparam, lparam);
}

//
//  FUNCTION: DispDefault(EDWP, HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Call the appropriate default window procedure.
//
//  PARAMETERS:
//    edwp - Enumerate specifying the appropriate default winow procedure.
//    hwnd - The window handle
//    uMessage - The message number
//    wparam - Message specific data
//    lparam - Message specific data
//
//  RETURN VALUE:
//    If there is a default proc, return the value returned by the
//    default proc.  Otherwise, return 0.
//
//  COMMENTS:
//    Calls the default procedure associated with edwp using the specified
//    parameters.
//

LRESULT DispDefault(EDWP   edwp,
                    HWND   hwnd,
                    UINT   uMessage,
                    WPARAM wparam,
                    LPARAM lparam)
{
    switch (edwp)
    {
        case edwpNone:
            return 0;
        case edwpWindow:
            return DefWindowProc(hwnd, uMessage, wparam, lparam);
        case edwpDialog:
            return DefDlgProc(hwnd, uMessage, wparam, lparam);
        case edwpMDIFrame:
            return DefFrameProc(hwnd, hwndMDIClient, uMessage, wparam, lparam);
        case edwpMDIChild:
            return DefMDIChildProc(hwnd, uMessage, wparam, lparam);
    }
    return 0;
}


EDITCTLS.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.
//
//  MODULE: EditCtls.c
//
//  PURPOSE: Handles the UI Edit controls to get data from user and display
//    data recieved from the COMM port.
//
//  EXPORTED FUNCTIONS:  These functions are for use by other modules.
//    InitEditCtls              - Initialize the edit controls.
//    SizeEditCtls              - Size and resize the edit controls.
//    SetFocusEditCtls          - Set the focus to the correct edit control.
//    WriteToDisplayCtl         - Write a string to the Display edit control.
//    PostWriteToDisplayCtl     - Posts a message to the UI thread
//                                with data to write.
//
//  INTERNAL FUNCTIONS:  These functions are for this module only.
//    SubclassInputEditProc     - Subclass proc to catch the 'enter' keystroke.
//
// COMMENTS:
//   This sample uses exceptionally simple terminal emulation: none.
//   There are two edit controls involved.  One for the user to type the
//   outgoing strings (called the InputCtl) and another to display both
//   transmitted and received strings (called the DisplayCtl).  There is
//   absolutely *no* translation of incoming strings, and the only
//   modifications to outgoing strings is to append "/r/n" to it.
//
//   This sample only emulates line mode transmission.  The string is only
//   read from the InputCtl when the 'Enter' key is typed.
//

#include <windows.h>
#include <string.h>
#include "globals.h"
#include "EditCtls.h"
#include "CommCode.h"
#include "toolbar.h"
#include "statbar.h"

// Maximum size of Edit control buffers

#define MAXDISPLAYSIZE 0xF000  // Almost 64K
#define MAXINPUTSIZE 0x400      // 1K

#define PWM_WRITESTRING WM_USER + 741 // Arbitrary number

HWND hWndInputCtl;      // Edit control to get input from user
HWND hWndDisplayCtl;    // Edit control to display all output
HWND hWndParent;        // Parent window of edit controls

WNDPROC lpfnInputEdit;  // Storage for subclassed edit control function

// Prototype for the subclassed procedure.
LRESULT CALLBACK SubclassInputEditProc(
    HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

//
//  FUNCTION: InitEditCtls(HWND)
//
//  PURPOSE: Initialize the UI edit controls.
//
//  PARAMETERS:
//    hWndMain - The window that is to contain the edit controls.
//
//  RETURN VALUE:
//    TRUE if the edit controls are successfully initialized.
//
//  COMMENTS:
//    This function should only need to be called once, when
//    creating the main window.  The only reason it could fail
//    is if the edit controls couldn't be created.
//

BOOL InitEditCtls(HWND hWndMain)
{
    hWndParent = hWndMain;

    if (!hWndMain)
    {
        OutputDebugString("Invalid parent hWnd for EditCtls!\n");
        return FALSE;
    }

    hWndDisplayCtl = CreateWindowEx(
        WS_EX_CLIENTEDGE,
        "EDIT","",
        WS_CHILD | WS_HSCROLL | WS_VSCROLL | ES_MULTILINE
        | ES_OEMCONVERT | WS_VISIBLE | WS_BORDER,
        0, 0, 0, 0, hWndParent,
        NULL, hInst, NULL);

    hWndInputCtl = CreateWindowEx(
        WS_EX_CLIENTEDGE,
        "EDIT","",
        WS_CHILD | ES_AUTOHSCROLL | WS_VISIBLE | WS_BORDER,
        0, 0, 0, 0, hWndParent,
        NULL, hInst, NULL);

    if (!hWndDisplayCtl || !hWndInputCtl)
    {
        OutputDebugString("Unable to create EditCtls.\n");
        return FALSE;
    }

    SendMessage(hWndInputCtl, EM_LIMITTEXT, MAXINPUTSIZE, 0);
    SendMessage(hWndDisplayCtl, EM_LIMITTEXT, MAXDISPLAYSIZE, 0);

        // This is where we subclass the input edit control
        // so we can catch the 'enter' keystroke
    lpfnInputEdit = (WNDPROC)
        SetWindowLong(hWndInputCtl, GWL_WNDPROC, (long) SubclassInputEditProc);

    return TRUE;
}

//
//  FUNCTION: SizeEditCtls
//
//  PURPOSE: Sizes, resizes and positions the edit controls.
//
//  PARAMETERS:
//    none
//
//  RETURN VALUE:
//    none
//
//  COMMENTS:
//    Anytime the parent window is resized, it needs to signal
//    the edit controls to also resize.  The size of the edit
//    controls assumes that the edit controls are going to
//    contain the full client area of the parent, allowing for
//    the toolbar and statusbar.
//

void SizeEditCtls()
{
    RECT rectParent, rectTemp;
    LONG HeightToolbar, HeightStatusbar;
    LONG HeightDisplayCtl, HeightInputCtl;
    LONG WidthCtls;
    LONG yposTopInputCtl, yposTopDisplayCtl;
    HDC hdc;
    TEXTMETRIC tm;

    // Get the client area of the parent.
    if (!GetClientRect(hWndParent, &rectParent))
    {
        OutputDebugString("GetClientRect on EditCtls parent failed\n");
        return;
    }

    // leave a pixel on each side
    WidthCtls = rectParent.right - 2;

    // Allow for the toolbar at the top.
    GetWindowRect(hWndToolbar, &rectTemp);
    HeightToolbar = rectTemp.bottom - rectTemp.top;

    // Allow for the statusbar at the bottom.
    GetWindowRect(hWndStatusbar, &rectTemp);
    HeightStatusbar = rectTemp.bottom - rectTemp.top;

    // How high should the input control be?
    hdc = GetDC(hWndInputCtl);
    GetTextMetrics(hdc, &tm);
    ReleaseDC(hWndInputCtl, hdc);
    HeightInputCtl = tm.tmHeight + tm.tmExternalLeading + 6;
        // 6 == 3 extra pixels between text and edit control vertical borders.

    // Position Input control.
    yposTopInputCtl = rectParent.bottom - HeightStatusbar - HeightInputCtl - 1;

    // Position Display control.
    yposTopDisplayCtl = rectParent.top + HeightToolbar + 2;
    HeightDisplayCtl = yposTopInputCtl - yposTopDisplayCtl - 1;

    // Move them both.
    MoveWindow(
        hWndInputCtl,
        1, yposTopInputCtl,
        WidthCtls, HeightInputCtl,
        TRUE);

    MoveWindow(
        hWndDisplayCtl,
        1, yposTopDisplayCtl,
        WidthCtls, HeightDisplayCtl,
        TRUE);
}

//
//  FUNCTION: SetFocusEditCtls
//
//  PURPOSE: Sets the focus to the correct edit control.
//
//  PARAMETERS:
//    none
//
//  RETURN VALUE:
//    none
//
//  COMMENTS:
//    Everytime the parent window gets focus, it needs to signal
//    the edit controls to set focus also.  This very simple
//    algorythm always sets focus to the Input control.
//

void SetFocusEditCtls()
{
    DefWindowProc(hWndParent, WM_SETFOCUS, (WPARAM) NULL, (LPARAM) NULL);

    if (GetFocus() == hWndParent)
        SetFocus(hWndInputCtl);

    return;
}

//
//  FUNCTION: SubclassInputEditProc
//
//  PURPOSE: Subclass proceedure of Input control.
//
//  PARAMETERS:
//    Standard WNDPROC parameters.
//
//  RETURN VALUE:
//    Standard WNDPROC return value.
//
//  COMMENTS:
//    We subclass the Input edit control so that we can catch the 'Enter'
//    keystroke (VK_RETURN).  This is how we know when the user wants to
//    transmit something to the COMM port.  This indeed means that only
//    line based terminal emulation is supported by this sample.  However,
//    it makes editing the outgoing text much easier.
//
//    WriteCommString is the call that writes a string to the COMM port.
//    However, the string that is written *must* be allocated with
//    LocalAlloc, and if WriteCommString succeeds, then it will also
//    LocalFree the string.  If WriteCommString fails, then it refused
//    the string and its our job to free it.
//

LRESULT CALLBACK SubclassInputEditProc(
    HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch(message)
    {
        case WM_CHAR:

            // Found a RETURN keystroke!
            if ((TCHAR) wParam == VK_RETURN)
            {

                LPSTR pszString;
                long lSizeofString;

                // Get the size of the string to send.
                lSizeofString =
                    SendMessage(hWndInputCtl, WM_GETTEXTLENGTH, 0, 0);

                // Allocate enough space for the string and "\r\0"
                pszString = (LPSTR) LocalAlloc(LPTR, lSizeofString +2);

                // Get the string
                SendMessage(hWndInputCtl, WM_GETTEXT,
                    lSizeofString + 1, (LPARAM) pszString);

                // Terminate it.
                strcat(pszString, "\r");
                lSizeofString += 1;

                // If the write to Comm failed, its probably because
                // a connection isn't made yet.
                if (WriteCommString(pszString, lSizeofString))
                {
                    // This is what we'd do to local echo user input
                    // Unnecessary usually.
                    //WriteToOutputEdit(pszString, lSizeofString);
                    ;
                }
                else
                {
                    WriteToDisplayCtl("Not yet connected\r", 20);

                    // The comm code refused the string, so we must free it.
                    LocalFree(pszString);
                }

                // Clear the Input control.
                SendMessage(hWndInputCtl, WM_SETTEXT, 0, (LPARAM) "");

                // Don't let default processing handle the RETURN key.
                return 0;
            }

            break;

        case PWM_WRITESTRING:
            WriteToDisplayCtl((LPSTR) lParam, (DWORD) wParam);
            LocalFree((LPSTR) lParam);
            break;

        default:
            break;
    }
    return CallWindowProc(lpfnInputEdit, hWnd, message, wParam, lParam);
}

//
//  FUNCTION: WriteToDisplayCtl(LPSTR, DWORD)
//
//  PURPOSE: Writes a string to the Display control.
//
//  PARAMETERS:
//    lpNewString       - The string to display.
//    dwSizeofNewString - The length of the string.
//
//  RETURN VALUE:
//    none.
//
//  COMMENTS:
//

void WriteToDisplayCtl(LPSTR lpNewString, DWORD dwSizeofNewString)
{
    DWORD dwSizeofEdit;

    dwSizeofEdit = SendMessage(hWndDisplayCtl, WM_GETTEXTLENGTH, 0, 0);

    if ((dwSizeofEdit + dwSizeofNewString) > MAXDISPLAYSIZE)
    {
        // Suggestion for future additions:
        // Handle edit control overflow by deleting from top of edit control
    }

    // Actually place the string into the Display control.
    SendMessage(hWndDisplayCtl, EM_SETSEL, dwSizeofEdit, dwSizeofEdit);
    SendMessage(hWndDisplayCtl, EM_REPLACESEL, 0, (LPARAM) lpNewString);
    SendMessage(hWndDisplayCtl, EM_SETSEL,
        dwSizeofEdit+dwSizeofNewString, dwSizeofEdit+dwSizeofNewString);
}

//
//  FUNCTION: PostWriteToDisplayCtl(LPSTR, DWORD)
//
//  PURPOSE: Writes a string to the Display control.
//
//  PARAMETERS:
//    lpNewString       - The string to display.
//    dwSizeofNewString - The length of the string.
//
//  RETURN VALUE:
//    none.
//
//  COMMENTS:
//
//    Its very important that when data is recieved from the comm port,
//    the thread reading the comm port doesn't have to wait for the
//    data to be interpreted.  Instead, it posts the data to the main UI
//    thread to be handled when it is convenient.  This is the API to
//    wrap up the PostMessage.
//
//    Note that the string posted must be LocalAlloc()d, and its the job
//    of the window recieving the message to LocalFree it.
//

BOOL PostWriteToDisplayCtl(LPSTR lpNewString, DWORD dwSizeofNewString)
{
    return PostMessage(hWndInputCtl, PWM_WRITESTRING,
        (WPARAM)dwSizeofNewString, (LPARAM) lpNewString);
}


EDITCTLS.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 1995 - 1998 Microsoft Corporation.  All Rights Reserved.
//
// PURPOSE:
//    Contains public declarations for the EditCtls module.
//

BOOL InitEditCtls(HWND hWndParent);
void SizeEditCtls();
void SetFocusEditCtls();

void WriteToDisplayCtl(LPSTR lpNewString, DWORD dwSizeofNewString);
BOOL PostWriteToDisplayCtl(LPSTR lpNewString, DWORD dwSizeofNewString);


GLOBALS.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 1995 - 1998 Microsoft Corporation.  All Rights Reserved.
//
// PURPOSE:
//
//  Contains declarations for all globally scoped names in the program
//

//
// Product identifier string defines
//
//  **TODO** Change these strings to the name of your application.

#define APPNAME  TAPICOMM
#define ICONFILE TAPICOMM.ICO
#define SZAPPNAME "TAPICOMM"
#define SZDESCRIPTION "TAPI/COMM Sample"
#define SZABOUT "About TAPICOMM"
#define SZVERSION "4.0"

//-------------------------------------------------------------------------
// Functions for handling main window messages.  The message-dispatching
// mechanism expects all message-handling functions to have the following
// prototype:
//
//     LRESULT FunctionName(HWND, UINT, WPARAM, LPARAM);

// **TODO**  Add message-handling function prototypes here.  Be sure to
//           add the function names to the main window message table in
//           TapiComm.c.

LRESULT MsgCommand   (HWND, UINT, WPARAM, LPARAM);
LRESULT MsgCreate    (HWND, UINT, WPARAM, LPARAM);
LRESULT MsgDestroy   (HWND, UINT, WPARAM, LPARAM);
LRESULT MsgSize      (HWND, UINT, WPARAM, LPARAM);
LRESULT MsgSetFocus  (HWND, UINT, WPARAM, LPARAM);
LRESULT MsgTimer     (HWND, UINT, WPARAM, LPARAM);
LRESULT MsgMousemove (HWND, UINT, WPARAM, LPARAM);
LRESULT MsgMenuSelect(HWND, UINT, WPARAM, LPARAM);
LRESULT MsgNotify    (HWND, UINT, WPARAM, LPARAM);
LRESULT MsgClose     (HWND, UINT, WPARAM, LPARAM);
LRESULT MsgPaint     (HWND, UINT, WPARAM, LPARAM);

//-------------------------------------------------------------------------
// Functions for handling main window commands--ie. functions for
// processing WM_COMMAND messages based on the wParam value.
// The message-dispatching mechanism expects all command-handling
// functions to have the following prototype:
//
//     LRESULT FunctionName(HWND, WORD, WORD, HWND);

// **TODO**  Add message-handling function prototypes here.  Be sure to
//           add the function names to the main window command table in
//           TapiComm.c.

LRESULT CmdExit (HWND, WORD, WORD, HWND);
LRESULT CmdAbout(HWND, WORD, WORD, HWND);
LRESULT CmdStub (HWND, WORD, WORD, HWND);
LRESULT CmdMakeCall   (HWND, WORD, WORD, HWND);
LRESULT CmdHangupCall (HWND, WORD, WORD, HWND);

//-------------------------------------------------------------------------
// Global function prototypes.

// **TODO**  Add global function prototypes here.

BOOL InitApplication(HINSTANCE, int);
BOOL CenterWindow(HWND, HWND);

    // Callback functions.  These are called by Windows.

// **TODO**  Add new callback function prototypes here.

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

//-------------------------------------------------------------------------
// Command ID definitions.  These definitions are used to associate menu
// items with commands.

// **TODO**  Add IDs for all menu commands here.  Be sure that the IDs you
//           add are also added to the main window command table in TapiComm.c
//           and the menu statements in the resource file.

// File menu
#define IDS_FILEMENU       1000
#define IDM_FILENEW        1001
#define IDM_FILEOPEN       1002
#define IDM_FILESAVE       1003
#define IDM_FILESAVEAS     1004
#define IDM_FILECLOSE      1005
#define IDM_FILEPRINT      1006
#define IDM_FILEPAGESU     1007
#define IDM_FILEPRINTSU    1008
#define IDM_EXIT           1009
#define IDM_MAKECALL       1010
#define IDM_HANGUPCALL     1011

// Edit menu
#define IDS_EDITMENU       2000
#define IDM_EDITUNDO       2001
#define IDM_EDITCUT        2002
#define IDM_EDITCOPY       2003
#define IDM_EDITPASTE      2004
#define IDM_EDITCLEAR      2005

// Help menu
#define IDS_HELPMENU       3000
#define IDM_ABOUT          3001

//-------------------------------------------------------------------------
// String Table ID definitions.

#define IDS_APPNAME     1
#define IDS_DESCRIPTION 2

//-------------------------------------------------------------------------
//  About dialog defines.

#define IDD_VERFIRST    100
#define IDD_VERLAST     104

// -----------------------------------------------------------------------
// Global Variable declarations
//

extern HINSTANCE hInst;     // The current instance
extern char szAppName[];    // The name of this application
extern char szTitle[];      // The title bar text

// **TODO**  For NON-MDI applications, uncomment line 1 below and comment
//           line 2.  For MDI applications, uncomment line 2 below, comment
//           line 1, and then define hwndMDIClient as a global variable in
//           INIT.C
#define hwndMDIClient NULL        /* (1) Stub for NON-MDI applications. */
// extern HWND hwndMDIClient;     /* (2) For MDI applications.          */

//-------------------------------------------------------------------------
// Message and command dispatch infrastructure.  The following type
// definitions and functions are used by the message and command dispatching
// mechanism and do not need to be changed.

    // Function pointer prototype for message handling functions.
typedef LRESULT (*PFNMSG)(HWND,UINT,WPARAM,LPARAM);

    // Function pointer prototype for command handling functions.
typedef LRESULT (*PFNCMD)(HWND,WORD,WORD,HWND);

    // Enumerated type used to determine which default window procedure
    // should be called by the message- and command-dispatching mechanism
    // if a message or command is not handled explicitly.
typedef enum
{
   edwpNone,            // Do not call any default procedure.
   edwpWindow,          // Call DefWindowProc.
   edwpDialog,          // Call DefDlgProc (This should be used only for
                        // custom dialogs - standard dialog use edwpNone).
   edwpMDIChild,        // Call DefMDIChildProc.
   edwpMDIFrame         // Call DefFrameProc.
} EDWP;                // Enumeration for Default Window Procedures

    // This structure maps messages to message handling functions.
typedef struct _MSD
{
    UINT   uMessage;
    PFNMSG pfnmsg;
} MSD;                 // MeSsage Dispatch structure

    // This structure contains all of the information that a window
    // procedure passes to DispMessage in order to define the message
    // dispatching behavior for the window.
typedef struct _MSDI
{
    int  cmsd;          // Number of message dispatch structs in rgmsd
    MSD *rgmsd;         // Table of message dispatch structures
    EDWP edwp;          // Type of default window handler needed.
} MSDI, FAR *LPMSDI;   // MeSsage Dipatch Information

    // This structure maps command IDs to command handling functions.
typedef struct _CMD
{
    WORD   wCommand;
    PFNCMD pfncmd;
} CMD;                 // CoMmand Dispatch structure

    // This structure contains all of the information that a command
    // message procedure passes to DispCommand in order to define the
    // command dispatching behavior for the window.
typedef struct _CMDI
{
    int  ccmd;          // Number of command dispatch structs in rgcmd
    CMD *rgcmd;         // Table of command dispatch structures
    EDWP edwp;          // Type of default window handler needed.
} CMDI, FAR *LPCMDI;   // CoMmand Dispatch Information

    // Message and command dispatching functions.  They look up messages
    // and commands in the dispatch tables and call the appropriate handler
    // function.
LRESULT DispMessage(LPMSDI, HWND, UINT, WPARAM, LPARAM);
LRESULT DispCommand(LPCMDI, HWND, WPARAM, LPARAM);

    // Message dispatch information for the main window
extern MSDI msdiMain;
    // Command dispatch information for the main window
extern CMDI cmdiMain;

#define SZRCOMPANYNAME "CompanyName"
#define SZRDESCRIPTION "FileDescription"
#define SZRVERSION     "FileVersion"
#define SZRAPPNAME     "InternalName"
#define SZRCOPYRIGHT   "LegalCopyright"
#define SZRTRADEMARK   "LegalTrademarks"
#define SZRPRODNAME    "ProductName"
#define SZRPRODVER     "ProuctVersion"


INIT.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.
//
//  MODULE:   init.c
//
//  PURPOSE:   Performs application and instance specific initialization.
//
//  FUNCTIONS:
//    InitApplication() - Initializes window data and registers window.
//
//  COMMENTS:
//

#include <windows.h>            // required for all Windows applications
#include "globals.h"            // prototypes specific to this application

HINSTANCE hInst;                // current instance

char szAppName[9];              // The name of this application
char szTitle[40];               // The title bar text

//
//  FUNCTION: InitApplication(HINSTANCE, int)
//
//  PURPOSE: Initializes window data and registers window class.
//
//  PARAMETERS:
//    hInstance - The handle to the instance of this application that
//                is currently being executed.
//    nCmdShow  - Specifies how the main window is to be displayed.
//
//  RETURN VALUE:
//    TRUE  - Success
//    FALSE - Initialization failed
//
//  COMMENTS:
//
//    This function is called at application initialization time.  It
//    performs initialization tasks for the current application instance.
//    Unlike Win16, in Win32, each instance of an application must register
//    window classes.
//
//    In this function, we initialize a window class by filling out a data
//    structure of type WNDCLASS and calling the Windows RegisterClass()
//    function.  Then we create the main window and show it.
//
//

BOOL InitApplication(HINSTANCE hInstance, int nCmdShow)
{
    WNDCLASS  wc;
    HWND      hwnd; // Main window handle.

    // Load the application name and description strings.

    LoadString(hInstance, IDS_APPNAME, szAppName, sizeof(szAppName));
    LoadString(hInstance, IDS_DESCRIPTION, szTitle, sizeof(szTitle));

    // Save the instance handle in static variable, which will be used in
    // many subsequence calls from this application to Windows.

    hInst = hInstance; // Store instance handle in our global variable

    // Fill in window class structure with parameters that describe the
    // main window.

    wc.style         = CS_HREDRAW | CS_VREDRAW; // Class style(s).
    wc.lpfnWndProc   = (WNDPROC)WndProc;        // Window Procedure
    wc.cbClsExtra    = 0;                       // No per-class extra data.
    wc.cbWndExtra    = 0;                       // No per-window extra data.
    wc.hInstance     = hInstance;               // Owner of this class
    wc.hIcon         = LoadIcon (hInstance, szAppName); // Icon name from .RC
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW); // Cursor
    wc.hbrBackground = CreateSolidBrush(GetSysColor(COLOR_3DFACE));
    wc.lpszMenuName  = szAppName;               // Menu name from .RC
    wc.lpszClassName = szAppName;               // Name to register as

    // Register the window class and return FALSE if unsuccesful.

    if (!RegisterClass(&wc))
    {
        return FALSE;
    }

    // Create a main window for this application instance.
    hwnd = CreateWindow(szAppName,           // See RegisterClass() call
                        szTitle,             // Text for window title bar
                        WS_OVERLAPPEDWINDOW, // Window style
                        CW_USEDEFAULT, 0,    // Use default positioning
                        CW_USEDEFAULT, 0,    // Use default size
                        NULL,                // Overlapped has no parent
                        NULL,                // Use the window class menu
                        hInstance,           // This instance owns this window
                        NULL                 // Don't need data in WM_CREATE
    );

    // If window could not be created, return "failure"
    if (!hwnd)
        return FALSE;

    // Make the window visible; update its client area; and return "success"
    ShowWindow(hwnd, nCmdShow);  // Show the window
    UpdateWindow(hwnd);          // Sends WM_PAINT message

    return TRUE;                 // We succeeded...
}


MISC.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.
//
//  MODULE:   misc.c
//
//  PURPOSE:  Contains all helper functions "global" to the application.
//
//  FUNCTIONS:
//    CenterWindow - Center one window over another.
//    CmdStub      - Handles unimplemented commands.
//                   Demonstrates the statusbar updating.
//
//  COMMENTS:
//

#include <windows.h>            // required for all Windows applications
#include <windowsx.h>
#include "globals.h"            // prototypes specific to this application
#include "statbar.h"

//
//  FUNCTION: CenterWindow(HWND, HWND)
//
//  PURPOSE:  Center one window over another.
//
//  PARAMETERS:
//    hwndChild - The handle of the window to be centered.
//    hwndParent- The handle of the window to center on.
//
//  RETURN VALUE:
//
//    TRUE  - Success
//    FALSE - Failure
//
//  COMMENTS:
//
//    Dialog boxes take on the screen position that they were designed
//    at, which is not always appropriate. Centering the dialog over a
//    particular window usually results in a better position.
//

BOOL CenterWindow(HWND hwndChild, HWND hwndParent)
{
    RECT    rcChild, rcParent;
    int     cxChild, cyChild, cxParent, cyParent;
    int     cxScreen, cyScreen, xNew, yNew;
    HDC     hdc;

    // Get the Height and Width of the child window
    GetWindowRect(hwndChild, &rcChild);
    cxChild = rcChild.right - rcChild.left;
    cyChild = rcChild.bottom - rcChild.top;

    // Get the Height and Width of the parent window
    GetWindowRect(hwndParent, &rcParent);
    cxParent = rcParent.right - rcParent.left;
    cyParent = rcParent.bottom - rcParent.top;

    // Get the display limits
    hdc = GetDC(hwndChild);
    cxScreen = GetDeviceCaps(hdc, HORZRES);
    cyScreen = GetDeviceCaps(hdc, VERTRES);
    ReleaseDC(hwndChild, hdc);

    // Calculate new X position, then adjust for screen
    xNew = rcParent.left + ((cxParent - cxChild) / 2);
    if (xNew < 0)
    {
        xNew = 0;
    }
    else if ((xNew + cxChild) > cxScreen)
    {
        xNew = cxScreen - cxChild;
    }

    // Calculate new Y position, then adjust for screen
    yNew = rcParent.top  + ((cyParent - cyChild) / 2);
    if (yNew < 0)
    {
        yNew = 0;
    }
    else if ((yNew + cyChild) > cyScreen)
    {
        yNew = cyScreen - cyChild;
    }

    // Set it, and return
    return SetWindowPos(hwndChild,
                        NULL,
                        xNew, yNew,
                        0, 0,
                        SWP_NOSIZE | SWP_NOZORDER);
}

//
//  FUNCTION: CmdStub(HWND, WORD, WORD, HWND)
//
//  PURPOSE:  Display statusbar updates by calling UpdateStatusBar
//
//  PARAMETERS:
//    hwnd     - The window.
//    wCommand - Menu command ID
//    wNotify  - Notification number (unused)
//    hwndCtrl - NULL (unused)
//
//  RETURN VALUE:
//    Always returns 0 - command handled.
//
//  COMMENTS:
//    Assumes there is a resource string describing this command with the
//    same ID as the command ID.  Loads the string and calls UpdateStatusBar
//    to put the string into main pane of the status bar.
//

LRESULT CmdStub(HWND hwnd, WORD wCommand, WORD wNotify, HWND hwndCtrl)
{
    char szBuffer[50];
    int  cbWritten = 0;

    cbWritten = LoadString(hInst, wCommand, szBuffer, sizeof(szBuffer));
    if(cbWritten == 0)
    {
        lstrcpy(szBuffer, "Unknown Command");
        UpdateStatusBar(szBuffer, 0, 0);
    }
    else
    {
        UpdateStatusBar(szBuffer, 0, 0);
        MessageBox (hwnd,
                    "Command Not Implemented:\r\n Demonstration Purposes Only",
                    "TAPICOMM",
                    MB_OK | MB_ICONEXCLAMATION);
    }
     /*
      * Once the command is executed, set the statusbar text to
      * original text.
      */
    UpdateStatusBar(SZDESCRIPTION, 0, 0);

    return 0;
}


ABOUT.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.
//
//  MODULE:   about.c
//
//  PURPOSE:   Displays the "About" dialog box
//
//  FUNCTIONS:
//    CmdAbout        - Displays the "About" dialog box
//    About           - Processes messages for "About" dialog box.
//    MsgAboutInit    - To initialize the about box with version info
//                      from resources.
//    MsgAboutCommand - Process WM_COMMAND message sent to the about box.
//    CmdAboutDone    - Free the about box and related data.
//
//  COMMENTS:
//
//

#include <windows.h>            // required for all Windows applications
#include <windowsx.h>
#include "globals.h"            // prototypes specific to this application

LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM);
LRESULT MsgAboutInit(HWND, UINT, WPARAM, LPARAM);
LRESULT MsgAboutCommand(HWND, UINT, WPARAM, LPARAM);
LRESULT CmdAboutDone(HWND, WORD, WORD, HWND);

// About dialog message table definition.
MSD rgmsdAbout[] =
{
    {WM_COMMAND,    MsgAboutCommand},
    {WM_INITDIALOG, MsgAboutInit}
};

MSDI msdiAbout =
{
    sizeof(rgmsdAbout) / sizeof(MSD),
    rgmsdAbout,
    edwpNone
};

// About dialog command table definition.
CMD rgcmdAbout[] =
{
    {IDOK,     CmdAboutDone},
    {IDCANCEL, CmdAboutDone}
};

CMDI cmdiAbout =
{
    sizeof(rgcmdAbout) / sizeof(CMD),
    rgcmdAbout,
    edwpNone
};

// Module specific "globals"  Used when a variable needs to be
// accessed in more than on handler function.

HFONT hFontCopyright;

//
//  FUNCTION: CmdAbout(HWND, WORD, WORD, HWND)
//
//  PURPOSE: Displays the "About" dialog box
//
//  PARAMETERS:
//    hwnd      - Window handle
//    wCommand  - IDM_ABOUT (unused)
//    wNotify   - Notification number (unused)
//    hwndCtrl  - NULL (unused)
//
//  RETURN VALUE:
//
//    Always returns 0 - Message handled
//
//  COMMENTS:
//    To process the IDM_ABOUT message, call DialogBox() to display the
//    about dialog box.

LRESULT CmdAbout(HWND hwnd, WORD wCommand, WORD wNotify, HWND hwndCtrl)
{
    DialogBox(hInst, "AboutBox", hwnd, (DLGPROC)About);
    return 0;
}

//
//  FUNCTION: About(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE:  Processes messages for "About" dialog box.
//
//  PARAMETERS:
//    hdlg - window handle of the dialog box
//    wMessage - type of message
//    wparam - message-specific information
//    lparam - message-specific information
//
//  RETURN VALUE:
//    TRUE - message handled
//    FALSE - message not handled
//
//  COMMENTS:
//
//     Display version information from the version section of the
//     application resource.
//
//     Wait for user to click on "Ok" button, then close the dialog box.
//

LRESULT CALLBACK About(HWND hdlg, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    return DispMessage(&msdiAbout, hdlg, uMessage, wparam, lparam);
}

//
//  FUNCTION: MsgAboutInit(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: To initialize the about box with version info from resources.
//
//  PARAMETERS:
//    hwnd - The window handing the message.
//    uMessage - The message number. (unused).
//    wparam - Message specific data (unused).
//    lparam - Message specific data (unused).
//
//  RETURN VALUE:
//    Always returns 0 - message handled.
//
//  COMMENTS:
//    Uses the version apis to retrieve version information for
//    each of the static text boxes in the about box.
//

LRESULT MsgAboutInit(HWND hdlg, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    #define POINTSIZE 8

    char  szFullPath[256];
    DWORD dwVerHnd;
    DWORD dwVerInfoSize;
    HDC   hDC;
    int   iLogPixelsY, iPointSize;

    // Center the dialog over the application window
    CenterWindow(hdlg, GetWindow(hdlg, GW_OWNER));

    // Set the copyright font to something smaller than default
    hDC = GetDC(hdlg);
    iLogPixelsY = GetDeviceCaps(hDC, LOGPIXELSY);
    ReleaseDC(hdlg, hDC);
    iPointSize = MulDiv(iLogPixelsY, POINTSIZE, 72);
    iPointSize *= -1;

    hFontCopyright = CreateFont(iPointSize,
                                0, 0, 0,
                                FW_BOLD,
                                0, 0, 0, 0,
                                0, 0, 0, 0,
                                "Arial");

    SendDlgItemMessage(hdlg,
                       IDD_VERLAST,
                       WM_SETFONT,
                       (WPARAM)hFontCopyright,
                       0L);

    // Get version information from the application
    GetModuleFileName(hInst, szFullPath, sizeof(szFullPath));
    dwVerInfoSize = GetFileVersionInfoSize(szFullPath, &dwVerHnd);
    if (dwVerInfoSize)
    {
        // If we were able to get the information, process it:
        HANDLE  hMem;
        LPVOID  lpvMem;
        char    szGetName[256];
        int     cchRoot;
        int     i;

        hMem = GlobalAlloc(GMEM_MOVEABLE, dwVerInfoSize);
        lpvMem = GlobalLock(hMem);
        GetFileVersionInfo(szFullPath, dwVerHnd, dwVerInfoSize, lpvMem);
        lstrcpy(szGetName, "\\StringFileInfo\\040904E4\\");
        cchRoot = lstrlen(szGetName);

        // Walk through the dialog items that we want to replace:
        for (i = IDD_VERFIRST; i <= IDD_VERLAST; i++)
        {
            BOOL  fRet;
            UINT  cchVer = 0;
            LPSTR lszVer = NULL;
            char  szResult[256];

            GetDlgItemText(hdlg, i, szResult, sizeof(szResult));
            lstrcpy(&szGetName[cchRoot], szResult);
            fRet = VerQueryValue(lpvMem, szGetName, &lszVer, &cchVer);

            if (fRet && cchVer && lszVer)
            {
                // Replace dialog item text with version info
                lstrcpy(szResult, lszVer);
                SetDlgItemText(hdlg, i, szResult);
            }
        }
        GlobalUnlock(hMem);
        GlobalFree(hMem);
    }
    return TRUE;
}

//
//  FUNCTION: MsgAboutCommand(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Process WM_COMMAND message sent to the about box.
//
//  PARAMETERS:
//    hwnd - The window handing the message.
//    uMessage - The message number. (unused).
//    wparam - Message specific data (unused).
//    lparam - Message specific data (unused).
//
//  RETURN VALUE:
//    Always returns 0 - message handled.
//
//  COMMENTS:
//    Uses this DipsCommand function defined in wndproc.c combined
//    with the cmdiAbout structure defined in this file to handle
//    the command messages for the about dialog box.
//

LRESULT MsgAboutCommand(HWND   hwnd,
                        UINT   uMessage,
                        WPARAM wparam,
                        LPARAM lparam)
{
    return DispCommand(&cmdiAbout, hwnd, wparam, lparam);
}

//
//  FUNCTION: CmdAboutDone(HWND, WORD, HWND)
//
//  PURPOSE: Free the about box and related data.
//
//  PARAMETERS:
//    hwnd - The window handling the command.
//    wCommand - The command to be handled (unused).
//    wNotify   - Notification number (unused)
//    hwndCtrl - NULL (unused).
//
//  RETURN VALUE:
//    Always returns TRUE.
//
//  COMMENTS:
//    Calls EndDialog to finish the dialog session.
//

LRESULT CmdAboutDone(HWND hdlg, WORD wCommand, WORD wNotify, HWND hwndCtrl)
{
    if (hFontCopyright)
       DeleteObject(hFontCopyright);

    EndDialog(hdlg, TRUE);          // Exit the dialog
    return TRUE;
}


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by TapiComm.RC
//
#define IDC_COUNTRYCODE                 1000
#define IDC_TAPILINE                    1001
#define IDC_CONFIGURELINE               1002
#define IDC_DIALINGPROPERTIES           1003
#define IDC_PHONENUMBER                 1004
#define IDC_DIAL                        1005
#define IDC_AREACODE                    1006
#define IDC_CALLINGCARD                 1007
#define IDC_USEDIALINGRULES             1008
#define IDC_LOCATION                    1009
#define IDC_CANONICALNUMBER             1010
#define IDC_DISPLAYABLENUMBER           1011
#define IDC_DIALABLENUMBER              1012
#define IDC_STATICCOUNTRYCODE           1013
#define IDC_STATICAREACODE              1014
#define IDC_STATICPHONENUMBER           1015
#define IDC_STATICLOCATION              1016
#define IDC_STATICCALLINGCARD           1017
#define IDC_STATICTAPILINE              1018
#define IDC_STATICCANONICAL             1019
#define IDC_STATICDIALABLE              1020
#define IDC_STATICDISPLAYABLE           1021
#define IDC_LINEICON                    1022

#define IDC_NOHELPCONTROLS              1101
#define IDC_STATICWHERETODIAL           1102
#define IDC_STATICHOWTODIAL             1103
#define IDC_STATICCONNECTUSING          1104
#define IDC_STATICPHONENUMBERS          1105

#define IDC_CONFIGURATIONCHANGED        1201
#define IDC_LINECREATE                  1202

#define IDC_STATIC                      -1

// 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         1301
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif


STATBAR.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.
//
//  MODULE: statbar.c
//
//  PURPOSE: Handles general routines for the TapiComm sample.
//
//  FUNCTIONS:
//    MsgTimer      - Handles the WM_TIMER messages to set the time on
//                    the status bar.
//    MsgMouseMove  - Handles the WM_MOUSEMOVE to display the cursor position.
//    MsgMenuSelect - Handle the WM_MENUSELECT message. This message will
//                    enable the status bar control to update when the user
//                    moves across menu items on the main window.
//    InitializeStatusBar - Sets the pane positions in the statusbar control
//    CreateSBar    - Calls CreateStatusWindow() to create the status bar
//    UpdateStatusBar - Updates the statusbar control with appropriate text
//

#include <windows.h>            // required for all Windows applications
#include <windowsx.h>
#include <commctrl.h>           // prototypes and defs for common controls
#include "globals.h"            // prototypes specific to this application
#include "statbar.h"            // prototypes specific to statbar.c

// Global Variables for the status bar control.

HWND  hWndStatusbar;

//  **TODO**  Add entries to the string table in TapiComm.rc for each menu
//            command.  MsgMenuSelect (below) loads these strings to display
//            information in the status bar.  MsgMenuSelect assumes that the
//            string ID is the same as the command ID and that a string
//            exists for every command.
//
// The following array contains resource string ID's for popup menus
// in the main application menu.  This array is used by MsgMenuSelect
// to display information in the status bar.
//
//  **TODO**  Add entries to this array for each popup menu in the same
//            positions as they appear in the main menu.  Remember to define
//            the ID's in globals.h and add the strings to TapiComm.rc.

UINT idPopup[] =
{
    IDS_FILEMENU,
    IDS_EDITMENU,
    IDS_HELPMENU,
};

//
//  FUNCTION: MsgMenuSelect(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE:  Upadates menu selections on the staus bar.
//
//
//  PARAMETERS:
//
//    hwnd      - Window handle  (Used)
//    uMessage  - Message number (Used)
//    wparam    - Extra data     (Used)
//    lparam    - Extra data     (Used)
//
//  RETURN VALUE:
//
//    Always returns 0 - Message handled
//
//  COMMENTS:
//    This message is sent when the user selects menu items by
//    by pulling down  a popup menu move the mouse around to highlite
//    different menu items.
//
//

#define MENUHACK 1              // Menu Select message has a bug..so workaround

LRESULT MsgMenuSelect(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    static char szBuffer[128];
    UINT   nStringID = 0;
    UINT   fuFlags = GET_WM_MENUSELECT_FLAGS(wparam, lparam) & 0xffff;
    UINT   uCmd    = GET_WM_MENUSELECT_CMD(wparam, lparam);
    HMENU  hMenu   = GET_WM_MENUSELECT_HMENU(wparam, lparam);

    szBuffer[0] = 0;                            // First reset the buffer

    if (fuFlags == 0xffff && hMenu == NULL)     // Menu has been closed
        nStringID = IDS_DESCRIPTION;

    else if (fuFlags & MFT_SEPARATOR)           // Ignore separators
        nStringID = 0;

    else if (fuFlags & MF_POPUP)                // Popup menu
    {
#ifdef MENUHACK                 // This code shouldn't be necessary...
        UINT i;
        for (i = 0; i < sizeof(idPopup)/sizeof(idPopup[0]); i++)
        {
            if(GetSubMenu(hMenu, i) == (HMENU)uCmd)
            {
                uCmd = i;
                break;
            }
        }
#endif
        if (fuFlags & MF_SYSMENU)               // System menu
            nStringID = IDS_SYSMENU;

        else
            // Get string ID for popup menu from idPopup array.
            nStringID = ((uCmd < sizeof(idPopup)/sizeof(idPopup[0])) ?
                            idPopup[uCmd] : 0);
    }  // for MF_POPUP

    else                                        // Must be a command item
        nStringID = uCmd;                       // String ID == Command ID

    // Load the string if we have an ID
    if (0 != nStringID)
        LoadString(hInst, nStringID, szBuffer, sizeof(szBuffer));

    // Finally... send the string to the status bar
    UpdateStatusBar(szBuffer, 0, 0);

    return 0;
}

//
//  FUNCTION: InitializeStatusBar(HWND)
//
//  PURPOSE:  Initialize statusbar control with time and mouse positions.
//
//
//  PARAMETERS:
//
//  hwndParent - Window handle of the status bar's parent
//
//
//  RETURN VALUE:  NONE
//
//
//  COMMENTS:
//
//   This function initializes the time  and mouse positions sections of
//   the statubar window. The Date for the time section is obtained by
//   calling SetTimer API. When the timer messages start comming in,
//   GetSytemTime() to fill the time section.
//   The WPARAM of SB_SETTEXT is divided into 2 parameters. The LOWORD
//   determines which section/part the text goes into, and the HIWORD
//   tells how the bar is drawn (popin or popout).
//

void InitializeStatusBar(HWND hwndParent)
{
    const cSpaceInBetween = 8;
    int   ptArray[2];   // Array defining the number of parts/sections
    SIZE  size = {0,0};         // the Status bar will display.
    RECT  rect;
    HDC   hDC;

   /*
    * Fill in the ptArray...
    */

    hDC = GetDC(hwndParent);
    GetClientRect(hwndParent, &rect);
    ReleaseDC(hwndParent, hDC);

    ptArray[0] = (rect.right) / 2;
    ptArray[1] = rect.right;

    SendMessage(hWndStatusbar,
                SB_SETPARTS,
                sizeof(ptArray)/sizeof(ptArray[0]),
                (LPARAM)(LPINT)ptArray);

    UpdateStatusBar(SZDESCRIPTION, 0, 0);
}

//
//  FUNCTION: CreateSBar(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE:  Calls CreateStatusWindow() to create the status bar
//
//
//  PARAMETERS:
//
//  hwndParent - Window handle of the status bar's parent
//
//  RETURN VALUE:
//
//  If both controls were created successfully Return TRUE,
//  else returns FALSE.
//
//  COMMENTS:
//
//

BOOL CreateSBar(HWND hwndParent)
{
    hWndStatusbar = CreateStatusWindow(WS_CHILD | WS_VISIBLE | WS_BORDER,
                                       SZDESCRIPTION,
                                       hwndParent,
                                       IDM_STATUSBAR);
    if(hWndStatusbar)
    {
        InitializeStatusBar(hwndParent);
        return TRUE;
    }

    return FALSE;
}

//
//  FUNCTION: UpdateStatusBar(HWND)
//
//  PURPOSE:  Updates the statusbar control with appropriate text
//
//
//  PARAMETERS:
//
//  lpszStatusString - text to be displayed
//  partNumber       - which part of the status bar to display text in
//  displayFlags     - display flags
//
//
//  RETURN VALUE: NONE
//
//
//  COMMENTS:
//      None
//
//

void UpdateStatusBar(LPSTR lpszStatusString, WORD partNumber, WORD displayFlags)
{
    SendMessage(hWndStatusbar,
                SB_SETTEXT,
                partNumber | displayFlags,
                (LPARAM)lpszStatusString);
}


STATBAR.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 1995 - 1998 Microsoft Corporation.  All Rights Reserved.
//
// PURPOSE:
//    Contains declarations for the status bar module.
//

//-------------------------------------------------------------------------
#define IDM_STATUSBAR       501
#define IDM_TIMER           701
#define TIMER_TIMEOUT       1000

// System Menu string ID's
#define IDS_SYSMENU         900

// Global variables for status bar window
extern HWND hWndStatusbar;

// Function prototypes for status bar creation
BOOL CreateSBar(HWND);
void InitializeStatusBar(HWND);
void UpdateStatusBar(LPSTR, WORD, WORD);


TAPICODE.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.
//
//  MODULE: TapiCode.c
//
//  PURPOSE: Handles all the TAPI routines for TapiComm.
//
//
//  EXPORTED FUNCTIONS:  These functions are for use by other modules.
//
//    InitializeTAPI    - Initialize this app with TAPI.
//    ShutdownTAPI      - Shutdown this app from TAPI.
//    DialCall          - Dial a Call.
//    HangupCall        - Hangup an existing Call.
//    PostHangupCall    - Posts a HangupCall message to the main window.
//
//  INTERNAL FUNCTIONS:  These functions are for this module only.
//
//    DialCallInParts           - Actually Dial the call.
//
//    lineCallbackFunc          - TAPI callback for async messages.
//
//    CheckAndReAllocBuffer     - Helper function for I_ wrappers functions.
//
//    I_lineNegotiateAPIVersion - Wrapper for lineNegotiateAPIVersion.
//    I_lineGetDevCaps          - Wrapper for lineGetDevCaps.
//    I_lineGetAddressStatus    - Wrapper for lineGetAddressStatus.
//    I_lineTranslateAddress    - Wrapper for lineTranslateAddress.
//    I_lineGetCallStatus       - Wrapper for lineGetCallStatus.
//    I_lineGetAddressCaps      - Wrapper for lineGetAddressCaps.
//
//    WaitForCallState          - Resynchronize by Waiting for a CallState.
//    WaitForReply              - Resynchronize by Waiting for a LINE_REPLY.
//
//    DoLineReply               - Handle asynchronous LINE_REPLY.
//    DoLineClose               - Handle asynchronous LINE_CLOSE.
//    DoLineDevState            - Handle asynchronous LINE_LINEDEVSTATE.
//    DoLineCallState           - Handle asynchronous LINE_CALLSTATE.
//    DoLineCreate              - Handle asynchronous LINE_CREATE.
//
//    HandleLineErr             - Handler for most LINEERR errors.
//
//    HandleIniFileCorrupt      - LINEERR handler for INIFILECORRUPT.
//    HandleNoDriver            - LINEERR handler for NODRIVER.
//    HandleNoDevicesInstalled  - LINEERR handler for NODEVICE.
//    HandleReInit              - LINEERR handler for REINIT.
//    HandleNoMultipleInstance  - LINEERR handler for NOMULTIPLEINSTANCE.
//    HandleNoMem               - LINEERR handler for NOMEM.
//    HandleOperationFailed     - LINEERR handler for OPERATIONFAILED.
//    HandleResourceUnavail     - LINEERR handler for RESOURCEUNAVAIL.
//
//    LaunchModemControlPanelAdd - Launches the Modem Control Panel.
//
//    WarningBox                - Warn user if a line in use is removed.
//
//    GetAddressToDial          - Launches a GetAddressToDial dialog.
//    DialDialogProc            - Dialog Proc for the GetAddressToDial API.
//
//    I_lineNegotiateLegacyAPIVersion - Wrapper to negoitiate with legacy TSPs
//    VerifyUsableLine          - Verify that a line device is usable
//    FillTAPILine              - Fill a combobox with TAPI Device names
//    VerifyAndWarnUsableLine   - Verify and warn if a line device is usable
//    FillCountryCodeList       - Fill a combobox with country codes
//    FillLocationInfo          - Fill a combobox with current TAPI locations
//    UseDialingRules           - Enable/Disable dialing rules controls
//    DisplayPhoneNumber        - Create and display a valid phone number
//    PreConfigureDevice        - Preconfigure a device line

#include <tapi.h>
#include <windows.h>
#include <string.h>
#include "globals.h"
#include "TapiInfo.h"
#include "TapiCode.h"
#include "CommCode.h"
#include "resource.h"
#include "statbar.h"
#include "toolbar.h"

// All TAPI line functions return 0 for SUCCESS, so define it.
#define SUCCESS 0

// Possible return error for resynchronization functions.
#define WAITERR_WAITABORTED  1
#define WAITERR_WAITTIMEDOUT 2

// Reasons why a line device might not be usable by TapiComm.
#define LINENOTUSEABLE_ERROR            1
#define LINENOTUSEABLE_NOVOICE          2
#define LINENOTUSEABLE_NODATAMODEM      3
#define LINENOTUSEABLE_NOMAKECALL       4
#define LINENOTUSEABLE_ALLOCATED        5
#define LINENOTUSEABLE_INUSE            6
#define LINENOTUSEABLE_NOCOMMDATAMODEM  7

// Constant used in WaitForCallState when any new
// callstate message is acceptable.
#define I_LINECALLSTATE_ANY 0

 // Wait up to 30 seconds for an async completion.
#define WAITTIMEOUT 30000

// TAPI version that this sample is designed to use.
#define SAMPLE_TAPI_VERSION 0x00010004

// Global TAPI variables.
HWND     g_hWndMainWindow = NULL;   // Apps main window.
HWND     g_hDlgParentWindow = NULL; // This will be the parent of all dialogs.
HLINEAPP g_hLineApp = NULL;
DWORD    g_dwNumDevs = 0;

// Global variable that holds the handle to a TAPI dialog
// that needs to be dismissed if line conditions change.
HWND g_hDialog = NULL;

// Global flags to prevent re-entrancy problems.
BOOL g_bShuttingDown = FALSE;
BOOL g_bStoppingCall = FALSE;
BOOL g_bInitializing = FALSE;

// This sample only supports one call in progress at a time.
BOOL g_bTapiInUse = FALSE;

// Data needed per call.  This sample only supports one call.
HCALL g_hCall = NULL;
HLINE g_hLine = NULL;
DWORD g_dwDeviceID = 0;
DWORD g_dwAPIVersion = 0;
DWORD g_dwCallState = 0;
char  g_szDisplayableAddress[1024] = "";
char  g_szDialableAddress[1024] = "";
BOOL  g_bConnected = FALSE;
LPVOID g_lpDeviceConfig = NULL;
DWORD g_dwSizeDeviceConfig;

// Global variables to allow us to do various waits.
BOOL  g_bReplyRecieved;
DWORD g_dwRequestedID;
long  g_lAsyncReply;
BOOL  g_bCallStateReceived;

// Structures needed to handle special non-dialable characters.
#define g_sizeofNonDialable (sizeof(g_sNonDialable)/sizeof(g_sNonDialable[0]))

typedef struct {
    LONG lError;
    DWORD dwDevCapFlag;
    LPSTR szToken;
    LPSTR szMsg;
} NONDIALTOKENS;

NONDIALTOKENS g_sNonDialable[] = {
    {LINEERR_DIALBILLING,  LINEDEVCAPFLAGS_DIALBILLING,  "$",
            "Wait for the credit card bong tone" },
    {LINEERR_DIALDIALTONE, LINEDEVCAPFLAGS_DIALDIALTONE, "W",
            "Wait for the second dial tone" },
    {LINEERR_DIALDIALTONE, LINEDEVCAPFLAGS_DIALDIALTONE, "w",
            "Wait for the second dial tone" },
    {LINEERR_DIALQUIET,    LINEDEVCAPFLAGS_DIALQUIET,    "@",
            "Wait for the remote end to answer" },
    {LINEERR_DIALPROMPT,   0,                            "?",
            "Press OK when you are ready to continue dialing"},
};

// "Dial" dialog controls and their associated help page IDs
DWORD g_adwSampleMenuHelpIDs[] =
{
    IDC_COUNTRYCODE          , IDC_COUNTRYCODE,
    IDC_STATICCOUNTRYCODE    , IDC_COUNTRYCODE,
    IDC_AREACODE             , IDC_AREACODE,
    IDC_STATICAREACODE       , IDC_AREACODE,
    IDC_PHONENUMBER          , IDC_PHONENUMBER,
    IDC_STATICPHONENUMBER    , IDC_PHONENUMBER,
    IDC_USEDIALINGRULES      , IDC_USEDIALINGRULES,
    IDC_LOCATION             , IDC_LOCATION,
    IDC_STATICLOCATION       , IDC_LOCATION,
    IDC_CALLINGCARD          , IDC_CALLINGCARD,
    IDC_STATICCALLINGCARD    , IDC_CALLINGCARD,
    IDC_DIALINGPROPERTIES    , IDC_DIALINGPROPERTIES,
    IDC_TAPILINE             , IDC_TAPILINE,
    IDC_STATICTAPILINE       , IDC_TAPILINE,
    IDC_CONFIGURELINE        , IDC_CONFIGURELINE,
    IDC_CANONICALNUMBER      , IDC_CANONICALNUMBER,
    IDC_STATICCANONICAL      , IDC_CANONICALNUMBER,
    IDC_DIALABLENUMBER       , IDC_DIALABLENUMBER,
    IDC_STATICDIALABLE       , IDC_DIALABLENUMBER,
    IDC_DISPLAYABLENUMBER    , IDC_DISPLAYABLENUMBER,
    IDC_STATICDISPLAYABLE    , IDC_DISPLAYABLENUMBER,
    IDC_DIAL                 , IDC_DIAL,
    IDC_LINEICON             , IDC_LINEICON,
    //IDC_STATICWHERETODIAL    , IDC_STATICWHERETODIAL,
    //IDC_STATICHOWTODIAL      , IDC_STATICHOWTODIAL,
    //IDC_STATICCONNECTUSING   , IDC_STATICCONNECTUSING,
    //IDC_STATICPHONENUMBER    , IDC_PHONENUMBER,
    0,0
};

//**************************************************
// Prototypes for functions used only in this module.
//**************************************************

BOOL DialCallInParts (
    LPLINEDEVCAPS lpLineDevCaps,
    LPCSTR lpszAddress,
    LPCSTR lpszDisplayableAddress);

LPLINECALLPARAMS CreateCallParams (
    LPLINECALLPARAMS lpCallParams,
    LPCSTR lpszDisplayableAddress);

DWORD I_lineNegotiateAPIVersion (
    DWORD dwDeviceID);

LPVOID CheckAndReAllocBuffer(
    LPVOID lpBuffer, size_t sizeBufferMinimum,
    LPCSTR szApiPhrase);

LPLINEDEVCAPS I_lineGetDevCaps (
    LPLINEDEVCAPS lpLineDevCaps,
    DWORD dwDeviceID,
    DWORD dwAPIVersion);

LPLINEADDRESSSTATUS I_lineGetAddressStatus (
    LPLINEADDRESSSTATUS lpLineAddressStatus,
    HLINE hLine,
    DWORD dwAddressID);

LPLINETRANSLATEOUTPUT I_lineTranslateAddress (
    LPLINETRANSLATEOUTPUT lpLineTranslateOutput,
    DWORD dwDeviceID,
    DWORD dwAPIVersion,
    LPCSTR lpszDialAddress);

LPLINECALLSTATUS I_lineGetCallStatus (
    LPLINECALLSTATUS lpLineCallStatus,
    HCALL hCall);

LPLINEADDRESSCAPS I_lineGetAddressCaps (
    LPLINEADDRESSCAPS lpLineAddressCaps,
    DWORD dwDeviceID, DWORD dwAddressID,
    DWORD dwAPIVersion, DWORD dwExtVersion);

long WaitForCallState (DWORD dwNewCallState);

long WaitForReply (long lRequestID);

void CALLBACK lineCallbackFunc(
    DWORD hDevice, DWORD dwMsg, DWORD dwCallbackInstance,
    DWORD dwParam1, DWORD dwParam2, DWORD dwParam3);

void DoLineReply(
    DWORD dwDevice, DWORD dwMsg, DWORD dwCallbackInstance,
    DWORD dwParam1, DWORD dwParam2, DWORD dwParam3);
void DoLineClose(
    DWORD dwDevice, DWORD dwMsg, DWORD dwCallbackInstance,
    DWORD dwParam1, DWORD dwParam2, DWORD dwParam3);
void DoLineDevState(
    DWORD dwDevice, DWORD dwsg, DWORD dwCallbackInstance,
    DWORD dwParam1, DWORD dwParam2, DWORD dwParam3);
void DoLineCallState(
    DWORD dwDevice, DWORD dwMsg, DWORD dwCallbackInstance,
    DWORD dwParam1, DWORD dwParam2, DWORD dwParam3);
void DoLineCreate(
    DWORD dwDevice, DWORD dwMessage, DWORD dwCallbackInstance,
    DWORD dwParam1, DWORD dwParam2, DWORD dwParam3);

BOOL HandleLineErr(long lLineErr);

BOOL HandleIniFileCorrupt();
BOOL HandleNoDriver();
BOOL HandleNoDevicesInstalled();
BOOL HandleReInit();
BOOL HandleNoMultipleInstance();
BOOL HandleNoMem();
BOOL HandleOperationFailed();
BOOL HandleResourceUnavail();

BOOL LaunchModemControlPanelAdd();

void WarningBox(LPCSTR lpszMessage);

BOOL CALLBACK DialDialogProc(
    HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);

BOOL GetAddressToDial();

DWORD I_lineNegotiateLegacyAPIVersion(DWORD dwDeviceID);
long VerifyUsableLine(DWORD dwDeviceID);
void FillTAPILine(HWND hwndDlg);
BOOL VerifyAndWarnUsableLine(HWND hwndDlg);
void FillCountryCodeList(HWND hwndDlg, DWORD dwDefaultCountryID);
void FillLocationInfo(HWND hwndDlg, LPSTR lpszCurrentLocation,
    LPDWORD lpdwCountryID, LPSTR lpszAreaCode);
void UseDialingRules(HWND hwndDlg);
void DisplayPhoneNumber(HWND hwndDlg);
void PreConfigureDevice(HWND hwndDlg, DWORD dwDeviceID);

//**************************************************
// Entry points from the UI
//**************************************************

//
//  FUNCTION: BOOL InitializeTAPI(HWND)
//
//  PURPOSE: Initializes TAPI
//
//  PARAMETERS:
//    hWndParent - Window to use as parent of any dialogs.
//
//  RETURN VALUE:
//    Always returns 0 - command handled.
//
//  COMMENTS:
//
//    This is the API that initializes the app with TAPI.
//    If NULL is passed for the hWndParent, then its assumed
//    that re-initialization has occurred and the previous hWnd
//    is used.
//
//

BOOL InitializeTAPI(HWND hWndParent)
{
    long lReturn;
    BOOL bTryReInit = TRUE;

    // If we're already initialized, then initialization succeeds.
    if (g_hLineApp)
        return TRUE;

    // If we're in the middle of initializing, then fail, we're not done.
    if (g_bInitializing)
        return FALSE;

    g_bInitializing = TRUE;

    // Initialize TAPI
    do
    {
        lReturn = lineInitialize(&g_hLineApp, hInst,
            lineCallbackFunc, "TapiComm", &g_dwNumDevs);

        // If we get this error, its because some other app has yet
        // to respond to the REINIT message.  Wait 5 seconds and try
        // again.  If it still doesn't respond, tell the user.
        if (lReturn == LINEERR_REINIT)
        {
            if (bTryReInit)
            {
                MSG msg;
                DWORD dwTimeStarted;

                dwTimeStarted = GetTickCount();

                while(GetTickCount() - dwTimeStarted < 5000)
                {
                    if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
                    {
                        TranslateMessage(&msg);
                        DispatchMessage(&msg);
                    }
                }

                bTryReInit = FALSE;
                continue;
            }
            else
            {
                MessageBox(g_hDlgParentWindow,
                    "A change to the system configuration requires that "
                    "all Telephony applications relinquish their use of "
                    "Telephony before any can progress.  "
                    "Some have not yet done so."
                    ,"Warning",MB_OK);
                g_bInitializing = FALSE;
                return FALSE;
            }
        }

        if (lReturn == LINEERR_NODEVICE)
        {
            if (HandleNoDevicesInstalled())
                continue;
            else
            {
                OutputDebugString("No devices installed.\n");
                g_bInitializing = FALSE;
                return FALSE;
            }
        }

        if (HandleLineErr(lReturn))
            continue;
        else
        {
            OutputDebugLineError(lReturn,
                "lineInitialize unhandled error: ");
            g_bInitializing = FALSE;
            return FALSE;
        }
    }
    while(lReturn != SUCCESS);

    // if hWndParent is a valid hWnd, we keep it as the parent for
    // all dialogs.
    if (IsWindow(hWndParent))
    {
        g_hDlgParentWindow = g_hWndMainWindow = hWndParent;
    }
    else
    {
        // Has the old g_hWndMainWindow gone away?
        if (!IsWindow(g_hWndMainWindow))
        {
            OutputDebugString("Main window unavailable.\n");
            g_hDlgParentWindow = g_hWndMainWindow = NULL;
        }
    }

    g_hCall = NULL;
    g_hLine = NULL;

    OutputDebugString("Tapi initialized.\n");
    g_bInitializing = FALSE;
    return TRUE;
}

//
//  FUNCTION: BOOL ShutdownTAPI()
//
//  PURPOSE: Shuts down all use of TAPI
//
//  PARAMETERS:
//    None
//
//  RETURN VALUE:
//    True if TAPI successfully shut down.
//
//  COMMENTS:
//
//    If ShutdownTAPI fails, then its likely either a problem
//    with the service provider (and might require a system
//    reboot to correct) or the application ran out of memory.
//
//

BOOL ShutdownTAPI()
{
    long lReturn;

    // If we aren't initialized, then Shutdown is unnecessary.
    if (g_hLineApp == NULL)
        return TRUE;

    // Prevent ShutdownTAPI re-entrancy problems.
    if (g_bShuttingDown)
        return TRUE;

    g_bShuttingDown = TRUE;

    HangupCall();

    do
    {
        lReturn = lineShutdown(g_hLineApp);
        if (HandleLineErr(lReturn))
            continue;
        else
        {
            OutputDebugLineError(lReturn, "lineShutdown unhandled error: ");
            break;
        }
    }
    while(lReturn != SUCCESS);

    g_bTapiInUse = FALSE;
    g_bConnected = FALSE;
    g_hLineApp = NULL;
    g_hCall = NULL;
    g_hLine = NULL;
    g_bShuttingDown = FALSE;
    OutputDebugString("TAPI uninitialized.\n");
    return TRUE;
}

//
//  FUNCTION: BOOL HangupCall()
//
//  PURPOSE: Hangup the call in progress if it exists.
//
//  PARAMETERS:
//    none
//
//  RETURN VALUE:
//    TRUE if call hung up successfully.
//
//  COMMENTS:
//
//    If HangupCall fails, then its likely either a problem
//    with the service provider (and might require a system
//    reboot to correct) or the application ran out of memory.
//
//

BOOL HangupCall()
{
    LPLINECALLSTATUS pLineCallStatus = NULL;
    long lReturn;

    // Prevent HangupCall re-entrancy problems.
    if (g_bStoppingCall)
        return TRUE;

    // if the 'Call' dialog is up, dismiss it.
    if (g_hDialog)
        PostMessage(g_hDialog, WM_COMMAND, IDCANCEL, 0);

    // If Tapi is not being used right now, then the call is hung up.
    if (!g_bTapiInUse)
        return TRUE;

    g_bStoppingCall = TRUE;
    OutputDebugString("Stopping Call in progress\n");

    // Disable the 'hangup call' user interface.
    EnableHangupCall(g_hWndMainWindow, FALSE);

    // Stop any data communications on the comm port.
    StopComm();

    // If there is a call in progress, drop and deallocate it.
    if (g_hCall)
    {
        // I_lineGetCallStatus returns a LocalAlloc()d buffer
        pLineCallStatus = I_lineGetCallStatus(pLineCallStatus, g_hCall);
        if (pLineCallStatus == NULL)
        {
            ShutdownTAPI();
            g_bStoppingCall = FALSE;
            return FALSE;
        }

        // Only drop the call when the line is not IDLE.
        if (!((pLineCallStatus -> dwCallState) & LINECALLSTATE_IDLE))
        {
            do
            {
                lReturn = WaitForReply(lineDrop(g_hCall, NULL, 0));

                if (lReturn == WAITERR_WAITTIMEDOUT)
                {
                    OutputDebugString("Call timed out in WaitForReply.\n");
                    break;
                }

                if (lReturn == WAITERR_WAITABORTED)
                {
                    OutputDebugString("lineDrop: WAITERR_WAITABORTED.\n");
                    break;
                }

                // Was the call already in IDLE?
                if (lReturn == LINEERR_INVALCALLSTATE)
                    break;

                if (HandleLineErr(lReturn))
                    continue;
                else
                {
                    OutputDebugLineError(lReturn,
                        "lineDrop unhandled error: ");
                    break;
                }
            }
            while(lReturn != SUCCESS);

            // Wait for the dropped call to go IDLE before continuing.
            lReturn = WaitForCallState(LINECALLSTATE_IDLE);

            if (lReturn == WAITERR_WAITTIMEDOUT)
                OutputDebugString("Call timed out waiting for IDLE state.\n");

            if (lReturn == WAITERR_WAITABORTED)
                OutputDebugString(
                    "WAITERR_WAITABORTED while waiting for IDLE state.\n");

            OutputDebugString("Call Dropped.\n");
        }

        // The call is now idle.  Deallocate it!
        do
        {
            lReturn = lineDeallocateCall(g_hCall);
            if (HandleLineErr(lReturn))
                continue;
            else
            {
                OutputDebugLineError(lReturn,
                    "lineDeallocateCall unhandled error: ");
                break;
            }
        }
        while(lReturn != SUCCESS);

        OutputDebugString("Call Deallocated.\n");
    }

    // if we have a line open, close it.
    if (g_hLine)
    {
        do
        {
            lReturn = lineClose(g_hLine);
            if (HandleLineErr(lReturn))
                continue;
            else
            {
                OutputDebugLineError(lReturn,
                    "lineClose unhandled error: ");
                break;
            }
        }
        while(lReturn != SUCCESS);

        OutputDebugString("Line Closed.\n");
    }

    // Call and Line are taken care of.  Finish cleaning up.

    // If there is device configuration information, free the memory.
    if (g_lpDeviceConfig)
        LocalFree(g_lpDeviceConfig);
    g_lpDeviceConfig = NULL;

    g_hCall = NULL;
    g_hLine = NULL;
    g_bConnected = FALSE;

    g_bTapiInUse = FALSE;
    g_bStoppingCall = FALSE; // allow HangupCall to be called again.
    OutputDebugString("Call stopped\n");

    // Update the user interface.
    UpdateStatusBar("Ready to make a call.",1,0);
    EnableMakeCall(g_hWndMainWindow, TRUE);

    // Need to free LocalAlloc()d buffer returned from I_lineGetCallStatus
    if (pLineCallStatus)
        LocalFree(pLineCallStatus);

    return TRUE;
}

//
//  FUNCTION: PostHangupCall()
//
//  PURPOSE: Posts a message to the main TAPI thread to hangup the call.
//
//  PARAMETERS:
//    none
//
//  RETURN VALUE:
//    none
//
//  COMMENTS:
//
//    TAPI is thread specific, meaning that only the thread that does the
//    lineInitialize can get asynchronous messages through the callback.
//    Since the HangupCall can potentially go into a loop waiting for
//    specific events, any other threads that call HangupCall can cause
//    timing confusion.  Best to just have other threads 'ask' the main thread
//    to hangup the call.
//

void PostHangupCall()
{
    PostMessage(g_hWndMainWindow, WM_COMMAND, IDM_HANGUPCALL, 0);
}

//
//  FUNCTION: DialCall()
//
//  PURPOSE: Get a number from the user and dial it.
//
//  PARAMETERS:
//    none
//
//  RETURN VALUE:
//    TRUE if able to get a number, find a line, and dial successfully.
//
//  COMMENTS:
//
//    This function makes several assumptions:
//    - The number dialed will always explicitly come from the user.
//    - There will only be one outgoing address per line.
//

BOOL DialCall()
{
    long lReturn;
    LPLINEADDRESSSTATUS lpLineAddressStatus = NULL;
    LPLINEDEVCAPS lpLineDevCaps = NULL;

    if (g_bTapiInUse)
    {
        OutputDebugString("A call is already being handled\n");
        return FALSE;
    }

    // If TAPI isn't initialized, its either because we couldn't initialize
    // at startup (and this might have been corrected by now), or because
    // a REINIT event was received.  In either case, try to init now.

    if (!g_hLineApp)
    {
        if (!InitializeTAPI(NULL))
            return FALSE;
    }

    // If there are no line devices installed on the machine, lets give
    // the user the opportunity to install one.
    if (g_dwNumDevs < 1)
    {
        if (!HandleNoDevicesInstalled())
            return FALSE;
    }

    // We now have a call active.  Prevent future calls.
    g_bTapiInUse = TRUE;
    EnableMakeCall(g_hWndMainWindow, FALSE);

    // Get a phone number from the user.
    // Phone number will be placed in global variables if successful
    if (!GetAddressToDial())
    {
        HangupCall();
        goto DeleteBuffers;
    }

    // Negotiate the API version to use for this device.
    g_dwAPIVersion = I_lineNegotiateAPIVersion(g_dwDeviceID);
    if (g_dwAPIVersion == 0)
    {
        MessageBox(g_hDlgParentWindow,
            "Line Version unsupported by this Sample",
            "Unable to Use Line",MB_OK);
        HangupCall();
        goto DeleteBuffers;
    }

    // Need to check the DevCaps to make sure this line is usable.
    // The 'Dial' dialog checks also, but better safe than sorry.
    lpLineDevCaps = I_lineGetDevCaps(lpLineDevCaps,
        g_dwDeviceID, g_dwAPIVersion);
    if (lpLineDevCaps == NULL)
    {
        HangupCall();
        MessageBox(g_hDlgParentWindow,
            "Error on Requested line",
            "Unable to Use Line",MB_OK);
        goto DeleteBuffers;
    }

    if (!(lpLineDevCaps->dwBearerModes & LINEBEARERMODE_VOICE ))
    {
        HangupCall();
        MessageBox(g_hDlgParentWindow,
            "Error on Requested line",
            "The selected line doesn't support VOICE capabilities",
            MB_OK);
        goto DeleteBuffers;
    }

    if (!(lpLineDevCaps->dwMediaModes & LINEMEDIAMODE_DATAMODEM))
    {
        HangupCall();
        MessageBox(g_hDlgParentWindow,
            "Error on Requested line",
            "The selected line doesn't support DATAMODEM capabilities",
            MB_OK);
        goto DeleteBuffers;
    }

    // Does this line have the capability to make calls?
    // It is possible that some lines can't make outbound calls.
    if (!(lpLineDevCaps->dwLineFeatures & LINEFEATURE_MAKECALL))
    {
        HangupCall();
        MessageBox(g_hDlgParentWindow,
            "Error on Requested line",
            "The selected line doesn't support MAKECALL capabilities",
            MB_OK);
        goto DeleteBuffers;
    }

    // Open the Line for an outgoing DATAMODEM call.
    do
    {
        lReturn = lineOpen(g_hLineApp, g_dwDeviceID, &g_hLine,
            g_dwAPIVersion, 0, 0,
            LINECALLPRIVILEGE_NONE, LINEMEDIAMODE_DATAMODEM,
            0);

        if(lReturn == LINEERR_ALLOCATED)
        {
            HangupCall();
            MessageBox(g_hDlgParentWindow,
                "Line is already in use by a non-TAPI application "
                "or by another TAPI Service Provider.",
                "Unable to Use Line",MB_OK);
            goto DeleteBuffers;
        }

        if (HandleLineErr(lReturn))
            continue;
        else
        {
            OutputDebugLineError(lReturn, "lineOpen unhandled error: ");
            MessageBox(g_hDlgParentWindow,
                "Error on Requested line",
                "Unable to Use Line",MB_OK);
            HangupCall();
            goto DeleteBuffers;
        }
    }
    while(lReturn != SUCCESS);

    // Tell the service provider that we want all notifications that
    // have anything to do with this line.
    do
    {
        // Set the messages we are interested in.

        // Note that while most applications aren't really interested
        // in dealing with all of the possible messages, its interesting
        // to see which come through the callback for testing purposes.

        lReturn = lineSetStatusMessages(g_hLine,
            LINEDEVSTATE_OTHER          |
            LINEDEVSTATE_RINGING        |
            LINEDEVSTATE_CONNECTED      |  // Important state!
            LINEDEVSTATE_DISCONNECTED   |  // Important state!
            LINEDEVSTATE_MSGWAITON      |
            LINEDEVSTATE_MSGWAITOFF     |
            LINEDEVSTATE_INSERVICE      |
            LINEDEVSTATE_OUTOFSERVICE   |  // Important state!
            LINEDEVSTATE_MAINTENANCE    |  // Important state!
            LINEDEVSTATE_OPEN           |
            LINEDEVSTATE_CLOSE          |
            LINEDEVSTATE_NUMCALLS       |
            LINEDEVSTATE_NUMCOMPLETIONS |
            LINEDEVSTATE_TERMINALS      |
            LINEDEVSTATE_ROAMMODE       |
            LINEDEVSTATE_BATTERY        |
            LINEDEVSTATE_SIGNAL         |
            LINEDEVSTATE_DEVSPECIFIC    |
            LINEDEVSTATE_REINIT         |  // Not allowed to disable this.
            LINEDEVSTATE_LOCK           |
            LINEDEVSTATE_CAPSCHANGE     |
            LINEDEVSTATE_CONFIGCHANGE   |
            LINEDEVSTATE_COMPLCANCEL    ,

            LINEADDRESSSTATE_OTHER      |
            LINEADDRESSSTATE_DEVSPECIFIC|
            LINEADDRESSSTATE_INUSEZERO  |
            LINEADDRESSSTATE_INUSEONE   |
            LINEADDRESSSTATE_INUSEMANY  |
            LINEADDRESSSTATE_NUMCALLS   |
            LINEADDRESSSTATE_FORWARD    |
            LINEADDRESSSTATE_TERMINALS  |
            LINEADDRESSSTATE_CAPSCHANGE);

        if (HandleLineErr(lReturn))
            continue;
        else
        {
            // If we do get an unhandled problem, we don't care.
            // We just won't get notifications.
            OutputDebugLineError(lReturn,
                "lineSetStatusMessages unhandled error: ");
            break;
        }
    }
    while(lReturn != SUCCESS);

    // Get LineAddressStatus so we can make sure the line
    // isn't already in use by a TAPI application.
    lpLineAddressStatus =
        I_lineGetAddressStatus(lpLineAddressStatus, g_hLine, 0);

    if (lpLineAddressStatus == NULL)
    {
        HangupCall();
        MessageBox(g_hDlgParentWindow,
            "Error on Requested line",
            "Unable to Use Line",MB_OK);
        goto DeleteBuffers;
    }

    // MAKECALL will be set if there are any available call appearances
    if ( ! ((lpLineAddressStatus -> dwAddressFeatures) &
            LINEADDRFEATURE_MAKECALL) )

{
        OutputDebugString("This line is not available to place a call.\n");
        HangupCall();
        MessageBox(g_hDlgParentWindow,
            "Requested line is already in use",
            "Unable to Use Line",MB_OK);
        goto DeleteBuffers;
    }

    // If the line was configured in the 'Dial' dialog, then
    // we need to actually complete the configuration.
    if (g_lpDeviceConfig)
        lineSetDevConfig(g_dwDeviceID, g_lpDeviceConfig,
            g_dwSizeDeviceConfig, "comm/datamodem");

    // Start dialing the number
    if (DialCallInParts(lpLineDevCaps, g_szDialableAddress,
            g_szDisplayableAddress))
    {
        OutputDebugString("DialCallInParts succeeded.\n");
    }
    else
    {
        OutputDebugString("DialCallInParts failed.\n");
        HangupCall();
        goto DeleteBuffers;
    }

DeleteBuffers:

    if (lpLineAddressStatus)
        LocalFree(lpLineAddressStatus);
    if (lpLineDevCaps)
        LocalFree(lpLineDevCaps);
    if (g_bTapiInUse)
        EnableHangupCall(g_hWndMainWindow, TRUE);

    return g_bTapiInUse;
}

//**************************************************
// These APIs are specific to this module
//**************************************************

//
//  FUNCTION: DialCallInParts(LPLINEDEVCAPS, LPCSTR, LPCSTR)
//
//  PURPOSE: Dials the call, handling special characters.
//
//  PARAMETERS:
//    lpLineDevCaps - LINEDEVCAPS for the line to be used.
//    lpszAddress   - Address to Dial.
//    lpszDisplayableAddress - Displayable Address.
//
//  RETURN VALUE:
//    Returns TRUE if we successfully Dial.
//
//  COMMENTS:
//
//    This function dials the Address and handles any
//    special characters in the address that the service provider
//    can't handle.  It requires input from the user to handle
//    these characters; this can cause problems for fully automated
//    dialing.
//
//    Note that we can return TRUE, even if we don't reach a
//    CONNECTED state.  DIalCallInParts returns as soon as the
//    Address is fully dialed or when an error occurs.
//
//

BOOL DialCallInParts(LPLINEDEVCAPS lpLineDevCaps,
    LPCSTR lpszAddress, LPCSTR lpszDisplayableAddress)
{
    LPLINECALLPARAMS  lpCallParams = NULL;
    LPLINEADDRESSCAPS lpAddressCaps = NULL;
    LPLINECALLSTATUS  lpLineCallStatus = NULL;

    long lReturn;
    int i;
    DWORD dwDevCapFlags;
    char szFilter[1+sizeof(g_sNonDialable)] = "";
    BOOL bFirstDial = TRUE;

    // Variables to handle Dialable Substring dialing.
    LPSTR lpDS; // This is just so we can free lpszDialableSubstring later.
    LPSTR lpszDialableSubstring;
    int nAddressLength = 0;
    int nCurrentAddress = 0;
    char chUnhandledCharacter;

    // Get the capabilities for the line device we're going to use.
    lpAddressCaps = I_lineGetAddressCaps(lpAddressCaps,
        g_dwDeviceID, 0, g_dwAPIVersion, 0);
    if (lpAddressCaps == NULL)
        return FALSE;

    // Setup our CallParams for DATAMODEM settings.
    lpCallParams = CreateCallParams (lpCallParams, lpszDisplayableAddress);
    if (lpCallParams == NULL)
        return FALSE;

    // Determine which special characters the service provider
    // does *not* handle so we can handle them manually.
    // Keep list of unhandled characters in szFilter.

    dwDevCapFlags = lpLineDevCaps -> dwDevCapFlags;  // SP handled characters.
    for (i = 0; i < g_sizeofNonDialable ; i++)
    {
        if ((dwDevCapFlags & g_sNonDialable[i].dwDevCapFlag) == 0)
        {
            strcat(szFilter, g_sNonDialable[i].szToken);
        }
    }

    // szFilter now contains the set of tokens which delimit dialable substrings

    // Setup the strings for substring dialing.

    nAddressLength = strlen(lpszAddress);
    lpDS = lpszDialableSubstring = (LPSTR) LocalAlloc(LPTR, nAddressLength + 1);
    if (lpszDialableSubstring == NULL)
    {
        OutputDebugLastError(GetLastError(), "LocalAlloc failed: ");
        HandleNoMem();
        goto errExit;
    }

    // Lets start dialing substrings!
    while (nCurrentAddress < nAddressLength)
    {
  retryAfterError:

        // Find the next undialable character
        i = strcspn(&lpszAddress[nCurrentAddress], szFilter);

        // Was there one before the end of the Address string?
        if (i + nCurrentAddress < nAddressLength)
        {
            // Make sure this device can handle partial dial.
            if (! (lpAddressCaps -> dwAddrCapFlags &
                   LINEADDRCAPFLAGS_PARTIALDIAL))
            {
                MessageBox(g_hDlgParentWindow,
                    "This line doesn't support partial dialing.\n",
                    "Warning",MB_OK);
                goto errExit;
            }
            // Remember what the unhandled character is so we can handle it.
            chUnhandledCharacter = lpszAddress[nCurrentAddress+i];

            // Copy the dialable string to the Substring.
            memcpy(lpszDialableSubstring, &lpszAddress[nCurrentAddress], i);

            // Terminate the substring with a ';' to signify the partial dial.
            lpszDialableSubstring[i] = ';';
            lpszDialableSubstring[i+1] = '\0';

            // Increment the address for next iteration.
            nCurrentAddress += i + 1;
        }
        else // No more partial dials.  Dial the rest of the Address.
        {
            lpszDialableSubstring = (LPSTR) &lpszAddress[nCurrentAddress];
            chUnhandledCharacter = 0;
            nCurrentAddress = nAddressLength;
        }

        do
        {
            if (bFirstDial)
                lReturn = WaitForReply(
                    lineMakeCall(g_hLine, &g_hCall, lpszDialableSubstring,
                        0, lpCallParams) );
            else
                lReturn = WaitForReply(
                    lineDial(g_hCall, lpszDialableSubstring, 0) );

            switch(lReturn)
            {
                // We should not have received these errors because of the
                // prefiltering strategy, but there may be some ill-behaved
                // service providers which do not correctly set their
                // devcapflags.  Add the character corresponding to the error
                // to the filter set and retry dialing.
                //
                case LINEERR_DIALBILLING:
                case LINEERR_DIALDIALTONE:
                case LINEERR_DIALQUIET:
                case LINEERR_DIALPROMPT:
                {
                    OutputDebugString("Service Provider incorrectly sets dwDevCapFlags\n");

                    for (i = 0; i < g_sizeofNonDialable; i++)
                        if (lReturn == g_sNonDialable[i].lError)
                        {
                            strcat(szFilter, g_sNonDialable[i].szToken);
                        }

                    goto retryAfterError;
                }

                case WAITERR_WAITABORTED:
                    OutputDebugString("While Dialing, WaitForReply aborted.\n");
                    goto errExit;

            }

            if (HandleLineErr(lReturn))
                continue;
            else
            {
                if (bFirstDial)
                    OutputDebugLineError(lReturn, "lineMakeCall unhandled error: ");
                else
                    OutputDebugLineError(lReturn, "lineDial unhandled error: ");

                goto errExit;
            }

        }
        while (lReturn != SUCCESS);

        bFirstDial = FALSE;

        // The dial was successful; now handle characters the service
        // provider didn't (if any).
        if (chUnhandledCharacter)
        {
            LPSTR lpMsg = "";

            // First, wait until we know we can continue dialing.  While the
            // last string is still pending to be dialed, we can't dial another.

            while(TRUE)
            {

                lpLineCallStatus = I_lineGetCallStatus(lpLineCallStatus, g_hCall);
                if (lpLineCallStatus == NULL)
                    goto errExit;

                // Does CallStatus say we can dial now?
                if ((lpLineCallStatus->dwCallFeatures) & LINECALLFEATURE_DIAL)
                {
                    OutputDebugString("Ok to continue dialing.\n");
                    break;
                }

                // We can't dial yet, so wait for a CALLSTATE message
                OutputDebugString("Waiting for dialing to be enabled.\n");

                if (WaitForCallState(I_LINECALLSTATE_ANY) != SUCCESS)
                    goto errExit;
            }

            for (i = 0; i < g_sizeofNonDialable; i++)
                if (chUnhandledCharacter == g_sNonDialable[i].szToken[0])
                    lpMsg = g_sNonDialable[i].szMsg;

            MessageBox(g_hDlgParentWindow, lpMsg, "Dialing Paused", MB_OK);
        }

    } // continue dialing until we dial all Dialable Substrings.

    LocalFree(lpCallParams);
    LocalFree(lpDS);
    LocalFree(lpAddressCaps);
    if (lpLineCallStatus)
        LocalFree(lpLineCallStatus);

    return TRUE;

  errExit:
        // if lineMakeCall has already been successfully called, there's a call in progress.
        // let the invoking routine shut down the call.
        // if the invoker did not clean up the call, it should be done here.

    if (lpLineCallStatus)
        LocalFree(lpLineCallStatus);
    if (lpDS)
        LocalFree(lpDS);
    if (lpCallParams)
        LocalFree(lpCallParams);
    if (lpAddressCaps)
        LocalFree(lpAddressCaps);

    return FALSE;
}

//
//  FUNCTION: CreateCallParams(LPLINECALLPARAMS, LPCSTR)
//
//  PURPOSE: Allocates and fills a LINECALLPARAMS structure
//
//  PARAMETERS:
//    lpCallParams -
//    lpszDisplayableAddress -
//
//  RETURN VALUE:
//    Returns a LPLINECALLPARAMS ready to use for dialing DATAMODEM calls.
//    Returns NULL if unable to allocate the structure.
//
//  COMMENTS:
//
//    If a non-NULL lpCallParams is passed in, it must have been allocated
//    with LocalAlloc, and can potentially be freed and reallocated.  It must
//    also have the dwTotalSize field correctly set.
//
//

LPLINECALLPARAMS CreateCallParams (
    LPLINECALLPARAMS lpCallParams, LPCSTR lpszDisplayableAddress)
{
    size_t sizeDisplayableAddress;

    if (lpszDisplayableAddress == NULL)
        lpszDisplayableAddress = "";

    sizeDisplayableAddress = strlen(lpszDisplayableAddress) + 1;

    lpCallParams = (LPLINECALLPARAMS) CheckAndReAllocBuffer(
        (LPVOID) lpCallParams,
        sizeof(LINECALLPARAMS) + sizeDisplayableAddress,
        "CreateCallParams: ");

    if (lpCallParams == NULL)
        return NULL;

    // This is where we configure the line for DATAMODEM usage.
    lpCallParams -> dwBearerMode = LINEBEARERMODE_VOICE;
    lpCallParams -> dwMediaMode  = LINEMEDIAMODE_DATAMODEM;

    // This specifies that we want to use only IDLE calls and
    // don't want to cut into a call that might not be IDLE (ie, in use).
    lpCallParams -> dwCallParamFlags = LINECALLPARAMFLAGS_IDLE;

    // if there are multiple addresses on line, use first anyway.
    // It will take a more complex application than a simple tty app
    // to use multiple addresses on a line anyway.
    lpCallParams -> dwAddressMode = LINEADDRESSMODE_ADDRESSID;
    lpCallParams -> dwAddressID = 0;

    // Since we don't know where we originated, leave these blank.
    lpCallParams -> dwOrigAddressSize = 0;
    lpCallParams -> dwOrigAddressOffset = 0;

    // Unimodem ignores these values.
    (lpCallParams -> DialParams) . dwDialSpeed = 0;
    (lpCallParams -> DialParams) . dwDigitDuration = 0;
    (lpCallParams -> DialParams) . dwDialPause = 0;
    (lpCallParams -> DialParams) . dwWaitForDialtone = 0;

    // Address we are dialing.
    lpCallParams -> dwDisplayableAddressOffset = sizeof(LINECALLPARAMS);
    lpCallParams -> dwDisplayableAddressSize = sizeDisplayableAddress;
    strcpy((LPSTR)lpCallParams + sizeof(LINECALLPARAMS),
           lpszDisplayableAddress);

    return lpCallParams;
}

//
//  FUNCTION: long WaitForReply(long)
//
//  PURPOSE: Resynchronize by waiting for a LINE_REPLY
//
//  PARAMETERS:
//    lRequestID - The asynchronous request ID that we're
//                 on a LINE_REPLY for.
//
//  RETURN VALUE:
//    - 0 if LINE_REPLY responded with a success.
//    - LINEERR constant if LINE_REPLY responded with a LINEERR
//    - 1 if the line was shut down before LINE_REPLY is received.
//
//  COMMENTS:
//
//    This function allows us to resynchronize an asynchronous
//    TAPI line call by waiting for the LINE_REPLY message.  It
//    waits until a LINE_REPLY is received or the line is shut down.
//
//    Note that this could cause re-entrancy problems as
//    well as mess with any message preprocessing that might
//    occur on this thread (such as TranslateAccelerator).
//
//    This function should to be called from the thread that did
//    lineInitialize, or the PeekMessage is on the wrong thread
//    and the synchronization is not guaranteed to work.  Also note
//    that if another PeekMessage loop is entered while waiting,
//    this could also cause synchronization problems.
//
//    One more note.  This function can potentially be re-entered
//    if the call is dropped for any reason while waiting.  If this
//    happens, just drop out and assume the wait has been canceled.
//    This is signaled by setting bReentered to FALSE when the function
//    is entered and TRUE when it is left.  If bReentered is ever TRUE
//    during the function, then the function was re-entered.
//
//    This function times out and returns WAITERR_WAITTIMEDOUT
//    after WAITTIMEOUT milliseconds have elapsed.
//
//

long WaitForReply (long lRequestID)
{
    static BOOL bReentered;
    bReentered = FALSE;

    if (lRequestID > SUCCESS)
    {
        MSG msg;
        DWORD dwTimeStarted;

        g_bReplyRecieved = FALSE;
        g_dwRequestedID = (DWORD) lRequestID;

        // Initializing this just in case there is a bug
        // that sets g_bReplyRecieved without setting the reply value.
        g_lAsyncReply = LINEERR_OPERATIONFAILED;

        dwTimeStarted = GetTickCount();

        while(!g_bReplyRecieved)
        {
            if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
            {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }

            // This should only occur if the line is shut down while waiting.
            if (!g_bTapiInUse || bReentered)
            {
                bReentered = TRUE;
                return WAITERR_WAITABORTED;
            }

            // Its a really bad idea to timeout a wait for a LINE_REPLY.
            // If we are execting a LINE_REPLY, we should wait till we get
            // it; it might take a long time to dial (for example).

            // If 5 seconds go by without a reply, it might be a good idea
            // to display a dialog box to tell the user that a
            // wait is in progress and to give the user the capability to
            // abort the wait.
        }

        bReentered = TRUE;
        return g_lAsyncReply;
    }

    bReentered = TRUE;
    return lRequestID;
}

//
//  FUNCTION: long WaitForCallState(DWORD)
//
//  PURPOSE: Wait for the line to reach a specific CallState.
//
//  PARAMETERS:
//    dwDesiredCallState - specific CallState to wait for.
//
//  RETURN VALUE:
//    Returns 0 (SUCCESS) when we reach the Desired CallState.
//    Returns WAITERR_WAITTIMEDOUT if timed out.
//    Returns WAITERR_WAITABORTED if call was closed while waiting.
//
//  COMMENTS:
//
//    This function allows us to synchronously wait for a line
//    to reach a specific LINESTATE or until the line is shut down.
//
//    Note that this could cause re-entrancy problems as
//    well as mess with any message preprocessing that might
//    occur on this thread (such as TranslateAccelerator).
//
//    One more note.  This function can potentially be re-entered
//    if the call is dropped for any reason while waiting.  If this
//    happens, just drop out and assume the wait has been canceled.
//    This is signaled by setting bReentered to FALSE when the function
//    is entered and TRUE when it is left.  If bReentered is ever TRUE
//    during the function, then the function was re-entered.
//
//    This function should to be called from the thread that did
//    lineInitialize, or the PeekMessage is on the wrong thread
//    and the synchronization is not guaranteed to work.  Also note
//    that if another PeekMessage loop is entered while waiting,
//    this could also cause synchronization problems.
//
//    If the constant value I_LINECALLSTATE_ANY is used for the
//    dwDesiredCallState, then WaitForCallState will return SUCCESS
//    upon receiving any CALLSTATE messages.
//
//
//

long WaitForCallState(DWORD dwDesiredCallState)
{
    MSG msg;
    DWORD dwTimeStarted;
    static BOOL bReentered;

    bReentered = FALSE;

    dwTimeStarted = GetTickCount();

    g_bCallStateReceived = FALSE;

    while ((dwDesiredCallState == I_LINECALLSTATE_ANY) ||
           (g_dwCallState != dwDesiredCallState))
    {
        if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }

        // If we are waiting for any call state and get one, succeed.
        if ((dwDesiredCallState == I_LINECALLSTATE_ANY) &&
            g_bCallStateReceived)
        {
            break;
        }

        // This should only occur if the line is shut down while waiting.
        if (!g_bTapiInUse || bReentered)
        {
            bReentered = TRUE;
            OutputDebugString("WAITABORTED\n");
            return WAITERR_WAITABORTED;
        }

        // If we don't get the reply in a reasonable time, we time out.
        if (GetTickCount() - dwTimeStarted > WAITTIMEOUT)
        {
            bReentered = TRUE;
            OutputDebugString("WAITTIMEDOUT\n");
            return WAITERR_WAITTIMEDOUT;
        }

    }

    bReentered = TRUE;
    return SUCCESS;
}

//**************************************************
// lineCallback Function and Handlers.
//**************************************************

//
//  FUNCTION: lineCallbackFunc(..)
//
//  PURPOSE: Receive asynchronous TAPI events
//
//  PARAMETERS:
//    dwDevice  - Device associated with the event, if any
//    dwMsg     - TAPI event that occurred.
//    dwCallbackInstance - User defined data supplied when opening the line.
//    dwParam1  - dwMsg specific information
//    dwParam2  - dwMsg specific information
//    dwParam3  - dwMsg specific information
//
//  RETURN VALUE:
//    none
//
//  COMMENTS:
//    This is the function where all asynchronous events will come.
//    Almost all events will be specific to an open line, but a few
//    will be general TAPI events (such as LINE_REINIT).
//
//    Its important to note that this callback will *ALWAYS* be
//    called in the context of the thread that does the lineInitialize.
//    Even if another thread (such as the COMM threads) calls the API
//    that would result in the callback being called, it will be called
//    in the context of the main thread (since in this sample, the main
//    thread does the lineInitialize).
//
//

void CALLBACK lineCallbackFunc(
    DWORD dwDevice, DWORD dwMsg, DWORD dwCallbackInstance,
    DWORD dwParam1, DWORD dwParam2, DWORD dwParam3)
{

    OutputDebugLineCallback(
        dwDevice, dwMsg, dwCallbackInstance,
        dwParam1, dwParam2, dwParam3);

    // All we do is dispatch the dwMsg to the correct handler.
    switch(dwMsg)
    {
        case LINE_CALLSTATE:
            DoLineCallState(dwDevice, dwMsg, dwCallbackInstance,
                dwParam1, dwParam2, dwParam3);
            break;

        case LINE_CLOSE:
            DoLineClose(dwDevice, dwMsg, dwCallbackInstance,
                dwParam1, dwParam2, dwParam3);
            break;

        case LINE_LINEDEVSTATE:
            DoLineDevState(dwDevice, dwMsg, dwCallbackInstance,
                dwParam1, dwParam2, dwParam3);
            break;

        case LINE_REPLY:
            DoLineReply(dwDevice, dwMsg, dwCallbackInstance,
                dwParam1, dwParam2, dwParam3);
            break;

        case LINE_CREATE:
            DoLineCreate(dwDevice, dwMsg, dwCallbackInstance,
                dwParam1, dwParam2, dwParam3);
            break;

        default:
            OutputDebugString("lineCallbackFunc message ignored\n");
            break;

    }

    return;

}

//
//  FUNCTION: DoLineReply(..)
//
//  PURPOSE: Handle LINE_REPLY asynchronous messages.
//
//  PARAMETERS:
//    dwDevice  - Line Handle associated with this LINE_REPLY.
//    dwMsg     - Should always be LINE_REPLY.
//    dwCallbackInstance - Unused by this sample.
//    dwParam1  - Asynchronous request ID.
//    dwParam2  - success or LINEERR error value.
//    dwParam3  - Unused.
//
//  RETURN VALUE:
//    none
//
//  COMMENTS:
//
//    All line API calls that return an asynchronous request ID
//    will eventually cause a LINE_REPLY message.  Handle it.
//
//    This sample assumes only one call at time, and that we wait
//    for a LINE_REPLY before making any other line API calls.
//
//    The only exception to the above is that we might shut down
//    the line before receiving a LINE_REPLY.
//
//

void DoLineReply(
    DWORD dwDevice, DWORD dwMessage, DWORD dwCallbackInstance,
    DWORD dwParam1, DWORD dwParam2, DWORD dwParam3)
{
    if ((long) dwParam2 != SUCCESS)
        OutputDebugLineError((long) dwParam2, "LINE_REPLY error: ");
    else
        OutputDebugString("LINE_REPLY: successfully replied.\n");

    // If we are currently waiting for this async Request ID
    // then set the global variables to acknowledge it.
    if (g_dwRequestedID == dwParam1)
    {
        g_bReplyRecieved = TRUE;
        g_lAsyncReply = (long) dwParam2;
    }
}

//
//  FUNCTION: DoLineClose(..)
//
//  PURPOSE: Handle LINE_CLOSE asynchronous messages.
//
//  PARAMETERS:
//    dwDevice  - Line Handle that was closed.
//    dwMsg     - Should always be LINE_CLOSE.
//    dwCallbackInstance - Unused by this sample.
//    dwParam1  - Unused.
//    dwParam2  - Unused.
//    dwParam3  - Unused.
//
//  RETURN VALUE:
//    none
//
//  COMMENTS:
//
//    This message is sent when something outside our app shuts
//    down a line in use.
//
//    The hLine (and any hCall on this line) are no longer valid.
//
//

void DoLineClose(
    DWORD dwDevice, DWORD dwMessage, DWORD dwCallbackInstance,
    DWORD dwParam1, DWORD dwParam2, DWORD dwParam3)
{
    // Line has been shut down.  Clean up our internal variables.
    g_hLine = NULL;
    g_hCall = NULL;
    UpdateStatusBar("Call was shut down.",1,0);
    MessageBox(g_hDlgParentWindow,
        "Call was shut down.","Warning",MB_OK);
    HangupCall();
}

//
//  FUNCTION: DoLineDevState(..)
//
//  PURPOSE: Handle LINE_LINEDEVSTATE asynchronous messages.
//
//  PARAMETERS:
//    dwDevice  - Line Handle that was closed.
//    dwMsg     - Should always be LINE_LINEDEVSTATE.
//    dwCallbackInstance - Unused by this sample.
//    dwParam1  - LINEDEVSTATE constant.
//    dwParam2  - Depends on dwParam1.
//    dwParam3  - Depends on dwParam1.
//
//  RETURN VALUE:
//    none
//
//  COMMENTS:
//
//    The LINE_LINEDEVSTATE message is received if the state of the line
//    changes.  Examples are RINGING, MAINTENANCE, MSGWAITON.  Very few of
//    these are relevant to this sample.
//
//    Assuming that any LINEDEVSTATE that removes the line from use by TAPI
//    will also send a LINE_CLOSE message.
//
//

void DoLineDevState(
    DWORD dwDevice, DWORD dwMessage, DWORD dwCallbackInstance,
    DWORD dwParam1, DWORD dwParam2, DWORD dwParam3)
{
    switch(dwParam1)
    {
        case LINEDEVSTATE_RINGING:
            UpdateStatusBar("Line Ringing",1,0);
            OutputDebugString("Line Ringing.\n");
            break;

        case LINEDEVSTATE_REINIT:
        // This is an important case!  Usually means that a service provider
        // has changed in such a way that requires TAPI to REINIT.
        // Note that there are both 'soft' REINITs and 'hard' REINITs.
        // Soft REINITs don't actually require a full shutdown but is instead
        // just an informational change that historically required a REINIT
        // to force the application to deal with.  TAPI API Version 1.3 apps
        // will still need to do a full REINIT for both hard and soft REINITs.

            switch(dwParam2)
            {
                // This is the hard REINIT.  No reason given, just REINIT.
                // TAPI is waiting for everyone to shutdown.
                // Our response is to immediately shutdown any calls,
                // shutdown our use of TAPI and notify the user.
                case 0:
                    ShutdownTAPI();
                    WarningBox("Tapi line configuration has changed.");
                    break;

                case LINE_CREATE:
                    OutputDebugString("Soft REINIT: LINE_CREATE.\n");
                    DoLineCreate(dwDevice, dwParam2, dwCallbackInstance,
                        dwParam3, 0, 0);
                    break;

                case LINE_LINEDEVSTATE:
                    OutputDebugString("Soft REINIT: LINE_LINEDEVSTATE.\n");
                    DoLineDevState(dwDevice, dwParam2, dwCallbackInstance,
                        dwParam3, 0, 0);
                    break;

                // There might be other reasons to send a soft reinit.
                // No need to to shutdown for these.
                default:
                    OutputDebugString("Ignoring soft REINIT\n");
                    break;
            }
            break;

        case LINEDEVSTATE_OUTOFSERVICE:
            WarningBox("Line selected is now Out of Service.");
            HangupCall();
            break;

        case LINEDEVSTATE_DISCONNECTED:
            WarningBox("Line selected is now disconnected.");
            HangupCall();
            break;

        case LINEDEVSTATE_MAINTENANCE:
            WarningBox("Line selected is now out for maintenance.");
            HangupCall();
            break;

        case LINEDEVSTATE_TRANSLATECHANGE:
            if (g_hDialog)
                PostMessage(g_hDialog, WM_COMMAND, IDC_CONFIGURATIONCHANGED, 0);
            break;

        case LINEDEVSTATE_REMOVED:
            OutputDebugString("A Line device has been removed;"
                " no action taken.\n");
            break;

        default:
            OutputDebugString("Unhandled LINEDEVSTATE message\n");
    }
}

//
//  FUNCTION: DoLineCreate(..)
//
//  PURPOSE: Handle LINE_LINECREATE asynchronous messages.
//
//  PARAMETERS:
//    dwDevice  - Unused.
//    dwMsg     - Should always be LINE_CREATE.
//    dwCallbackInstance - Unused.
//    dwParam1  - dwDeviceID of new Line created.
//    dwParam2  - Unused.
//    dwParam3  - Unused.
//
//  RETURN VALUE:
//    none
//
//  COMMENTS:
//
//    This message is new for Windows 95.  It is sent when a new line is
//    added to the system.  This allows us to handle new lines without having
//    to REINIT.  This allows for much more graceful Plug and Play.
//
//    This sample just changes the number of devices available and can use
//    it next time a call is made.  It also tells the "Dial" dialog.
//
//

void DoLineCreate(
    DWORD dwDevice, DWORD dwMessage, DWORD dwCallbackInstance,
    DWORD dwParam1, DWORD dwParam2, DWORD dwParam3)
{
    // dwParam1 is the Device ID of the new line.
    // Add one to get the number of total devices.
    if (g_dwNumDevs <= dwParam1)
        g_dwNumDevs = dwParam1+1;
    if (g_hDialog)
        PostMessage(g_hDialog, WM_COMMAND, IDC_LINECREATE, 0);

}

//
//  FUNCTION: DoLineCallState(..)
//
//  PURPOSE: Handle LINE_CALLSTATE asynchronous messages.
//
//  PARAMETERS:
//    dwDevice  - Handle to Call who's state is changing.
//    dwMsg     - Should always be LINE_CALLSTATE.
//    dwCallbackInstance - Unused by this sample.
//    dwParam1  - LINECALLSTATE constant specifying state change.

//    dwParam2  - Specific to dwParam1.
//    dwParam3  - LINECALLPRIVILEGE change, if any.
//
//  RETURN VALUE:
//    none
//
//  COMMENTS:
//
//    This message is received whenever a call changes state.  Lots of
//    things we do, ranging from notifying the user to closing the line
//    to actually connecting to the target of our phone call.
//
//    What we do is usually obvious based on the call state change.
//

void DoLineCallState(
    DWORD dwDevice, DWORD dwMessage, DWORD dwCallbackInstance,
    DWORD dwParam1, DWORD dwParam2, DWORD dwParam3)
{

    // Error if this CALLSTATE doesn't apply to our call in progress.
    if ((HCALL) dwDevice != g_hCall)
    {
        OutputDebugPrintf("LINE_CALLSTATE: Unknown device ID '0x%lx'.",
            dwDevice);
        return;
    }

    // This sets the global g_dwCallState variable so if we are waiting
    // for a specific call state change, we will know when it happens.
    g_dwCallState = dwParam1;
    g_bCallStateReceived = TRUE;

    // dwParam3 contains changes to LINECALLPRIVILEGE, if there are any.
    switch (dwParam3)
    {
        case 0:
            break; // no change to call state

         // close line if we are made monitor.  Shouldn't happen!
         case LINECALLPRIVILEGE_MONITOR:
            OutputDebugString("line given monitor privilege; closing\n");
            HangupCall();
            return;

         // close line if we are made owner.  Shouldn't happen!
        case LINECALLPRIVILEGE_OWNER:
            OutputDebugString("line given owner privilege; closing\n");
            HangupCall();
            break;

        default: // Shouldn't happen!  All cases handled.
            OutputDebugString("Unknown LINECALLPRIVILEGE message: closing\n");
            HangupCall();
            return;
    }

    // dwParam1 is the specific CALLSTATE change that is occurring.
    switch (dwParam1)
    {
        case LINECALLSTATE_DIALTONE:
            UpdateStatusBar("Dial Tone",1,0);
            OutputDebugString("Dial Tone\n");
            break;

        case LINECALLSTATE_DIALING:
            UpdateStatusBar("Dialing Call",1,0);
            OutputDebugString("Dialing\n");
            break;

        case LINECALLSTATE_PROCEEDING:
            UpdateStatusBar("Call is Proceeding",1,0);
            OutputDebugString("Proceeding\n");
            break;

        case LINECALLSTATE_RINGBACK:
            UpdateStatusBar("RingBack",1,0);
            OutputDebugString("RingBack\n");
            break;

        case LINECALLSTATE_BUSY:
            UpdateStatusBar("Line is busy",1,0);
            OutputDebugString("Line busy, shutting down\n");
            HangupCall();
            break;

        case LINECALLSTATE_IDLE:
            UpdateStatusBar("Line is idle",1,0);
            OutputDebugString("Line idle\n");
            HangupCall();
            break;

        case LINECALLSTATE_SPECIALINFO:
            UpdateStatusBar(
                "Special Info, probably couldn't dial number",1,0);
            OutputDebugString(
                "Special Info, probably couldn't dial number\n");
            HangupCall();
            break;

        case LINECALLSTATE_DISCONNECTED:
        {
            LPSTR pszReasonDisconnected;

            switch (dwParam2)
            {
                case LINEDISCONNECTMODE_NORMAL:
                    pszReasonDisconnected = "Remote Party Disconnected";
                    break;

                case LINEDISCONNECTMODE_UNKNOWN:
                    pszReasonDisconnected = "Disconnected: Unknown reason";
                    break;

                case LINEDISCONNECTMODE_REJECT:
                    pszReasonDisconnected = "Remote Party rejected call";
                    break;

                case LINEDISCONNECTMODE_PICKUP:
                    pszReasonDisconnected =
                        "Disconnected: Local phone picked up";
                    break;

                case LINEDISCONNECTMODE_FORWARDED:
                    pszReasonDisconnected = "Disconnected: Forwarded";
                    break;

                case LINEDISCONNECTMODE_BUSY:
                    pszReasonDisconnected = "Disconnected: Busy";
                    break;

                case LINEDISCONNECTMODE_NOANSWER:
                    pszReasonDisconnected = "Disconnected: No Answer";
                    break;

                case LINEDISCONNECTMODE_BADADDRESS:
                    pszReasonDisconnected = "Disconnected: Bad Address";
                    break;

                case LINEDISCONNECTMODE_UNREACHABLE:
                    pszReasonDisconnected = "Disconnected: Unreachable";
                    break;

                case LINEDISCONNECTMODE_CONGESTION:
                    pszReasonDisconnected = "Disconnected: Congestion";
                    break;

                case LINEDISCONNECTMODE_INCOMPATIBLE:
                    pszReasonDisconnected = "Disconnected: Incompatible";
                    break;

                case LINEDISCONNECTMODE_UNAVAIL:
                    pszReasonDisconnected = "Disconnected: Unavail";
                    break;

                case LINEDISCONNECTMODE_NODIALTONE:
                    pszReasonDisconnected = "Disconnected: No Dial Tone";
                    break;

                default:
                    pszReasonDisconnected =
                        "Disconnected: LINECALLSTATE; Bad Reason";
                    break;

            }

            UpdateStatusBar(pszReasonDisconnected,1,0);
            OutputDebugString(pszReasonDisconnected);
            OutputDebugString("\n");
            HangupCall();
            break;
        }

        case LINECALLSTATE_CONNECTED:  // CONNECTED!!!
        {
            LPVARSTRING lpVarString = NULL;
            DWORD dwSizeofVarString = sizeof(VARSTRING) + 1024;
            HANDLE hCommFile = NULL;
            long lReturn;

            // Very first, make sure this isn't a duplicated message.
            // A CALLSTATE message can be sent whenever there is a
            // change to the capabilities of a line, meaning that it is
            // possible to receive multiple CONNECTED messages per call.
            // The CONNECTED CALLSTATE message is the only one in TapiComm
            // where it would cause problems if it where sent more
            // than once.

            if (g_bConnected)
                break;

            g_bConnected = TRUE;

            // Get the handle to the comm port from the driver so we can start
            // communicating.  This is returned in a LPVARSTRING structure.
            do
            {
                // Allocate the VARSTRING structure
                lpVarString = CheckAndReAllocBuffer((LPVOID) lpVarString,
                    dwSizeofVarString,"lineGetID: ");

                if (lpVarString == NULL)
                    goto ErrorConnecting;

                // Fill the VARSTRING structure
                lReturn = lineGetID(0, 0, g_hCall, LINECALLSELECT_CALL,
                    lpVarString, "comm/datamodem");

                if (HandleLineErr(lReturn))
                    ; // Still need to check if structure was big enough.
                else
                {
                    OutputDebugLineError(lReturn,
                        "lineGetID unhandled error: ");
                    goto ErrorConnecting;
                }

                // If the VARSTRING wasn't big enough, loop again.
                if ((lpVarString -> dwNeededSize) > (lpVarString -> dwTotalSize))
                {
                    dwSizeofVarString = lpVarString -> dwNeededSize;
                    lReturn = -1; // Lets loop again.
                }
            }
            while(lReturn != SUCCESS);

            OutputDebugString("Connected!  Starting communications!\n");

            // Again, the handle to the comm port is contained in a
            // LPVARSTRING structure.  Thus, the handle is the very first
            // thing after the end of the structure.  Note that the name of
            // the comm port is right after the handle, but I don't want it.
            hCommFile =
                *((LPHANDLE)((LPBYTE)lpVarString +
                    lpVarString -> dwStringOffset));

            // Started communications!
            if (StartComm(hCommFile))
            {
                char szBuff[300];

                wsprintf(szBuff,"Connected to '%s'",g_szDisplayableAddress);
                UpdateStatusBar(szBuff, 1, 0);

                LocalFree(lpVarString);
                break;
            }

            // Couldn't start communications.  Clean up instead.
          ErrorConnecting:

            // Its very important that we close all Win32 handles.
            // The CommCode module is responsible for closing the hCommFile
            // handle if it succeeds in starting communications.
            if (hCommFile)
                CloseHandle(hCommFile);

            HangupCall();
            {
                char szBuff[300];
                wsprintf(szBuff,"Failed to Connect to '%s'",
                    g_szDisplayableAddress);
                UpdateStatusBar(szBuff, 1, 0);
            }

            if (lpVarString)
                LocalFree(lpVarString);

            break;
        }

        default:
            OutputDebugString("Unhandled LINECALLSTATE message\n");
            break;
    }
}

//**************************************************
// line API Wrapper Functions.
//**************************************************

//
//  FUNCTION: LPVOID CheckAndReAllocBuffer(LPVOID, size_t, LPCSTR)
//
//  PURPOSE: Checks and ReAllocates a buffer if necessary.
//
//  PARAMETERS:
//    lpBuffer          - Pointer to buffer to be checked.  Can be NULL.
//    sizeBufferMinimum - Minimum size that lpBuffer should be.
//    szApiPhrase       - Phrase to print if an error occurs.
//
//  RETURN VALUE:
//    Returns a pointer to a valid buffer that is guarenteed to be
//    at least sizeBufferMinimum size.
//    Returns NULL if an error occured.
//
//  COMMENTS:
//
//    This function is a helper function intended to make all of the
//    line API Wrapper Functions much simplier.  It allocates (or
//    reallocates) a buffer of the requested size.
//
//    The returned pointer has been allocated with LocalAlloc,
//    so LocalFree has to be called on it when you're finished with it,
//    or there will be a memory leak.
//
//    Similarly, if a pointer is passed in, it *must* have been allocated
//    with LocalAlloc and it could potentially be LocalFree()d.
//
//    If lpBuffer == NULL, then a new buffer is allocated.  It is
//    normal to pass in NULL for this parameter the first time and only
//    pass in a pointer if the buffer needs to be reallocated.
//
//    szApiPhrase is used only for debugging purposes.
//
//    It is assumed that the buffer returned from this function will be used
//    to contain a variable sized structure.  Thus, the dwTotalSize field
//    is always filled in before returning the pointer.
//
//

LPVOID CheckAndReAllocBuffer(
    LPVOID lpBuffer, size_t sizeBufferMinimum, LPCSTR szApiPhrase)
{
    size_t sizeBuffer;

    if (lpBuffer == NULL)  // Allocate the buffer if necessary.
    {
        sizeBuffer = sizeBufferMinimum;
        lpBuffer = (LPVOID) LocalAlloc(LPTR, sizeBuffer);

        if (lpBuffer == NULL)
        {
            OutputDebugString(szApiPhrase);
            OutputDebugLastError(GetLastError(),"LocalAlloc : ");
            HandleNoMem();
            return NULL;
        }
    }
    else // If the structure already exists, make sure its good.
    {
        sizeBuffer = LocalSize((HLOCAL) lpBuffer);

        if (sizeBuffer == 0) // Bad pointer?
        {
            OutputDebugString(szApiPhrase);
            OutputDebugLastError(GetLastError(),"LocalSize : ");
            return NULL;
        }

        // Was the buffer big enough for the structure?
        if (sizeBuffer < sizeBufferMinimum)
        {
            OutputDebugString(szApiPhrase);
            OutputDebugString("Reallocating structure\n");
            LocalFree(lpBuffer);
            return CheckAndReAllocBuffer(NULL, sizeBufferMinimum, szApiPhrase);
        }

        // Lets zero the buffer out.
        memset(lpBuffer, 0, sizeBuffer);
    }

    ((LPVARSTRING) lpBuffer ) -> dwTotalSize = (DWORD) sizeBuffer;
    return lpBuffer;
}

//
//  FUNCTION: DWORD I_lineNegotiateAPIVersion(DWORD)
//
//  PURPOSE: Negotiate an API Version to use for a specific device.
//
//  PARAMETERS:
//    dwDeviceID - device to negotiate an API Version for.
//
//  RETURN VALUE:
//    Returns the API Version to use for this line if successful.
//    Returns 0 if negotiations fall through.
//
//  COMMENTS:
//
//    This wrapper function not only negotiates the API, but handles
//    LINEERR errors that can occur while negotiating.
//
//

DWORD I_lineNegotiateAPIVersion(DWORD dwDeviceID)
{
    LINEEXTENSIONID LineExtensionID;
    long lReturn;
    DWORD dwLocalAPIVersion;

    do
    {
        lReturn = lineNegotiateAPIVersion(g_hLineApp, dwDeviceID,
            SAMPLE_TAPI_VERSION, SAMPLE_TAPI_VERSION,
            &dwLocalAPIVersion, &LineExtensionID);

        if (lReturn == LINEERR_INCOMPATIBLEAPIVERSION)
        {
            OutputDebugString(
                "lineNegotiateAPIVersion, INCOMPATIBLEAPIVERSION.\n");
            return 0;
        }

        if (HandleLineErr(lReturn))
            continue;
        else
        {
            OutputDebugLineError(lReturn,
                "lineNegotiateAPIVersion unhandled error: ");
            return 0;
        }
    }
    while(lReturn != SUCCESS);

    return dwLocalAPIVersion;
}

//
//  FUNCTION: I_lineGetDevCaps(LPLINEDEVCAPS, DWORD , DWORD)
//
//  PURPOSE: Retrieve a LINEDEVCAPS structure for the specified line.
//
//  PARAMETERS:
//    lpLineDevCaps - Pointer to a LINEDEVCAPS structure to use.
//    dwDeviceID    - device to get the DevCaps for.
//    dwAPIVersion  - API Version to use while getting DevCaps.
//
//  RETURN VALUE:
//    Returns a pointer to a LINEDEVCAPS structure if successful.
//    Returns NULL if unsuccessful.
//
//  COMMENTS:
//
//    This function is a wrapper around lineGetDevCaps to make it easy
//    to handle the variable sized structure and any errors received.
//
//    The returned structure has been allocated with LocalAlloc,
//    so LocalFree has to be called on it when you're finished with it,
//    or there will be a memory leak.
//
//    Similarly, if a lpLineDevCaps structure is passed in, it *must*
//    have been allocated with LocalAlloc and it could potentially be
//    LocalFree()d.
//
//    If lpLineDevCaps == NULL, then a new structure is allocated.  It is
//    normal to pass in NULL for this parameter unless you want to use a
//    lpLineDevCaps that has been returned by a previous I_lineGetDevCaps
//    call.
//
//

LPLINEDEVCAPS I_lineGetDevCaps(
    LPLINEDEVCAPS lpLineDevCaps,
    DWORD dwDeviceID, DWORD dwAPIVersion)
{
    size_t sizeofLineDevCaps = sizeof(LINEDEVCAPS) + 1024;
    long lReturn;

    // Continue this loop until the structure is big enough.
    while(TRUE)
    {
        // Make sure the buffer exists, is valid and big enough.
        lpLineDevCaps =
            (LPLINEDEVCAPS) CheckAndReAllocBuffer(
                (LPVOID) lpLineDevCaps, // Pointer to existing buffer, if any
                sizeofLineDevCaps,      // Minimum size the buffer should be
                "lineGetDevCaps");      // Phrase to tag errors, if any.

        if (lpLineDevCaps == NULL)
            return NULL;

        // Make the call to fill the structure.
        do
        {
            lReturn =
                lineGetDevCaps(g_hLineApp,
                    dwDeviceID, dwAPIVersion, 0, lpLineDevCaps);

            if (HandleLineErr(lReturn))
                continue;
            else
            {
                OutputDebugLineError(lReturn,
                    "lineGetDevCaps unhandled error: ");
                LocalFree(lpLineDevCaps);
                return NULL;
            }
        }
        while (lReturn != SUCCESS);

        // If the buffer was big enough, then succeed.
        if ((lpLineDevCaps -> dwNeededSize) <= (lpLineDevCaps -> dwTotalSize))
            return lpLineDevCaps;

        // Buffer wasn't big enough.  Make it bigger and try again.
        sizeofLineDevCaps = lpLineDevCaps -> dwNeededSize;
    }
}

//
//  FUNCTION: I_lineGetAddressStatus(LPLINEADDRESSSTATUS, HLINE, DWORD)
//
//  PURPOSE: Retrieve a LINEADDRESSSTATUS structure for the specified line.

//
//  PARAMETERS:
//    lpLineAddressStatus - Pointer to a LINEADDRESSSTATUS structure to use.
//    hLine       - Handle of line to get the AddressStatus of.
//    dwAddressID - Address ID on the hLine to be used.
//
//  RETURN VALUE:
//    Returns a pointer to a LINEADDRESSSTATUS structure if successful.
//    Returns NULL if unsuccessful.
//
//  COMMENTS:
//
//    This function is a wrapper around lineGetAddressStatus to make it easy
//    to handle the variable sized structure and any errors received.
//
//    The returned structure has been allocated with LocalAlloc,
//    so LocalFree has to be called on it when you're finished with it,
//    or there will be a memory leak.
//
//    Similarly, if a lpLineAddressStatus structure is passed in, it *must*
//    have been allocated with LocalAlloc and it could potentially be
//    LocalFree()d.
//
//    If lpLineAddressStatus == NULL, then a new structure is allocated.  It
//    is normal to pass in NULL for this parameter unless you want to use a
//    lpLineAddressStatus that has been returned by previous
//    I_lineGetAddressStatus call.
//
//

LPLINEADDRESSSTATUS I_lineGetAddressStatus(
    LPLINEADDRESSSTATUS lpLineAddressStatus,
    HLINE hLine, DWORD dwAddressID)
{
    size_t sizeofLineAddressStatus = sizeof(LINEADDRESSSTATUS) + 1024;
    long lReturn;

    // Continue this loop until the structure is big enough.
    while(TRUE)
    {
        // Make sure the buffer exists, is valid and big enough.
        lpLineAddressStatus =
            (LPLINEADDRESSSTATUS) CheckAndReAllocBuffer(
                (LPVOID) lpLineAddressStatus,
                sizeofLineAddressStatus,
                "lineGetAddressStatus");

        if (lpLineAddressStatus == NULL)
            return NULL;

        // Make the call to fill the structure.
        do
        {
            lReturn =
                lineGetAddressStatus(hLine, dwAddressID, lpLineAddressStatus);

            if (HandleLineErr(lReturn))
                continue;
            else
            {
                OutputDebugLineError(lReturn,
                    "lineGetAddressStatus unhandled error: ");
                LocalFree(lpLineAddressStatus);
                return NULL;
            }
        }
        while (lReturn != SUCCESS);

        // If the buffer was big enough, then succeed.
        if ((lpLineAddressStatus -> dwNeededSize) <=
            (lpLineAddressStatus -> dwTotalSize))
        {
            return lpLineAddressStatus;
        }

        // Buffer wasn't big enough.  Make it bigger and try again.
        sizeofLineAddressStatus = lpLineAddressStatus -> dwNeededSize;
    }
}

//
//  FUNCTION: I_lineGetCallStatus(LPLINECALLSTATUS, HCALL)
//
//  PURPOSE: Retrieve a LINECALLSTATUS structure for the specified line.
//
//  PARAMETERS:
//    lpLineCallStatus - Pointer to a LINECALLSTATUS structure to use.
//    hCall - Handle of call to get the CallStatus of.
//
//  RETURN VALUE:
//    Returns a pointer to a LINECALLSTATUS structure if successful.
//    Returns NULL if unsuccessful.
//
//  COMMENTS:
//
//    This function is a wrapper around lineGetCallStatus to make it easy
//    to handle the variable sized structure and any errors received.
//
//    The returned structure has been allocated with LocalAlloc,
//    so LocalFree has to be called on it when you're finished with it,
//    or there will be a memory leak.
//
//    Similarly, if a lpLineCallStatus structure is passed in, it *must*
//    have been allocated with LocalAlloc and it could potentially be
//    LocalFree()d.
//
//    If lpLineCallStatus == NULL, then a new structure is allocated.  It
//    is normal to pass in NULL for this parameter unless you want to use a
//    lpLineCallStatus that has been returned by previous I_lineGetCallStatus
//    call.
//
//

LPLINECALLSTATUS I_lineGetCallStatus(
    LPLINECALLSTATUS lpLineCallStatus,
    HCALL hCall)
{
    size_t sizeofLineCallStatus = sizeof(LINECALLSTATUS) + 1024;
    long lReturn;

    // Continue this loop until the structure is big enough.
    while(TRUE)
    {
        // Make sure the buffer exists, is valid and big enough.
        lpLineCallStatus =
            (LPLINECALLSTATUS) CheckAndReAllocBuffer(
                (LPVOID) lpLineCallStatus,
                sizeofLineCallStatus,
                "lineGetCallStatus");

        if (lpLineCallStatus == NULL)
            return NULL;

        // Make the call to fill the structure.
        do
        {
            lReturn =
                lineGetCallStatus(hCall, lpLineCallStatus);

            if (HandleLineErr(lReturn))
                continue;
            else
            {
                OutputDebugLineError(lReturn,
                    "lineGetCallStatus unhandled error: ");
                LocalFree(lpLineCallStatus);
                return NULL;
            }
        }
        while (lReturn != SUCCESS);

        // If the buffer was big enough, then succeed.
        if ((lpLineCallStatus -> dwNeededSize) <=
            (lpLineCallStatus -> dwTotalSize))
        {
            return lpLineCallStatus;
        }

        // Buffer wasn't big enough.  Make it bigger and try again.
        sizeofLineCallStatus = lpLineCallStatus -> dwNeededSize;
    }
}

//
//  FUNCTION: I_lineTranslateAddress
//              (LPLINETRANSLATEOUTPUT, DWORD, DWORD, LPCSTR)
//
//  PURPOSE: Retrieve a LINECALLSTATUS structure for the specified line.
//
//  PARAMETERS:
//    lpLineTranslateOutput - Pointer to a LINETRANSLATEOUTPUT structure.
//    dwDeviceID      - Device that we're translating for.
//    dwAPIVersion    - API Version to use.
//    lpszDialAddress - pointer to the DialAddress string to translate.
//
//  RETURN VALUE:
//    Returns a pointer to a LINETRANSLATEOUTPUT structure if successful.
//    Returns NULL if unsuccessful.
//
//  COMMENTS:
//
//    This function is a wrapper around lineGetTranslateOutput to make it
//    easy to handle the variable sized structure and any errors received.
//
//    The returned structure has been allocated with LocalAlloc,
//    so LocalFree has to be called on it when you're finished with it,
//    or there will be a memory leak.
//
//    Similarly, if a lpLineTranslateOutput structure is passed in, it
//    *must* have been allocated with LocalAlloc and it could potentially be
//    LocalFree()d.
//
//    If lpLineTranslateOutput == NULL, then a new structure is allocated.
//    It is normal to pass in NULL for this parameter unless you want to use
//    a lpLineTranslateOutput that has been returned by previous
//    I_lineTranslateOutput call.
//
//

LPLINETRANSLATEOUTPUT I_lineTranslateAddress(
    LPLINETRANSLATEOUTPUT lpLineTranslateOutput,
    DWORD dwDeviceID, DWORD dwAPIVersion,
    LPCSTR lpszDialAddress)
{
    size_t sizeofLineTranslateOutput = sizeof(LINETRANSLATEOUTPUT) + 1024;
    long lReturn;

    // Continue this loop until the structure is big enough.
    while(TRUE)
    {
        // Make sure the buffer exists, is valid and big enough.
        lpLineTranslateOutput =
            (LPLINETRANSLATEOUTPUT) CheckAndReAllocBuffer(
                (LPVOID) lpLineTranslateOutput,
                sizeofLineTranslateOutput,
                "lineTranslateOutput");

        if (lpLineTranslateOutput == NULL)
            return NULL;

        // Make the call to fill the structure.
        do
        {
            // Note that CALLWAITING is disabled
            // (assuming the service provider can disable it)
            lReturn =
                lineTranslateAddress(g_hLineApp, dwDeviceID, dwAPIVersion,
                    lpszDialAddress, 0,
                    LINETRANSLATEOPTION_CANCELCALLWAITING,
                    lpLineTranslateOutput);

            // If the address isn't translatable, notify the user.
            if (lReturn == LINEERR_INVALADDRESS)
                MessageBox(g_hDlgParentWindow,
                    "Unable to translate phone number","Warning",MB_OK);

            if (HandleLineErr(lReturn))
                continue;
            else
            {
                OutputDebugLineError(lReturn,
                    "lineTranslateOutput unhandled error: ");
                LocalFree(lpLineTranslateOutput);
                return NULL;
            }
        }
        while (lReturn != SUCCESS);

        // If the buffer was big enough, then succeed.
        if ((lpLineTranslateOutput -> dwNeededSize) <=
            (lpLineTranslateOutput -> dwTotalSize))
        {
            return lpLineTranslateOutput;
        }

        // Buffer wasn't big enough.  Make it bigger and try again.
        sizeofLineTranslateOutput = lpLineTranslateOutput -> dwNeededSize;
    }
}

//
//  FUNCTION: I_lineGetAddressCaps(LPLINEADDRESSCAPS, ..)
//
//  PURPOSE: Retrieve a LINEADDRESSCAPS structure for the specified line.
//
//  PARAMETERS:
//    lpLineAddressCaps - Pointer to a LINEADDRESSCAPS, or NULL.
//    dwDeviceID        - Device to get the address caps for.
//    dwAddressID       - This sample always assumes the first address.
//    dwAPIVersion      - API version negotiated for the device.
//    dwExtVersion      - Always 0 for this sample.
//
//  RETURN VALUE:
//    Returns a pointer to a LINEADDRESSCAPS structure if successful.
//    Returns NULL if unsuccessful.
//
//  COMMENTS:
//
//    This function is a wrapper around lineGetAddressCaps to make it easy
//    to handle the variable sized structure and any errors received.
//
//    The returned structure has been allocated with LocalAlloc,
//    so LocalFree has to be called on it when you're finished with it,
//    or there will be a memory leak.
//
//    Similarly, if a lpLineAddressCaps structure is passed in, it *must*
//    have been allocated with LocalAlloc and it could potentially be
//    LocalFree()d.  It also *must* have the dwTotalSize field set.
//
//    If lpLineAddressCaps == NULL, then a new structure is allocated.  It
//    is normal to pass in NULL for this parameter unless you want to use a
//    lpLineCallStatus that has been returned by previous I_lineGetAddressCaps
//    call.
//
//

LPLINEADDRESSCAPS I_lineGetAddressCaps (
    LPLINEADDRESSCAPS lpLineAddressCaps,
    DWORD dwDeviceID, DWORD dwAddressID,
    DWORD dwAPIVersion, DWORD dwExtVersion)
{
    size_t sizeofLineAddressCaps = sizeof(LINEADDRESSCAPS) + 1024;
    long lReturn;

    // Continue this loop until the structure is big enough.
    while(TRUE)
    {
        // Make sure the buffer exists, is valid and big enough.
        lpLineAddressCaps =
            (LPLINEADDRESSCAPS) CheckAndReAllocBuffer(
                (LPVOID) lpLineAddressCaps,
                sizeofLineAddressCaps,
                "lineGetAddressCaps");

        if (lpLineAddressCaps == NULL)
            return NULL;

        // Make the call to fill the structure.
        do
        {
            lReturn =
                lineGetAddressCaps(g_hLineApp,
                    dwDeviceID, dwAddressID, dwAPIVersion, dwExtVersion,
                    lpLineAddressCaps);

            if (HandleLineErr(lReturn))
                continue;
            else
            {
                OutputDebugLineError(lReturn,
                    "lineGetAddressCaps unhandled error: ");
                LocalFree(lpLineAddressCaps);
                return NULL;
            }
        }
        while (lReturn != SUCCESS);

        // If the buffer was big enough, then succeed.
        if ((lpLineAddressCaps -> dwNeededSize) <=
            (lpLineAddressCaps -> dwTotalSize))
        {
            return lpLineAddressCaps;
        }

        // Buffer wasn't big enough.  Make it bigger and try again.
        sizeofLineAddressCaps = lpLineAddressCaps -> dwNeededSize;

}
}

//**************************************************
// LINEERR Error Handlers
//**************************************************

//
//  FUNCTION: HandleLineErr(long)
//
//  PURPOSE: Handle several standard LINEERR errors
//
//  PARAMETERS:
//    lLineErr - Error code to be handled.
//
//  RETURN VALUE:
//    Return TRUE if lLineErr wasn't an error, or if the
//      error was successfully handled and cleared up.
//    Return FALSE if lLineErr was an unhandled error.
//
//  COMMENTS:
//
//    This is the main error handler for all TAPI line APIs.
//    It handles (by correcting or just notifying the user)
//    most of the errors that can occur while using TAPI line APIs.
//
//    Note that many errors still return FALSE (unhandled) even
//    if a dialog is displayed.  Often, the dialog is just notifying
//    the user why the action was canceled.
//
//
//

BOOL HandleLineErr(long lLineErr)
{
    // lLineErr is really an async request ID, not an error.
    if (lLineErr > SUCCESS)
        return FALSE;

    // All we do is dispatch the correct error handler.
    switch(lLineErr)
    {
        case SUCCESS:
            return TRUE;

        case LINEERR_INVALCARD:
        case LINEERR_INVALLOCATION:
        case LINEERR_INIFILECORRUPT:
            return HandleIniFileCorrupt();

        case LINEERR_NODRIVER:
            return HandleNoDriver();

        case LINEERR_REINIT:
            return HandleReInit();

        case LINEERR_NOMULTIPLEINSTANCE:
            return HandleNoMultipleInstance();

        case LINEERR_NOMEM:
            return HandleNoMem();

        case LINEERR_OPERATIONFAILED:
            return HandleOperationFailed();

        case LINEERR_RESOURCEUNAVAIL:
            return HandleResourceUnavail();

        // Unhandled errors fail.
        default:
            return FALSE;
    }
}

//
//  FUNCTION: HandleIniFileCorrupt
//
//  PURPOSE: Handle INIFILECORRUPT error.
//
//  PARAMETERS:
//    none
//
//  RETURN VALUE:
//    TRUE  - error was corrected.
//    FALSE - error was not corrected.
//
//  COMMENTS:
//
//    This error shouldn't happen under Windows 95 anymore.  The TAPI.DLL
//    takes care of correcting this problem.  If it does happen, just
//    notify the user.
//

BOOL HandleIniFileCorrupt()
{
    if (IDCANCEL == MessageBox(g_hDlgParentWindow,
        "Configuration information relating to Telephony "
        "services appears to be corrupt.\n"
        "This could be the first time you have used the Telephony services.\n"
        "Would you like to configure the Telephony services?",
        "Warning",MB_OKCANCEL))
    {
        return FALSE;
    }

    lineTranslateDialog(g_hLineApp, 0, SAMPLE_TAPI_VERSION,
        g_hDlgParentWindow, NULL);

    return TRUE;
}

//
//  FUNCTION: HandleNoDriver
//
//  PURPOSE: Handle NODRIVER error.
//
//  PARAMETERS:
//    none
//
//  RETURN VALUE:
//    TRUE  - error was corrected.
//    FALSE - error was not corrected.
//
//  COMMENTS:
//
//

BOOL HandleNoDriver()
{
    int mbRet;
    mbRet = MessageBox(g_hDlgParentWindow,
        "One of the components of the Telephony device driver is missing.\n"
            "Use the Control Panel to set up the driver properly.",
        "Warning",MB_OK);

    return FALSE;
}

//
//  FUNCTION: HandleNoMultipleInstance
//
//  PURPOSE: Handle NOMULTIPLEINSTANCE error.
//
//  PARAMETERS:
//    none
//
//  RETURN VALUE:
//    TRUE  - error was corrected.
//    FALSE - error was not corrected.
//
//  COMMENTS:
//
//

BOOL HandleNoMultipleInstance()
{
    MessageBox(g_hDlgParentWindow,
        "You have two copies of the same Telephony driver installed.\n"
            "Use the Control Panel to remove one of the copies.",
        "Warning",MB_OK);

    return FALSE;
}

//
//  FUNCTION: HandleReInit
//
//  PURPOSE: Handle REINIT error.
//
//  PARAMETERS:
//    none
//
//  RETURN VALUE:
//    TRUE  - error was corrected.
//    FALSE - error was not corrected.
//
//  COMMENTS:
//
//

BOOL HandleReInit()
{
    ShutdownTAPI();
    return FALSE;
}

//
//  FUNCTION: HandleNoMem
//
//  PURPOSE: Handle NOMEM error.
//
//  PARAMETERS:
//    none
//
//  RETURN VALUE:
//    TRUE  - error was corrected.
//    FALSE - error was not corrected.
//
//  COMMENTS:
//    This is also called if I run out of memory for LocalAlloc()s
//
//

BOOL HandleNoMem()
{
    MessageBox(g_hDlgParentWindow,
        "Out of Memory error, canceling action.","Error", MB_OK);
    return FALSE;
}

//
//  FUNCTION: HandleOperationFailed
//
//  PURPOSE: Handle OPERATIONFAILED error.
//
//  PARAMETERS:
//    none
//
//  RETURN VALUE:
//    TRUE  - error was corrected.
//    FALSE - error was not corrected.
//
//  COMMENTS:
//
//

BOOL HandleOperationFailed()
{
    MessageBox(g_hDlgParentWindow,
        "TAPI Operation Failed for unknown reasons.",
        "Error", MB_OK);
    return FALSE;
}

//
//  FUNCTION: HandleResourceUnavail
//
//  PURPOSE: Handle RESOURCEUNAVAIL error.
//
//  PARAMETERS:
//    none
//
//  RETURN VALUE:
//    TRUE  - error was corrected.
//    FALSE - error was not corrected.
//
//  COMMENTS:
//
//

BOOL HandleResourceUnavail()
{
    int mbRet;

    mbRet = MessageBox(g_hDlgParentWindow,
        "A Telephony resource is temporarily unavaiable.  "
        "This could mean a short wait is necessary or "
        "that a non-TAPI application is using the line.",
        "Warning",MB_RETRYCANCEL);

    if (mbRet == IDRETRY)
        return TRUE;

    return FALSE;
}

//
//  FUNCTION: HandleNoDevicesInstalled
//
//  PURPOSE: Handle cases when we know NODEVICE error
//    is returned because there are no devices installed.
//
//  PARAMETERS:
//    none
//
//  RETURN VALUE:
//    TRUE  - error was corrected.
//    FALSE - error was not corrected.
//
//  COMMENTS:
//
//    This function is not part of standard error handling
//    but is only used when we know that the NODEVICE error
//    means that no devices are installed.
//
//

BOOL HandleNoDevicesInstalled()
{
    int mbRet;
    mbRet = MessageBox(g_hDlgParentWindow,
       "There are no devices configured for Telephony use.\n"
       "Would you like to run the Modem Control Panel to add a modem driver?",
       "Warning",MB_YESNO);

    if (mbRet == IDYES)
    {
        if (LaunchModemControlPanelAdd())
            return TRUE;
    }

    return FALSE;
}

//
//  FUNCTION: LaunchModemControlPanelAdd
//
//  PURPOSE: Launch Add Modem Control Panel applet.
//
//  PARAMETERS:
//    none
//
//  RETURN VALUE:
//    TRUE  - Control Panel launched successfully.
//    FALSE - It didn't.
//
//  COMMENTS:
//
//

BOOL LaunchModemControlPanelAdd()
{
    PROCESS_INFORMATION piProcInfo;
    STARTUPINFO siStartupInfo;

    siStartupInfo.cb = sizeof(STARTUPINFO);
    siStartupInfo.lpReserved = NULL;
    siStartupInfo.lpDesktop = NULL;
    siStartupInfo.lpTitle = NULL;
    siStartupInfo.dwFlags = STARTF_USESHOWWINDOW;
    siStartupInfo.wShowWindow = SW_SHOWNORMAL;
    siStartupInfo.cbReserved2 = 0;
    siStartupInfo.lpReserved2 = NULL;

    // The string to launch the modem control panel is *VERY* likely
    // to change on NT.  If nothing else, this is 'contrl32' on NT
    // instead of 'control'.
    if (CreateProcess(
            NULL,
            "CONTROL.EXE MODEM.CPL,,ADD",
            NULL, NULL, FALSE,
            NORMAL_PRIORITY_CLASS,
            NULL, NULL,
            &siStartupInfo,
            &piProcInfo))
    {
        CloseHandle(piProcInfo.hThread);

        // Control panel 'Add New Modem' has been launched.  Now we should
        // wait for it to go away before continueing.

        // If we WaitForSingleObject for the control panel to exit, then we
        // get into a deadlock situation if we need to respond to any messages
        // from the control panel.

        // If we use a PeekMessage loop to wait, we run into
        // message re-entrancy problems.  (The user can get back to our UI
        // and click 'dial' again).

        // Instead, we take the easy way out and return FALSE to abort
        // the current operation.

        CloseHandle(piProcInfo.hProcess);
    }
    else
    {
        OutputDebugLastError(GetLastError(),
            "Unable to LaunchModemControlPanelAdd: ");
        MessageBox(g_hDlgParentWindow,
            "Unable to launch the Modem Control Panel",
            "Warning", MB_OK);
    }

    return FALSE;
}

//
//  FUNCTION: WarningBox(LPCSTR)
//
//  PURPOSE: Prints a warning box when conditions remove a line in use.
//
//  PARAMETERS:
//    lpszMessage - String that specifies why the line was removed form use.
//
//  RETURN VALUE:
//    none
//
//  COMMENTS:
//
//    If there is a call in progress on the line removed, then display a message
//    specifying why the line was removed and that the call is being canceled.
//
//

void WarningBox(LPCSTR lpszMessage)
{
    char szBuff[1024];

    strcpy(szBuff, lpszMessage);

    // If there is a call open, tell user we're going to close it.
    if (g_hCall)
        strcat(szBuff, "\nClosing existing call.");

    MessageBox(g_hDlgParentWindow, szBuff, "Warning", MB_OK);

    strcat(szBuff, "\r\n");
    OutputDebugString(szBuff);
}

//**************************************************
//
// All the functions from this point on are used solely by the "Dial" dialog.
// This dialog is used to get both the 'phone number' address,
// the line device to be used as well as allow the user to configure
// dialing properties and the line device.
//
//**************************************************

//
//  FUNCTION: DWORD I_lineNegotiateLegacyAPIVersion(DWORD)
//
//  PURPOSE: Negotiate an API Version to use for a specific device.
//
//  PARAMETERS:
//    dwDeviceID - device to negotiate an API Version for.
//
//  RETURN VALUE:
//    Returns the API Version to use for this line if successful.
//    Returns 0 if negotiations fall through.
//
//  COMMENTS:
//
//    This wrapper is slightly different from the I_lineNegotiateAPIVersion.
//    This wrapper allows TapiComm to negotiate an API version between
//    1.3 and SAMPLE_TAPI_VERSION.  Normally, this sample is specific to
//    API Version SAMPLE_TAPI_VERSION.  However, there are a few times when
//    TapiComm needs to get information from a service provider, but also knows
//    that a lower API Version would be ok.  This allows TapiComm to recognize
//    legacy service providers even though it can't use them.  1.3 is the
//    lowest API Version a legacy service provider should support.
//
//

DWORD I_lineNegotiateLegacyAPIVersion(DWORD dwDeviceID)
{
    LINEEXTENSIONID LineExtensionID;
    long lReturn;
    DWORD dwLocalAPIVersion;

    do
    {
        lReturn = lineNegotiateAPIVersion(g_hLineApp, dwDeviceID,
            0x00010003, SAMPLE_TAPI_VERSION,
            &dwLocalAPIVersion, &LineExtensionID);

        if (lReturn == LINEERR_INCOMPATIBLEAPIVERSION)
        {
            OutputDebugString("INCOMPATIBLEAPIVERSION in Dial Dialog.\n");
            return 0;
        }

        if (HandleLineErr(lReturn))
            continue;
        else
        {
            OutputDebugLineError(lReturn,
                "lineNegotiateAPIVersion in Dial Dialog unhandled error: ");
            return 0;
        }
    }
    while(lReturn != SUCCESS);

    return dwLocalAPIVersion;
}

//
//  FUNCTION: long VerifyUsableLine(DWORD)
//
//  PURPOSE: Verifies that a specific line device is useable by TapiComm.
//
//  PARAMETERS:
//    dwDeviceID - The ID of the line device to be verified
//
//  RETURN VALUE:
//    Returns SUCCESS if dwDeviceID is a usable line device.
//    Returns a LINENOTUSEABLE_ constant otherwise.
//
//  COMMENTS:
//
//    VerifyUsableLine takes the give device ID and verifies step by step
//    that the device supports all the features that TapiComm requires.
//
//

long VerifyUsableLine(DWORD dwDeviceID)
{
    LPLINEDEVCAPS lpLineDevCaps = NULL;
    LPLINEADDRESSSTATUS lpLineAddressStatus = NULL;
    LPVARSTRING lpVarString = NULL;
    DWORD dwAPIVersion;
    long lReturn;
    long lUsableLine = SUCCESS;
    HLINE hLine = 0;

    OutputDebugPrintf("Testing Line ID '0x%lx'",dwDeviceID);

    // The line device must support an API Version that TapiComm does.
    dwAPIVersion = I_lineNegotiateAPIVersion(dwDeviceID);
    if (dwAPIVersion == 0)
        return LINENOTUSEABLE_ERROR;

    lpLineDevCaps = I_lineGetDevCaps(lpLineDevCaps,
        dwDeviceID, dwAPIVersion);

    if (lpLineDevCaps == NULL)
        return LINENOTUSEABLE_ERROR;

    // Must support LINEBEARERMODE_VOICE
    if (!(lpLineDevCaps->dwBearerModes & LINEBEARERMODE_VOICE ))
    {
        lUsableLine = LINENOTUSEABLE_NOVOICE;
        OutputDebugString("LINEBEARERMODE_VOICE not supported\n");
        goto DeleteBuffers;
    }

    // Must support LINEMEDIAMODE_DATAMODEM
    if (!(lpLineDevCaps->dwMediaModes & LINEMEDIAMODE_DATAMODEM))
    {
        lUsableLine = LINENOTUSEABLE_NODATAMODEM;
        OutputDebugString("LINEMEDIAMODE_DATAMODEM not supported\n");
        goto DeleteBuffers;
    }

    // Must be able to make calls
    if (!(lpLineDevCaps->dwLineFeatures & LINEFEATURE_MAKECALL))
    {
        lUsableLine = LINENOTUSEABLE_NOMAKECALL;
        OutputDebugString("LINEFEATURE_MAKECALL not supported\n");
        goto DeleteBuffers;
    }

    // It is necessary to open the line so we can check if
    // there are any call appearances available.  Other TAPI
    // applications could be using all call appearances.
    // Opening the line also checks for other possible problems.
    do
    {
        lReturn = lineOpen(g_hLineApp, dwDeviceID, &hLine,
            dwAPIVersion, 0, 0,
            LINECALLPRIVILEGE_NONE, LINEMEDIAMODE_DATAMODEM,
            0);

        if(lReturn == LINEERR_ALLOCATED)
        {
            OutputDebugString(
                "Line is already in use by a non-TAPI app or"
                " another Service Provider.\n");
            lUsableLine = LINENOTUSEABLE_ALLOCATED;
            goto DeleteBuffers;
        }

        if (HandleLineErr(lReturn))
            continue;
        else
        {
            OutputDebugLineError(lReturn, "lineOpen unhandled error: ");
            lUsableLine = LINENOTUSEABLE_ERROR;
            goto DeleteBuffers;
        }
    }
    while(lReturn != SUCCESS);

    // Get LineAddressStatus to make sure the line isn't already in use.
    lpLineAddressStatus =
        I_lineGetAddressStatus(lpLineAddressStatus, hLine, 0);

    if (lpLineAddressStatus == NULL)
    {
        lUsableLine = LINENOTUSEABLE_ERROR;
        goto DeleteBuffers;
    }

    // Are there any available call appearances (ie: is it in use)?
    if ( !((lpLineAddressStatus -> dwAddressFeatures) &
           LINEADDRFEATURE_MAKECALL) )
    {
        OutputDebugString("LINEADDRFEATURE_MAKECALL not available\n");
        lUsableLine = LINENOTUSEABLE_INUSE;
        goto DeleteBuffers;
    }

    // Make sure the "comm/datamodem" device class is supported
    // Note that we don't want any of the 'extra' information
    // normally returned in the VARSTRING structure.  All we care
    // about is if lineGetID succeeds.
    do
    {
        lpVarString = CheckAndReAllocBuffer((LPVOID) lpVarString,
            sizeof(VARSTRING),"VerifyUsableLine:lineGetID: ");

        if (lpVarString == NULL)
        {
            lUsableLine = LINENOTUSEABLE_ERROR;
            goto DeleteBuffers;
        }

        lReturn = lineGetID(hLine, 0, 0, LINECALLSELECT_LINE,
            lpVarString, "comm/datamodem");

        if (HandleLineErr(lReturn))
            continue;
        else
        {
            OutputDebugLineError(lReturn,
                "lineGetID unhandled error: ");
            lUsableLine = LINENOTUSEABLE_NOCOMMDATAMODEM;
            goto DeleteBuffers;
        }
    }
    while(lReturn != SUCCESS);

    OutputDebugString("Line is suitable and available for use.\n");

  DeleteBuffers:

    if (hLine)
        lineClose(hLine);
    if (lpLineAddressStatus)
        LocalFree(lpLineAddressStatus);
    if (lpLineDevCaps)
        LocalFree(lpLineDevCaps);
    if (lpVarString)
        LocalFree(lpVarString);

    return lUsableLine;
}

//
//  FUNCTION: void FillTAPILine(HWND)
//
//  PURPOSE: Fills the 'TAPI Line' control with the available line devices.
//
//  PARAMETERS:
//    hwndDlg - handle to the current "Dial" dialog
//
//  RETURN VALUE:
//    none
//
//  COMMENTS:
//
//    This function enumerates through all the TAPI line devices and
//    queries each for the device name.  The device name is then put into
//    the 'TAPI Line' control.  These device names are kept in order rather
//    than sorted.  This allows "Dial" to know which device ID the user
//    selected just by the knowing the index of the selected string.
//
//    There are default values if there isn't a device name, if there is
//    an error on the device, or if the device name is an empty string.
//    The device name is also checked to make sure it is null terminated.
//
//    Note that a Legacy API Version is negotiated.  Since the fields in
//    the LINEDEVCAPS structure that we are interested in haven't moved, we
//    can negotiate a lower API Version than this sample is designed for
//    and still be able to access the necessary structure members.
//
//    The first line that is usable by TapiComm is selected as the 'default'
//    line.  Also note that if there was a previously selected line, this
//    remains the default line.  This would likely only occur if this
//    function is called after the dialog has initialized once; for example,
//    if a new line is added.
//
//

void FillTAPILine(HWND hwndDlg)
{
    DWORD dwDeviceID;
    DWORD dwAPIVersion;
    LPLINEDEVCAPS lpLineDevCaps = NULL;
    char szLineUnavail[] = "Line Unavailable";
    char szLineUnnamed[] = "Line Unnamed";
    char szLineNameEmpty[] = "Line Name is Empty";
    LPSTR lpszLineName;
    long lReturn;
    DWORD dwDefaultDevice = MAXDWORD;

    // Make sure the control is empty.  If it isn't,
    // hold onto the currently selected ID and then reset it.
    if (SendDlgItemMessage(hwndDlg, IDC_TAPILINE, CB_GETCOUNT, 0, 0))
    {
        dwDefaultDevice = SendDlgItemMessage(hwndDlg, IDC_TAPILINE,
            CB_GETCURSEL, 0, 0);
        SendDlgItemMessage(hwndDlg, IDC_TAPILINE, CB_RESETCONTENT, 0, 0);
    }

    for (dwDeviceID = 0; dwDeviceID < g_dwNumDevs; dwDeviceID ++)
    {
        dwAPIVersion = I_lineNegotiateLegacyAPIVersion(dwDeviceID);
        if (dwAPIVersion)
        {
            lpLineDevCaps = I_lineGetDevCaps(lpLineDevCaps,
                dwDeviceID, dwAPIVersion);
            if (lpLineDevCaps)
            {
                if ((lpLineDevCaps -> dwLineNameSize) &&
                    (lpLineDevCaps -> dwLineNameOffset) &&
                    (lpLineDevCaps -> dwStringFormat == STRINGFORMAT_ASCII))
                {
                    // This is the name of the device.
                    lpszLineName = ((char *) lpLineDevCaps) +
                        lpLineDevCaps -> dwLineNameOffset;

                    if (lpszLineName[0] != '\0')
                    {
        // Reverse indented to make this fit

        // Make sure the device name is null terminated.
        if (lpszLineName[lpLineDevCaps->dwLineNameSize -1] != '\0')
        {
            // If the device name is not null terminated, null
            // terminate it.  Yes, this looses the end character.
            // Its a bug in the service provider.
            lpszLineName[lpLineDevCaps->dwLineNameSize-1] = '\0';
            OutputDebugPrintf(
                "Device name for device 0x%lx is not null terminated.\n",
                dwDeviceID);
        }
                    }
                    else // Line name started with a NULL.
                        lpszLineName = szLineNameEmpty;
                }
                else  // DevCaps doesn't have a valid line name.  Unnamed.
                    lpszLineName = szLineUnnamed;
            }
            else  // Couldn't GetDevCaps.  Line is unavail.
                lpszLineName = szLineUnavail;
        }
        else  // Couldn't NegotiateAPIVersion.  Line is unavail.
            lpszLineName = szLineUnavail;

        // Put the device name into the control
        lReturn = SendDlgItemMessage(hwndDlg, IDC_TAPILINE,
            CB_ADDSTRING, 0, (LPARAM) (LPCTSTR) lpszLineName);

        // If this line is usable and we don't have a default initial
        // line yet, make this the initial line.
        if ((lpszLineName != szLineUnavail) &&
            (dwDefaultDevice == MAXDWORD) &&
            (VerifyUsableLine(dwDeviceID) == SUCCESS))
        {
            dwDefaultDevice = dwDeviceID;
        }
    }

    if (lpLineDevCaps)
        LocalFree(lpLineDevCaps);

    if (dwDefaultDevice == MAXDWORD)
        dwDefaultDevice = 0;

    // Set the initial default line
    SendDlgItemMessage(hwndDlg, IDC_TAPILINE,
        CB_SETCURSEL, dwDefaultDevice, 0);
}

//
//  FUNCTION: BOOL VerifyAndWarnUsableLine(HWND)
//
//  PURPOSE: Verifies the line device selected by the user.
//
//  PARAMETERS:
//    hwndDlg - The handle to the current "Dial" dialog.
//
//  RETURN VALUE:
//    Returns TRUE if the currently selected line device is useable
//      by TapiComm.  Returns FALSE if it isn't.
//
//  COMMENTS:
//
//    This function is very specific to the "Dial" dialog.  It gets
//    the device selected by the user from the 'TAPI Line' control and
//    VerifyUsableLine to make sure this line device is usable.  If the
//    line isn't useable, it notifies the user and disables the 'Dial'
//    button so that the user can't initiate a call with this line.
//
//    This function is also responsible for filling in the line specific
//    icon found on the "Dial" dialog.
//
//

BOOL VerifyAndWarnUsableLine(HWND hwndDlg)
{
    DWORD dwDeviceID;
    long lReturn;
    HICON hIcon = 0;
    HWND hControlWnd;

    // Get the selected line device.
    dwDeviceID = SendDlgItemMessage(hwndDlg, IDC_TAPILINE,
                        CB_GETCURSEL, 0, 0);

    // Get the "comm" device icon associated with this line device.
    lReturn = lineGetIcon(dwDeviceID, "comm", &hIcon);

    if (lReturn == SUCCESS)
        SendDlgItemMessage(hwndDlg, IDC_LINEICON, STM_SETICON,
            (WPARAM) hIcon, 0);
    else
        // Any failure to get an icon makes us use the default icon.
        SendDlgItemMessage(hwndDlg, IDC_LINEICON, WM_SETTEXT,
            0, (LPARAM) (LPCTSTR) "TapiComm");

/*  // It turns out that TAPI will always return an icon, even if
    // the device class isn't supported by the TSP or even if the TSP
    // doesn't return any icons at all.  This code is unnecessary.
    // The only reason lineGetIcon would fail is due to resource problems.

    else
    {
        // If the line doesn't have a "comm" device icon, use its default one.
        lReturn = lineGetIcon(dwDeviceID, NULL, &hIcon);
        if (lReturn == SUCCESS)
        {
            OutputDebugString("Line doesn't support a \"comm\" icon.\n");
            SendDlgItemMessage(hwndDlg, IDC_LINEICON, STM_SETICON,
                (WPARAM) hIcon, 0);
        }
        else
        {
            // If lineGetIcon fails, just use TapiComms icon.
            OutputDebugLineError(lReturn, "lineGetIcon: ");
            SendDlgItemMessage(hwndDlg, IDC_LINEICON, WM_SETTEXT,
                0, (LPARAM) (LPCTSTR) "TapiComm");
        }
    }
*/

    // Verify if the device is usable by TapiComm.
    lReturn = VerifyUsableLine(dwDeviceID);

    // Enable or disable the 'Dial' button, depending on if the line is ok.
    // Make sure there is a number to dial before enabling the button.
    hControlWnd = GetDlgItem(hwndDlg, IDC_DIAL);
    if (SendDlgItemMessage(hwndDlg, IDC_CANONICALNUMBER,
        WM_GETTEXTLENGTH, 0, 0) == 0)
    {
        EnableWindow(hControlWnd, FALSE);
    }
    else
        EnableWindow(hControlWnd, (lReturn == SUCCESS));

    // Any errors on this line prevent us from configuring it
    // or using dialing properties.
    if (lReturn == LINENOTUSEABLE_ERROR)
    {
        EnableWindow(GetDlgItem(hwndDlg, IDC_CONFIGURELINE), FALSE);
        EnableWindow(GetDlgItem(hwndDlg, IDC_DIALINGPROPERTIES), FALSE);
    }
    else
    {
        EnableWindow(GetDlgItem(hwndDlg, IDC_CONFIGURELINE), TRUE);
        if (SendDlgItemMessage(hwndDlg, IDC_USEDIALINGRULES, BM_GETCHECK, 0, 0))
            EnableWindow(GetDlgItem(hwndDlg, IDC_DIALINGPROPERTIES), TRUE);
    }

    switch(lReturn)
    {
        case SUCCESS:
            g_dwDeviceID = dwDeviceID;
            return TRUE;

        case LINENOTUSEABLE_ERROR:
            MessageBox(hwndDlg,
                "The selected line is incompatible with the TapiComm sample",
                "Warning",MB_OK);
            break;
        case LINENOTUSEABLE_NOVOICE:
            MessageBox(hwndDlg,
                "The selected line doesn't support VOICE capabilities",
                "Warning",MB_OK);
            break;
        case LINENOTUSEABLE_NODATAMODEM:
            MessageBox(hwndDlg,
                "The selected line doesn't support DATAMODEM capabilities",
                "Warning",MB_OK);
            break;
        case LINENOTUSEABLE_NOMAKECALL:
            MessageBox(hwndDlg,
                "The selected line doesn't support MAKECALL capabilities",
                "Warning",MB_OK);
            break;
        case LINENOTUSEABLE_ALLOCATED:
            MessageBox(hwndDlg,
                "The selected line is already in use by a non-TAPI application",
                "Warning",MB_OK);
            break;
        case LINENOTUSEABLE_INUSE:
            MessageBox(hwndDlg,
                "The selected line is already in use by a TAPI application",
                "Warning",MB_OK);
            break;

        case LINENOTUSEABLE_NOCOMMDATAMODEM:
            MessageBox(hwndDlg,
                "The selected line doesn't support the COMM/DATAMODEM device class",
                "Warning",MB_OK);
            break;
    }

    // g_dwDeviceID == MAXDWORD mean the selected device isn't usable.
    g_dwDeviceID = MAXDWORD;
    return FALSE;
}

//
//  FUNCTION: void FillCountryCodeList(HWND, DWORD)
//
//  PURPOSE: Fill the 'Country Code' control
//
//  PARAMETERS:
//    hwndDlg - handle to the current "Dial" dialog
//    dwDefaultCountryID - ID of the 'default' country to be selected
//
//  RETURN VALUE:
//    none
//
//  COMMENTS:
//
//    This function fills the 'Country Code' control with country names.
//    The country code is appended to the end of the name and the names
//    are added to the control sorted.  Because the country code is
//    embedded in the string along with the country name, there is no need
//    for any of the country information structures to be kept around.  The
//    country code can be extracted from the selected string at any time.
//
//

void FillCountryCodeList(HWND hwndDlg, DWORD dwDefaultCountryID)
{
    LPLINECOUNTRYLIST lpLineCountryList = NULL;
    DWORD dwSizeofCountryList = sizeof(LINECOUNTRYLIST);
    long lReturn;
    DWORD dwCountry;
    LPLINECOUNTRYENTRY lpLineCountryEntries;
    char szRenamedCountry[256];

    // Get the country information stored in TAPI
    do
    {
        lpLineCountryList = (LPLINECOUNTRYLIST) CheckAndReAllocBuffer(
            (LPVOID) lpLineCountryList, dwSizeofCountryList,
            "FillCountryCodeList");

        if (lpLineCountryList == NULL)
            return;

        lReturn = lineGetCountry (0, SAMPLE_TAPI_VERSION, lpLineCountryList);

        if (HandleLineErr(lReturn))
            ;
        else
        {
            OutputDebugLineError(lReturn, "lineGetCountry unhandled error: ");
            LocalFree(lpLineCountryList);
            return;
        }

        if ((lpLineCountryList -> dwNeededSize) >
            (lpLineCountryList -> dwTotalSize))
        {
            dwSizeofCountryList = lpLineCountryList ->dwNeededSize;

lReturn = -1; // Lets loop again.
        }
    }
    while (lReturn != SUCCESS);

    // Find the first country entry
    lpLineCountryEntries = (LPLINECOUNTRYENTRY)
        (((LPBYTE) lpLineCountryList)
         + lpLineCountryList -> dwCountryListOffset);

    // Now enumerate through all the countries
    for (dwCountry = 0;
         dwCountry < lpLineCountryList -> dwNumCountries;
         dwCountry++)
    {
        // append the country code to the country name
        wsprintf(szRenamedCountry,"%s (%lu)",
            (((LPSTR) lpLineCountryList) +
                lpLineCountryEntries[dwCountry].dwCountryNameOffset),
            lpLineCountryEntries[dwCountry].dwCountryCode);

        // Now put this country name / code string into the combobox
        lReturn = SendDlgItemMessage(hwndDlg, IDC_COUNTRYCODE, CB_ADDSTRING,
                    0, (LPARAM) (LPCTSTR) szRenamedCountry);

        // If this country is the default country, select it.
        if (lpLineCountryEntries[dwCountry].dwCountryID
            == dwDefaultCountryID)
        {
            SendDlgItemMessage(hwndDlg, IDC_COUNTRYCODE, CB_SETCURSEL, lReturn, 0);
        }
    }

    LocalFree(lpLineCountryList);
    return;
}

//
//  FUNCTION: void FillLocationInfo(HWND, LPSTR, LPDWORD, LPSTR)
//
//  PURPOSE: Fill (or refill) the 'Your Location' control
//
//  PARAMETERS:
//    hwndDlg - handle to the current "Dial" dialog
//    lpszCurrentLocation - Name of current location, or NULL
//    lpdwCountryID - location to store the current country ID or NULL
//    lpszAreaCode - location to store the current area code or NULL
//
//  RETURN VALUE:
//    none
//
//  COMMENTS:
//
//    This function is moderately multipurpose.
//
//    If lpszCurrentLocation is NULL, then the 'Your Location' control
//    is filled with all the locations stored in TAPI and the TAPI 'default'
//    location is selected.  This is done during initialization and
//    also after the 'Dialing Properties' dialog has been displayed.
//    This last is done because the user can change the current location
//    or add and delete locations while in the 'Dialing Properties' dialog.
//
//    If lpszCurrentLocation is a valid string pointer, then it is assumed
//    that the 'Your Location' control is already filled and that the user
//    is selecting a specific location.  In this case, all of the existing
//    TAPI locations are enumerated until the specified location is found.
//    At this point, the specified location is set to the current location.
//
//    In either case, if lpdwCountryID is not NULL, it is filled with the
//    country ID for the current location.  If lpszAreaCode is not NULL, it
//    is filled with the area code defined for the current location.  These
//    values can be used later to initialize other "Dial" controls.
//
//    This function also fills the 'Calling Card' control based on
//    the information stored in the current location.
//
//

void FillLocationInfo(HWND hwndDlg, LPSTR lpszCurrentLocation,
    LPDWORD lpdwCountryID, LPSTR lpszAreaCode)
{
    LPLINETRANSLATECAPS lpTranslateCaps = NULL;
    DWORD dwSizeofTranslateCaps = sizeof(LINETRANSLATECAPS);
    long lReturn;
    DWORD dwCounter;
    LPLINELOCATIONENTRY lpLocationEntry;
    LPLINECARDENTRY lpLineCardEntry = NULL;
    DWORD dwPreferredCardID = MAXDWORD;

    // First, get the TRANSLATECAPS
    do
    {
        lpTranslateCaps = (LPLINETRANSLATECAPS) CheckAndReAllocBuffer(
            (LPVOID) lpTranslateCaps, dwSizeofTranslateCaps,
            "FillLocationInfo");

        if (lpTranslateCaps == NULL)
            return;

        lReturn = lineGetTranslateCaps(g_hLineApp, SAMPLE_TAPI_VERSION,
                    lpTranslateCaps);

        if (HandleLineErr(lReturn))
            ;
        else
        {
            OutputDebugLineError(lReturn,
                "lineGetTranslateCaps unhandled error: ");
            LocalFree(lpTranslateCaps);
            return;
        }

        if ((lpTranslateCaps -> dwNeededSize) >
            (lpTranslateCaps -> dwTotalSize))
        {
            dwSizeofTranslateCaps = lpTranslateCaps ->dwNeededSize;
            lReturn = -1; // Lets loop again.
        }
    }
    while(lReturn != SUCCESS);

    // Find the location information in the TRANSLATECAPS
    lpLocationEntry = (LPLINELOCATIONENTRY)
        (((LPBYTE) lpTranslateCaps) + lpTranslateCaps->dwLocationListOffset);

    // If lpszCurrentLocation, then make that location 'current'
    if (lpszCurrentLocation)
    {
        // loop through all locations, looking for a location match
        for(dwCounter = 0;
            dwCounter < lpTranslateCaps -> dwNumLocations;
            dwCounter++)
        {
            if (strcmp((((LPSTR) lpTranslateCaps) +
                            lpLocationEntry[dwCounter].dwLocationNameOffset),
                        lpszCurrentLocation)
                == 0)
            {
                // Found it!  Set the current location.
                lineSetCurrentLocation(g_hLineApp,
                    lpLocationEntry[dwCounter].dwPermanentLocationID);

                // Set the return values.
                if (lpdwCountryID)
                    *lpdwCountryID = lpLocationEntry[dwCounter].dwCountryID;

                if (lpszAreaCode)
                    strcpy(lpszAreaCode, (((LPSTR) lpTranslateCaps) +
                            lpLocationEntry[dwCounter].dwCityCodeOffset));

                // Store the preferred card ID for later use.
                dwPreferredCardID = lpLocationEntry[dwCounter].dwPreferredCardID;
                break;
            }
        }

        // Was a match for lpszCurrentLocation found?
        if (dwPreferredCardID == MAXDWORD)
        {
            OutputDebugString("lpszCurrentLocation not found\n");
            SendDlgItemMessage(hwndDlg, IDC_CALLINGCARD, WM_SETTEXT, 0,
                (LPARAM) (LPCSTR) "Invalid Location Selected");
            LocalFree(lpTranslateCaps);
            return;
        }
    }
    else // fill the combobox and use the TAPI 'current' location.
    {
        // First empty the combobox
        SendDlgItemMessage(hwndDlg, IDC_LOCATION, CB_RESETCONTENT, 0, 0);

        // enumerate all the locations
        for(dwCounter = 0;
            dwCounter < lpTranslateCaps -> dwNumLocations;
            dwCounter++)
        {
            // Put each one into the combobox
            lReturn = SendDlgItemMessage(hwndDlg, IDC_LOCATION, CB_ADDSTRING,
                0, (LPARAM) (((LPBYTE) lpTranslateCaps) +
                    lpLocationEntry[dwCounter].dwLocationNameOffset));

            // Is this location the 'current' location?
            if (lpLocationEntry[dwCounter].dwPermanentLocationID ==
                lpTranslateCaps->dwCurrentLocationID)
            {
                // Return the requested information
                if (lpdwCountryID)
                    *lpdwCountryID = lpLocationEntry[dwCounter].dwCountryID;

                if (lpszAreaCode)
                    strcpy(lpszAreaCode, (((LPSTR) lpTranslateCaps) +
                            lpLocationEntry[dwCounter].dwCityCodeOffset));

                // Set this to be the active location.
                SendDlgItemMessage(hwndDlg, IDC_LOCATION, CB_SETCURSEL, lReturn, 0);
                dwPreferredCardID = lpLocationEntry[dwCounter].dwPreferredCardID;
            }
        }
    }

    // Now locate the prefered card and display it.

    lpLineCardEntry = (LPLINECARDENTRY)
        (((LPBYTE) lpTranslateCaps) + lpTranslateCaps->dwCardListOffset);

    for(dwCounter = 0;
        dwCounter < lpTranslateCaps -> dwNumCards;
        dwCounter++)
    {
        if (lpLineCardEntry[dwCounter].dwPermanentCardID == dwPreferredCardID)
        {
            SendDlgItemMessage(hwndDlg, IDC_CALLINGCARD, WM_SETTEXT, 0,
                (LPARAM) (((LPBYTE) lpTranslateCaps) +
                    lpLineCardEntry[dwCounter].dwCardNameOffset));
            break;
        }
    }

    LocalFree(lpTranslateCaps);
}

//
//  FUNCTION: void UseDialingRules(HWND)
//
//  PURPOSE: Enable/disable Dialing Rule controls
//
//  PARAMETERS:
//    hwndDlg - handle to the current "Dial" dialog
//
//  RETURN VALUE:
//    none
//
//  COMMENTS:
//
//    The sole purpose of this function is to enable or disable
//    the controls that apply to dialing rules if the
//    "Use Country Code and Area Code" checkbox is checked or unchecked,
//    as appropriate.
//
//

void UseDialingRules(HWND hwndDlg)
{
    HWND hControl;
    BOOL bEnableWindow;

    bEnableWindow = SendDlgItemMessage(hwndDlg,
        IDC_USEDIALINGRULES, BM_GETCHECK, 0, 0);

    hControl = GetDlgItem(hwndDlg, IDC_STATICCOUNTRYCODE);
    EnableWindow(hControl, bEnableWindow);

    hControl = GetDlgItem(hwndDlg, IDC_COUNTRYCODE);
    EnableWindow(hControl, bEnableWindow);

    hControl = GetDlgItem(hwndDlg, IDC_STATICAREACODE);
    EnableWindow(hControl, bEnableWindow);

    hControl = GetDlgItem(hwndDlg, IDC_AREACODE);
    EnableWindow(hControl, bEnableWindow);

    hControl = GetDlgItem(hwndDlg, IDC_STATICLOCATION);
    EnableWindow(hControl, bEnableWindow);

    hControl = GetDlgItem(hwndDlg, IDC_LOCATION);
    EnableWindow(hControl, bEnableWindow);

    hControl = GetDlgItem(hwndDlg, IDC_STATICCALLINGCARD);
    EnableWindow(hControl, bEnableWindow);

    hControl = GetDlgItem(hwndDlg, IDC_CALLINGCARD);
    EnableWindow(hControl, bEnableWindow);

    if (IsWindowEnabled(GetDlgItem(hwndDlg, IDC_CONFIGURELINE)))
    {
        hControl = GetDlgItem(hwndDlg, IDC_DIALINGPROPERTIES);
        EnableWindow(hControl, bEnableWindow);
    }
}

//
//  FUNCTION: void DisplayPhoneNumber(HWND)
//
//  PURPOSE: Create, Translate and Display the Phone Number
//
//  PARAMETERS:
//    hwndDlg - handle to the current "Dial" dialog
//
//  RETURN VALUE:
//    none
//
//  COMMENTS:
//
//    This function uses the information stored in many other controls
//    to build the phone number, translate it, and display it.  Also
//    makes sure the Dial button is enabled or disabled, based on if the
//    number can be dialed or not.
//
//    There are actually three phone numbers generated during this
//    process:  canonical, dialable and displayable.  Normally, only the
//    displayable number is shown to the user; the other two numbers are
//    to be used by the program internally.  However, for demonstration
//    purposes (and because it is cool for developers to see these numbers),
//    all three numbers are displayed.
//

void DisplayPhoneNumber(HWND hwndDlg)
{
    char szPreTranslatedNumber[1024] = "";
    int  nPreTranslatedSize = 0;
    char szTempBuffer[512];
    int  i;
    DWORD dwDeviceID;
    LPLINETRANSLATEOUTPUT lpLineTranslateOutput = NULL;

    // Disable the 'dial' button if there isn't a number to dial
    if (0 == SendDlgItemMessage(hwndDlg, IDC_PHONENUMBER,
            WM_GETTEXTLENGTH, 0, 0))
    {
        SendDlgItemMessage(hwndDlg, IDC_CANONICALNUMBER, WM_SETTEXT, 0,
            (LPARAM) NULL);
        SendDlgItemMessage(hwndDlg, IDC_DIALABLENUMBER, WM_SETTEXT, 0,
            (LPARAM) NULL);
        SendDlgItemMessage(hwndDlg, IDC_DISPLAYABLENUMBER, WM_SETTEXT, 0,
            (LPARAM) (LPCTSTR) "No Phone Number");

        EnableWindow(GetDlgItem(hwndDlg, IDC_DIAL), FALSE);
        return;
    }

    // If we use the dialing rules, lets make canonical format.
    // Canonical format is explained in the TAPI documentation and the
    // string format needs to be followed very strictly.
    if (SendDlgItemMessage(hwndDlg, IDC_USEDIALINGRULES,
        BM_GETCHECK, 0, 0))
    {
        // First character *has* to be the plus sign.
        szPreTranslatedNumber[0] = '+';
        nPreTranslatedSize = 1;

        // The country code *has* to be next.
        // Country code was stored in the string with the country
        // name and needs to be extracted at this point.
        i = SendDlgItemMessage(hwndDlg, IDC_COUNTRYCODE,
                CB_GETCURSEL, 0, 0);
        SendDlgItemMessage(hwndDlg, IDC_COUNTRYCODE,
            CB_GETLBTEXT, (WPARAM) i, (LPARAM) (LPCTSTR) szTempBuffer);

        // Country code is at the end of the string, surounded by parens.
        // This makes it easy to identify the country code.
        i = strlen(szTempBuffer);
        while(szTempBuffer[--i] != '(');

        while(szTempBuffer[++i] != ')')
            szPreTranslatedNumber[nPreTranslatedSize++] = szTempBuffer[i];

        // Next is the area code.
        i = SendDlgItemMessage(hwndDlg, IDC_AREACODE, WM_GETTEXT,
                510, (LPARAM) (LPCTSTR) szTempBuffer);

        // Note that the area code is optional.  If it is included,
        // then it has to be preceeded by *exactly* one space and it
        // *has* to be surrounded by parens.
        if (i)
            nPreTranslatedSize +=
                wsprintf(&szPreTranslatedNumber[nPreTranslatedSize],
                    " (%s)", szTempBuffer);

        // There has to be *exactly* one space before the rest of the number.
        szPreTranslatedNumber[nPreTranslatedSize++] = ' ';

        // At this point, the phone number is appended to the
        // canonical number.  The next step is the same whether canonical
        // format is used or not; just the prepended area code and
        // country code are different.
    }

    SendDlgItemMessage(hwndDlg, IDC_PHONENUMBER, WM_GETTEXT,
        510, (LPARAM) (LPCTSTR) szTempBuffer);

    strcat(&szPreTranslatedNumber[nPreTranslatedSize], szTempBuffer);

    dwDeviceID = SendDlgItemMessage(hwndDlg, IDC_TAPILINE,
                        CB_GETCURSEL, 0, 0);

    // Translate the address!
    lpLineTranslateOutput = I_lineTranslateAddress(
        lpLineTranslateOutput, dwDeviceID, SAMPLE_TAPI_VERSION,
        szPreTranslatedNumber);

    // Unable to translate it?
    if (lpLineTranslateOutput == NULL)
    {
        SendDlgItemMessage(hwndDlg, IDC_CANONICALNUMBER, WM_SETTEXT, 0,
            (LPARAM) NULL);
        SendDlgItemMessage(hwndDlg, IDC_DIALABLENUMBER, WM_SETTEXT, 0,
            (LPARAM) NULL);
        SendDlgItemMessage(hwndDlg, IDC_DISPLAYABLENUMBER, WM_SETTEXT, 0,
            (LPARAM) (LPCTSTR) "Invalid Phone Number or Area Code");

        EnableWindow(GetDlgItem(hwndDlg, IDC_DIAL), FALSE);
        return;
    }

    // Is the selected device useable with TapiComm?
    if (g_dwDeviceID != MAXDWORD)
        EnableWindow(GetDlgItem(hwndDlg, IDC_DIAL), TRUE);

    // Fill the appropriate phone number controls.
    SendDlgItemMessage(hwndDlg, IDC_CANONICALNUMBER, WM_SETTEXT, 0,
        (LPARAM) (LPCTSTR) szPreTranslatedNumber);

    SendDlgItemMessage(hwndDlg, IDC_DIALABLENUMBER, WM_SETTEXT, 0,
        (LPARAM) ((LPSTR) lpLineTranslateOutput +
            lpLineTranslateOutput -> dwDialableStringOffset));

    SendDlgItemMessage(hwndDlg, IDC_DISPLAYABLENUMBER, WM_SETTEXT, 0,
        (LPARAM) ((LPSTR) lpLineTranslateOutput +
            lpLineTranslateOutput -> dwDisplayableStringOffset));

    LocalFree(lpLineTranslateOutput);

}

//
//  FUNCTION: void PreConfigureDevice(HWND, DWORD)
//
//  PURPOSE:
//
//  PARAMETERS:
//    hwndDlg - handle to the current "Dial" dialog
//    dwDeviceID - line device to be configured
//
//  RETURN VALUE:
//    none
//
//  COMMENTS:
//
//    At one point, PreConfigureDevice used lineConfigDialog to
//    configure the device.  This has the unfortunate effect of configuring
//    the device immediately, even if it is in use by another TAPI app.
//    This can be really bad if data communications are already in
//    progress (like with RAS).
//
//    Now, PreConfigureDevice uses lineConfigDialogEdit to give the
//    user the configuration UI, but it doesn't actually do anything to
//    the line device.  TapiComm stores the configuration information so
//    that it can be set later, just before making the call.
//
//

void PreConfigureDevice(HWND hwndDlg, DWORD dwDeviceID)
{
    long lReturn;
    LPVARSTRING lpVarString = NULL;
    DWORD dwSizeofVarString = sizeof(VARSTRING);

    // If there isn't already any device configuration information,
    // then we need to get some.
    if (g_lpDeviceConfig == NULL)
    {
        do
        {
            lpVarString = (LPVARSTRING) CheckAndReAllocBuffer(
                (LPVOID) lpVarString, dwSizeofVarString,
                "PreConfigureDevice - lineGetDevConfig: ");

            if (lpVarString == NULL)
                return;

            lReturn = lineGetDevConfig(dwDeviceID, lpVarString,
                "comm/datamodem");

            if (HandleLineErr(lReturn))
                ;
            else
            {
                OutputDebugLineError(lReturn,
                    "lineGetDevCaps unhandled error: ");
                LocalFree(lpVarString);
                return;
            }

            if ((lpVarString -> dwNeededSize) > (lpVarString -> dwTotalSize))
            {
                dwSizeofVarString = lpVarString -> dwNeededSize;
                lReturn = -1; // Lets loop again.
            }
        }
        while (lReturn != SUCCESS);

        g_dwSizeDeviceConfig = lpVarString -> dwStringSize;

        // The extra byte allocated is in case dwStringSize is 0.
        g_lpDeviceConfig = CheckAndReAllocBuffer(
                g_lpDeviceConfig, g_dwSizeDeviceConfig+1,
                "PreConfigureDevice - Allocate device config: ");

        if (!g_lpDeviceConfig)
        {
            LocalFree(lpVarString);
            return;
        }

        memcpy(g_lpDeviceConfig,
            ((LPBYTE) lpVarString + lpVarString -> dwStringOffset),
            g_dwSizeDeviceConfig);
    }

    // Next make the lineConfigDialogEdit call.

    // Note that we determine the initial size of the VARSTRING
    // structure based on the known size of the existing configuration
    // information.  I make the assumption that this configuration
    // information is very unlikely to grow by more than 5K or by
    // more than 5 times.  This is a *very* conservative number.
    // We do *not* want lineConfigDialogEdit to fail just because there
    // wasn't enough room to stored the data.  This would require the user
    // to go through configuration again and that would be annoying.

    dwSizeofVarString = 5 * g_dwSizeDeviceConfig + 5000;

    do
    {
        lpVarString = (LPVARSTRING) CheckAndReAllocBuffer(
            (LPVOID) lpVarString, dwSizeofVarString,
            "PreConfigureDevice - lineConfigDialogEdit: ");

        if (lpVarString == NULL)
            return;

        lReturn = lineConfigDialogEdit(dwDeviceID, hwndDlg, "comm/datamodem",
            g_lpDeviceConfig, g_dwSizeDeviceConfig, lpVarString);

        if (HandleLineErr(lReturn))
            ;
        else
        {
            OutputDebugLineError(lReturn,
                "lineConfigDialogEdit unhandled error: ");
            LocalFree(lpVarString);
            return;
        }

        if ((lpVarString -> dwNeededSize) > (lpVarString -> dwTotalSize))
        {
            // We had been conservative about making sure the structure was
            // big enough.  Unfortunately, not conservative enough.  Hopefully,
            // this will not happen a second time because we are *DOUBLING*
            // the NeededSize.
            MessageBox(hwndDlg,
                "Internal Error: Unable to set line configuration.\n"
                "Please try again.",
                "Oops", MB_OK);
            dwSizeofVarString = (lpVarString -> dwNeededSize) * 2;
            lReturn = -1; // Lets loop again.
        }
    }
    while (lReturn != SUCCESS);

    // Store the configuration information into a global structure
    // so it can be set at a later time.
    g_dwSizeDeviceConfig = lpVarString -> dwStringSize;
    g_lpDeviceConfig = CheckAndReAllocBuffer(
            g_lpDeviceConfig, g_dwSizeDeviceConfig+1,
            "PreConfigureDevice - Reallocate device config: ");

    if (!g_lpDeviceConfig)
    {
        LocalFree(lpVarString);
        return;
    }

    memcpy(g_lpDeviceConfig,
        ((LPBYTE) lpVarString + lpVarString -> dwStringOffset),
        g_dwSizeDeviceConfig);

    LocalFree(lpVarString);
}

//
//  FUNCTION: BOOL GetAddressToDial
//
//  PURPOSE: Get an address to dial from the user.
//
//  PARAMETERS:
//    none
//
//  RETURN VALUE:
//    TRUE if a valid device and phone number have been entered by
//    the user.  FALSE if the user canceled the dialing process.
//
//  COMMENTS:
//
//    All this function does is launch the "Dial" dialog.
//
//

BOOL GetAddressToDial()
{
    BOOL bRet;

    UpdateStatusBar("Getting Number to Dial",1,0);
    bRet = DialogBoxParam(hInst, "DialDialog", g_hDlgParentWindow,
        DialDialogProc, 0);
    g_hDialog = NULL;
    g_hDlgParentWindow = g_hWndMainWindow;

    if (bRet == FALSE)
        UpdateStatusBar("Dial aborted",1,0);

    return bRet;
}

//
//  FUNCTION: DialDialogProc(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Dialog callback procedure for the dialing dialog
//
//  PARAMETERS:
//    hwndDlg - Dialog calling the callback.
//    uMsg    - Dialog message.
//    wParam  - uMsg specific.
//    lParam  - uMsg specific.
//
//  RETURN VALUE:
//    returns 0 - command handled.
//    returns non-0 - command unhandled
//
//  COMMENTS:
//
//    This is the dialog to get the phone number and line device
//    from the user.  All the relavent information is stored in global
//    variables to be used later if the dialog returns successfully.
//
//

BOOL CALLBACK DialDialogProc(
    HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    // Static variables to store the information from last time the
    // "Dial" dialog was displayed.  That way the phone number can be
    // typed once but used several times.

    static char szCountryName[512] = "";
    static char szAreaCode[256] = "Not Valid";
    static char szPhoneNumber[512] = "Not Valid";
    static DWORD dwUsedDeviceID = MAXDWORD;
    static BOOL bUsedCountryAndArea = FALSE;
    static BOOL bHistoryValid = FALSE;

    switch(uMsg)
    {
        case WM_INITDIALOG:
        {
            DWORD dwCountryID = 0;

            // Store the Dialog Window so it can be dismissed if necessary
            g_hDialog = hwndDlg;

            // This dialog should be parent to all dialogs.
            g_hDlgParentWindow = hwndDlg;

            // Initialize the Dialog Box. Lots to do here.

            FillTAPILine(hwndDlg);
            if (g_lpDeviceConfig)
            {
                LocalFree(g_lpDeviceConfig);
                g_lpDeviceConfig = NULL;
            }

            // If there is a valid history, use it to initialize the controls.
            if (bHistoryValid)
            {
                FillLocationInfo(hwndDlg, NULL, NULL, NULL);
                FillCountryCodeList(hwndDlg, 0);

                SendDlgItemMessage(hwndDlg, IDC_COUNTRYCODE, CB_SELECTSTRING,
                    (WPARAM) -1, (LPARAM) (LPCTSTR) szCountryName);

                SendDlgItemMessage(hwndDlg, IDC_PHONENUMBER, WM_SETTEXT, 0,
                    (LPARAM) (LPCTSTR) szPhoneNumber);

                SendDlgItemMessage(hwndDlg, IDC_USEDIALINGRULES,
                    BM_SETCHECK, (WPARAM) bUsedCountryAndArea, 0);

                SendDlgItemMessage(hwndDlg, IDC_TAPILINE, CB_SETCURSEL,
                    g_dwDeviceID, 0);
            }
            else
            {
                FillLocationInfo(hwndDlg, NULL, &dwCountryID, szAreaCode);
                FillCountryCodeList(hwndDlg, dwCountryID);
                SendDlgItemMessage(hwndDlg, IDC_USEDIALINGRULES,
                    BM_SETCHECK, 1, 0);
            }

            SendDlgItemMessage(hwndDlg, IDC_AREACODE, WM_SETTEXT,
                0, (LPARAM) (LPCTSTR) szAreaCode);

            UseDialingRules(hwndDlg);
            DisplayPhoneNumber(hwndDlg);
            VerifyAndWarnUsableLine(hwndDlg);

            return TRUE;
        }

        case WM_COMMAND:
        {
            switch(LOWORD(wParam))
            {
                case IDC_TAPILINE:
                    if (HIWORD(wParam) == CBN_SELENDOK)
                    {
                        if (g_lpDeviceConfig)
                        {
                            LocalFree(g_lpDeviceConfig);
                            g_lpDeviceConfig = NULL;
                        }
                        DisplayPhoneNumber(hwndDlg);
                        VerifyAndWarnUsableLine(hwndDlg);
                    }
                    return TRUE;

                case IDC_CONFIGURELINE:
                {
                    DWORD dwDeviceID;
                    dwDeviceID = SendDlgItemMessage(hwndDlg, IDC_TAPILINE,
                        CB_GETCURSEL, 0, 0);
                    PreConfigureDevice(hwndDlg, dwDeviceID);
                    DisplayPhoneNumber(hwndDlg);
                    return TRUE;
                }

                case IDC_COUNTRYCODE:
                    if (HIWORD(wParam) == CBN_SELENDOK)
                        DisplayPhoneNumber(hwndDlg);
                    return TRUE;

                case IDC_AREACODE:
                case IDC_PHONENUMBER:
                    if (HIWORD(wParam) == EN_CHANGE)
                        DisplayPhoneNumber(hwndDlg);
                    return TRUE;

                case IDC_USEDIALINGRULES:
                    if (HIWORD(wParam) == BN_CLICKED)
                    {
                        UseDialingRules(hwndDlg);
                        DisplayPhoneNumber(hwndDlg);
                    }
                    return TRUE;

                case IDC_LOCATION:
                    if (HIWORD(wParam) == CBN_CLOSEUP)
                    {
                        char szCurrentLocation[1024];
                        int nCurrentSelection;

                        nCurrentSelection = SendDlgItemMessage(hwndDlg,
                            IDC_LOCATION, CB_GETCURSEL, 0, 0);
                        SendDlgItemMessage(hwndDlg, IDC_LOCATION,
                            CB_GETLBTEXT, nCurrentSelection,
                            (LPARAM) (LPCTSTR) szCurrentLocation);

                        // If the user selected a 'location', make it current.
                        FillLocationInfo(hwndDlg, szCurrentLocation, NULL, NULL);
                        DisplayPhoneNumber(hwndDlg);
                    }
                    return TRUE;

                case IDC_DIALINGPROPERTIES:
                {
                    char szAddress[1024];
                    DWORD dwDeviceID;
                    long lReturn;

                    dwDeviceID = SendDlgItemMessage(hwndDlg, IDC_TAPILINE,
                            CB_GETCURSEL, 0, 0);

                    SendDlgItemMessage(hwndDlg, IDC_CANONICALNUMBER,
                        WM_GETTEXT, 1023, (LPARAM) (LPCTSTR) szAddress);

                    lReturn = lineTranslateDialog(g_hLineApp, dwDeviceID,
                        SAMPLE_TAPI_VERSION, hwndDlg, szAddress);

                    if (lReturn != SUCCESS)
                        OutputDebugLineError(lReturn,"lineTranslateDialog: ");

                    // The user could have changed the default location, or
                    // added or removed a location while in the 'Dialing
                    // Properties' dialog.  Refill the Location Info.
                    FillLocationInfo(hwndDlg, NULL, NULL, NULL);
                    DisplayPhoneNumber(hwndDlg);

                    return TRUE;
                }

                case IDCANCEL:
                    EndDialog(hwndDlg, FALSE);
                    return TRUE;

                case IDC_DIAL:
                {
                    // The Dial button has to be enabled and the line has
                    // to be currently usable to continue.
                    if (!(IsWindowEnabled((HWND)lParam) &&
                          VerifyAndWarnUsableLine(hwndDlg)))
                        return TRUE;

                    DisplayPhoneNumber(hwndDlg);

                    // Get the displayable and dialable numbers and store
                    // them in global variables to be used while dialing.
                    SendDlgItemMessage(hwndDlg, IDC_DISPLAYABLENUMBER,
                        WM_GETTEXT, 1023, (LPARAM) (LPCTSTR) g_szDisplayableAddress);

                    SendDlgItemMessage(hwndDlg, IDC_DIALABLENUMBER,
                        WM_GETTEXT, 1023, (LPARAM) (LPCTSTR) g_szDialableAddress);

                    // Store all the relavent information in static

// variables so they will be available the next time a
                    // number is dialed.
                    SendDlgItemMessage(hwndDlg, IDC_COUNTRYCODE,
                        WM_GETTEXT, 511, (LPARAM) (LPCTSTR) szCountryName);

                    SendDlgItemMessage(hwndDlg, IDC_AREACODE,
                        WM_GETTEXT, 255, (LPARAM) (LPCTSTR) szAreaCode);

                    SendDlgItemMessage(hwndDlg, IDC_PHONENUMBER,
                        WM_GETTEXT, 511, (LPARAM) (LPCTSTR) szPhoneNumber);

                    bUsedCountryAndArea = (BOOL) SendDlgItemMessage(hwndDlg,
                        IDC_USEDIALINGRULES, BM_GETCHECK, 0, 0);

                    bHistoryValid = TRUE;

                    EndDialog(hwndDlg, TRUE);
                    return TRUE;
                }

                // This message is actually posted to the dialog from the
                // lineCallbackFunc when it receives a
                // LINEDEVSTATE_TRANSLATECHANGE message.  Notify the user and
                // retranslate the number.  Also refill the Location Info
                // since this could have been generated by a location change.
                case IDC_CONFIGURATIONCHANGED:
                {
                    FillLocationInfo(hwndDlg, NULL, NULL, NULL);
                    DisplayPhoneNumber(hwndDlg);

                    MessageBox(hwndDlg,
                        "Location Configuration has been changed.",
                        "Warning",MB_OK);

                    return TRUE;
                }

                // If we get a LINE_CREATE message, all that needs to be done
                // is to reset this controls contents.  The selected line
                // won't change and no lines will be removed.
                case IDC_LINECREATE:
                {
                    FillTAPILine(hwndDlg);
                    return TRUE;
                }

                default:
                    break;
            }

            break;
        }

        // This dialog has the DS_CONTEXTMENU flag so that the right mouse
        // button will send this message.  Bring up the appropriate help page.
        case WM_CONTEXTMENU:
        {

            if (hwndDlg != (HWND) wParam)
                WinHelp ((HWND)wParam,
                    "TAPICOMM.HLP",
                     HELP_CONTEXTMENU,
                    (DWORD)(LPVOID) g_adwSampleMenuHelpIDs);
            break;
        }

        // Bring up the appropriate help page.
        case WM_HELP:
        {
            LPHELPINFO lphi;

            lphi = (LPHELPINFO)lParam;
            if ((lphi->iContextType == HELPINFO_WINDOW) &&
                (hwndDlg != lphi->hItemHandle) &&
                (lphi->iCtrlId < IDC_NOHELPCONTROLS))
            {
                WinHelp (lphi->hItemHandle,
                    "TAPICOMM.HLP",
                    HELP_WM_HELP,
                    (DWORD)(LPVOID) g_adwSampleMenuHelpIDs);
            }

        return TRUE;
        }

        default:
            break;
    }

    return FALSE;
}


TAPICODE.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 1995 - 1998 Microsoft Corporation.  All Rights Reserved.
//
// PURPOSE:
//    Contains public declarations for the TapiCode module.
//

BOOL InitializeTAPI(HWND hParentWnd);
BOOL ShutdownTAPI();
BOOL DialCall();
BOOL HangupCall();
void PostHangupCall();


TAPICOMM.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.
//
//  MODULE: TapiComm.c
//
//  PURPOSE: Handles general routines for the TapiComm sample.
//
//  FUNCTIONS:
//    WndProc     - Processes messages for the main window.
//    MsgCommand  - Handle the WM_COMMAND messages for the main window.
//    MsgCreate   - Handle the WM_CREATE messages for the main window.
//    MsgSize     - Handles the WM_SIZE message by calling SendMessage() to
//                  pass the WM_SIZE message onto the status bar and tool bar
//                  controls.
//    MsgDestroy  - Handles the WM_DESTROY message by calling
//                  PostQuitMessage().
//    CmdExit     - Handles the file exit command by calling destory
//                  window on the main window.
//
//  COMMENTS:
//    Message dispatch table -
//      For every message to be handled by the main window procedure
//      place the message number and handler function pointer in
//      rgmsd (the message dispatch table).  Place the prototype
//      for the function in globals.h and the definition of the
//      function in the appropriate module.
//    Command dispatch table -
//      For every command to be handled by the main window procedure
//      place the command number and handler function pointer in
//      rgcmd (the command dispatch table).  Place the prototype
//      for the function in globals.h and the definition of the
//      function in the appropriate module.
//    Globals.h Contains the definitions of the structures and dispatch.c
//      contains the functions that use these structures.
//

#include <windows.h>            // required for all Windows applications
#include <windowsx.h>
#include <commctrl.h>           // prototypes and defs for common controls
#include "globals.h"            // prototypes specific to this application
#include "statbar.h"            // prototypes specific to statbar.c
#include "toolbar.h"            // prototypes specific to toolbar.c
#include "EditCtls.h"
#include "TapiCode.h"
#include "resource.h"

LRESULT CmdCreateFile1(HWND hWnd, WORD wCommand, WORD wNotify, HWND hwndCtrl);
LRESULT CmdCreateFile2(HWND hWnd, WORD wCommand, WORD wNotify, HWND hwndCtrl);

// Main window message table definition.
MSD rgmsd[] =
{
    {WM_COMMAND,    MsgCommand   },
    {WM_MENUSELECT, MsgMenuSelect},
    {WM_SIZE,       MsgSize      },
    {WM_NOTIFY,     MsgNotify    },
    {WM_CLOSE,      MsgClose     },
    {WM_CREATE,     MsgCreate    },
    {WM_SETFOCUS,   MsgSetFocus  },
    //{WM_PAINT,      MsgPaint     },
    {WM_DESTROY,    MsgDestroy   }
};

MSDI msdiMain =
{
    sizeof(rgmsd) / sizeof(MSD),
    rgmsd,
    edwpWindow
};

// Main window command table definition.
CMD rgcmd[] =
{
    {IDM_MAKECALL,    CmdMakeCall},
    {IDM_HANGUPCALL,  CmdHangupCall},
    {IDM_EXIT,        CmdExit},

    {IDM_EDITUNDO,    CmdStub},
    {IDM_EDITCUT,     CmdStub},
    {IDM_EDITCOPY,    CmdStub},
    {IDM_EDITPASTE,   CmdStub},
    {IDM_EDITCLEAR,   CmdStub},

    {IDM_ABOUT,       CmdAbout},
};

CMDI cmdiMain =
{
    sizeof(rgcmd) / sizeof(CMD),
    rgcmd,
    edwpWindow
};

//
//  FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE:  Processes messages for the main window.
//
//  PARAMETERS:
//    hwnd     - window handle
//    uMessage - message number
//    wparam   - additional information (dependant on message number)
//    lparam   - additional information (dependant on message number)
//
//  RETURN VALUE:
//    The return value depends on the message number.  If the message
//    is implemented in the message dispatch table, the return value is
//    the value returned by the message handling function.  Otherwise,
//    the return value is the value returned by the default window procedure.
//
//  COMMENTS:
//    Call the DispMessage() function with the main window's message dispatch
//    information (msdiMain) and the message specific information.
//

LRESULT CALLBACK WndProc(HWND   hwnd,
                         UINT   uMessage,
                         WPARAM wparam,
                         LPARAM lparam)
{
    return DispMessage(&msdiMain, hwnd, uMessage, wparam, lparam);
}

//
//  FUNCTION: MsgCommand(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Handle the WM_COMMAND messages for the main window.
//
//  PARAMETERS:
//    hwnd     - window handle
//    uMessage - WM_COMMAND (Unused)
//    GET_WM_COMMAND_ID(wparam, lparam)   - Command identifier
//    GET_WM_COMMAND_HWND(wparam, lparam) - Control handle
//
//  RETURN VALUE:
//    The return value depends on the message number.  If the message
//    is implemented in the message dispatch table, the return value is
//    the value returned by the message handling function.  Otherwise,
//    the return value is the value returned by the default window procedure.
//
//  COMMENTS:
//    Call the DispCommand() function with the main window's command dispatch
//    information (cmdiMain) and the command specific information.
//

LRESULT MsgCommand(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    return DispCommand(&cmdiMain, hwnd, wparam, lparam);
}

//
//  FUNCTION: MsgCreate(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Handle the WM_CREATE messages for the main window.
//           and call InitCommonControls() API to initialize the
//           common control library.
//
//  PARAMETERS:
//    hwnd     - window handle
//
//  RETURN VALUE:
//    Return 0 if the StatusBar and ToolBar Windows could be created
//    successfully. Otherwise, returns -1 to abort the main window
//    creation.
//
//  COMMENTS:
//    Call the CreateTSBars function with the main window's window handle
//    information (msdiMain).
//
//    Must also initialize TAPI.
//

LRESULT MsgCreate(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    InitCommonControls() ; // Initialize the common control library.

    if (InitEditCtls(hwnd) == FALSE)
        return -1;

    InitializeTAPI(hwnd);

    if(!(CreateTBar(hwnd) && CreateSBar(hwnd)))
        return -1;   // Tool and status bars were not created, so return -1.

    UpdateStatusBar("Ready to make a call.",1,0);

    EnableMakeCall(hwnd, TRUE);
    EnableHangupCall(hwnd, FALSE);
    return 0;
}

//
//  FUNCTION: MsgSize(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE:  This function resizes the toolbar and statusbar controls.
//
//
//  PARAMETERS:
//
//    hwnd      - Window handle  (Used)
//    uMessage  - Message number (Used)
//    wparam    - Extra data     (Used)
//    lparam    - Extra data     (Used)
//
//  RETURN VALUE:
//
//    Always returns 0 - Message handled
//
//  COMMENTS:
//
//    When the window procdure that has the status and tool bar controls
//    receive the WM_SIZE message, it has to pass the message on to these
//    controls so that these controls can adjust their size accordingly.
//
//    It also has to resize the edit controls that are the UI for TAPI.
//
//

LRESULT MsgSize(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    SendMessage(hWndStatusbar,  uMessage, wparam, lparam);
    SendMessage(hWndToolbar, uMessage, wparam, lparam);

    // Re-position the panes in the status bar
    InitializeStatusBar(hwnd);

    SizeEditCtls();

    return 0 ;
}

//
//  FUNCTION: MsgSetFocus(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE:  This function puts the focus where is should be.
//
//
//  PARAMETERS:
//
//    hwnd      - Window handle  (Used)
//    uMessage  - Identifies window that lost focus (Used)
//    wparam    - Extra data     (Used)
//    lparam    - Extra data     (Used)
//
//  RETURN VALUE:
//
//    Always returns 0 - Message handled
//
//  COMMENTS:
//
//    Just signal the edit controls to set the focus where it belongs.
//
//

LRESULT MsgSetFocus(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    SetFocusEditCtls();

    return 0 ;
}

//
//  FUNCTION: MsgDestroy(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Calls PostQuitMessage().
//
//  PARAMETERS:
//
//    hwnd      - Window handle  (Unused)
//    uMessage  - Message number (Unused)
//    wparam    - Extra data     (Unused)
//    lparam    - Extra data     (Unused)
//
//  RETURN VALUE:
//
//    Always returns 0 - Message handled
//
//  COMMENTS:
//
//

LRESULT MsgDestroy(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    PostQuitMessage(0);
    return 0;
}

//
//  FUNCTION: MsgClose(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Exits the application.
//
//  PARAMETERS:
//
//    hwnd      - The window.
//    uMessage  - Message number (Unused)
//    wparam    - Extra data     (Unused)
//    lparam    - Extra data     (Unused)
//
//  RETURN VALUE:
//
//    Always returns 0 - Message handled
//
//  COMMENTS:
//
//    Make sure TAPI is stopped before exiting.
//
//

LRESULT MsgClose(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    ShutdownTAPI();

    DestroyWindow(hwnd);
    return 0;
}

//
//  FUNCTION: MsgPaint(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Paints the client area of the window.
//
//  PARAMETERS:
//
//    hwnd      - The window.
//    uMessage  - Message number (Unused)
//    wparam    - Extra data     (Unused)
//    lparam    - Extra data     (Unused)
//
//  RETURN VALUE:
//
//    Always returns 0 - Message handled
//
//  COMMENTS:
//
//    Not sure what needs to be painted, maybe text
//    labeling the edit controls?
//
//

LRESULT MsgPaint(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    HDC hdc;
    PAINTSTRUCT ps;

    hdc = BeginPaint(hwnd, &ps);

    EndPaint(hwnd, &ps);
    return 0;
}

//
//  FUNCTION: CmdExit(HWND, WORD, WORD, HWND)
//
//  PURPOSE: Exit the application.
//
//  PARAMETERS:
//    hwnd     - The window.
//    wCommand - IDM_EXIT (unused)
//    wNotify  - Notification number (unused)
//    hwndCtrl - NULL (unused)
//
//  RETURN VALUE:
//    Always returns 0 - command handled.
//
//  COMMENTS:
//
//    Make sure TAPI is stopped before exiting.
//
//

LRESULT CmdExit(HWND hwnd, WORD wCommand, WORD wNotify, HWND hwndCtrl)
{
    char szBuffer[50];
    int  cbWritten = 0;

    ShutdownTAPI();

    cbWritten = LoadString(hInst, wCommand, szBuffer, sizeof(szBuffer));
    if(cbWritten == 0)
        lstrcpy(szBuffer, "Unknown Command");

    UpdateStatusBar(szBuffer, 0, 0);

    DestroyWindow(hwnd);
    return 0;
}

//
//  FUNCTION: CmdHangupCall(HWND, WORD, WORD, HWND)
//
//  PURPOSE: Stops TAPI
//
//  PARAMETERS:
//    hwnd     - The window.
//    wCommand - IDM_HANGUPCALL (unused)
//    wNotify  - Notification number (unused)
//    hwndCtrl - NULL (unused)
//
//  RETURN VALUE:
//    Always returns 0 - command handled.
//
//  COMMENTS:
//
//    Tells TAPI to close any opened lines.
//
//

LRESULT CmdHangupCall(HWND hWnd, WORD wCommand, WORD wNotify, HWND hwndCtrl)
{
    HangupCall();
    return 0;
}

//
//  FUNCTION: CmdMakeCall(HWND, WORD, WORD, HWND)
//
//  PURPOSE: Starts TAPI
//
//  PARAMETERS:
//    hwnd     - The window.
//    wCommand - IDM_MAKECALL (unused)
//    wNotify  - Notification number (unused)
//    hwndCtrl - NULL (unused)
//
//  RETURN VALUE:
//    Always returns 0 - command handled.
//
//  COMMENTS:
//
//    Starts TAPI by calling the Dialing Dialog box.
//    Code for this dialog is with the rest of the TAPI code.
//
//

LRESULT CmdMakeCall(HWND hWnd, WORD wCommand, WORD wNotify, HWND hwndCtrl)
{
    DialCall();
    return 0;
}


TAPIINFO.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.
//
//  MODULE: TapiInfo.c
//
//  PURPOSE: Handles all Pretty Printing functions for the TapiComm sample.
//
//  EXPORTED FUNCTIONS:  These functions are for use by other modules.
//
//      All of these pretty print to the debugging output.
//    OutputDebugLineCallback       - Calls FormatLineCallback.
//    OutputDebugLineError          - Calls OutputDebugLineErrorFileLine.
//    OutputDebugLastError          - Calls OutputDebugLineErrorFileLine.
//    OutputDebugPrintf             - Calls wsprintf
//    OutputDebugLineErrorFileLine  - Calls FormatLineError
//    OutputDebugLastErrorFileLine  - Calls FormatLastError
//
//      All of these functions pretty print to a string buffer.
//    FormatLineError               - Prints a LINEERR
//    FormatLastError               - Prints a GetLastError error.
//    FormatLineCallback            - Prints a lineCallbackFunc message.
//
//  INTERNAL FUNCTION:  These functions are for this module only.
//    strBinaryArrayAppend          - prints a binary flag array to a buffer.

#include <windows.h>
#include <tapi.h>
#include "TapiInfo.h"

// Maximum length of all internal string buffers.
#define MAXOUTPUTSTRINGLENGTH 4096

// define to make accessing arrays easy.
#define sizeofArray(pArray) (sizeof(pArray) / sizeof((pArray)[0]))

//*****************************************
// Internal prototypes.
//*****************************************

static long strBinaryArrayAppend(LPSTR pszOutputBuffer, DWORD dwFlags,
     LPSTR szStringArray[], DWORD dwSizeofStringArray);

//*****************************************
// Global arrays for interpreting TAPI constants.
//*****************************************

LPSTR pszLineErrorNameArray[] =
{
"",
"LINEERR_ALLOCATED",
"LINEERR_BADDEVICEID",
"LINEERR_BEARERMODEUNAVAIL",
"LINEERR Unused constant, ERROR!!",
"LINEERR_CALLUNAVAIL",
"LINEERR_COMPLETIONOVERRUN",
"LINEERR_CONFERENCEFULL",
"LINEERR_DIALBILLING",
"LINEERR_DIALDIALTONE",
"LINEERR_DIALPROMPT",
"LINEERR_DIALQUIET",
"LINEERR_INCOMPATIBLEAPIVERSION",
"LINEERR_INCOMPATIBLEEXTVERSION",
"LINEERR_INIFILECORRUPT",
"LINEERR_INUSE",
"LINEERR_INVALADDRESS",
"LINEERR_INVALADDRESSID",
"LINEERR_INVALADDRESSMODE",
"LINEERR_INVALADDRESSSTATE",
"LINEERR_INVALAPPHANDLE",
"LINEERR_INVALAPPNAME",
"LINEERR_INVALBEARERMODE",
"LINEERR_INVALCALLCOMPLMODE",
"LINEERR_INVALCALLHANDLE",
"LINEERR_INVALCALLPARAMS",
"LINEERR_INVALCALLPRIVILEGE",
"LINEERR_INVALCALLSELECT",
"LINEERR_INVALCALLSTATE",
"LINEERR_INVALCALLSTATELIST",
"LINEERR_INVALCARD",
"LINEERR_INVALCOMPLETIONID",
"LINEERR_INVALCONFCALLHANDLE",
"LINEERR_INVALCONSULTCALLHANDLE",
"LINEERR_INVALCOUNTRYCODE",
"LINEERR_INVALDEVICECLASS",
"LINEERR_INVALDEVICEHANDLE",
"LINEERR_INVALDIALPARAMS",
"LINEERR_INVALDIGITLIST",
"LINEERR_INVALDIGITMODE",
"LINEERR_INVALDIGITS",
"LINEERR_INVALEXTVERSION",
"LINEERR_INVALGROUPID",
"LINEERR_INVALLINEHANDLE",
"LINEERR_INVALLINESTATE",
"LINEERR_INVALLOCATION",
"LINEERR_INVALMEDIALIST",
"LINEERR_INVALMEDIAMODE",
"LINEERR_INVALMESSAGEID",
"LINEERR Unused constant, ERROR!!",
"LINEERR_INVALPARAM",
"LINEERR_INVALPARKID",
"LINEERR_INVALPARKMODE",
"LINEERR_INVALPOINTER",
"LINEERR_INVALPRIVSELECT",
"LINEERR_INVALRATE",
"LINEERR_INVALREQUESTMODE",
"LINEERR_INVALTERMINALID",
"LINEERR_INVALTERMINALMODE",
"LINEERR_INVALTIMEOUT",
"LINEERR_INVALTONE",
"LINEERR_INVALTONELIST",
"LINEERR_INVALTONEMODE",
"LINEERR_INVALTRANSFERMODE",
"LINEERR_LINEMAPPERFAILED",
"LINEERR_NOCONFERENCE",
"LINEERR_NODEVICE",
"LINEERR_NODRIVER",
"LINEERR_NOMEM",
"LINEERR_NOREQUEST",
"LINEERR_NOTOWNER",
"LINEERR_NOTREGISTERED",
"LINEERR_OPERATIONFAILED",
"LINEERR_OPERATIONUNAVAIL",
"LINEERR_RATEUNAVAIL",
"LINEERR_RESOURCEUNAVAIL",
"LINEERR_REQUESTOVERRUN",
"LINEERR_STRUCTURETOOSMALL",
"LINEERR_TARGETNOTFOUND",
"LINEERR_TARGETSELF",
"LINEERR_UNINITIALIZED",
"LINEERR_USERUSERINFOTOOBIG",
"LINEERR_REINIT",
"LINEERR_ADDRESSBLOCKED",
"LINEERR_BILLINGREJECTED",
"LINEERR_INVALFEATURE",
"LINEERR_NOMULTIPLEINSTANCE"
};

LPSTR psz_dwMsg[] = {
    "LINE_ADDRESSSTATE",
    "LINE_CALLINFO",
    "LINE_CALLSTATE",
    "LINE_CLOSE",
    "LINE_DEVSPECIFIC",
    "LINE_DEVSPECIFICFEATURE",
    "LINE_GATHERDIGITS",
    "LINE_GENERATE",
    "LINE_LINEDEVSTATE",
    "LINE_MONITORDIGITS",
    "LINE_MONITORMEDIA",
    "LINE_MONITORTONE",
    "LINE_REPLY",
    "LINE_REQUEST",
    "PHONE_BUTTON",
    "PHONE_CLOSE",
    "PHONE_DEVSPECIFIC",
    "PHONE_REPLY",
    "PHONE_STATE",
    "LINE_CREATE",
    "PHONE_CREATE"
};

LPSTR pszfLINEADDRESSSTATE[] =
{
    "Unknown LINEADDRESSSTATE information",
    "LINEADDRESSSTATE_OTHER",
    "LINEADDRESSSTATE_DEVSPECIFIC",
    "LINEADDRESSSTATE_INUSEZERO",
    "LINEADDRESSSTATE_INUSEONE",
    "LINEADDRESSSTATE_INUSEMANY",
    "LINEADDRESSSTATE_NUMCALLS",
    "LINEADDRESSSTATE_FORWARD",
    "LINEADDRESSSTATE_TERMINALS",
    "LINEADDRESSSTATE_CAPSCHANGE"
};

LPSTR pszfLINECALLINFOSTATE[] =
{
    "Unknown LINECALLINFOSTATE state",
    "LINECALLINFOSTATE_OTHER",
    "LINECALLINFOSTATE_DEVSPECIFIC",
    "LINECALLINFOSTATE_BEARERMODE",
    "LINECALLINFOSTATE_RATE",
    "LINECALLINFOSTATE_MEDIAMODE",
    "LINECALLINFOSTATE_APPSPECIFIC",
    "LINECALLINFOSTATE_CALLID",
    "LINECALLINFOSTATE_RELATEDCALLID",
    "LINECALLINFOSTATE_ORIGIN",
    "LINECALLINFOSTATE_REASON",
    "LINECALLINFOSTATE_COMPLETIONID",
    "LINECALLINFOSTATE_NUMOWNERINCR",
    "LINECALLINFOSTATE_NUMOWNERDECR",
    "LINECALLINFOSTATE_NUMMONITORS",
    "LINECALLINFOSTATE_TRUNK",
    "LINECALLINFOSTATE_CALLERID",
    "LINECALLINFOSTATE_CALLEDID",
    "LINECALLINFOSTATE_CONNECTEDID",
    "LINECALLINFOSTATE_REDIRECTIONID",
    "LINECALLINFOSTATE_REDIRECTINGID",
    "LINECALLINFOSTATE_DISPLAY",
    "LINECALLINFOSTATE_USERUSERINFO",
    "LINECALLINFOSTATE_HIGHLEVELCOMP",
    "LINECALLINFOSTATE_LOWLEVELCOMP",
    "LINECALLINFOSTATE_CHARGINGINFO",
    "LINECALLINFOSTATE_TERMINAL",
    "LINECALLINFOSTATE_DIALPARAMS",
    "LINECALLINFOSTATE_MONITORMODES"
};

LPSTR pszfLINECALLSTATE[] =
{
    "Unknown LINECALLSTATE state",
    "LINECALLSTATE_IDLE",
    "LINECALLSTATE_OFFERING",
    "LINECALLSTATE_ACCEPTED",
    "LINECALLSTATE_DIALTONE",
    "LINECALLSTATE_DIALING",
    "LINECALLSTATE_RINGBACK",
    "LINECALLSTATE_BUSY",
    "LINECALLSTATE_SPECIALINFO",
    "LINECALLSTATE_CONNECTED",
    "LINECALLSTATE_PROCEEDING",
    "LINECALLSTATE_ONHOLD",
    "LINECALLSTATE_CONFERENCED",
    "LINECALLSTATE_ONHOLDPENDCONF",
    "LINECALLSTATE_ONHOLDPENDTRANSFER",
    "LINECALLSTATE_DISCONNECTED",
    "LINECALLSTATE_UNKNOWN"
};

LPSTR pszfLINEDIALTONEMODE[] =
{
    "Unknown LINEDIALTONE information",
    "LINEDIALTONEMODE_NORMAL",
    "LINEDIALTONEMODE_SPECIAL",
    "LINEDIALTONEMODE_INTERNAL",
    "LINEDIALTONEMODE_EXTERNAL",
    "LINEDIALTONEMODE_UNKNOWN",
    "LINEDIALTONEMODE_UNAVAIL"
};

LPSTR pszfLINEBUSYMODE[] =
{
    "Unknown LINEBUSYMODE information",
    "LINEBUSYMODE_STATION",
    "LINEBUSYMODE_TRUNK",
    "LINEBUSYMODE_UNKNOWN",
    "LINEBUSYMODE_UNAVAIL"
};

LPSTR pszfLINESPECIALINFO[] =
{
    "Unknown LINESPECIALINFO information",
    "LINESPECIALINFO_NOCIRCUIT",
    "LINESPECIALINFO_CUSTIRREG",
    "LINESPECIALINFO_REORDER",
    "LINESPECIALINFO_UNKNOWN",
    "LINESPECIALINFO_UNAVAIL"
};

LPSTR pszfLINEDISCONNECTED[] =
{
    "Unknown LINEDISCONNECTED information",
    "LINEDISCONNECTMODE_NORMAL",
    "LINEDISCONNECTMODE_UNKNOWN",
    "LINEDISCONNECTMODE_REJECT",
    "LINEDISCONNECTMODE_PICKUP",
    "LINEDISCONNECTMODE_FORWARDED",
    "LINEDISCONNECTMODE_BUSY",
    "LINEDISCONNECTMODE_NOANSWER",
    "LINEDISCONNECTMODE_BADADDRESS",
    "LINEDISCONNECTMODE_UNREACHABLE",
    "LINEDISCONNECTMODE_CONGESTION",
    "LINEDISCONNECTMODE_INCOMPATIBLE",
    "LINEDISCONNECTMODE_UNAVAIL",
    "LINEDISCONNECTMODE_NODIALTONE"
};

LPSTR pszfLINECALLPRIVILEGE[] =
{
    "No change to LINECALLPRIVILEGE",
    "LINECALLPRIVILEGE_NONE",
    "LINECALLPRIVILEGE_MONITOR",
    "LINECALLPRIVILEGE_OWNER"
};

LPSTR pszfLINEGATHERTERM[] =
{
    "Unknown LINEGATHERTERM message",
    "LINEGATHERTERM_BUFFERFULL",
    "LINEGATHERTERM_TERMDIGIT",
    "LINEGATHERTERM_FIRSTTIMEOUT",
    "LINEGATHERTERM_INTERTIMEOUT",
    "LINEGATHERTERM_CANCEL"
};

LPSTR pszfLINEGENERATETERM[] =
{
    "Unknown LINEGENERATETERM message",
    "LINEGENERATETERM_DONE",
    "LINEGENERATETERM_CANCEL"
};

LPSTR pszfLINEDEVSTATE[] =
{
    "Unknown LINEDEVESTATE state",
    "LINEDEVSTATE_OTHER",
    "LINEDEVSTATE_RINGING",
    "LINEDEVSTATE_CONNECTED",
    "LINEDEVSTATE_DISCONNECTED",
    "LINEDEVSTATE_MSGWAITON",
    "LINEDEVSTATE_MSGWAITOFF",
    "LINEDEVSTATE_INSERVICE",
    "LINEDEVSTATE_OUTOFSERVICE",
    "LINEDEVSTATE_MAINTENANCE",
    "LINEDEVSTATE_OPEN",
    "LINEDEVSTATE_CLOSE",
    "LINEDEVSTATE_NUMCALLS",
    "LINEDEVSTATE_NUMCOMPLETIONS",
    "LINEDEVSTATE_TERMINALS",
    "LINEDEVSTATE_ROAMMODE",
    "LINEDEVSTATE_BATTERY",
    "LINEDEVSTATE_SIGNAL",
    "LINEDEVSTATE_DEVSPECIFIC",
    "LINEDEVSTATE_REINIT",
    "LINEDEVSTATE_LOCK",
    "LINEDEVSTATE_CAPSCHANGE",
    "LINEDEVSTATE_CONFIGCHANGE",
    "LINEDEVSTATE_TRANSLATECHANGE",
    "LINEDEVSTATE_COMPLCANCEL",
    "LINEDEVSTATE_REMOVED"
};

LPSTR pszfLINEDIGITMODE[] =
{
    "Unknown LINEDIGITMODE mode",
    "LINEDIGITMODE_PULSE",
    "LINEDIGITMODE_DTMF",
    "LINEDIGITMODE_DTMFEND"
};

LPSTR pszfLINEMEDIAMODE[] =
{
    "Unknown LINEMEDIAMODE mode",
    "UnUsed LINEMEDIAMODE mode, ERROR!!",
    "LINEMEDIAMODE_UNKNOWN",
    "LINEMEDIAMODE_INTERACTIVEVOICE",
    "LINEMEDIAMODE_AUTOMATEDVOICE",
    "LINEMEDIAMODE_DATAMODEM",
    "LINEMEDIAMODE_G3FAX",
    "LINEMEDIAMODE_TDD",
    "LINEMEDIAMODE_G4FAX",
    "LINEMEDIAMODE_DIGITALDATA",
    "LINEMEDIAMODE_TELETEX",
    "LINEMEDIAMODE_VIDEOTEX",
    "LINEMEDIAMODE_TELEX",
    "LINEMEDIAMODE_MIXED",
    "LINEMEDIAMODE_ADSI",
    "LINEMEDIAMODE_VOICEVIEW"
};

LPSTR pszfLINEREQUESTMODE[] =
{
    "Unknown LINEREQUESTMODE message",
    "LINEREQUESTMODE_MAKECALL",
    "LINEREQUESTMODE_MEDIACALL",
    "LINEREQUESTMODE_DROP"
};

//
//  MACRO: OutputDebugLineError(long, LPSTR)
//
//  PURPOSE: Pretty print a line error to the debugging output.
//
//  PARAMETERS:
//    lLineError - Actual error code to decipher.
//    pszPrefix  - String to prepend to the printed message.
//
//  RETURN VALUE:
//    none
//
//  COMMENTS:
//    This macro is actually defined in the .h file.
//    It will take a LINEERR error, turn it into a human
//    readable string, prepend pszPrefix (so you
//    can tag your errors), append __FILE__ and __LINE__
//    and print it to the debugging output.

//    This macro is just a wrapper around OutputDebugLineErrorFileLine
//    that is necessary to get proper values for __FILE__ and __LINE__.
//
//

/*
#define OuputDebugLineError(lLineError, pszPrefix) \
    OutputDebugLineErrorFileLine(lLineError, pszPrefix,\
        __FILE__, __LINE__)
*/

//
//  FUNCTION: OutputDebugLineErrorFileLine(..)
//
//  PURPOSE: Pretty print a line error to the debugging output.
//
//  PARAMETERS:
//    lLineError  - Actual error code to decipher.
//    pszPrefix   - String to prepend to the printed message.
//    szFileName  - Filename the error occured in.
//    nLineNumber - Line number the error occured at.
//
//  RETURN VALUE:
//    none
//
//  COMMENTS:
//    This is the actual function that OutputDebugLineError
//    expands to.  Its not likely to be usefull except
//    through the OutputDebugLineError macro, or to print
//    errors without line and file information.
//
//    If szFileName == NULL, then the File and Line are not printed.
//
//    Note that there is an internal string length limit of
//    MAXOUTPUTSTRINGLENGTH.  If this length is exceeded,
//    the behavior will be the same as wsprintf, although
//    it will be undetectable.  *KEEP szPrefix SHORT!*
//
//

void OutputDebugLineErrorFileLine(
    long lLineError, LPSTR szPrefix,
    LPSTR szFileName, DWORD nLineNumber)
{
    LPSTR szLineError;
    char szOutputLineError[MAXOUTPUTSTRINGLENGTH];

    if (szPrefix == NULL)
        szPrefix = "";

    // Pretty print the error message.
    szLineError = FormatLineError(lLineError, NULL, 0);

    // The only reason FormatLineError should fail is "Out of memory".
    if (szLineError == NULL)
    {
        if (szFileName == NULL)
            wsprintf(szOutputLineError, "%sOut of memory", szPrefix);
        else
            wsprintf(szOutputLineError,
                "%sOut of memory in file %s, line %d\r\n",
                szPrefix, szFileName, nLineNumber);

        OutputDebugString(szOutputLineError);

        return;
    }

    // If szFileName, then use it; else don't.
    if (szFileName != NULL)
    {
        wsprintf(szOutputLineError,
            "%sTapi Line Error: \"%s\" in File \"%s\", Line %d\r\n",
            szPrefix, szLineError, szFileName, nLineNumber);
    }
    else
    {
        wsprintf(szOutputLineError,
            "%sTapi Line Error: \"%s\"\r\n",
            szPrefix, szLineError);
    }

    // Pointer returned from FormatLineError *must* be freed!
    LocalFree(szLineError);

    // Print it!
    OutputDebugString(szOutputLineError);

    return;
}

//
//  FUNCTION: FormatLineError(long, LPSTR, DWORD)
//
//  PURPOSE: Pretty print a line error to a string.
//
//  PARAMETERS:
//    lLineError           - Actual error code to decipher.
//    szOutputBuffer       - String buffer to pretty print to.
//    dwSizeofOutputBuffer - Size of String buffer.
//
//  RETURN VALUE:
//    Returns the buffer printed to.
//
//  COMMENTS:
//    If szOutputBuffer isn't big enough to hold the whole string,
//    then the string gets truncated to fit the buffer.
//
//    If szOutputBuffer == NULL, then dwSizeofOutputBuffer
//    is ignored, a buffer 'big enough' is LocalAlloc()d and
//    a pointer to it is returned.  However, its *very* important
//    that this pointer be LocalFree()d by the calling application.
//
//

LPSTR FormatLineError(long lLineError,
    LPSTR szOutputBuffer, DWORD dwSizeofOutputBuffer)
{
    char szUnknownLineError[256];
    LPSTR szLineError;
    int nSizeofLineError;
    long lErrorIndex;
    DWORD * pdwLineError;

    // Strip off the high bit to make the error code positive.
    pdwLineError = &lLineError;
    lErrorIndex = (long) (0x7FFFFFFF & *pdwLineError);

    // Is it an unknown error?
    if ((lErrorIndex >= sizeofArray(pszLineErrorNameArray)) ||
        (lErrorIndex < 0))
    {
        nSizeofLineError =
            wsprintf(szUnknownLineError, "Unknown TAPI line error code: 0x%lx",
                lLineError);
        szLineError = szUnknownLineError;
    }
    else
    {
        szLineError = pszLineErrorNameArray[lErrorIndex];
        nSizeofLineError = strlen(szLineError);
    }

    // allocate a buffer if necessary
    if (szOutputBuffer == NULL)
    {
        szOutputBuffer = (LPSTR) LocalAlloc(LPTR, nSizeofLineError + 1);
        if (szOutputBuffer == NULL)
            return NULL;
    }
    else // truncate string if it won't fit in the specified buffer.
    {
        if ((DWORD) nSizeofLineError >= dwSizeofOutputBuffer)
            nSizeofLineError = dwSizeofOutputBuffer - 1;
    }

    // Put the string into the buffer and null terminate.
    memcpy(szOutputBuffer, szLineError, nSizeofLineError);
    szOutputBuffer[nSizeofLineError] = '\0';

    return szOutputBuffer;
}

//
//  MACRO: OutputDebugLastError(DWORD, LPSTR)
//
//  PURPOSE: Pretty print a system error to the debugging output.
//
//  PARAMETERS:
//    dwLastError - Actual error code to decipher.
//    pszPrefix   - String to prepend to the printed message.
//
//  RETURN VALUE:
//    none
//
//  COMMENTS:
//    This macro is actually defined in the .h file.
//    It will take an error that was retrieved by GetLastError(),
//    turn it into a human readable string, prepend pszPrefix
//    (so you can tag your errors), append __FILE__ and __LINE__
//    and print it to the debugging output.
//
//    This macro is just a wrapper around OutputDebugLastErrorFileLine
//    that is necessary to get proper values for __FILE__ and __LINE__.
//
//

/*
#define OuputDebugLastError(dwLastError, pszPrefix) \
    OutputDebugLastErrorFileLine(dwLastError, pszPrefix,\
        __FILE__, __LINE__)
*/

//
//  FUNCTION: OutputDebugLastErrorFileLine(..)
//
//  PURPOSE: Pretty print a line error to the debugging output.
//
//  PARAMETERS:
//    dwLastError - Actual error code to decipher.
//    pszPrefix   - String to prepend to the printed message.
//    szFileName  - Filename the error occured in.
//    nLineNumber - Line number the error occured at.
//
//  RETURN VALUE:
//    none
//
//  COMMENTS:
//    This is the actual function that OutputDebugLastError
//    expands to.  Its not likely to be usefull except
//    through the OutputDebugLastError macro or to print
//    errors without line and file information.
//
//    If szFileName == NULL, then the File and Line are not printed.
//
//    Note that there is an internal string length limit of
//    MAXOUTPUTSTRINGLENGTH.  If this length is exceeded,
//    the behavior will be the same as wsprintf, although
//    it will be undetectable.  *KEEP szPrefix SHORT!*
//
//

void OutputDebugLastErrorFileLine(
    DWORD dwLastError, LPSTR szPrefix,
    LPSTR szFileName, DWORD nLineNumber)
{
    LPSTR szLastError;
    char szOutputLastError[MAXOUTPUTSTRINGLENGTH];

    if (szPrefix == NULL)
        szPrefix = "";

    // Pretty print the error.
    szLastError = FormatLastError(dwLastError, NULL, 0);

    // The only reason FormatLastError should fail is "Out of memory".
    if (szLastError == NULL)
    {
        if (szFileName == NULL)
            wsprintf(szOutputLastError, "%sOut of memory\r\n", szPrefix);
        else
            wsprintf(szOutputLastError, "%sOut of memory in file %s, line %d\r\n",
                szPrefix, szFileName, nLineNumber);

        OutputDebugString(szOutputLastError);

        return;
    }

    // If szFileName, then use it; else don't.
    if (szFileName != NULL)
    {
        wsprintf(szOutputLastError,
            "%sGetLastError returned: \"%s\" in File \"%s\", Line %d\r\n",
            szPrefix, szLastError, szFileName, nLineNumber);
    }
    else
    {
        wsprintf(szOutputLastError,
            "%sGetLastError returned: \"%s\"\r\n",
            szPrefix, szLastError);
    }

    // Pointer returned from FormatLineError *must* be freed!
    LocalFree(szLastError);

    // Print it!
    OutputDebugString(szOutputLastError);
    return;
}

//
//  FUNCTION: FormatLastError(DWORD, LPSTR, DWORD)
//
//  PURPOSE: Pretty print a system error to a string.
//
//  PARAMETERS:
//    dwLastError          - Actual error code to decipher.
//    szOutputBuffer       - String buffer to pretty print to.
//    dwSizeofOutputBuffer - Size of String buffer.
//
//  RETURN VALUE:
//    Returns the buffer printed to.
//
//  COMMENTS:
//    If szOutputBuffer isn't big enough to hold the whole string,
//    then the string gets truncated to fit the buffer.
//
//    If szOutputBuffer == NULL, then dwSizeofOutputBuffer
//    is ignored, a buffer 'big enough' is LocalAlloc()d and
//    a pointer to it is returned.  However, its *very* important
//    that this pointer be LocalFree()d by the calling application.
//
//

LPSTR FormatLastError(DWORD dwLastError,
    LPSTR szOutputBuffer, DWORD dwSizeofOutputBuffer)
{
    DWORD dwRetFM;
    DWORD dwFlags = FORMAT_MESSAGE_FROM_SYSTEM;

    // Should we allocate a buffer?
    if (szOutputBuffer == NULL)
    {
        // Actually, we make FormatMessage allocate the buffer, if needed.
        dwFlags |= FORMAT_MESSAGE_ALLOCATE_BUFFER;

        // minimum size FormatMessage should allocate.
        dwSizeofOutputBuffer = 1;
    }

    // Make FormatMessage pretty print the system error.
    dwRetFM = FormatMessage(
        dwFlags, NULL, dwLastError,
        MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
        (LPTSTR) &szOutputBuffer, dwSizeofOutputBuffer,
        NULL);

    // FormatMessage failed to print the error.
    if (dwRetFM == 0)
    {
        DWORD dwGetLastError;
        LPSTR szFormatMessageError;

        dwGetLastError = GetLastError();

        // If we asked FormatMessage to allocate a buffer, then it
        // might have allocated one.  Lets be safe and LocalFree it.
        if (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER)
        {
            __try
            {
                LocalFree(szOutputBuffer);
            }
            __except(EXCEPTION_EXECUTE_HANDLER)
            {
                // Actually, we do nothing for this fault.  If
                // there was a fault, it meant the buffer wasn't
                // allocated, and the LocalFree was unnecessary.
                ;
            }

            szOutputBuffer = LocalAlloc(LPTR, MAXOUTPUTSTRINGLENGTH);
            dwSizeofOutputBuffer = MAXOUTPUTSTRINGLENGTH;

            if (szOutputBuffer == NULL)
            {
                OutputDebugString("Out of memory trying to FormatLastError\r\n");
                return NULL;
            }
        }

        szFormatMessageError =
            FormatLastError(dwGetLastError, NULL, 0);

        if (szFormatMessageError == NULL)
            return NULL;

        wsprintf(szOutputBuffer,
            "FormatMessage failed on error 0x%lx for the following reason: %s",
            dwLastError, szFormatMessageError);

        LocalFree(szFormatMessageError);
    }

    return szOutputBuffer;
}

//
//  FUNCTION: OutputDebugLineCallback(...)
//
//  PURPOSE: Pretty print a message passed into a lineCallbackFunc.
//
//  PARAMETERS:
//    Standard lineCallbackFunc parameters.
//
//  RETURN VALUE:
//    none.
//
//  COMMENTS:
//
//    This function takes all of the parameters passed into a
//    lineCallbackFunc callback function, and pretty prints the
//    meaning of the message.  It then prints the result to
//    the debugging output.
//
//

void OutputDebugLineCallback(
    DWORD dwDevice, DWORD dwMsg, DWORD dwCallbackInstance,
    DWORD dwParam1, DWORD dwParam2, DWORD dwParam3)
{
    char szOutputBuff[MAXOUTPUTSTRINGLENGTH];

    FormatLineCallback(szOutputBuff,
        dwDevice, dwMsg, dwCallbackInstance,
        dwParam1, dwParam2, dwParam3);

    strcat(szOutputBuff,"\r\n");

    OutputDebugString(szOutputBuff);
}

//
//  FUNCTION: FormatLineCallback(...)
//
//  PURPOSE: Pretty prints into a buffer a lineCallbackFunc message.
//
//  PARAMETERS:
//    Standard lineCallbackFunc parameters.
//
//  RETURN VALUE:
//    The pointer to the buffer that has the resulting string.
//
//  COMMENTS:
//
//    This function takes all of the parameters passed into a
//    lineCallbackFunc callback function, and pretty prints the
//    meaning of the message.  It then returns the pointer to
//    the buffer containing this string.
//
//    If szOutputBuffer == NULL, then a buffer is LocalAlloc()d
//    and returned.  However, it is *very* important that this buffer
//    is LocalFree()d.
//

LPSTR FormatLineCallback(LPSTR szOutputBuffer,
    DWORD dwDevice, DWORD dwMsg, DWORD dwCallbackInstance,
    DWORD dwParam1, DWORD dwParam2, DWORD dwParam3)
{
    long lBufferIndex = 0;

    // Allocate the buffer if necessary.
    if (szOutputBuffer == NULL)
    {
        szOutputBuffer = (LPSTR) LocalAlloc(LPTR, MAXOUTPUTSTRINGLENGTH);

        if (szOutputBuffer == NULL)
            return NULL;
    }

    // Is this a known message?
    if (dwMsg >= sizeofArray(psz_dwMsg))
    {
        wsprintf(szOutputBuffer, "lineCallback: Unknown dwMsg: '0x%lx', "
            "dwDevice: '0x%lx', dwCallbackInstance: '0x%lx', "
            "dwParam1: '0x%lx', dwParam2: '0x%lx', dwParam3: '0x%lx'", dwMsg,
            dwDevice, dwCallbackInstance, dwParam1, dwParam2, dwParam3);
        return szOutputBuffer;
    }

    // Lets start pretty printing.
    lBufferIndex +=
        wsprintf(szOutputBuffer, "lineCallback: %s; dwDevice: '0x%lx'; ",
            psz_dwMsg[dwMsg], dwDevice);

    // Which message was it?  And start decoding it!
    // How the message is decoded depends entirely on the message.
    // READ THE HELP FILES if you more information on this.
    switch(dwMsg)
    {
        case LINE_ADDRESSSTATE:
        {
            lBufferIndex +=
                wsprintf(&(szOutputBuffer[lBufferIndex]),
                    "Address ID: 0x%lx, Address State: ", dwParam1);

            lBufferIndex +=
                strBinaryArrayAppend(&(szOutputBuffer[lBufferIndex]),
                    dwParam2,
                    pszfLINEADDRESSSTATE, sizeofArray(pszfLINEADDRESSSTATE));

            break;
        }

        case LINE_CALLINFO:
        {
            lBufferIndex +=
                strBinaryArrayAppend(&(szOutputBuffer[lBufferIndex]),
                    dwParam1,
                    pszfLINECALLINFOSTATE, sizeofArray(pszfLINECALLINFOSTATE));

            break;
        }

        case LINE_CALLSTATE:
        {

            lBufferIndex +=
                strBinaryArrayAppend(&(szOutputBuffer[lBufferIndex]),
                    dwParam3,
                    pszfLINECALLPRIVILEGE, sizeofArray(pszfLINECALLPRIVILEGE));
            lBufferIndex +=
                wsprintf(&(szOutputBuffer[lBufferIndex]),
                    "; ");

            lBufferIndex +=
                strBinaryArrayAppend(&(szOutputBuffer[lBufferIndex]),
                    dwParam1,
                    pszfLINECALLSTATE, sizeofArray(pszfLINECALLSTATE));

            switch(dwParam1)
            {
                case LINECALLSTATE_DIALTONE:
                {
                    lBufferIndex +=
                        wsprintf(&(szOutputBuffer[lBufferIndex]),
                            ": ");

                    lBufferIndex +=
                        strBinaryArrayAppend(&(szOutputBuffer[lBufferIndex]),
                        dwParam2,
                        pszfLINEDIALTONEMODE, sizeofArray(pszfLINEDIALTONEMODE));

                    break;
                }

                case LINECALLSTATE_BUSY:
                {
                    lBufferIndex +=
                        wsprintf(&(szOutputBuffer[lBufferIndex]),
                            ": ");

                    lBufferIndex +=
                        strBinaryArrayAppend(&(szOutputBuffer[lBufferIndex]),
                        dwParam2,
                        pszfLINEBUSYMODE, sizeofArray(pszfLINEBUSYMODE));

                    break;
                }

                case LINECALLSTATE_SPECIALINFO:
                {
                    lBufferIndex +=
                        wsprintf(&(szOutputBuffer[lBufferIndex]),
                            ": ");

                    lBufferIndex +=
                        strBinaryArrayAppend(&(szOutputBuffer[lBufferIndex]),
                        dwParam2,
                        pszfLINESPECIALINFO, sizeofArray(pszfLINESPECIALINFO));

                    break;
                }

                case LINECALLSTATE_DISCONNECTED:
                {
                    lBufferIndex +=
                        wsprintf(&(szOutputBuffer[lBufferIndex]),
                            ": ");

                    lBufferIndex +=
                        strBinaryArrayAppend(&(szOutputBuffer[lBufferIndex]),
                        dwParam2,
                        pszfLINEDISCONNECTED, sizeofArray(pszfLINEDISCONNECTED));

                    break;
                }

                case LINECALLSTATE_CONFERENCED:
                {
                    lBufferIndex +=
                        wsprintf(&(szOutputBuffer[lBufferIndex]),
                            ": Parent conference call handle: 0x%lx", dwParam2);

                    break;
                }
            }

            break;

}

        case LINE_CLOSE:
            break;

        case LINE_DEVSPECIFIC:
            break;

        case LINE_DEVSPECIFICFEATURE:
            break;

        case LINE_GATHERDIGITS:
        {
            lBufferIndex +=
                strBinaryArrayAppend(&(szOutputBuffer[lBufferIndex]),
                    dwParam1,
                    pszfLINEGATHERTERM, sizeofArray(pszfLINEGATHERTERM));

            break;
        }

        case LINE_GENERATE:
        {
            lBufferIndex +=
                strBinaryArrayAppend(&(szOutputBuffer[lBufferIndex]),
                    dwParam1,
                    pszfLINEGENERATETERM, sizeofArray(pszfLINEGENERATETERM));

            break;
        }

        case LINE_LINEDEVSTATE:
        {
            lBufferIndex +=
                strBinaryArrayAppend(&(szOutputBuffer[lBufferIndex]),
                    dwParam1,
                    pszfLINEDEVSTATE, sizeofArray(pszfLINEDEVSTATE));

            switch(dwParam1)
            {
                case LINEDEVSTATE_RINGING:
                    lBufferIndex +=
                        wsprintf(&(szOutputBuffer[lBufferIndex]),
                            "; Ring Mode: 0x%lx, Ring Count: %lu"
                            ,dwParam2, dwParam3);
                    break;

                case LINEDEVSTATE_REINIT:
                {
                    switch(dwParam2)
                    {
                        case LINE_CREATE:
                            lBufferIndex +=
                                wsprintf(&(szOutputBuffer[lBufferIndex]),
                                    "; ReInit reason: LINE_CREATE, "
                                        "New Line Device ID '0x%lx'"
                                    , dwParam3);
                            break;

                        case LINE_LINEDEVSTATE:
                            lBufferIndex +=
                                wsprintf(&(szOutputBuffer[lBufferIndex]),
                                    "; ReInit reason: LINE_LINEDEVSTATE, ");

                            lBufferIndex +=
                                strBinaryArrayAppend(&(szOutputBuffer[lBufferIndex]),
                                    dwParam3,
                                    pszfLINEDEVSTATE, sizeofArray(pszfLINEDEVSTATE));

                            break;

                        case 0:
                            break;
                        default:
                            lBufferIndex +=
                                wsprintf(&(szOutputBuffer[lBufferIndex]),
                                    "; ReInit reason: %s, dwParam3: 0x%lx"
                                    ,psz_dwMsg[dwParam2], dwParam3);
                            break;

                    }

                    break;
                }

                default:
                    break;
            }

            break;
        }

        case LINE_MONITORDIGITS:
        {
            lBufferIndex +=
                strBinaryArrayAppend(&(szOutputBuffer[lBufferIndex]),
                dwParam2,
                pszfLINEDIGITMODE, sizeofArray(pszfLINEDIGITMODE));

            lBufferIndex +=
                wsprintf(&(szOutputBuffer[lBufferIndex]),
                    ", Received: '%c'", LOBYTE(LOWORD(dwParam1)));

            break;
        }

        case LINE_MONITORMEDIA:
        {
            lBufferIndex +=
                strBinaryArrayAppend(&(szOutputBuffer[lBufferIndex]),
                    dwParam1,
                    pszfLINEMEDIAMODE, sizeofArray(pszfLINEMEDIAMODE));

            break;
        }

        case LINE_MONITORTONE:
        {
            lBufferIndex +=
                wsprintf(&(szOutputBuffer[lBufferIndex]),
                    "AppSpecific tone '%lu'", dwParam1);
            break;
        }

        case LINE_REPLY:
        {
            if (dwParam2 == 0)
            {
                lBufferIndex +=
                    wsprintf(&(szOutputBuffer[lBufferIndex]),
                        "Request ID: 0x%lx; Successful reply!", dwParam1);
            }
            else
            {
                char szTmpBuff[256];

                FormatLineError((long) dwParam2, szTmpBuff, 255);

                lBufferIndex +=
                    wsprintf(&(szOutputBuffer[lBufferIndex]),
                        "Request ID: 0x%lx; UnSuccessful reply; %s",
                        dwParam1, szTmpBuff);
            }

            break;
        }

        case LINE_REQUEST:
        {
            lBufferIndex +=
                strBinaryArrayAppend(&(szOutputBuffer[lBufferIndex]),
                dwParam1,
                pszfLINEREQUESTMODE, sizeofArray(pszfLINEREQUESTMODE));

            switch(dwParam1)
            {
                case LINEREQUESTMODE_DROP:
                {
                    char szHwndName[1024];

                    SendMessage((HWND) dwParam2, WM_GETTEXT, 1024, (long) szHwndName);

                    lBufferIndex +=
                        wsprintf(&(szOutputBuffer[lBufferIndex]),
                            ": hwnd dropping = 0x%lx, \"%s\"; wRequestID = %u",
                            dwParam2, szHwndName, LOWORD(dwParam3));
                    break;
                }
                default:
                    break;
            }

            break;
        }

        case LINE_CREATE:
        {
            lBufferIndex +=
                wsprintf(&(szOutputBuffer[lBufferIndex]),
                    "New Line Device ID '0x%lx'", dwParam1);
            break;
        }

        case PHONE_CREATE:
        {
            lBufferIndex +=
                wsprintf(&(szOutputBuffer[lBufferIndex]),
                    "New Phone Device ID '0x%lx'", dwParam1);
            break;
        }

        default:
            lBufferIndex +=
                wsprintf(&(szOutputBuffer[lBufferIndex]),
                    "dwParam1: 0x%lx , dwParam2: 0x%lx , dwParam3: 0x%lx",
                    dwParam1, dwParam2, dwParam3);
            break;

    } // End switch(dwMsg)

    // return that pointer!
    return szOutputBuffer;

}

//
//  FUNCTION: strBinaryArrayAppend(LPSTR, DWORD, LPSTR *, DWORD)
//
//  PURPOSE: Takes a bitmapped DWORD, an array representing that
//    binary mapping, and pretty prints it to a buffer.
//
//  PARAMETERS:
//    szOutputBuffer      - Buffer to print to.
//    dwFlags             - Binary mapped flags to interpret.
//    szStringArray       - Array of strings.
//    dwSizeofStringArray - number of elements in szStringArray.

//
//  RETURN VALUE:
//    The number of characters printed into szOutputBuffer.
//
//  COMMENTS:
//
//    This function takes dwFlags and checks each bit.  If the
//    bit is set, the appropriate string (taken from szStringArray)
//    is printed to the szOutputBuffer string buffer.  If there were
//    more bits set in the string than elements in the array, and error
//    is also tagged on the end.
//
//    This function is intended to be used only within the TapiInfo module.
//

static long strBinaryArrayAppend(LPSTR szOutputBuffer, DWORD dwFlags,
     LPSTR szStringArray[], DWORD dwSizeofStringArray)
{
    DWORD dwIndex = 1, dwPower = 1;
    long lBufferIndex = 0;
    BOOL bFirst = TRUE;

    // The zeroth element in every bitmapped array is the "unknown" or
    // "unchanged" message.
    if (dwFlags == 0)
    {
        lBufferIndex =
            wsprintf(szOutputBuffer, "%s", szStringArray[0]);
        return lBufferIndex;
    }

    // iterate through the flags and check each one.
    while(dwIndex < dwSizeofStringArray)
    {
        // If we find one, print it.
        if (dwFlags & dwPower)
            // Seporate each printing with a ", " except the first one.
            if (bFirst)
            {
                lBufferIndex +=
                    wsprintf(&(szOutputBuffer[lBufferIndex]),
                        "%s", szStringArray[dwIndex]);
                bFirst = FALSE;
            }
            else
                lBufferIndex +=
                    wsprintf(&(szOutputBuffer[lBufferIndex]),
                        ", %s", szStringArray[dwIndex]);

        dwIndex ++;
        dwFlags &= ~dwPower;  // clear it so we know we checked it.
        dwPower *= 2;
    }

    // If there are any flags left, they were not in the array.
    if (dwFlags)
    {
        if (bFirst)
            lBufferIndex +=
                wsprintf(&(szOutputBuffer[lBufferIndex]),
                    "Unknown flags '0x%lx'", dwFlags);
        else
            lBufferIndex +=
                wsprintf(&(szOutputBuffer[lBufferIndex]),
                    ", Unknown flags '0x%lx'", dwFlags);
    }

    // how many did we print into the buffer?
    return lBufferIndex;
}

//
//  FUNCTION: OutputDebugPrintf(LPCSTR, ...)
//
//  PURPOSE: wsprintf to the debugging output.
//
//  PARAMETERS:
//    Exactly the same as wsprintf.
//
//  RETURN VALUE:
//    none.
//
//  COMMENTS:
//
//    This function takes exactly the same parameters as wsprintf and
//    prints the results to the debugging output.
//

void __cdecl OutputDebugPrintf(LPCSTR lpszFormat, ...)
{
    char szOutput[MAXOUTPUTSTRINGLENGTH];
    va_list v1;
    DWORD dwSize;

    va_start(v1, lpszFormat);

    dwSize = wvsprintf(szOutput, lpszFormat, v1);

    if (szOutput[dwSize-1] != '\n')
        strcat(szOutput, "\r\n");

    OutputDebugString(szOutput);
}


TAPIINFO.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 1995 - 1998 Microsoft Corporation.  All Rights Reserved.
//
// PURPOSE:
//    Contains public declarations for the TapiInfo module.
//

#define OutputDebugLineError(lLineError, pszPrefix) \
    OutputDebugLineErrorFileLine(lLineError, pszPrefix,\
        __FILE__, __LINE__)

#define OutputDebugLastError(dwLastError, pszPrefix) \
    OutputDebugLastErrorFileLine(dwLastError, pszPrefix,\
        __FILE__, __LINE__)

void OutputDebugLineErrorFileLine(
    long lLineError, LPSTR szPrefix,
    LPSTR szFileName, DWORD nLineNumber);

void OutputDebugLastErrorFileLine(
    DWORD dwLastError, LPSTR szPrefix,
    LPSTR szFileName, DWORD nLineNumber);

void OutputDebugLineCallback(
    DWORD dwDevice, DWORD dwMsg, DWORD dwCallbackInstance,
    DWORD dwParam1, DWORD dwParam2, DWORD dwParam3);

void __cdecl OutputDebugPrintf(LPCSTR lpszFormat, ...);

LPSTR FormatLineError(long lLineError,
    LPSTR szOutputBuffer, DWORD dwSizeofOutputBuffer);

LPSTR FormatLastError(DWORD dwLastError,
    LPSTR szOutputBuffer, DWORD dwSizeofOutputBuffer);

LPSTR FormatLineCallback(LPSTR pszOutputBuffer,
    DWORD dwDevice, DWORD dwMsg, DWORD dwCallbackInstance,
    DWORD dwParam1, DWORD dwParam2, DWORD dwParam3);


TOOLBAR.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.
//
//  MODULE: toolbar.c
//
//  PURPOSE: Handles general routines for the Toolbar control
//
//  FUNCTIONS:
//    CreateTBar    - Creates the Toolbar control for the sample.
//    MsgNotify     - Handles the WM_NOTIFY message that gets sent to
//                    the parent window to get ToolTip Text.
//  COMMENTS:
//

#include <windows.h>            // required for all Windows applications
#include <windowsx.h>
#include <commctrl.h>           // prototypes and defs for common controls
#include "globals.h"            // prototypes specific to this application
#include "toolbar.h"            // prototypes and #defines for toolbar.c

// Global Variable for the toolbar control.

HWND    hWndToolbar;

//  **TODO**  Change the following values to match your toolbar bitmap
//
// NUMIMAGES    = Number of images in toolbar.bmp.  Note that this is not
//                the same as the number of elements on the toolbar.
// IMAGEWIDTH   = Width of a single button image in toolbar.bmp
// IMAGEHEIGHT  = Height of a single button image in toolbar.bmp
// BUTTONWIDTH  = Width of a button on the toolbar (zero = default)
// BUTTONHEIGHT = Height of a button on the toolbar (zero = default)

#define NUMIMAGES       9
#define IMAGEWIDTH      18
#define IMAGEHEIGHT     17
#define BUTTONWIDTH     0
#define BUTTONHEIGHT    0

//  **TODO**  Add/remove entries in the following array to define the
//            toolbar buttons (see documentation for TBBUTTON).

TBBUTTON tbButton[] =
{
    {1, IDM_MAKECALL,   TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0},
    {2, IDM_HANGUPCALL, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0},
    {0, 0,              TBSTATE_ENABLED, TBSTYLE_SEP,    0, 0},
    {7, IDM_ABOUT,      TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0},
};

//
//  FUNCTION: CreateTBar(HWND)
//
//  PURPOSE:  Calls CreateToolBarEx()
//
//
//  PARAMETERS:
//
//  hwnd - Window handle : Used for the hWndParent parameter of the control.
//
//  RETURN VALUE:
//
//  If toolbar control was created successfully Return TRUE,
//  else returns FALSE.
//
//  COMMENTS:
//
//

BOOL CreateTBar(HWND hwnd)
{
    hWndToolbar = CreateToolbarEx(hwnd,
                                  WS_CHILD | WS_VISIBLE | TBSTYLE_TOOLTIPS,
                                  IDM_TOOLBAR,
                                  NUMIMAGES,
                                  hInst,
                                  IDB_BMP,
                                  tbButton,
                                  sizeof(tbButton)/sizeof(TBBUTTON),
                                  BUTTONWIDTH,
                                  BUTTONHEIGHT,
                                  IMAGEWIDTH,
                                  IMAGEHEIGHT,
                                  sizeof(TBBUTTON));

    return (hWndToolbar != NULL);
}

//
//  FUNCTION: MsgNotify(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE:  WM_NOTIFY is sent to the parent window to get the
//            tooltip text assoc'd with that toolbar button.
//
//  PARAMETERS:
//
//    hwnd      - Window handle  (Unused)
//    uMessage  - Message number (Unused)
//    wparam    - Extra data     (Unused)
//    lparam    - TOOLTIPTEXT FAR*
//
//  RETURN VALUE:
//    Always returns 0 - Message handled
//
//
//  COMMENTS:
//    This message fills in the lpszText field of the TOOLTIPTEXT
//    structure if code == TTN_NEEDTEXT
//

LRESULT MsgNotify(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    LPTOOLTIPTEXT lpToolTipText;
    static char   szBuffer[64];

    lpToolTipText = (LPTOOLTIPTEXT)lparam;
    if (lpToolTipText->hdr.code == TTN_NEEDTEXT)
    {
        LoadString(hInst,
                   lpToolTipText->hdr.idFrom,   // string ID == command ID
                   szBuffer,
                   sizeof(szBuffer));

        lpToolTipText->lpszText = szBuffer;
    }
    return 0;
}

void EnableMakeCall(HWND hWnd, BOOL bEnable)
{
    EnableMenuItem(GetMenu(hWnd), IDM_MAKECALL, MF_BYCOMMAND |
        (bEnable ? MF_ENABLED : MF_GRAYED));
    SendMessage(hWndToolbar, TB_ENABLEBUTTON, (WPARAM) IDM_MAKECALL,
        (LPARAM) MAKELONG(bEnable, 0));

}

void EnableHangupCall(HWND hWnd, BOOL bEnable)
{
    EnableMenuItem(GetMenu(hWnd), IDM_HANGUPCALL, MF_BYCOMMAND |
        (bEnable ? MF_ENABLED : MF_GRAYED));
    SendMessage(hWndToolbar, TB_ENABLEBUTTON, (WPARAM) IDM_HANGUPCALL,
        (LPARAM) MAKELONG(bEnable, 0));
}


TOOLBAR.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 1995 - 1998 Microsoft Corporation.  All Rights Reserved.
//
// PURPOSE:
//    Contains declarations for the toolbar module.
//

//-------------------------------------------------------------------------
#define IDM_TOOLBAR         500 // ID for the toolbar.
#define IDB_BMP             700 // Bitmap ID for the toolbar.

// Global variable for Toolbar window
extern HWND hWndToolbar;

// Function protoptype for Creating Toolbar
BOOL CreateTBar(HWND);
void EnableMakeCall(HWND hWnd, BOOL bEnable);
void EnableHangupCall(HWND hWnd, BOOL bEnable);


WINMAIN.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.
//
//  MODULE:   winmain.c
//
//  PURPOSE:   Calls initialization functions and processes the message loop
//
//
//  PLATFORMS: Windows 95
//
//  FUNCTIONS:
//    WinMain() - calls initialization functions, processes message loop
//
//  COMMENTS:
//
//

#include <windows.h>            // required for all Windows applications
#include "globals.h"            // prototypes specific to this application

//
//  FUNCTION: WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
//
//  PURPOSE: calls initialization function, processes message loop
//
//  PARAMETERS:
//
//    hInstance - The handle to the instance of this application that
//          is currently being executed.
//
//    hPrevInstance - This parameter is always NULL in Win32
//          applications.
//
//    lpCmdLine - A pointer to a null terminated string specifying the
//          command line of the application.
//
//    nCmdShow - Specifies how the main window is to be diplayed.
//
//  RETURN VALUE:
//    If the function terminates before entering the message loop,
//    return FALSE.
//    Otherwise, return the WPARAM value sent by the WM_QUIT message.
//
//
//  COMMENTS:
//
//    Windows recognizes this function by name as the initial entry point
//    for the program.  This function calls the 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 APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR lpCmdLine,
                     int nCmdShow)

{
    MSG msg;
    HANDLE hAccelTable;

    // Initialize application by setting up the main window.
    if (!InitApplication(hInstance, nCmdShow))
    {
        return FALSE;           // Exits if unable to initialize
    }

    hAccelTable = LoadAccelerators(hInstance, szAppName);

    // Acquire and dispatch messages until a WM_QUIT message is received.
    while( GetMessage(&msg, NULL, 0, 0))
    {

        if (!TranslateAccelerator (msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);  // Translates virtual key codes
            DispatchMessage(&msg);   // Dispatches message to window
        }
    }
    return msg.wParam;  // Returns the value from PostQuitMessage
}


README.TXT

TAPI 3.0
INCOMING Sample Application

Overview:

Incoming is a sample TAPI 3.0 application that waits for and answers
incoming phone calls.

In order to receive incoming call notifications, as well as any call
state event notifications, the outgoing ITCallNotification interface
must be implemented by the TAPI 3.0 application, and registered with
TAPI 3.0 through the IConnectionPoint mechanism.  For more information
on IConnectionPoint, and IConnectiontPointContainer, please refer
to the COM documentation.

CALLNOT.CPP and CALLNOT.H show the implementation of the ITCallNotification
interface.  ITCallNotification is defined by TAPI 3.0, and the interface
definition is in tapi3.h.

INCOMING.CPP shows how to register the interface, and how to answer
and incoming call.

How to build the sample:

To run the INCOMING sample application, set the SDK build environment, then
type "nmake" in the incoming directory.  This will build t3in.exe

How to use the sample:

After the sample is built, run t3in.exe.

A small dialog box will appear, and the status will be "Waiting for a call."
If your computer does not have any TAPI devices, the application will display
an error as is starts up.  The application will wait for calls on all TAPI
addresses that support audio calls.  If a call comes in that has both
audio and video, the sample will also set up the video stream.

When a call arrives on one of the TAPI addresses, the "Answer" button will
be enabled, and the status message will change to "Incoming Owner Call".

When the "Answer" button is clicked, the application will answer
the call.

What functionality does this sample show:

The incoming sample application demonstrates to to receive new call
notification and call state event notifications.  It also demostrates
the basic TAPI 3.0 functions involved in answering a call.

What this sample does not show:

This sample does not let the user choose the TAPI address for the
application to use.  It does not allow the user to decide what media
types to listen for, and it does not allow the user to decide what
terminals to use.

Hints:

This sample should be able to run as long as you have TAPI devices
installed.  Many computers have a modem.  If the modem is installed
correctly, it will show up as a TAPI device.


CALLNOT.H

////////////////////////////////////////////////////////////////////////////
// CallNotification.h : Declaration of the CCallNotification object
//
////////////////////////////////////////////////////////////////////////////
#ifndef __CALLNOTIFICATION_H__
#define __CALLNOTIFICATION_H__

/////////////////////////////////////////////////////////////////////////////
// CCallNotification
class CCallNotification : public ITCallNotification
{
private:

    DWORD       m_dwRefCount;
    ITAddress * m_pAddress;

public:

    // CCallNotification implements ITCallNotification
    //  Declare ITCallNotification methods here
    HRESULT STDMETHODCALLTYPE CallEventNotification(
        ITAddress * pAddress,
        CALL_EVENT_TYPE type,
        IDispatch * pEvent
        );

// other COM stuff:
public:

    // constructor
    CCallNotification(){}
    // destructor
    ~CCallNotification(){}

    // initialization function
    // this stuff could also be done in the
    // constructor
    HRESULT Initialize( ITAddress * pAddress )
    {
        m_dwRefCount = 1;
        m_pAddress = pAddress;
        m_pAddress->AddRef();

        return S_OK;
    }

    void Shutdown()
    {
        if (NULL != m_pAddress)
        {
            m_pAddress->Release();
        }
    }

    // IUnknown implementation
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **ppvObject)
    {
        if (iid == IID_ITCallNotification)
        {
            m_dwRefCount++;
            *ppvObject = (void *)this;
            return S_OK;
        }

        if (iid == IID_IUnknown)
        {
            m_dwRefCount++;
            *ppvObject = (void *)this;
            return S_OK;
        }

        return S_FALSE;
    }
ULONG STDMETHODCALLTYPE AddRef()
    {
        m_dwRefCount++;
        return m_dwRefCount;
    }

ULONG STDMETHODCALLTYPE Release()
    {
        ULONG l;

        l  = m_dwRefCount--;

        if ( 0 == m_dwRefCount)
        {
            delete this;
        }

        return l;
    }

};

#endif //__CALLNOTIFICATION_H__


INCOMING.CPP

#define UNICODE
#include <list>
#include <windows.h>
#include <tapi3.h>
#include "callnot.h"
#include "resource.h"

//////////////////////////////////////////////////////////
// T3IN.EXE
//
// Sample application that handling incoming TAPI calls.
// In order to receive incoming calls, the application must
// implement and register the outgoing ITCallNotification
// interface.
//
// This application will register to recieve calls on
// all addresses that support at least audioin and audioout.
//
// NOTE:  This application is limited to working with one call at
// at time, and will not work correctly if multiple calls
// are present at the same time.
//////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////
// constants
//////////////////////////////////////////////////////////

const DWORD MAXTERMINALS    = 5;

//////////////////////////////////////////////////////////
// TYPEDEFS
//////////////////////////////////////////////////////////

using namespace std;
typedef list<CCallNotification *> CCallNotificationPtrList;

//////////////////////////////////////////////////////////
// GLOBALS
//////////////////////////////////////////////////////////

HINSTANCE               ghInst;
ITTAPI *                gpTapi;
ITBasicCallControl *    gpCall;
HWND                    ghDlg = NULL;

BSTR                    gbstrAudioIn;
BSTR                    gbstrAudioOut;
BSTR                    gbstrVideoIn;
BSTR                    gbstrVideoOut;

WCHAR gszTapi30[] = L"TAPI 3.0 Incoming Call Sample";

// list of outgoing interface objects we have registered
CCallNotificationPtrList gpCallNotificationList;

//////////////////////////////////////////////////////////
// PROTOTYPES
//////////////////////////////////////////////////////////
BOOL
CALLBACK
MainDialogProc(
               HWND hDlg,
               UINT uMsg,
               WPARAM wParam,
               LPARAM lParam
              );

HRESULT
GetMediaTerminal(
                 ITAddress *,
                 BSTR bstrMedia,
                 ITMediaTerminal ** ppMediaTerminal
                );
HRESULT
ListenOnAddresses();

HRESULT
ListenOnThisAddress(
                    ITAddress * pAddress
                   );

HRESULT
AnswerTheCall();

HRESULT
DisconnectTheCall();

void
ReleaseTheCall();

void
DoMessage(
          LPWSTR pszMessage
         );

void
SetStatusMessage(
                 LPWSTR pszMessage
                );

HRESULT
InitializeTapi();

void
ShutdownTapi();

void
EnableButton(
             int ID
            );
void
DisableButton(
              int ID
             );

//////////////////////////////////////////////////////////
//
//              FUNCTIONS
//
//////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////
// WinMain
//////////////////////////////////////////////////////////
int
WINAPI
WinMain(
        HINSTANCE hInst,
        HINSTANCE hPrevInst,
        LPSTR lpCmdLine,
        int nCmdShow
       )
{
    ghInst = hInst;

    // need to coinit
    if (!SUCCEEDED(CoInitialize(NULL)))
    {
        return 0;
    }

    // do all tapi initialization
    if (S_OK != InitializeTapi())
    {
        return 0;
    }

    // everything is initialized, so
    // start the main dialog box
    DialogBox(
              ghInst,
              MAKEINTRESOURCE(IDD_MAINDLG),
              NULL,
              MainDialogProc
             );

    // clean up
    ShutdownTapi();

    CoUninitialize();

    return 1;
}

//////////////////////////////////////////////////////////////
// InitializeTapi
//
// Various initializations
///////////////////////////////////////////////////////////////
HRESULT
InitializeTapi()
{
    HRESULT         hr;
    LPWSTR          psz;

    // cocreate the TAPI object
    hr = CoCreateInstance(
                          CLSID_TAPI,
                          NULL,
                          CLSCTX_INPROC_SERVER,
                          IID_ITTAPI,
                          (LPVOID *)&gpTapi
                         );

    if (hr != S_OK)
    {
        DoMessage(L"CoCreateInstance on TAPI failed");
        return hr;
    }

    // call initialize.  this must be called before
    // any other tapi functions are called.
    hr = gpTapi->Initialize();

    if (S_OK != hr)
    {
        DoMessage(L"TAPI failed to initialize");

        gpTapi->Release();
        gpTapi = NULL;

        return hr;
    }

    // convert the TAPIMEDIATYPEs to BSTRs for
    // convenience throughout the program

    StringFromIID( TAPIMEDIATYPE_AudioIn, &psz );
    gbstrAudioIn = SysAllocString( psz );
    CoTaskMemFree( psz );

    StringFromIID( TAPIMEDIATYPE_AudioOut, &psz );
    gbstrAudioOut = SysAllocString( psz );
    CoTaskMemFree( psz );

    StringFromIID( TAPIMEDIATYPE_VideoIn, &psz );
    gbstrVideoIn = SysAllocString( psz );
    CoTaskMemFree( psz );

    StringFromIID( TAPIMEDIATYPE_VideoOut, &psz );
    gbstrVideoOut = SysAllocString( psz );
    CoTaskMemFree( psz );

    // find all address objects that
    // we will use to listen for calls on
    hr = ListenOnAddresses();

    if (S_OK != hr)
    {
        DoMessage(L"Could not find any addresses to listen on");

        gpTapi->Release();
        gpTapi = NULL;

        return hr;
    }

    return S_OK;

}

///////////////////////////////////////////////////////////////
// ShutdownTapi
///////////////////////////////////////////////////////////////
void
ShutdownTapi()
{
    CCallNotificationPtrList::iterator i;
    // if there is still a call,
    // release it
    if (NULL != gpCall)
    {
        gpCall->Release();
        gpCall = NULL;
    }

    // Release all CallNotification objects
    for (
         i = gpCallNotificationList.begin();
         i != gpCallNotificationList.end();
         i++
        )
    {

        (*i)->Shutdown();

        // At this point, we should call Unadvise on
        // the ConnectionPoint.  However, TAPI 3.0
        // does not support Unadvise for the ITCallNotification
        // interface.  So, here we just release the object
        (*i)->Release();
    }

    // release main object.
    if (NULL != gpTapi)
    {
        gpTapi->Shutdown();
        gpTapi->Release();
    }

    // free the BSTRs
    SysFreeString( gbstrAudioIn );
    SysFreeString( gbstrAudioOut );
    SysFreeString( gbstrVideoIn );
    SysFreeString( gbstrVideoOut );
}

///////////////////////////////////////////////////////////////////////////
// MainDlgProc
///////////////////////////////////////////////////////////////////////////
BOOL
CALLBACK
MainDialogProc(
               HWND hDlg,
               UINT uMsg,
               WPARAM wParam,
               LPARAM lParam
              )
{
    switch (uMsg)
    {
        case WM_INITDIALOG:
        {
            // set up dialog
            ghDlg = hDlg;

            DisableButton( IDC_ANSWER );
            DisableButton( IDC_DISCONNECT );

            SetStatusMessage( L"Waiting for a call..." );

            return 0;
        }

        case WM_COMMAND:
        {
            if ( LOWORD(wParam) == IDCANCEL )
            {
                // quit
                EndDialog( hDlg, 0 );

                return 1;
            }

            switch ( LOWORD(wParam) )
            {
                // dial request
                case IDC_ANSWER:
                {
                    SetStatusMessage(L"Answering...");
                    // answer the call
                    if ( S_OK == AnswerTheCall() )
                    {
                        SetStatusMessage(L"Connected");

                        EnableButton( IDC_DISCONNECT );
                        DisableButton( IDC_ANSWER );
                    }
                    else
                    {
                        DisableButton( IDC_ANSWER );
                        DoMessage(L"Answer failed");
                        SetStatusMessage(L"Waiting for a call...");
                    }

                    return 1;
                }

                // disconnect request
                case IDC_DISCONNECT:
                {
                    SetStatusMessage(L"Disconnecting...");
                    // disconnect
                    if (S_OK != DisconnectTheCall())
                    {
                        DoMessage(L"Disconnect failed");
                    }

                    return 1;
                }

                // disconnected notification
                case IDC_DISCONNECTED:
                {
                    // release
                    ReleaseTheCall();

                    EnableButton( IDOK );
                    DisableButton( IDC_DISCONNECT );

                    SetStatusMessage(L"Waiting for a call...");

                    return 1;
                }
                default:

                    return 0;
            }
        }
        default:

            return 0;
    }
}

////////////////////////////////////////////////////////////////////////
// ListenOnAddresses
//
// This procedure will find all addresses that support audioin and audioout
// and will call ListenOnThisAddress to start listening on it.
////////////////////////////////////////////////////////////////////////
HRESULT
ListenOnAddresses()
{
    HRESULT             hr = S_OK;
    IEnumAddress *      pEnumAddress;
    ITAddress *         pAddress;
    ITMediaSupport *    pMediaSupport;
    VARIANT_BOOL        bSupport;

    // enumerate the addresses
    hr = gpTapi->_EnumerateAddresses( &pEnumAddress );

    if (S_OK != hr)
    {
        return hr;
    }

    while ( TRUE )
    {
        // get the next address
        hr = pEnumAddress->Next( 1, &pAddress, NULL );

        if (S_OK != hr)
        {
            break;
        }

        pAddress->QueryInterface( IID_ITMediaSupport, (void **)&pMediaSupport );

        // does it support AudioIn?
        pMediaSupport->QueryMediaType(
                                      gbstrAudioIn,
                                      &bSupport
                                     );

        if (bSupport)
        {
            // does it also support AudioOut?
            pMediaSupport->QueryMediaType(
                                          gbstrAudioOut,
                                          &bSupport
                                         );

            if (bSupport)
            {
                // listen
                hr = ListenOnThisAddress( pAddress );
                if (S_OK != hr)
                {
                    DoMessage(L"Listen failed on an address");
                }
            }
        }

        pMediaSupport->Release();
        pAddress->Release();
    }

    pEnumAddress->Release();

    return S_OK;
}

///////////////////////////////////////////////////////////////////
// ListenOnThisAddress
//
// Perform the steps involved in setting up an address to listen
// for calls.  Setting up an application to listen for calls is a two
// step process.
//
// First, the app must call RegisterCallTypes
// on the address that it wants calls on
//
// Second, it must register it's implementation of ITCallNotification
// with the address.  Registration is done via COM's ConnectionPoint
// methods.  See the COM documentation for more informations on
// ConnectionPoints
//
///////////////////////////////////////////////////////////////////
HRESULT
ListenOnThisAddress(
                    ITAddress * pAddress
                   )
{
    HRESULT hr = S_OK;
    IConnectionPointContainer * pCPC;
    IConnectionPoint *          pCP;
    CCallNotification *         pCallNotification;
    DWORD                       dwCookie;
    VARIANT var;

    // RegisterCallTypes takes an array (in a variant) of
    // MediaTypes for the address to listen for.
    // If NULL is specified for the array, that means to listen
    // for all the media types that the address supports.
    //
    // Since we already know the address supports audioin/audioout,
    // just tell it to listen for all types.  Also, we will tell
    // it that we are only interested in being the owner of calls,
    // not monitoring

    VariantInit( &var );
    var.vt = VT_ARRAY;
    var.parray = NULL;

    hr = pAddress->RegisterCallTypes(
                                     FALSE,
                                     TRUE,  // Want to be owner
                                     FALSE, // Don't want to be monitor
                                     var    // mediatypes
                                    );

    if (S_OK != hr)
    {
        return hr;
    }

    // now we need to register our outgoing interface through
    // the ConnectionPoint methods.

    // First we create our CCallNotification object
    pCallNotification = new CCallNotification;

    if (NULL == pCallNotification)
    {
        return E_OUTOFMEMORY;
    }

    // initialize the object
    hr = pCallNotification->Initialize( pAddress );

    if (S_OK != hr)
    {
        pCallNotification->Release();
        return hr;
    }

    // get the IConnectionPointContainer interface
    // from the address object
    hr = pAddress->QueryInterface(
                                  IID_IConnectionPointContainer,
                                  (void **)&pCPC
                                 );

    if (S_OK != hr)
    {
        pCallNotification->Release();
        pCallNotification = NULL;
        return hr;
    }

    // find the ConnectionPoint we are interested in
    hr = pCPC->FindConnectionPoint( IID_ITCallNotification, &pCP );

    pCPC->Release();

    if (S_OK != hr)
    {
        pCallNotification->Release();
        pCallNotification = NULL;
        return hr;
    }

    // call the advise function
    //
    // if this function succeeds, the address will start
    // listening for calls.
    hr = pCP->Advise(
                     (IUnknown *)pCallNotification,
                     &dwCookie
                    );

    pCP->Release();

    if (S_OK != hr)
    {
        pCallNotification->Release();
        pCallNotification = NULL;
        return hr;
    }

    // save the notification object
    // note that normally you would want
    // to save the dwCookie as well.  Refer
    // to the COM ConnectionPoint documentation
    // for more details.
    gpCallNotificationList.push_back( pCallNotification );

    return S_OK;
}

/////////////////////////////////////////////////////////
// GetMediaTerminal
//
// Creates a MediaTerminal for the bstrMediaType passed
// in, using the default terminal for the bstrMediaType
//
/////////////////////////////////////////////////////////
HRESULT
GetMediaTerminal(
                 ITAddress * pAddress,
                 BSTR bstrMediaType,
                 ITMediaTerminal ** ppMediaTerminal
                )
{
    HRESULT             hr = S_OK;
    ITTerminalSupport * pTerminalSupport;
    ITTerminal *        pTerminal;

    // get the terminal support interface
    pAddress->QueryInterface( IID_ITTerminalSupport, (void **)&pTerminalSupport );

    // get the default terminal for MediaType
    hr = pTerminalSupport->GetDefaultTerminal(
                                              bstrMediaType,
                                              &pTerminal
                                             );

    pTerminalSupport->Release();

    if (S_OK != hr)
    {
        return hr;
    }

    // Create a media terminal for MediaType
    // Use the default terminal we just got
    hr = gpTapi->CreateMediaTerminal(
                                     bstrMediaType,
                                     pTerminal,
                                     ppMediaTerminal
                                    );

    pTerminal->Release();

    if (S_OK != hr)
    {
        return hr;
    }

    return S_OK;

}

/////////////////////////////////////////////////////////
// GetVideoInMediaTerminal
//
// Creates a MediaTerminal for the VideoIn mediatype
// This is a dynamic terminal type.
//
/////////////////////////////////////////////////////////
HRESULT
GetVideoInMediaTerminal(
                 ITAddress * pAddress,
                 ITMediaTerminal ** ppMediaTerminal
                )
{
    HRESULT             hr = S_OK;
    ITTerminalSupport * pTerminalSupport;
    ITTerminal *        pTerminal;

    // get the terminal support interface
    pAddress->QueryInterface( IID_ITTerminalSupport, (void **)&pTerminalSupport );

    BSTR bstrTerminalClass;
    LPOLESTR lpTerminalClass;

    StringFromIID(
                CLSID_VideoWindowTerm,
                &lpTerminalClass
                );

    bstrTerminalClass = SysAllocString ( lpTerminalClass );

    CoTaskMemFree( lpTerminalClass );

    hr = pTerminalSupport->CreateTerminal(
        bstrTerminalClass,
        &pTerminal
        );

    SysFreeString( bstrTerminalClass );
    pTerminalSupport->Release();

    if (FAILED(hr))
    {
        return hr;
    }

    // Create a media terminal for MediaType
    // Use the default terminal we just got
    hr = gpTapi->CreateMediaTerminal(
                                     gbstrVideoIn,
                                     pTerminal,
                                     ppMediaTerminal
                                    );

    pTerminal->Release();

    if (S_OK != hr)
    {
        return hr;
    }

    return S_OK;

}

/////////////////////////////////////////////////////////////////
// CreateMediaTerminals
//
// Create audioin and audioout terminals. Videoin and videoout terminals
// are created only when the address supports them.
//
// This function assumes that ppMediaTerminals has a size no less than four.
/////////////////////////////////////////////////////////////////
HRESULT
CreateMediaTerminals(
                     ITAddress *pAddress,
                     ITMediaTerminal ** ppMediaTerminals,
                     PLONG pNumMediaTerminals
                    )
{
    int count = 0;
    HRESULT hr;

    // get the mediaterminal for audioin
    hr = GetMediaTerminal(
                          pAddress,
                          gbstrAudioIn,
                          &ppMediaTerminals[count]
                         );

    if (S_OK != hr)
    {
        return hr;
    }

    count ++;

    // get the mediaterminal for audioout
    hr = GetMediaTerminal(
                          pAddress,
                          gbstrAudioOut,
                          &ppMediaTerminals[count]
                         );

    if (S_OK != hr)
    {
        ppMediaTerminals[0]->Release();
        return hr;
    }

    count ++;

    // Find out if the address supports video.
    ITMediaSupport *pMediaSupport;
    VARIANT_BOOL    bSupport;

    pAddress->QueryInterface( IID_ITMediaSupport, (void **)&pMediaSupport );

    // does it support VideoIn?
    pMediaSupport->QueryMediaType(
                                  gbstrVideoIn,
                                  &bSupport
                                 );

    if (bSupport)
    {
        // get the mediaterminal for VideoIn
        hr = GetVideoInMediaTerminal(
                                     pAddress,
                                     &ppMediaTerminals[count]
                                    );

        if (S_OK == hr)
        {
            count ++;
        }

        // does it also support VideoOut?
        pMediaSupport->QueryMediaType(
            gbstrVideoOut,
            &bSupport
            );

        if (bSupport)
        {
            // get the mediaterminal for Videoout
            hr = GetMediaTerminal(
                                  pAddress,
                                  gbstrVideoOut,
                                  &ppMediaTerminals[count]
                                 );

            if (S_OK == hr)
            {
                count ++;
            }
        }
    }

    pMediaSupport->Release();

    *pNumMediaTerminals = count;

    return S_OK;
}

/////////////////////////////////////////////////////////////////
// CreateMediaTerminalSafeArray
//
/////////////////////////////////////////////////////////////////
SAFEARRAY *
CreateMediaTerminalSafeArray(
                             ITMediaTerminal ** ppMediaTerminals,
                             LONG nNumMediaTerminals
                            )
{
    SAFEARRAY *             psa;
    SAFEARRAYBOUND          sabound[1];

    // create a safearray with two elements
    // to pass the mediaterminals to tapi
    sabound[0].lLbound = 0;
    sabound[0].cElements = nNumMediaTerminals;

    psa = SafeArrayCreate(
                          VT_UNKNOWN,
                          1,
                          sabound
                         );

    if (NULL != psa)
    {
        // save them in the safearray
        for (long i = 0; i < nNumMediaTerminals; i ++)
        {
            SafeArrayPutElement(
                            psa,
                            &i,
                            ppMediaTerminals[i]
                           );
        }
    }
    return psa;
}

/////////////////////////////////////////////////////////////////
// ReleaseMediaTerminals
//
/////////////////////////////////////////////////////////////////
void
ReleaseMediaTerminals(
                      ITMediaTerminal ** ppMediaTerminals,
                      LONG nNumMediaTerminals
                     )
{
    for (long i = 0; i < nNumMediaTerminals; i ++)
    {
        if (ppMediaTerminals[i])
        {
            ppMediaTerminals[i]->Release();
        }
    }
}

/////////////////////////////////////////////////////////////////////
// Answer the call
/////////////////////////////////////////////////////////////////////
HRESULT
AnswerTheCall()
{
    HRESULT                 hr;
    ITCallInfo *            pCallInfo;
    ITAddress *             pAddress;
    ITMediaTerminal *       ppMediaTerminals[MAXTERMINALS];
    long                    nNumMediaTerminals = MAXTERMINALS;
    SAFEARRAY *             psa;

    if (NULL == gpCall)
    {
        return E_UNEXPECTED;
    }

    // get the address object of this call
    gpCall->QueryInterface( IID_ITCallInfo, (void**)&pCallInfo );
    pCallInfo->get_Address( &pAddress );
    pCallInfo->Release();

    // create the media terminals for this call
    hr = CreateMediaTerminals(
        pAddress,
        ppMediaTerminals,
        &nNumMediaTerminals
        );

    // release the address
    pAddress->Release();

    if (S_OK != hr)
    {
        gpCall->Release();
        gpCall = NULL;
        return hr;
    }

    psa = CreateMediaTerminalSafeArray(ppMediaTerminals, nNumMediaTerminals);

    if (S_OK != hr)
    {
        gpCall->Release();
        gpCall = NULL;
        ReleaseMediaTerminals(ppMediaTerminals, nNumMediaTerminals);
        return hr;
    }

    // put the safearray in a variant
    VARIANT                 var;
    VariantInit(&var);
    var.vt = VT_ARRAY;
    var.parray = psa;

    // call SelectMediaTerminals
    hr = gpCall->SelectMediaTerminals(
                                      var
                                     );

    ReleaseMediaTerminals(ppMediaTerminals, nNumMediaTerminals);

    SafeArrayDestroy( psa );

    if (S_OK != hr)
    {
        gpCall->Release();
        gpCall = NULL;
        return hr;
    }

    // answer the call
    hr = gpCall->Answer();

    return hr;
}

//////////////////////////////////////////////////////////////////////
// DisconnectTheCall
//
// Disconnects the call
//////////////////////////////////////////////////////////////////////
HRESULT
DisconnectTheCall()
{
    HRESULT         hr = S_OK;

    if (NULL != gpCall)
    {
        hr = gpCall->Disconnect( DC_NORMAL );

        gpCall->Release();
        gpCall = NULL;

        return hr;
    }

    return S_FALSE;
}

//////////////////////////////////////////////////////////////////////
// ReleaseTheCall
//
// Releases the call
//////////////////////////////////////////////////////////////////////
void
ReleaseTheCall()
{
    if (NULL != gpCall)
    {
        gpCall->Release();
        gpCall = NULL;
    }
}

///////////////////////////////////////////////////////////////////
//
// HELPER FUNCTIONS
//
///////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////
// DoMessage
///////////////////////////////////////////////////////////////////
void
DoMessage(
          LPWSTR pszMessage
         )
{
    MessageBox(
               ghDlg,
               pszMessage,
               gszTapi30,
               MB_OK
              );
}

//////////////////////////////////////////////////////////////////
// SetStatusMessage
//////////////////////////////////////////////////////////////////
void
SetStatusMessage(
                 LPWSTR pszMessage
                )
{
    SetDlgItemText(
                   ghDlg,
                   IDC_STATUS,
                   pszMessage
                  );
}

///////////////////////////////////////////////////////////////
// EnableButton
//
// Enable, make default, and setfocus to a button
///////////////////////////////////////////////////////////////
void
EnableButton(
             int ID
            )
{
    SendDlgItemMessage(
                       ghDlg,
                       ID,
                       BM_SETSTYLE,
                       BS_DEFPUSHBUTTON,
                       0
                      );
    EnableWindow(
                 GetDlgItem( ghDlg, ID ),
                 TRUE
                );
    SetFocus(
             GetDlgItem( ghDlg, ID )
            );
}

//////////////////////////////////////////////////////////////
// DisableButton
//
// Disable a button
//////////////////////////////////////////////////////////////
void
DisableButton(
              int ID
             )
{
    SendDlgItemMessage(
                       ghDlg,
                       ID,
                       BM_SETSTYLE,
                       BS_PUSHBUTTON,
                       0
                      );
    EnableWindow(
                 GetDlgItem( ghDlg, ID ),
                 FALSE
                );
}


CALLNOT.CPP

//////////////////////////////////////////////////////////////////////
// callnot.cpp
//
// Implementation of the ITCallNotification interface.
//
// This is an outgoing interface that is defined by TAPI 3.0.  This
// is basically a callback function that TAPI 3.0 calls to inform
// the application of events related to calls (on a specific address)
//
// Please refer to COM documentation for information on outgoing
// interfaces.
//
// An application must implement and register this interface in order
// to receive calls and events related to calls
//
//////////////////////////////////////////////////////////////////////

#include "windows.h"
#include "tapi3.h"
#include "callnot.h"
#include "resource.h"

extern ITBasicCallControl * gpCall;
extern HWND ghDlg;

void
DoMessage(
          LPWSTR pszMessage
         );

void
SetStatusMessage(
                 LPWSTR pszMessage
                );

void
EnableButton(
             int ID
            );
void
DisableButton(
              int ID
             );

///////////////////////////////////////////////////////////////////
// CallEventNotification
//
// The only method in the ITCallEventNotification interface.  This gets
// called by TAPI 3.0 when there is a call event to report
//
///////////////////////////////////////////////////////////////////
HRESULT
STDMETHODCALLTYPE
CCallNotification::CallEventNotification(
                                         ITAddress * pAddress,
                                         CALL_EVENT_TYPE EventType,
                                         IDispatch * pEvent
                                        )
{
    HRESULT hr;

    // EventType can be CET_CALLMONITOR, CET_CALLOWNER, or CET_CALLSTATEEVENT
    switch ( EventType )
    {
        case CET_CALLMONITOR:
        {
            // CET_CALLMONITOR means that the application is being notified
            // of a new call, and the application has monitor privileges on
            // that call.  pEvent is the Call object.

            // We should not get any CET_CALLMONTOR notifications in
            // this application, since we only registered for owner
            // in RegisterCallTypes

            break;
        }

        case CET_CALLOWNER:
        {
            // CET_CALLOWNER means that the application is being notified
            // of a call, and the applications has owner privileges on
            // that call.  pEvent is the Call object.
            //
            // Note that we don't answer to call at this point.  The application
            // should wait for a CS_OFFERING CallState message before answering
            // the call.

            // pEvent should be the call.  Get the ITBasicCallControl interface
            hr = pEvent->QueryInterface( IID_ITBasicCallControl, (void **)&gpCall );

            if (S_OK != hr)
            {
                DoMessage( L"Incoming call, but failed to get the interface");
                gpCall->Release();
            }
            else
            {
                EnableButton( IDC_ANSWER );
                DisableButton( IDC_DISCONNECT );
                SetStatusMessage(L"Incoming Owner Call");
            }

            break;
        }

        case CET_CALLSTATEEVENT:
        {
            // CET_CALLSTATEEVENT is a call state event.  pEvent is
            // an ITCallStateEvent object

            CALL_STATE       cs;
            ITCallStateEvent * pCallStateEvent;

            // Get the interface
            pEvent->QueryInterface( IID_ITCallStateEvent, (void **)&pCallStateEvent );

            // get the CallState that we are being notified of.
            pCallStateEvent->get_State( &cs );

            // if it's offering, update our UI
            if (CS_OFFERING == cs)
            {
                SetStatusMessage(L"Click the Answer button");
            }
            else if (CS_DISCONNECTED == cs)
            {
                PostMessage(ghDlg, WM_COMMAND, IDC_DISCONNECTED, 0);
            }

            // Release the interface
            pCallStateEvent->Release();

            break;
        }
    }

    return S_OK;
}


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by Script1.rc
//
#define IDD_MAINDLG                     101
#define IDC_STATUS                      1000
#define IDC_ANSWER                      1001
#define IDC_DISCONNECT                  1002
#define IDC_DISCONNECTED                1003
#define IDC_STATIC                      -1

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        102
#define _APS_NEXT_COMMAND_VALUE         40001
#define _APS_NEXT_CONTROL_VALUE         1003
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif


README.TXT

TAPI 3.0
OUTGOING Sample Application

Overview:

Outgoing is a sample TAPI 3.0 application that makes outgoing phone
calls.

This sample shows the most basic TAPI 3.0 functionality - finding an
appropriate address object and creating a call on it.

How to use:

To run the OUTGOING sample application, simply build and run t3out.exe.

A small dialog box will appear.  The user can type in a destination address
and choose the address type of the destination address.  TAPI 3.0 currently
supports the following address types:

    Conference Name
    Email Name
    Machine Name
    Phone Number
    IP Address

Microsoft's H.323 Service Provider supports the Email Name, Machine Name,
and IP Address address types.  Microsoft's IP Conferencing Service
Provider supports the Conference Name address type.

When the user types in a destination address, chooses an address type,
and presses the "Dial" button, the application will find a TAPI address
that support the address type selected, as well as the audioin and audio
out media types.  Then it will make a call on that address.

If the address also support video, and the machine has video capabilities,
the application will also set up a video stream.

What functionality does this sample show:

The outgoing sample application shows how to make a phone call with
TAPI 3.0.

What this sample does not show:

This sample is extremely simple.  It does not allow the user to select
the address or terminals that the user is interested in.  It does not
see call state messages, and does not listen for incoming calls.

Hints:

This sample should be able to run as long as you have TAPI devices
installed.  Many computers have a modem.  If the modem is installed
correctly, it will show up as a TAPI device.


OUTGOING.CPP

#define UNICODE
#include <windows.h>
#include <tapi3.h>
#include "resource.h"

//////////////////////////////////////////////////////////
// T3OUT.EXE
//
// Example of making an outgoing call with TAPI 3.0
//
// This application will allow a user to make a call
// by using TAPI 3.0.  The application will simply look
// for the first TAPI line that support Audio, and can
// dial a phone number.  It will then use that line to
// make calls.
//
// This application does not handle incoming calls, and
// does not process incoming messages.
//
//////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////
// Constants
//////////////////////////////////////////////////////////

const DWORD ADDRESSLENGTH   = 128;
const DWORD MAXTERMINALS    = 5;

const WCHAR * const gszTapi30           = L"TAPI 3.0 Outgoing Call Sample";

const WCHAR * const gszConferenceName   = L"Conference Name";
const WCHAR * const gszEmailName        = L"Email Name";
const WCHAR * const gszMachineName      = L"Machine Name";
const WCHAR * const gszPhoneNumber      = L"Phone Number";
const WCHAR * const gszIPAddress        = L"IP Address";

//////////////////////////////////////////////////////////
// GLOBALS
//////////////////////////////////////////////////////////
HINSTANCE               ghInst;
HWND                    ghDlg = NULL;
ITTAPI *                gpTapi;
ITAddress *             gpAddress;
ITBasicCallControl *    gpCall;

BSTR                    gbstrAudioIn;
BSTR                    gbstrAudioOut;
BSTR                    gbstrVideoIn;
BSTR                    gbstrVideoOut;

//////////////////////////////////////////////////////////
// PROTOTYPES
//////////////////////////////////////////////////////////
BOOL
CALLBACK
MainDialogProc(
               HWND hDlg,
               UINT uMsg,
               WPARAM wParam,
               LPARAM lParam
              );

HRESULT
FindAnAddress(
              DWORD dwAddressType
             );

HRESULT
GetMediaTerminal(
                 BSTR bstrMedia,
                 ITMediaTerminal ** ppMediaTerminal
                );

HRESULT
GetVideoInMediaTerminal(
                 ITMediaTerminal ** ppMediaTerminal
                );

HRESULT
MakeTheCall(
            DWORD dwAddressType,
            PWCHAR szAddressToCall
           );

HRESULT
DisconnectTheCall();

void
DoMessage(
          LPWSTR pszMessage
         );

HRESULT
InitializeTapi();

void
ShutdownTapi();

void
EnableButton(
             HWND hDlg,
             int ID
            );
void
DisableButton(
              HWND hDlg,
              int ID
             );

//////////////////////////////////////////////////////////
// WinMain
//////////////////////////////////////////////////////////
int
WINAPI
WinMain(
        HINSTANCE hInst,
        HINSTANCE hPrevInst,
        LPSTR lpCmdLine,
        int nCmdShow
       )
{
    ghInst = hInst;

    // need to coinit
    if (!SUCCEEDED(CoInitialize(NULL)))
    {
        return 0;
    }

    if (S_OK != InitializeTapi())
    {
        return 0;
    }

    // everything is initialized, so
    // start the main dialog box
    DialogBox(
              ghInst,
              MAKEINTRESOURCE(IDD_MAINDLG),
              NULL,
              MainDialogProc
             );

    ShutdownTapi();

    CoUninitialize();

    return 1;
}

//////////////////////////////////////////////////////////////
// InitializeTapi
//
// Various initializations
///////////////////////////////////////////////////////////////
HRESULT
InitializeTapi()
{
    HRESULT         hr;
    LPWSTR          psz;

    // cocreate the TAPI object
    hr = CoCreateInstance(
                          CLSID_TAPI,
                          NULL,
                          CLSCTX_INPROC_SERVER,
                          IID_ITTAPI,
                          (LPVOID *)&gpTapi
                         );

    if (hr != S_OK)
    {
        DoMessage(L"CoCreateInstance on TAPI failed");
        return hr;
    }

    // call initialize.  this must be called before
    // any other tapi functions are called.
    hr = gpTapi->Initialize();

    if (S_OK != hr)
    {
        DoMessage(L"TAPI failed to initialize");

        gpTapi->Release();
        gpTapi = NULL;

        return hr;
    }

    // convert the TAPIMEDIATYPEs to BSTRs for
    // convenience throughout the program
    StringFromIID(
                  TAPIMEDIATYPE_AudioIn,
                  &psz
                 );

    gbstrAudioIn = SysAllocString( psz );

    CoTaskMemFree( psz );

    StringFromIID(
                  TAPIMEDIATYPE_AudioOut,
                  &psz
                 );

    gbstrAudioOut = SysAllocString( psz );

    CoTaskMemFree( psz );

    StringFromIID(
                  TAPIMEDIATYPE_VideoIn,
                  &psz
                 );

    gbstrVideoIn = SysAllocString( psz );

    CoTaskMemFree( psz );

    StringFromIID(
                  TAPIMEDIATYPE_VideoOut,
                  &psz
                 );

    gbstrVideoOut = SysAllocString( psz );

    CoTaskMemFree( psz );

    return S_OK;
}

///////////////////////////////////////////////////////////////
// ShutdownTapi
///////////////////////////////////////////////////////////////
void
ShutdownTapi()
{
    // if there is still a call,
    // release it
    if (NULL != gpCall)
    {
        gpCall->Release();
        gpCall = NULL;
    }

    // if we have an address object
    // release it
    if (NULL != gpAddress)
    {
        gpAddress->Release();
    }

    // release main object.
    if (NULL != gpTapi)
    {
        gpTapi->Shutdown();
        gpTapi->Release();
    }

    SysFreeString( gbstrAudioIn );
    SysFreeString( gbstrAudioOut );
    SysFreeString( gbstrVideoIn );
    SysFreeString( gbstrVideoOut );
}

///////////////////////////////////////////////////////////////////////////
// InitAddressTypeComboBox
//
// Put address type string in the combo box
// and save the addresstype with the string
//
///////////////////////////////////////////////////////////////////////////
void
InitAddressTypeComboBox(
    HWND hComboBox
    )
{
    int i;

    i = SendMessage( hComboBox, CB_ADDSTRING, 0, (long)gszConferenceName );

    SendMessage(
                hComboBox,
                CB_SETITEMDATA ,
                i,
                (long)T3_ADDRESSTYPE_CONFERENCENAME
               );

    i = SendMessage( hComboBox, CB_ADDSTRING, 0, (long)gszEmailName );

    SendMessage(
                hComboBox,
                CB_SETITEMDATA ,
                i,
                (long)T3_ADDRESSTYPE_EMAILNAME
               );

    i = SendMessage( hComboBox, CB_ADDSTRING, 0, (long)gszMachineName );

    SendMessage(
                hComboBox,
                CB_SETITEMDATA ,
                i,
                (long)T3_ADDRESSTYPE_DOMAINNAME
               );

    i = SendMessage( hComboBox, CB_ADDSTRING, 0, (long)gszPhoneNumber );

    SendMessage(
                hComboBox,
                CB_SETITEMDATA ,
                i,
                (long)T3_ADDRESSTYPE_PHONENUMBER
               );

    SendMessage( hComboBox, CB_SETCURSEL, i, 0 );

    i = SendMessage( hComboBox, CB_ADDSTRING, 0, (long)gszIPAddress );

    SendMessage(
                hComboBox,
                CB_SETITEMDATA ,
                i,
                (long)T3_ADDRESSTYPE_IPADDRESS
               );

}

///////////////////////////////////////////////////////////////////////////
// MainDlgProc
///////////////////////////////////////////////////////////////////////////
BOOL
CALLBACK
MainDialogProc(
               HWND hDlg,
               UINT uMsg,
               WPARAM wParam,
               LPARAM lParam
              )
{
    switch (uMsg)
    {
        case WM_INITDIALOG:
        {
            HWND hComboBox;

            // set up dialog
            ghDlg = hDlg;

            EnableButton( hDlg, IDOK );
            DisableButton( hDlg, IDC_DISCONNECT );

            hComboBox = GetDlgItem( hDlg, IDC_ADDRESSTYPE );

            InitAddressTypeComboBox(hComboBox);

            SetFocus( hComboBox );

            return 0;
        }

        case WM_COMMAND:
        {
            if ( LOWORD(wParam) == IDCANCEL )
            {
                // quit
                EndDialog( hDlg, 0 );

                return 1;
            }

            // dial request
            if ( LOWORD(wParam) == IDOK )
            {
                HWND hComboBox;
                DWORD dwIndex;
                DWORD dwAddressType;
                WCHAR szAddressToCall[ADDRESSLENGTH];

                // get the address type the user selected.
                hComboBox = GetDlgItem( hDlg, IDC_ADDRESSTYPE );
                dwIndex = SendMessage( hComboBox, CB_GETCURSEL, 0, 0 );

                dwAddressType = SendMessage(
                                             hComboBox,
                                             CB_GETITEMDATA,
                                             dwIndex,
                                             0
                                           );

                // get the address the user wants to call
                GetDlgItemText(
                               hDlg,
                               IDC_ADDRESS,
                               szAddressToCall,
                               ADDRESSLENGTH
                              );

                // make the call
                if ( S_OK == MakeTheCall(dwAddressType, szAddressToCall) )
                {
                    EnableButton( hDlg, IDC_DISCONNECT );
                    DisableButton( hDlg, IDOK );
                }
                else
                {
                    DoMessage(L"The call failed to connect");
                }

                return 1;
            }

            // disconnect requestion
            if ( LOWORD( wParam ) == IDC_DISCONNECT )
            {
                // disconnect
                if (S_OK == DisconnectTheCall())
                {
                    EnableButton( hDlg, IDOK );
                    DisableButton( hDlg, IDC_DISCONNECT );
                }
                else
                {
                    DoMessage(L"The call failed to disconnect");
                }

                return 1;
            }

            return 0;
        }
        default:

            return 0;
    }
}

////////////////////////////////////////////////////////////////////////
// FindAnAddress
//
// Finds an address object that this application will use to make calls on.
//
// This function finds an address that supports the addresstype passed
// in, as well as the audioin and audioout media types.
//
// Return Value
//          S_OK if it finds an address
//          S_FALSE if it does not find an address
////////////////////////////////////////////////////////////////////////
HRESULT
FindAnAddress(
              DWORD dwAddressType
             )
{
    HRESULT             hr = S_OK;
    BOOL                bFoundAddress = FALSE;
    IEnumAddress *      pEnumAddress;
    ITAddress *         pAddress;
    IEnumAddressType *  pEnumAddressTypes;
    DWORD               dwType;

    // if we have an address object
    // release it
    if (NULL != gpAddress)
    {
        gpAddress->Release();
        gpAddress = NULL;
    }

    // enumerate the addresses
    hr = gpTapi->_EnumerateAddresses( &pEnumAddress );

    if (S_OK != hr)
    {
        return hr;
    }

    while ( !bFoundAddress )
    {
        // get the next address
        hr = pEnumAddress->Next( 1, &pAddress, NULL );

        if (S_OK != hr)
        {
            break;
        }

        // enumerate the address types that
        // this address supports
        hr = pAddress->_EnumerateAddressType( &pEnumAddressTypes );

        if (S_OK != hr)
        {
            pAddress->Release();

            continue;
        }

        while (TRUE)
        {
            ITMediaSupport * pMediaSupport;
            VARIANT_BOOL     bSupport;

            // get the next address type
            hr = pEnumAddressTypes->Next( 1, &dwType, NULL );

            if (S_OK != hr)
            {
                break;
            }

            // is the type we are looking for?
            if ( dwAddressType == dwType )
            {
                // yes
                pAddress->QueryInterface( IID_ITMediaSupport, (void **)&pMediaSupport );

                // does it support AudioIn?
                pMediaSupport->QueryMediaType(
                                              gbstrAudioIn,
                                              &bSupport
                                             );

                if (bSupport)
                {
                    // does it also support AudioOut?
                    pMediaSupport->QueryMediaType(
                        gbstrAudioOut,
                        &bSupport
                        );

                    if (bSupport)
                    {
                        // save
                        pMediaSupport->Release();

                        gpAddress = pAddress;
                        gpAddress->AddRef();

                        bFoundAddress = TRUE;
                        break;
                    }
                }

                pMediaSupport->Release();
            }
        }

        pAddress->Release();
        pEnumAddressTypes->Release();
    }

    pEnumAddress->Release();

    if (!bFoundAddress)
    {
        return S_FALSE;
    }

    return S_OK;
}

/////////////////////////////////////////////////////////////////
// CreateMediaTerminals
//
// Create audioin and audioout terminals.
//
// If the address also supports video, create
// Videoin and videoout terminals, too.
//
// This function assumes that ppMediaTerminals has a size no less than four.
/////////////////////////////////////////////////////////////////
HRESULT
CreateMediaTerminals(
                     ITMediaTerminal ** ppMediaTerminals,
                     PLONG pNumMediaTerminals
                    )
{
    int count = 0;
    HRESULT hr;

    // get the mediaterminal for audioin
    hr = GetMediaTerminal(
                          gbstrAudioIn,
                          &ppMediaTerminals[count]
                         );

    if (S_OK != hr)
    {
        return hr;
    }

    count ++;

    // get the mediaterminal for audioout
    hr = GetMediaTerminal(
                          gbstrAudioOut,
                          &ppMediaTerminals[count]
                         );

    if (S_OK != hr)
    {
        ppMediaTerminals[0]->Release();
        return hr;
    }

    count ++;

    // Find out if the address supports video.
    ITMediaSupport * pMediaSupport;
    VARIANT_BOOL     bSupport;

    gpAddress->QueryInterface( IID_ITMediaSupport, (void **)&pMediaSupport );

    // does it support VideoIn?
    pMediaSupport->QueryMediaType(
                                  gbstrVideoIn,
                                  &bSupport
                                 );

    if (bSupport)
    {
        // get the mediaterminal for VideoIn
        hr = GetVideoInMediaTerminal(
                                  &ppMediaTerminals[count]
                                 );

        if (S_OK == hr)
        {
            count ++;
        }

        // does it also support VideoOut?
        pMediaSupport->QueryMediaType(
            gbstrVideoOut,
            &bSupport
            );

        if (bSupport)
        {
            // get the mediaterminal for Videoout
            hr = GetMediaTerminal(
                                  gbstrVideoOut,
                                  &ppMediaTerminals[count]
                                 );

            if (S_OK == hr)
            {
                count ++;
            }
        }
    }

    pMediaSupport->Release();

    *pNumMediaTerminals = count;

    return S_OK;
}

/////////////////////////////////////////////////////////////////
// CreateMediaTerminalSafeArray
//
/////////////////////////////////////////////////////////////////
SAFEARRAY *
CreateMediaTerminalSafeArray(
                             ITMediaTerminal ** ppMediaTerminals,
                             LONG nNumMediaTerminals
                            )
{
    SAFEARRAY *             psa;
    SAFEARRAYBOUND          sabound[1];

    // create a safearray with two elements
    // to pass the mediaterminals to tapi
    sabound[0].lLbound = 0;
    sabound[0].cElements = nNumMediaTerminals;

    psa = SafeArrayCreate(
                          VT_UNKNOWN,
                          1,
                          sabound
                         );

    if (NULL != psa)
    {
        // save them in the safearray
        for (long i = 0; i < nNumMediaTerminals; i ++)
        {
            SafeArrayPutElement(
                            psa,
                            &i,
                            ppMediaTerminals[i]
                           );
        }
    }
    return psa;
}

/////////////////////////////////////////////////////////////////
// ReleaseMediaTerminals
//
/////////////////////////////////////////////////////////////////
void
ReleaseMediaTerminals(
                      ITMediaTerminal ** ppMediaTerminals,
                      LONG nNumMediaTerminals
                     )
{
    for (long i = 0; i < nNumMediaTerminals; i ++)
    {
        if (ppMediaTerminals[i])
        {
            ppMediaTerminals[i]->Release();
        }
    }
}

/////////////////////////////////////////////////////////////////
// MakeTheCall
//
// Sets up and makes a call
/////////////////////////////////////////////////////////////////
HRESULT
MakeTheCall(
            DWORD dwAddressType,
            PWCHAR szAddressToCall
           )
{
    HRESULT                 hr = S_OK;
    ITMediaTerminal *       ppMediaTerminals[MAXTERMINALS];
    long                    nNumMediaTerminals = MAXTERMINALS;
    SAFEARRAY *             psa;
    VARIANT                 var;
    BSTR                    bstrAddressToCall;

    // find an address object that
    // we will use to make calls on
    hr = FindAnAddress(dwAddressType);

    if (S_OK != hr)
    {
        DoMessage(L"Could not find an address to make a phone call on");

        return hr;
    }

    bstrAddressToCall = SysAllocString( szAddressToCall );

    hr = gpAddress->CreateCall( bstrAddressToCall, &gpCall );

    SysFreeString ( bstrAddressToCall );

    if (S_OK != hr)
    {
        return hr;
    }

    hr = CreateMediaTerminals(ppMediaTerminals, &nNumMediaTerminals);

    if (S_OK != hr)
    {
        gpCall->Release();
        gpCall = NULL;
        return hr;
    }

    psa = CreateMediaTerminalSafeArray(ppMediaTerminals, nNumMediaTerminals);

    if (S_OK != hr)
    {
        gpCall->Release();
        gpCall = NULL;
        ReleaseMediaTerminals(ppMediaTerminals, nNumMediaTerminals);
        return hr;
    }

    // put the safearray in a variant
    VariantInit(&var);
    var.vt = VT_ARRAY;
    var.parray = psa;

    // call SelectMediaTerminals
    hr = gpCall->SelectMediaTerminals( var );

    ReleaseMediaTerminals(ppMediaTerminals, nNumMediaTerminals);

    SafeArrayDestroy( psa );

    if (S_OK != hr)
    {
        return hr;
    }

    // We're now ready to call connect.
    //
    // the TRUE parameter indicates that this
    // call is sychronous - that is, it won't
    // return until the call is in the connected
    // state (or fails to connect)
    // Since this is called in the UI thread,
    // this means that the app will appear
    // to hang until this function returns.
    // Some TAPI service providers may take a long
    // time for a call to reach the connected state.
    hr = gpCall->Connect( TRUE );

    if (S_OK != hr)
    {
        gpCall->Release();
        gpCall = NULL;
        return hr;
    }

    return hr;
}

/////////////////////////////////////////////////////////
// GetMediaTerminal
//
// Creates a MediaTerminal for the bstrMediaType passed
// in, using the default terminal for the bstrMediaType
//
/////////////////////////////////////////////////////////
HRESULT
GetMediaTerminal(
                 BSTR bstrMediaType,
                 ITMediaTerminal ** ppMediaTerminal
                )
{
    HRESULT             hr = S_OK;
    ITTerminalSupport * pTerminalSupport;
    ITTerminal *        pTerminal;

    // get the terminal support interface
    gpAddress->QueryInterface( IID_ITTerminalSupport, (void **)&pTerminalSupport );

    // get the default terminal for MediaType
    hr = pTerminalSupport->GetDefaultTerminal(
                                              bstrMediaType,
                                              &pTerminal
                                             );

    pTerminalSupport->Release();

    if (S_OK != hr)
    {
        return hr;
    }

    // Create a media terminal for MediaType
    // Use the default terminal we just got
    hr = gpTapi->CreateMediaTerminal(
                                     bstrMediaType,
                                     pTerminal,
                                     ppMediaTerminal
                                    );

    pTerminal->Release();

    if (S_OK != hr)
    {
        return hr;
    }

    return S_OK;

}

/////////////////////////////////////////////////////////
// GetVideoInMediaTerminal
//
// Creates a MediaTerminal for the VideoIn mediatype
// This is a dynamic terminal type.
//
/////////////////////////////////////////////////////////
HRESULT
GetVideoInMediaTerminal(
                 ITMediaTerminal ** ppMediaTerminal
                )
{
    HRESULT             hr = S_OK;
    ITTerminalSupport * pTerminalSupport;
    ITTerminal *        pTerminal;

    // get the terminal support interface
    gpAddress->QueryInterface( IID_ITTerminalSupport, (void **)&pTerminalSupport );

    BSTR bstrTerminalClass;
    LPOLESTR lpTerminalClass;

    StringFromIID(
                CLSID_VideoWindowTerm,
                &lpTerminalClass
                );

    bstrTerminalClass = SysAllocString ( lpTerminalClass );

    CoTaskMemFree( lpTerminalClass );

    hr = pTerminalSupport->CreateTerminal(
        bstrTerminalClass,
        &pTerminal
        );

    SysFreeString( bstrTerminalClass );
    pTerminalSupport->Release();

    if (FAILED(hr))
    {
        return hr;
    }

    // Create a media terminal for MediaType
    // Use the default terminal we just got
    hr = gpTapi->CreateMediaTerminal(
                                     gbstrVideoIn,
                                     pTerminal,
                                     ppMediaTerminal
                                    );

    pTerminal->Release();

    if (S_OK != hr)
    {
        return hr;
    }

    return S_OK;

}

//////////////////////////////////////////////////////////////////////
// DisconnectTheCall
//
// Disconnects the call
//////////////////////////////////////////////////////////////////////
HRESULT
DisconnectTheCall()
{
    HRESULT         hr = S_OK;

    if (NULL != gpCall)
    {
        hr = gpCall->Disconnect( DC_NORMAL );

        gpCall->Release();
        gpCall = NULL;

        return hr;
    }

    return S_FALSE;
}

///////////////////////////////////////////////////////////////////
//
// HELPER FUNCTIONS
//
///////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////
// DoMessage
///////////////////////////////////////////////////////////////////
void
DoMessage(
          LPWSTR pszMessage
         )
{
    MessageBox(
               ghDlg,
               pszMessage,
               gszTapi30,
               MB_OK
              );
}

///////////////////////////////////////////////////////////////
// EnableButton
//
// Enable, make default, and setfocus to a button
///////////////////////////////////////////////////////////////
void
EnableButton(
             HWND hDlg,
             int ID
            )
{
    SendDlgItemMessage(
                       hDlg,
                       ID,
                       BM_SETSTYLE,
                       BS_DEFPUSHBUTTON,
                       0
                      );
    EnableWindow(
                 GetDlgItem( hDlg, ID ),
                 TRUE
                );
    SetFocus(
             GetDlgItem( hDlg, ID )
            );
}

//////////////////////////////////////////////////////////////
// DisableButton
//
// Disable a button
//////////////////////////////////////////////////////////////
void
DisableButton(
              HWND hDlg,
              int ID
             )
{
    SendDlgItemMessage(
                       hDlg,
                       ID,
                       BM_SETSTYLE,
                       BS_PUSHBUTTON,
                       0
                      );
    EnableWindow(
                 GetDlgItem( hDlg, ID ),
                 FALSE
                );
}


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by Script1.rc
//
#define IDD_MAINDLG                     102
#define IDC_ADDRESSTYPE                 1002
#define IDC_ADDRESS                     1009
#define IDC_DISCONNECT                  1001
#define IDC_STATIC                      -1

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NO_MFC                     1
#define _APS_NEXT_RESOURCE_VALUE        103
#define _APS_NEXT_COMMAND_VALUE         40001
#define _APS_NEXT_CONTROL_VALUE         1010
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif


README.TXT

THIS IS NOT SUPPORTED BY MICROSOFT CORPORATION. IT IS PROVIDED "AS IS" BECAUSE WE BELIEVE IT MAY BE USEFUL TO YOU

TAPI 3.0 Rendezvous Controls Sample Application

What the sample does?

Rend is a simple program demonstrates how to use TAPI 3.0 Rendezvous Controls COM interfaces to manipulate a conference directory.

This simple sample shows the most basic conference directory functionality:
. initialize a conference directory,
. create a conference,
. get a conference by name,
. delete a conference by name, and
. enumerate conferences.

How to use:

To run the REND sample program, simply build and run rend.exe.
The conference directory server name and directory path used should be specified in registry:
HKLM\Software\Microsoft\Windows\CurrentVersion\Dynamic Directory\ServerName [REG_SZ], and
HKLM\Software\Microsoft\Windows\CurrentVersion\Dynamic Directory\DirectoyPath [REG_SZ].

What this sample does not show:

This sample is extremely simple.  It does not allow the user to specify the conference name or change properties of a conference, which could be done with other Rendezvous Controls or SDP BLOB COM interfaces. While the default directoy server and path names in registry are used in this sample, one can modify the program to use any other server or path names in initializing ITConferenceDirectoy interface.


REND.CPP

/*++

Copyright (c) 1995 Microsoft Corporation

Module Name:

    rend.cpp

Abstract:

    This demonstrates how to use TAPI 3.0 Rendezvous Controls APIs to :
    . initialize conference directory, get directory server name and path,
    . create a conference, get a conference, and delete a conference,
    . and enumerate conferences.

Notes:

    1. Build the program, and the exe is obj\*\rend.exe
    2. To run it, type "rend" on the command line.
    3. The directory server and path should be specified in registry:
        HKLM\Software\Microsoft\Windows\CurentVersion\Dynamic Directory
        as "ServerName" (REG_SZ), and "DirectoryPath" (REG_SZ)

Author:

    Bin Li 17-Aug-1997

Revision History:

    dd-mmm-yyyy <email>

--*/

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// include                                                                   //
//                                                                           //

#include <windows.h>
#include <objbase.h>
#include <stdio.h>
#include <oleauto.h>
#include <rend.h>

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// define                                                                    //
//                                                                           //

#define MAX_STRING_LENGTH255
#define MAX_NUM_CONFERENCES5
#define CONF_NAME_PREFIXL"Rendezvous Controls Sample Program - CONFERENCE No. "

#define RETURN_ON_FAILURE_NODIR(hr)   \
    {if (FAILED(hr)) {  wprintf(L"Error 0x%x\n", hr); return hr; } }

#define RETURN_ON_FAILURE_DIR(hr)\
    {if (FAILED(hr))                \
     {  wprintf(L"Error 0x%x\n", hr);\
        pITDir->Release();          \
        return hr;                  \
     }                              \
    }

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// functions                                                                 //
//                                                                           //

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// main                                                                      //
//                                                                           //

INT _CRTAPI1
main()
{
ITConferenceDirectory*pITDir;
BSTRbszConfName[MAX_NUM_CONFERENCES];

    /*
     * 1. initialize conference directory server and path
     */

wprintf(L"\n\nInitialize conference directory ...\n");

::CoInitialize( NULL );

// query for conference directory interface
HRESULT hr = ::CoCreateInstance(CLSID_ConferenceDirectory,
                                    NULL,
                                    CLSCTX_INPROC_SERVER,
                                    IID_ITConferenceDirectory,
                                    (void **)&pITDir
                                    );

    RETURN_ON_FAILURE_NODIR( hr );

// initialize conference directory with default values in registry
    // you can also specify different directoy server or path by replacing
    // "NULL" with the respective name or path
hr = pITDir->Init( NULL, NULL );

RETURN_ON_FAILURE_NODIR( hr );

    BSTR    bszServer   = NULL;
    BSTR    bszDirPath  = NULL;

    // query the server name
hr = pITDir->get_ServerName( &bszServer );
RETURN_ON_FAILURE_DIR( hr );

    // query the directoy path
hr = pITDir->get_DirectoryPath( &bszDirPath );
RETURN_ON_FAILURE_DIR( hr );

wprintf(L"\tServer = %s\tDirectoryPath=%s\n",
bszServer, bszDirPath);

::SysFreeString( bszServer );
::SysFreeString( bszDirPath );

/*
 * 2. create conferences
 */

wprintf(L"Create conference ...\n");
for (int i = 0; i < MAX_NUM_CONFERENCES; i ++)
{
WCHARtszConfName[MAX_STRING_LENGTH];

swprintf( tszConfName, L"%s%d",CONF_NAME_PREFIX,i );
bszConfName[i]  = ::SysAllocString( tszConfName );

ITConference*pITConf;

        // to ensure a successful creating, delete it first in case a same
        // name conference already exists
hr = pITDir->DeleteConference( bszConfName[i] );

wprintf(L"\t%s", bszConfName[i]);
hr = pITDir->CreateConference( bszConfName[i], &pITConf );

RETURN_ON_FAILURE_DIR( hr );

        // release the interface, as we don't need it now
        // also, (RefCount == 0) will cause the conferenc blob to be written
        // to the ILS server
        pITConf->Release();

wprintf(L"\n");
}

/*
 * 3. enumerate conferences
 */

// set a filter used for enumeration to limit the set of conferences
    // that will be enumerated.
    // if no filter is set, all conferences will be retrieved

WCHARtszFilter[MAX_STRING_LENGTH];

swprintf( tszFilter, L"uid=%s*", CONF_NAME_PREFIX );
BSTRbszFilter = SysAllocString( tszFilter );

wprintf(L"Set a filter \"%s\" ...", bszFilter );
hr = pITDir->put_Filter( bszFilter );

::SysFreeString( bszFilter );

RETURN_ON_FAILURE_DIR( hr );
wprintf(L"\n");

// get the enumerator
IEnumVARIANT*pIEnumConf;

wprintf(L"Get an enumerator ...");
hr = pITDir->get__NewEnum( (IUnknown **) &pIEnumConf );

RETURN_ON_FAILURE_DIR( hr );
wprintf(L"\n");

// enumerate each conference
wprintf(L"Enumerate conference ...\n");
i = 0;
do
{
VARIANTvarITConf;
ULONGcConferencesGet;

// initialization is required for NT5 Beta-1
::VariantInit( &varITConf );

wprintf(L"\t%d - ", i);
hr = pIEnumConf->Next(1, &varITConf, &cConferencesGet);

if ( SUCCEEDED(hr) && (hr != S_FALSE) )
{
ITConference*pITConference = (ITConference *)
     V_DISPATCH( &varITConf );
BSTR bConfNameGet = NULL;

hr = pITConference->get_Name( &bConfNameGet );

            pITConference->Release();

RETURN_ON_FAILURE_DIR( hr );

wprintf(L"%s\n", bConfNameGet);
::SysFreeString( bConfNameGet );
i = i + 1;
}
}
while ( SUCCEEDED(hr) && (hr != S_FALSE) );

    pIEnumConf->Release();

RETURN_ON_FAILURE_DIR( hr );

wprintf(L"\n");

/*
 *  4. get and delete conferences
 */

wprintf(L"Get then delete conference ...\n");
for (i = 0; i < MAX_NUM_CONFERENCES; i ++)
{
// get a conference
ITConference*pITConf;

wprintf(L"\t%d - %s", i, bszConfName[i]);
hr = pITDir->GetConference( bszConfName[i], &pITConf );

        pITConf->Release();

RETURN_ON_FAILURE_DIR( hr );

// delete the conference
hr = pITDir->DeleteConference( bszConfName[i] );
wprintf(L"\n");

::SysFreeString( bszConfName[i] );

RETURN_ON_FAILURE_DIR( hr );
}

pITDir->Release();

::CoUninitialize();

    wprintf(L"\n\n");

return 0;

} // end of main


README.TXT

-----------------------------------------------------------
                      Java TAPI 3.0 Outgoing Call Sample
                                 August 1997
          -----------------------------------------------------------
                   (c) Copyright Microsoft Corporation, 1997

--------------------
      CONTENTS
--------------------
SUMMARY
BEFORE YOU COMPILE/RUN THE SAMPLE
HOW TO COMPILE THE SAMPLE
HOW TO RUN THE SAMPLE
USER'S GUIDE
MISC INFO ABOUT THE SAMPLE

SUMMARY
=======

The JT3Call sample application/applet demonstrates the use of the Telephony API
3.0 to make outgoing calls.

Depending on installed hardware, the applet will allow you to communicate with
others over a network connection using streaming audio and video.  One-to-one
connections are supported, but you may also connect to network conferences in
which you can communicate with multiple parties simultaneously.

BEFORE YOU COMPILE/RUN THE SAMPLE
=================================

Before you can compile/run the sample you must perform the following steps:

1. Configure the Telephoney API 3.0
-----------------------------------
TAPI 3.0 is installed by default in Windows NT 5.0.  To ensure that telephoney
support is properly configured on your machine, go to the control panel and
open the "Telephoney" control panel applet.

2. Install the MS Virtual Machine for Java (build 4.79.2174 or greater)
-----------------------------------------------------------------------
To check which version of the VM you currently have, open Windows Explorer and
go to the root directory of your Windows installation (e.g., C:\Winnt).  Find
the file Jview.exe and right-click it with the mouse.  On the popup menu that
appears click the "Properties" item.  When the properties dialog appears
select the "Version" tab.

If you must upgrade to a newer version of the VM, install the version that
comes with the SDK for Java 2.0 (http://www.microsoft.com/java) since you will
need the SDK for the following step.

3. Generate Java/COM Integration Classes for TAPI 3.0
-----------------------------------------------------
To generate these classes you will need to have the SDK for Java 2.0 installed.
Once you have installed the SDK, go to the \bin directory under the main SDK
install directory (C:\SDK-Java.20 by default) and execute the following
commands:

JActiveX /javatlb /xh /X:m- C:\Winnt\system32\tapi3.dll
Jvc C:\Winnt\java\trustlib\tapi3\*.java

WARNING! You must use Jvc version 1.02.3920 or greater to compile files
 generated by JActiveX.

This will generate the .class files needed to access TAPI 3.0 from Java in the
C:\Winnt\java\trustlib\tapi3 directory.

Note, you should replace "C:\Winnt" in the above paths with the path of your
Windows installation.  For more information on how to generate Java/COM
integration classes refer to the SDK for Java 2.0 documentation.

HOW TO COMPILE THE SAMPLE
=========================

Once the above steps are complete you can compile JT3Call by executing the
makeall.bat file.

HOW TO RUN THE SAMPLE
=====================

You may run the sample as an application by typing the following from the
sample directory:

Jview JT3Call.class

or

JT3Call.exe

The sample is also an applet and may be executed by viewing JT3Call.htm in
Microsoft Internet Explorer.

USER'S GUIDE
============

The first thing to note is the "Source Address" combo-box.  It displays
addresses for different service providers installed on your machine.  If a
connect operation does not work, it could be that you are trying to connect
using the wrong source address (e.g., you must use a conferencing source
address to join conferences).

The "Destination Address" text field is where you enter the address you wish
to connect to.  This can be an IP address, a machine name, etc. depending on
the type of connection you are trying to establish.

The "Static Terminals" listbox shows the available static terminals.

Just below the static terminals listbox is the "Number of Video Wnds" text
field.  Here, you enter the number of incoming video streams you want to
see.  Video windows are dynamic terminals.

By the way, terminals are end-points for connections.  There are two types
of them: static and dynamic.  Static terminals are usually tied to hardware
(e.g., speakers or microphones) and have a specific count.  Examples of
dynamic terminals are video windows and files.

MISC INFO ABOUT THE SAMPLE
==========================

Platforms Supported
-------------------
The sample has been tested on Windows NT 5.0 using the Microsoft Virtual
Machine for Java.

Hardware Requirements
---------------------
Hardware requirements for each type of media is listed below:
Audio Capture  ... Sound card and microphone
Audio Playback ... Sound card and speakers
Video Capture  ... Video capture card and camera
Video Playback ... N/A (just uses monitor)


README.TXT

-----------------------------------------------------------
                      Java TAPI 3.0 Conferencing Sample
                                 August 1997
          -----------------------------------------------------------
                   (c) Copyright Microsoft Corporation, 1997

--------------------
      CONTENTS
--------------------
SUMMARY
BEFORE YOU COMPILE/RUN THE SAMPLE
HOW TO COMPILE THE SAMPLE
HOW TO RUN THE SAMPLE
USER'S GUIDE
MISC INFO ABOUT THE SAMPLE

SUMMARY
=======

The JT3Conf sample application/applet demonstrates the use of the Telephony API
3.0 and Rendevous Controls to create, delete, and join conferences.

Depending on installed hardware, the applet will allow you to communicate with
others over a network connection using streaming audio and video.

BEFORE YOU COMPILE/RUN THE SAMPLE
=================================

Before you can compile/run the sample you must perform the following steps:

1. Configure the Telephoney API 3.0
-----------------------------------
TAPI 3.0 is installed by default in Windows NT 5.0.  To ensure that telephoney
support is properly configured on your machine, go to the control panel and
open the "Telephoney" control panel applet.

2. Install the MS Virtual Machine for Java (build 4.79.2174 or greater)
-----------------------------------------------------------------------
To check which version of the VM you currently have, open Windows Explorer and
go to the root directory of your Windows installation (e.g., C:\Winnt).  Find
the file Jview.exe and right-click it with the mouse.  On the popup menu that
appears click the "Properties" item.  When the properties dialog appears
select the "Version" tab.

If you must upgrade to a newer version of the VM, install the version that
comes with the SDK for Java 2.0 (http://www.microsoft.com/java) since you will
need the SDK for the following step.

3. Generate Java/COM Integration Classes
----------------------------------------
Java classes must be generated for TAPI 3.0 and the Rendevous Controls.
To generate these classes you will need to have the SDK for Java 2.0 installed.
Once you have installed the SDK, go to the \bin directory under the main SDK
install directory (C:\SDK-Java.20 by default) and execute the following
commands:

JActiveX /javatlb /xh /X:m- C:\Winnt\system32\tapi3.dll
Jvc C:\Winnt\java\trustlib\tapi3\*.java

JActiveX /javatlb /xh /X:m- C:\Winnt\system32\rend.dll
Jvc C:\Winnt\java\trustlib\rend\*.java

WARNING! You must use Jvc version 1.02.3920 or greater to compile files
 generated by JActiveX.

This will generate the .class files needed to access TAPI 3.0 and the
Rendevous Controls from Java in the C:\Winnt\java\trustlib\tapi3 and
C:\Winnt\java\trustlib\rend directories.

Note, you should replace "C:\Winnt" in the above paths with the path of your
Windows installation.  For more information on how to generate Java/COM
integration classes refer to the SDK for Java 2.0 documentation.

HOW TO COMPILE THE SAMPLE
=========================

Once the above steps are complete you can compile JT3Conf by executing the
makeall.bat file.

HOW TO RUN THE SAMPLE
=====================

You may run the sample as an application by typing the following from the
sample directory:

Jview JT3Conf.class

or

JT3Conf.exe

The sample is also an applet and may be executed by viewing JT3Conf.htm in
Microsoft Internet Explorer.

USER'S GUIDE
============

When the app starts you will see a list of conferences (the list may be empty).
The use of the program is straight forward.  To join a conference, just select
it and click the Join button.  The other operations are similar.

MISC INFO ABOUT THE SAMPLE
==========================

Platforms Supported
-------------------
The sample has been tested on Windows NT 5.0 using the Microsoft Virtual
Machine for Java.

Hardware Requirements
---------------------
Hardware requirements for each type of media is listed below:
Audio Capture  ... Sound card and microphone
Audio Playback ... Sound card and speakers
Video Capture  ... Video capture card and camera
Video Playback ... N/A (just uses monitor)


README.TXT

-----------------------------------------------------------
                      Java TAPI 3.0 Incomming Call Sample
                                 August 1997
          -----------------------------------------------------------
                   (c) Copyright Microsoft Corporation, 1997

--------------------
      CONTENTS
--------------------
SUMMARY
BEFORE YOU COMPILE/RUN THE SAMPLE
HOW TO COMPILE THE SAMPLE
HOW TO RUN THE SAMPLE
USER'S GUIDE
MISC INFO ABOUT THE SAMPLE

SUMMARY
=======

The JT3Rec sample application/applet demonstrates the use of the Telephony API
3.0 to receive incoming calls.

Depending on installed hardware, the applet will allow you to communicate with
others over a network connection using streaming audio and video.

BEFORE YOU COMPILE/RUN THE SAMPLE
=================================

Before you can compile/run the sample you must perform the following steps:

1. Configure the Telephoney API 3.0
-----------------------------------
TAPI 3.0 is installed by default in Windows NT 5.0.  To ensure that telephoney
support is properly configured on your machine, go to the control panel and
open the "Telephoney" control panel applet.

2. Install the MS Virtual Machine for Java (build 4.79.2174 or greater)
-----------------------------------------------------------------------
To check which version of the VM you currently have, open Windows Explorer and
go to the root directory of your Windows installation (e.g., C:\Winnt).  Find
the file Jview.exe and right-click it with the mouse.  On the popup menu that
appears click the "Properties" item.  When the properties dialog appears
select the "Version" tab.

If you must upgrade to a newer version of the VM, install the version that
comes with the SDK for Java 2.0 (http://www.microsoft.com/java) since you will
need the SDK for the following step.

3. Generate Java/COM Integration Classes
----------------------------------------
Java classes must be generated for TAPI 3.0.  To generate these classes you
will need to have the SDK for Java 2.0 installed.  Once you have installed
the SDK, go to the \bin directory under the main SDK install directory
(C:\SDK-Java.20 by default) and execute the following commands:

JActiveX /javatlb /xh /X:m- C:\Winnt\system32\tapi3.dll
Jvc C:\Winnt\java\trustlib\tapi3\*.java

WARNING! You must use Jvc version 1.02.3920 or greater to compile files
 generated by JActiveX.

This will generate the .class files needed to access TAPI 3.0 from Java in
the C:\Winnt\java\trustlib\tapi3 directory.

Note, you should replace "C:\Winnt" in the above paths with the path of your
Windows installation.  For more information on how to generate Java/COM
integration classes refer to the SDK for Java 2.0 documentation.

HOW TO COMPILE THE SAMPLE
=========================

Once the above steps are complete you can compile JT3Rec by executing the
makeall.bat file.

HOW TO RUN THE SAMPLE
=====================

You may run the sample as an application by typing the following from the
sample directory:

Jview JT3Rec.class

or

JT3Rec.exe

The sample is also an applet and may be executed by viewing JT3Rec.htm in
Microsoft Internet Explorer.

USER'S GUIDE
============

When the app starts available media types (e.g., Audio In, Audio Out, etc.)
will be listed.  In order to receive incoming calls you must select mediatypes
that you wish to receive and click the Listen button.  To stop listening for
calls, just click the Stop button.

When a call arrives the status box will prompt you to click Answer or Reject.
Clicking Answer will attempt to connect you to the caller and clicking Reject
will hurt the caller's feelings ;(.

MISC INFO ABOUT THE SAMPLE
==========================

Platforms Supported
-------------------
The sample has been tested on Windows NT 5.0 using the Microsoft Virtual
Machine for Java.

Hardware Requirements
---------------------
Hardware requirements for each type of media is listed below:
Audio Capture  ... Sound card and microphone
Audio Playback ... Sound card and speakers
Video Capture  ... Video capture card and camera
Video Playback ... N/A (just uses monitor)


README.TXT

TAPI 3.0
ASYNC Sample Application

Overview:

ASYNC is a sample TAPI 3.0 application that makes outgoing asynchronous
audio (and eventually video) calls.
It was developed using Visual Basic 5.

This sample shows the most basic TAPI 3.0 functionality - finding an
appropriate address object and creating a call on it.

How to use:

To run the ASYNC sample application, simply build and run ASYNC.exe, using
Visual Basic 5.
A dialog box will appear, and the user can type in a destination address
to dial. The user also must indicate the address type (phone number, machine
name, etc). So the destination address must match the selected address type.
Simply select an address type (e.g. "phone number"), type in a destination
address (e.g. a phone number) and press the Dial button.

What functionality does this sample show:

The ASYNC sample application shows how to make an asynchronous outgoing
call with TAPI 3.0 from VB.
It will make an audio call and, if present, video will also be selected
on the call. If no audio capabilities are detected, no call is made at all.

What this sample does not show:

This sample does not allow the user to select the media types or terminals
that the user is interested in.

It also selects an address by picking up the first one that matches the
selected address type and that supports at least the audio media types. So if
there are more addresses that meet this same criteria, only the first address
will be selected, the user cannot choose among them.

The sample does not show how to receive incoming calls.

Hints:

1. In order for this sample to run for each of the possible address types
(phone number, machine name, email name, ip address, conference name), on
the computer there must be installed those service providers that support
these address types: Unimodem for "phone number", H323 for "machine name,
email name, ip address", IPConf for "conference name". Check Control Panel's
Telephony Applet to see which providers are installed and to add new providers.

2. Depending on the selected address type, a certain device must be installed
on the machine, in order for the call to succeed:
- for "phone number": a modem must be installed (either data modem or voice
modem).
- for all other address types: a full duplex sound card must be installed
(or a sound card with a full duplex driver) so that both "audio in" and
"audio out" can be selected on the call in the same time. See also the next
paragraph.

3. The application selects on the call the audio media types (audio in and out),
and, if present, also the video media types (video in and out).
This will work only if the corresponding default device supports full duplex:
the default audio device should support full duplex audio, and the default
video device (if video is supported) should allow full duplex video.
Check Control Panel - Multimedia Applet to see what are the installed
audio terminals, and see/change which is the default one. If the installed
sound card is not full duplex, the sample should be changed to use only
1 audio media type instead of 2.

4. The addresses of type "machine name", "email name", "ip address", and
"conference name" support the video (in/out) media type too. The "video in"
should always be available for these address types even if the machine does
not have any special hardware (but the network card, that is used to
implement the address object).
The "video out" media type can be used only if a video card with a camera
are installed on the computer.
If video "in" or "out" capabilities are detected, the sample application
will use them, however the calls will succeed even without a video card being
installed. If no video capabilities are present, the calls will be audio only.

Note: in order to use the "video in" media type the dynamic terminal
"video window terminal" must be used, because there exists no default terminal
for "video in". Dynamic terminals are created by using the desired dynamic
terminal class from the collection of dynamic terminal classes exposed by the
desired address. Only H323 and IPCONF addresses will enumerate video media
types and therefore video dynamic terminal class.


ASYNC.FRM

VERSION 5.00
Begin VB.Form Form1
   Caption         =   "Async"
   ClientHeight    =   4260
   ClientLeft      =   60
   ClientTop       =   345
   ClientWidth     =   4680
   LinkTopic       =   "Form1"
   MaxButton       =   0   'False
   ScaleHeight     =   4260
   ScaleWidth      =   4680
   StartUpPosition =   3  'Windows Default
   Begin VB.ComboBox cmbAddressTypes
      Height          =   315
      Left            =   1920
      Style           =   2  'Dropdown List
      TabIndex        =   0
      Top             =   720
      Width           =   2295
   End
   Begin VB.CommandButton pbDial
      Caption         =   "Dial"
      Default         =   -1  'True
      Height          =   375
      Index           =   0
      Left            =   960
      TabIndex        =   2
      Top             =   1920
      Width           =   3135
   End
   Begin VB.CommandButton pbDisconnect
      Caption         =   "Disconnect"
      Height          =   375
      Index           =   1
      Left            =   960
      TabIndex        =   3
      Top             =   2520
      Width           =   3135
   End
   Begin VB.TextBox txtDestAddress
      Height          =   375
      Left            =   1920
      TabIndex        =   1
      Top             =   1320
      Width           =   2295
   End
   Begin VB.Label lblStatus
      Height          =   855
      Left            =   1800
      TabIndex        =   7
      Top             =   3120
      Width           =   2415
   End
   Begin VB.Label lblStatusTitle
      Caption         =   "Status:"
      Height          =   255
      Left            =   480
      TabIndex        =   6
      Top             =   3120
      Width           =   855
   End
   Begin VB.Label Label2
      Caption         =   "Address:"
      Height          =   255
      Left            =   480
      TabIndex        =   5
      Top             =   1320
      Width           =   1095
   End
   Begin VB.Label Label1
      Caption         =   "Address Type:"
      Height          =   255
      Left            =   480
      TabIndex        =   4
      Top             =   720
      Width           =   1215
   End
End
Attribute VB_Name = "Form1"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False

' ASYNC.EXE
'
' Example of making an asynchronous outgoing call with TAPI 3.0
'
' This application will allow a user to make an asynchronous call
' by using TAPI 3.0. The application will simply look for the 1st address
' that matches the address type selected by the user in the combo box.
' It will actually pick up the address that also supports at least the
' media types audio in and audio out (therefore it will skip all others,
' even if they match the desired address type).
' It will then use that address to make calls. On the call, it will
' select the media types "audio in" and "audio out", and, if these are supported
' too, it will also select the media types "video in" and "video out"; for each
' of these media types it will use their default terminals; except for video in
' which needs a dynamic terminal to be created.
' The name of the destination address is taken by means of an edit box.
' The app will display messages to announce whenever a state event fires.
'
' This application has the limitation that it only supports one call
' at a time. But Tapi 3.0 has support for multiple calls per address,
' and the number of calls per address is dictated actually by the
' provider. (e.g. Unimodem would allow only 1 call per address at a time)
'
' This application doesn't have code for error handling.
'
' This application does not handle incoming calls.
'
' TBD: there is a bug when null terminal is selected for video out
' (this happens when no video card is installed). The sample has some
' temporary code to handle it, this code must be removed when the bug is
' fixed.
'

Option Explicit

Dim gnPreviousAddressIndex As Long
Dim WithEvents gobjAddressWithEvents As Address
Attribute gobjAddressWithEvents.VB_VarHelpID = -1
Dim gobjOrigAddress As Address
Dim gobjTapi As TAPI
Dim gobjCall As ITBasicCallControl

Private Sub Form_Load()

    gnPreviousAddressIndex = 0  'valid indexes are between 1 and addresses count

    'populate combo box with names of all addresses types
    'attach to each combo item the address type itself

    cmbAddressTypes.AddItem ("Phone Number")
    cmbAddressTypes.ItemData(cmbAddressTypes.NewIndex) = ADDRESSTYPE_PHONENUMBER

    cmbAddressTypes.AddItem ("Email Name")
    cmbAddressTypes.ItemData(cmbAddressTypes.NewIndex) = ADDRESSTYPE_EMAILNAME

    cmbAddressTypes.AddItem ("Machine Name")
    cmbAddressTypes.ItemData(cmbAddressTypes.NewIndex) = ADDRESSTYPE_DOMAINNAME

    cmbAddressTypes.AddItem ("IP Address")
    cmbAddressTypes.ItemData(cmbAddressTypes.NewIndex) = ADDRESSTYPE_IPADDRESS

    cmbAddressTypes.AddItem ("Conference Name")
    cmbAddressTypes.ItemData(cmbAddressTypes.NewIndex) = ADDRESSTYPE_CONFERENCENAME

    'set selection on 1st item
    cmbAddressTypes.ListIndex = 0

End Sub

Private Sub Form_Unload(Cancel As Integer)

    'release all global variables

    Set gobjCall = Nothing
    Set gobjAddressWithEvents = Nothing
    Set gobjOrigAddress = Nothing
    If Not (gobjTapi Is Nothing) Then
        gobjTapi.Shutdown
    End If
    Set gobjTapi = Nothing

    'empty combo box
    Dim i
    For i = 1 To cmbAddressTypes.ListCount
        cmbAddressTypes.RemoveItem (0)
    Next i
End Sub

Private Sub PrintCallState(State As CALL_STATE)
    Select Case State
        Case CS_CONNECTED
            lblStatus.Caption = "call state: CS_CONNECTED"
        Case CS_DISCONNECTED
            Dim strMsg
            strMsg = "call state: CS_DISCONNECTED"
            strMsg = strMsg & Chr(13) & "Call was disconnected. "
            strMsg = strMsg & "Now you can press Connect again or just quit."
            lblStatus.Caption = strMsg
        Case CS_HOLD
            lblStatus.Caption = "call state: CS_HOLD"
        Case CS_IDLE
            lblStatus.Caption = "call state: CS_IDLE"
        Case CS_INPROGRESS
            lblStatus.Caption = "call state: CS_INPROGRESS"
        Case CS_OFFERING
            lblStatus.Caption = "call state: CS_OFFERING"
        Case CS_QUEUED
            lblStatus.Caption = "call state: CS_QUEUED"
        Case Else
            lblStatus.Caption = "call state: unknown!!"
    End Select
End Sub

Private Sub gobjAddressWithEvents_CallEventNotification(ByVal pAddress As TAPI3Lib.ITAddress, ByVal CallEventType As TAPI3Lib.CALL_EVENT_TYPE, ByVal pEvent As Object)
    Dim objCallStateEvent As ITCallStateEvent

    If CallEventType <> CET_CALLSTATEEVENT Then
        'I am interested only by CallStateEvent-s
        lblStatus.Caption = "Event type <> CET_CALLSTATEEVENT ignored."
        Set pEvent = Nothing
        Exit Sub
    End If

    'pEvent is an unknown object; query its ITCallStateEvent interface
    Set objCallStateEvent = pEvent

    PrintCallState (objCallStateEvent.State)

    If objCallStateEvent.State = CS_DISCONNECTED Then

        'release call, you don't need it anymore
        Set gobjCall = Nothing

        Set objCallStateEvent = Nothing
        Set pEvent = Nothing
        Exit Sub

    End If

    'release all objects that aren't needed anymore
    'it is important to release also the received pEvent

    Set objCallStateEvent = Nothing
    Set pEvent = Nothing
    Exit Sub
End Sub

Private Sub pbDial_Click(index As Integer)
    Dim bVideoIn As Boolean, bVideoOut As Boolean
    Dim objTerminalAudioIn As Terminal, objTerminalAudioOut As Terminal
    Dim objTerminalVideoIn As Terminal, objTerminalVideoOut As Terminal

    Dim nOrigAddressIndex As Long
    Dim strDestAddress As String

    'second call not supported by this app
    If Not (gobjCall Is Nothing) Then
        lblStatus.Caption = "Cannot connect new call. Wait for the previous one to be disconnected."
        Exit Sub
    End If

    'check if user typed input data
    strDestAddress = txtDestAddress.Text
    If strDestAddress = "" Then
        lblStatus.Caption = "Enter destination addres!"
        Exit Sub
    End If

    'global variables might already contain something, from previous run

    If gobjTapi Is Nothing Then

        'create the Tapi object
        Set gobjTapi = CreateObject("TAPI.TAPI.1")

        'call Initialize.  this must be called before
        'any other tapi functions are called.
        gobjTapi.Initialize

    End If

    'pick up the collection of Address objects
    Dim objCollAddresses As ITCollection
    Set objCollAddresses = gobjTapi.Addresses

    'find address that supports the desired type (the one selected in combo)
    nOrigAddressIndex = FindOriginationAddressIndex(objCollAddresses)

    'validate the returned value
    If nOrigAddressIndex < 1 Or nOrigAddressIndex > objCollAddresses.Count Then
        lblStatus.Caption = "Could not find an appropriate address to make the call from."

        'release not needed objects
        Set objCollAddresses = Nothing

        Exit Sub
    End If

    '
    'pick up the found address
    '

    If gnPreviousAddressIndex <> nOrigAddressIndex Then
        'If another address was selected, release the previous one
        'and save a "pointer" to the new one.
        'If the same address was selected, don't release/save it again,
        'just keep the previous one.

        'release previous objects
        Set gobjAddressWithEvents = Nothing
        Set gobjOrigAddress = Nothing

        'save new address
        Set gobjOrigAddress = objCollAddresses.Item(nOrigAddressIndex)

        'can't register the outgoing interface here, we'll do it
        'later, after RegisterCallTypes;
        'so, for now, leave empty the variable gobjAddressWithEvents

        'save also the new index
        gnPreviousAddressIndex = nOrigAddressIndex
    End If

    'release objects that are not needed anymore
    '(this decrements the reference count)
    Set objCollAddresses = Nothing

    '
    'find out if media types video in/out are supported as well
    '

    Dim objMediaSupport As ITMediaSupport

    'query ITMediaSupport interface from Address object
    Set objMediaSupport = gobjOrigAddress

    'find out if video is supported
    bVideoIn = False
    bVideoOut = False
    If objMediaSupport.QueryMediaType(TAPIMEDIATYPE_String_VideoIn) Then
        bVideoIn = True
    End If

    If objMediaSupport.QueryMediaType(TAPIMEDIATYPE_String_VideoOut) Then

        'Temporary: TBD: the null terminal causes a bug, so refuse it:

        'If there is no static terminal for video out,
        'better don't use the Null terminal, instead consider video out
        'as being not supported.

        Dim objTerminalSupport As ITTerminalSupport
        Set objTerminalSupport = gobjOrigAddress
        Set objTerminalVideoOut = objTerminalSupport.GetDefaultTerminal(TAPIMEDIATYPE_String_VideoOut)
        Set objTerminalSupport = Nothing

        If Not (objTerminalVideoOut Is Nothing) Then
            bVideoOut = True
        End If

        Set objTerminalVideoOut = Nothing
    End If

    'release not needed objects
    Set objMediaSupport = Nothing

    '
    'pick up the default terminal for each media type (except for VideoIn, which
    'needs a dynamic "video window" terminal to be created)
    '

    'Dim objTerminalSupport As ITTerminalSupport    TBD: when bug fixed, decomment this

    'query ITTerminalSupport from Address object
    Set objTerminalSupport = gobjOrigAddress

    Set objTerminalAudioIn = objTerminalSupport.GetDefaultTerminal(TAPIMEDIATYPE_String_AudioIn)
    Set objTerminalAudioOut = objTerminalSupport.GetDefaultTerminal(TAPIMEDIATYPE_String_AudioOut)

    If bVideoIn = True Then
        Set objTerminalVideoIn = objTerminalSupport.CreateTerminal(CLSID_String_VideoWindowTerm)
    End If
    If bVideoOut = True Then
        Set objTerminalVideoOut = objTerminalSupport.GetDefaultTerminal(TAPIMEDIATYPE_String_VideoOut)
    End If

    Set objTerminalSupport = Nothing

    '
    'create MediaTerminals then put them in array
    '

    Dim arrMediaTerminals() As ITMediaTerminal
    If bVideoIn = False And bVideoOut = False Then
        ReDim arrMediaTerminals(1 To 2) As ITMediaTerminal
    ElseIf bVideoIn = True And bVideoOut = False Or bVideoIn = False And bVideoOut = True Then
        ReDim arrMediaTerminals(1 To 3) As ITMediaTerminal
    Else
        ReDim arrMediaTerminals(1 To 4) As ITMediaTerminal
    End If

    Dim objMediaTerminal As ITMediaTerminal

    Set objMediaTerminal = gobjTapi.CreateMediaTerminal( _
        TAPIMEDIATYPE_String_AudioIn, objTerminalAudioIn)
    Set arrMediaTerminals(1) = objMediaTerminal
    Set objMediaTerminal = Nothing
    Set objTerminalAudioIn = Nothing

    Set objMediaTerminal = gobjTapi.CreateMediaTerminal( _
        TAPIMEDIATYPE_String_AudioOut, objTerminalAudioOut)
    Set arrMediaTerminals(2) = objMediaTerminal
    Set objMediaTerminal = Nothing
    Set objTerminalAudioOut = Nothing

    If bVideoIn = True Then
        Set objMediaTerminal = gobjTapi.CreateMediaTerminal( _
            TAPIMEDIATYPE_String_VideoIn, objTerminalVideoIn)
        Set arrMediaTerminals(3) = objMediaTerminal
        Set objMediaTerminal = Nothing
        Set objTerminalVideoIn = Nothing
    End If

    If bVideoOut = True Then
        Set objMediaTerminal = gobjTapi.CreateMediaTerminal( _
            TAPIMEDIATYPE_String_VideoOut, objTerminalVideoOut)
        If bVideoIn = True Then
            Set arrMediaTerminals(4) = objMediaTerminal
        Else
            Set arrMediaTerminals(3) = objMediaTerminal
        End If
        Set objMediaTerminal = Nothing
        Set objTerminalVideoOut = Nothing
    End If

    '
    'register for receiving events: call RegisterCallTypes, then
    'register the outgoing interface. This tells Tapi what type of events
    'you want to receive, and for what media types; use the
    'same media types that are used for making the call
    '

    If gobjAddressWithEvents Is Nothing Then
        'this variable will contain Nothing only if address was changed;
        'if it already contains a value, it means the address did not change,
        'so there is no need to register the outgoing interface again.

        'bOutgoing must be set on True in order to receive call state events
        'for calls that the application owns
        Dim bOutgoing, bOwner, bMonitor
        bOutgoing = True
        bOwner = False      'this is for receiving incoming calls
        bMonitor = False    'this is for monitoring calls on the address

        'put media type in array, then put array in a variant,
        'as required by RegisterCallTypes
        Dim arrMediaTypes() As String
        If bVideoIn = False And bVideoOut = False Then
            ReDim arrMediaTypes(1 To 2) As String
        ElseIf bVideoIn = True And bVideoOut = False Or bVideoIn = False And bVideoOut = True Then
            ReDim arrMediaTypes(1 To 3) As String
        Else
            ReDim arrMediaTypes(1 To 4) As String
        End If
        Dim MediaTypes As Variant

        arrMediaTypes(1) = TAPIMEDIATYPE_String_AudioIn
        arrMediaTypes(2) = TAPIMEDIATYPE_String_AudioOut
        If bVideoIn = True Then
            arrMediaTypes(3) = TAPIMEDIATYPE_String_VideoIn
        End If
        If bVideoOut = True Then
            If bVideoIn = True Then
                arrMediaTypes(4) = TAPIMEDIATYPE_String_VideoOut
            Else
                arrMediaTypes(3) = TAPIMEDIATYPE_String_VideoOut
            End If
        End If
        MediaTypes = arrMediaTypes

        Call gobjOrigAddress.RegisterCallTypes(bOutgoing, bOwner, bMonitor, MediaTypes)

        '
        'register the outgoing interface that will receive the events
        '

        Set gobjAddressWithEvents = gobjOrigAddress

    End If

    '
    'create the call
    '
    Set gobjCall = gobjOrigAddress.CreateCall(strDestAddress)

    'put the array of media terminals in a variant
    Dim MediaTerminals As Variant
    MediaTerminals = arrMediaTerminals

    '
    'select on the call the MediaTerminal
    '
    gobjCall.SelectMediaTerminals (MediaTerminals)

    'release media terminals
    Set arrMediaTerminals(1) = Nothing
    Set arrMediaTerminals(2) = Nothing
    Set MediaTerminals(1) = Nothing
    Set MediaTerminals(2) = Nothing
    If bVideoIn = True Or bVideoOut = True Then
        Set arrMediaTerminals(3) = Nothing
        Set MediaTerminals(3) = Nothing
    End If
    If bVideoIn = True And bVideoOut = True Then
        Set arrMediaTerminals(4) = Nothing
        Set MediaTerminals(4) = Nothing
    End If

    '
    'Connect the call; False means that the call is made asynchronously.
    'The call to Connect will return immediately, before the call
    'gets to "connected" state; events will fire each time the
    'state of the call changes (to "connected", "disconnected"),
    'meanwhile the application can go on.
    '
    On Error Resume Next
    gobjCall.Connect (False)
    If Err.Number <> 0 Then
        lblStatus.Caption = "Connect failed."
        Err.Clear

        'don't need this failed call anymore
        Set gobjCall = Nothing
    End If

End Sub

Private Sub pbDisconnect_Click(index As Integer)

    If gobjCall Is Nothing Then
        lblStatus.Caption = "There is no call to be disconnected."
        Exit Sub
    End If

    gobjCall.Disconnect (DC_NORMAL)

End Sub

'Search through all addresses and return the index of the one that matches
'the address type selected in the combo box with address types. Also, the
'found address must support at least the media types "audio in" and "audio out"
'Return 0 if no address found. Otherwise return its index, which will be
'between 1 and Addresses.Count
Private Function FindOriginationAddressIndex(objCollAddresses As ITCollection) As Long
    Dim nSelectedType As Long
    Dim indexAddr As Long, indexType As Long
    Dim objCrtAddress As Address
    Dim objCollAddrTypes As ITCollection
    Dim objMediaSupport As ITMediaSupport
    Dim bFound As Boolean

    '
    'retrieve from combo box the type of the selected address type
    '
    nSelectedType = cmbAddressTypes.ItemData(cmbAddressTypes.ListIndex)

    '
    'search through all addresses the first one that matches this type
    'and also supports at least the media types "audio in" and "audio out"
    '
    bFound = False

    For indexAddr = 1 To objCollAddresses.Count
        Set objCrtAddress = objCollAddresses.Item(indexAddr)
        Set objMediaSupport = objCrtAddress

        If (objMediaSupport.QueryMediaType(TAPIMEDIATYPE_String_AudioIn) And _
        objMediaSupport.QueryMediaType(TAPIMEDIATYPE_String_AudioOut)) Then
            Set objCollAddrTypes = objCrtAddress.AddressTypes

            For indexType = 1 To objCollAddrTypes.Count
                If nSelectedType = objCollAddrTypes.Item(indexType) Then
                    bFound = True
                    Exit For
                End If
            Next indexType

            Set objCollAddrTypes = Nothing
        End If

        Set objMediaSupport = Nothing
        Set objCrtAddress = Nothing

        If bFound = True Then Exit For
    Next indexAddr

    '
    'return the index of the found address, or 0 if no address found
    '
    If bFound = True Then
        FindOriginationAddressIndex = indexAddr
    Else
        FindOriginationAddressIndex = 0
    End If
    Exit Function
End Function


README.TXT

TAPI 3.0
INCOMING Sample Application

Overview:

INCOMING is a Visual Basic sample TAPI 3.0 application that waits for
and answers incoming calls: phone calls or H323 audio calls.
This sample was developed using Visual Basic 5.

In order to receive incoming call notifications, as well as any call
state event notifications, the application must call RegisterCallTypes
specifying the media types for which it wants to be notified (therefore
not all incoming calls will be offered to the app, but only those that
have the desired media types). This sample registers the first two
media types, that are expected to be audio in and out.

The application must also declare an Address object "with events", and
implement for it the event procedure that processes the event
"CallEventNotification". This is actually the function that will get
called by Tapi3.dll each time when a new call is offered, and also when
the call changes its state.

In order for Tapi3.dll to know about this event procedure, the address
with events must be assigned the value of the real address object
that corresponds to the address on which calls are to be received.
This is equivalent with "registering an outgoing interface" from C/C++.
For more information about declaring and using objects with events, see
Visual Basic's Books Online.

INCOMING shows how to use an address object with events in order to
receive incoming calls, and how to answer and eventually disconnect
the incoming call.

How to use:

To run the INCOMING sample application, simply build and run incoming.exe,
using Visual Basic 5.
A small dialog box will appear, and the user is supposed to select the
desired call type and press "Register". After that, the status will be
"Waiting for a call."
The 2 possible call types (for this application) are: phone calls and
H323 calls. In order to succesfully register for receiving phone calls,
a voice modem must be used.

In the case of phone calls, a phone set is needed to be plugged into
the modem. Whenever a phone call is received, the application will
display the "offering" state of the call, and allow the call to be
answered, and after that the phone set must be used to actually talk
with the phone from the other end of the call.

In the case of an H323 call, the application counts on the existence
of a sound card (full duplex). The app selects on the call the
first 2 media types (that should be "audio in" and "audio out") and
their default terminals (that should be "speakers" and "microphone"),
therefore after the call is answered you should be able to use
the microphone and the speakers currently existing on the computer
to speak with the person from the other end of the call. Note that
the other end of the call should be an H323 client too.

Limitations:

The way this sample was implemented, in the case of phone calls it
will allow only "interactive voice" calls, which means that a phone
set must be plugged into the modem and used for talking, after the
connection is established. But Tapi 3.0 actually allows also two
other types of phone calls: "automated voice" calls, and "data" calls.
In order to support those too, the application should be changed in
the following way:
- for "automated voice" calls: "audio in" and "audio out" are the
media types that must be registered, but instead of using their
default terminals, the terminals that correspond to the devices
"speakers" and "microphone" should be used.
- for "data" calls: "data modem" is the media type that should be
registered instead of "audio in" and "audio out", in conjunction
with the "Null" terminal, which anyway corresponds to the default
terminal.

The "Register" button can be pressed only once. This actually means
that the application must be restarted in order to change the type
of calls that the application can listen for.

What functionality does this sample show:

The incoming sample application demostrates how to receive new call
notification and call state event notifications.  It also demonstrates
the basic TAPI 3.0 functions involved in answering a call.

What this sample does not show:

This sample does not let the user choose the TAPI address for the
application to use (in case there are several addresses that meet the
same criteria, the sample will always use the first one). It does not
allow the user to decide what media types to listen for, and it does
not allow the user to decide what terminals to use: the sample always
selects the first 2 media types (which are expected to be audio in
and audio out) and their default terminals.

Hints:

In the case of an H323 call: if the sound card installed on the
computer is not full duplex, the sample should be changed to select
only 1 media type on the call, instead of 2.
An H323 address will exist on your computer if H323 provider is
installed, and if the computer is part of a network: the H323 provider
uses the machine's IP address to actually implement the "H323 address".
Control Panel - Telephony applet should be use for adding TSPs
(telephony service providers).

In case you want to use the sample for receiving phone calls, a voice
modem should be installed; the data modems will not work with this sample
because they don't support registering for receving "interactive voice"
calls (which is the only case illustrated by this sample).


INCOMING.FRM

VERSION 5.00
Begin VB.Form Form1
   Caption         =   "Incoming"
   ClientHeight    =   5025
   ClientLeft      =   60
   ClientTop       =   345
   ClientWidth     =   4800
   LinkTopic       =   "Form1"
   ScaleHeight     =   5025
   ScaleWidth      =   4800
   StartUpPosition =   3  'Windows Default
   Begin VB.CommandButton pbDisconnect
      Caption         =   "Disconnect"
      Height          =   375
      Index           =   1
      Left            =   1440
      TabIndex        =   3
      Top             =   2760
      Width           =   2055
   End
   Begin VB.CommandButton pbAnswer
      Caption         =   "Answer"
      Default         =   -1  'True
      Height          =   375
      Index           =   0
      Left            =   1440
      TabIndex        =   2
      Top             =   2160
      Width           =   2055
   End
   Begin VB.CommandButton RegisterForReceiveCall
      Caption         =   "Register the Application  for Receiving Calls"
      Height          =   615
      Left            =   1440
      TabIndex        =   1
      Top             =   1200
      Width           =   2055
   End
   Begin VB.Frame Frame1
      Caption         =   "Register"
      Height          =   1815
      Left            =   240
      TabIndex        =   4
      Top             =   240
      Width           =   4335
      Begin VB.ComboBox cmbAddressTypes
         Height          =   315
         Left            =   1800
         TabIndex        =   0
         Top             =   360
         Width           =   2175
      End
      Begin VB.Label Label1
         Caption         =   "Calls you want to receive:"
         Height          =   495
         Left            =   360
         TabIndex        =   5
         Top             =   360
         Width           =   1095
      End
   End
   Begin VB.Label lblStatus
      Height          =   1335
      Left            =   1080
      TabIndex        =   7
      Top             =   3480
      Width           =   3495
   End
   Begin VB.Label lblStatusTitle
      Caption         =   "Status:"
      Height          =   255
      Left            =   360
      TabIndex        =   6
      Top             =   3480
      Width           =   615
   End
End
Attribute VB_Name = "Form1"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
' INCOMING.EXE
'
' Example of receiving calls with TAPI 3.0
'
' This application will allow a user to receive audio calls
' by using TAPI 3.0.
'
' The application will simply look for the 1st address that is able
' to receive calls of the specified type (phone calls, or h323 calls -
' the user is allowed to decide). The address is also chosen
' only if it supports at least 2 media types (the idea is that
' usually the first 2 media types will be "audio in" and "audio out").
' It will register then itself on this address to receive calls.
' On the received calls, it will select 2 media terminals before
' calling answer. The media terminals are created by using the
' first 2 media types enumerated by the address, with their default
' terminals.
' The app will display messages to announce whenever a new call
' arrives and also when a call state event fires.
'
' This application does not make outgoing calls.
'

Option Explicit

Dim gobjTapi As TAPI
Dim gobjDestAddress As Address
Dim WithEvents gobjAddressWithEvents As Address
Attribute gobjAddressWithEvents.VB_VarHelpID = -1
Dim gobjTerminal1 As Terminal
Dim gobjTerminal2 As Terminal
Dim gguidMediaType1
Dim gguidMediaType2
Dim gobjReceivedCallInfo As ITCallInfo

Dim gbSupportedCall As Boolean

Private Sub Form_Load()

    pbAnswer(0).Enabled = False

    'populate combo box with names of call types
    'attach to each combo item the address type that
    'can receive that type of call

    cmbAddressTypes.AddItem ("Phone Calls")
    cmbAddressTypes.ItemData(cmbAddressTypes.NewIndex) = ADDRESSTYPE_PHONENUMBER

    cmbAddressTypes.AddItem ("H323 Calls")
    cmbAddressTypes.ItemData(cmbAddressTypes.NewIndex) = ADDRESSTYPE_IPADDRESS

    'set selection on 1st item
    cmbAddressTypes.ListIndex = 0

End Sub

Private Sub Form_Unload(Cancel As Integer)

    'release all global objects

    Set gobjReceivedCallInfo = Nothing
    Set gobjTerminal1 = Nothing
    Set gobjTerminal2 = Nothing
    Set gobjAddressWithEvents = Nothing
    Set gobjDestAddress = Nothing
    If Not (gobjTapi Is Nothing) Then
        gobjTapi.Shutdown
    End If
    Set gobjTapi = Nothing

    'empty combo box
    Dim i
    For i = 1 To cmbAddressTypes.ListCount
        cmbAddressTypes.RemoveItem (0)
    Next i
End Sub

Private Sub DisplayCallState(State As CALL_STATE)
    Dim strMsg As String

    Select Case State
        Case CS_CONNECTED
            strMsg = "call state: CS_CONNECTED" & Chr(13)
            strMsg = strMsg & "The call was answered, now you can disconnect "
            strMsg = strMsg & "or wait for disconnected state. "
            strMsg = strMsg & "Don't press Answer before new call arrives. "
            lblStatus.Caption = strMsg

        Case CS_DISCONNECTED
            strMsg = "call state: CS_DISCONNECTED" & Chr(13)
            strMsg = strMsg & "Call will be released. "
            strMsg = strMsg & "New incoming calls will be accepted. "
            lblStatus.Caption = strMsg

        Case CS_HOLD
            lblStatus.Caption = "call state: CS_HOLD"
        Case CS_IDLE
            lblStatus.Caption = "call state: CS_IDLE"

        Case CS_INPROGRESS
            lblStatus.Caption = "call state: CS_INPROGRESS"

        Case CS_OFFERING
            If gbSupportedCall = True Then
                strMsg = "call state: CS_OFFERING"
                strMsg = strMsg & Chr(13) & "A call was received. You can answer it"
                lblStatus.Caption = strMsg
            End If

        Case CS_QUEUED
            lblStatus.Caption = "call state: CS_QUEUED"
        Case Else
            lblStatus.Caption = "call state: unknown!!"
    End Select
End Sub

Private Sub pbAnswer_Click(index As Integer)
    Dim strMsg

    If gobjReceivedCallInfo Is Nothing Then
        strMsg = "There is no call to be answered!"
        lblStatus.Caption = strMsg
        Exit Sub
    End If

    If Not (gobjReceivedCallInfo.CallState = CS_OFFERING) Then
        strMsg = "Cannot answer call that doesn't have offering state."
        strMsg = strMsg & " Did you already press Answer?"
        lblStatus.Caption = strMsg
        Exit Sub
    End If

    'query ITBasicCallControl, the call control interface
    Dim objCallControl As ITBasicCallControl
    Set objCallControl = gobjReceivedCallInfo

    '
    'prepare the terminals (take the default terminals)
    '
    'query ITTerminalSupport from Address object
    Dim objTerminalSupport As ITTerminalSupport

    Set objTerminalSupport = gobjDestAddress

    Set gobjTerminal1 = objTerminalSupport.GetDefaultTerminal(gguidMediaType1)
    Set gobjTerminal2 = objTerminalSupport.GetDefaultTerminal(gguidMediaType2)

    'release not needed objects
    Set objTerminalSupport = Nothing

    'Select the media-terminals before answering

    'Create the media-terminals, then put them in array,
    'then put array in variant
    Dim MediaTerminals As Variant
    Dim arrMediaTerminals(0 To 1) As ITMediaTerminal
    MediaTerminals = arrMediaTerminals
    Set MediaTerminals(0) = gobjTapi.CreateMediaTerminal( _
        gguidMediaType1, gobjTerminal1)
    Set MediaTerminals(1) = gobjTapi.CreateMediaTerminal( _
        gguidMediaType2, gobjTerminal2)
    objCallControl.SelectMediaTerminals (MediaTerminals)

    Set MediaTerminals(0) = Nothing
    Set MediaTerminals(1) = Nothing
    Set gobjTerminal1 = Nothing
    Set gobjTerminal2 = Nothing

    'Answer
    objCallControl.Answer

    'disable the button
    pbAnswer(0).Enabled = False

    'release the call control interface
    Set objCallControl = Nothing

End Sub

Private Sub pbDisconnect_Click(index As Integer)

    If gobjReceivedCallInfo Is Nothing Then
        Dim strMsg As String
        strMsg = "There is no call to be disconnected."
        lblStatus.Caption = strMsg
        Exit Sub
    End If

    'disconnect the call: need to query its call control interface for this
    Dim objCallControl As ITBasicCallControl

    Set objCallControl = gobjReceivedCallInfo

    objCallControl.Disconnect (DC_NORMAL)

    'release the call control interface
    Set objCallControl = Nothing

End Sub

'this "callback" function must be reentrant
Private Sub gobjAddressWithEvents_CallEventNotification(ByVal pAddress As TAPI3Lib.ITAddress, ByVal CallEventType As TAPI3Lib.CALL_EVENT_TYPE, ByVal pEvent As Object)

    Dim strMsg

    If CallEventType = CET_CALLOWNER Then

        gbSupportedCall = True

        If Not (gobjReceivedCallInfo Is Nothing) Then
            gbSupportedCall = False

            strMsg = "This app doesn't support a second call. "
            strMsg = strMsg & "Unsupported second call will be rejected!"
            lblStatus.Caption = strMsg

            'pEvent is declared as Object, so for using it you must
            'query its call control interface, ITBasicCallControl
            Dim objReceivedCallControl As ITBasicCallControl
            Set objReceivedCallControl = pEvent

            'Reject the not supported call by calling Disconnect
            'Note: this second call will arrive only if tapi provider
            'supports more than 1 call per address.
            Dim code As DISCONNECT_CODE
            code = DC_REJECTED
            objReceivedCallControl.Disconnect (code)

            'release all objects that are not needed any longer
            'it is important to release also the received object pEvent

            Set objReceivedCallControl = Nothing
            Set pEvent = Nothing

            Exit Sub
        End If

        'query ITCallInfo interface from pEvent, and store it
        Set gobjReceivedCallInfo = pEvent

        'reenable the button
        pbAnswer(0).Enabled = True

    End If

    If CallEventType = CET_CALLSTATEEVENT Then

        'for this type of event, the object pEvent must be
        'queried for its ITCallStateEvent interface
        Dim objCallStateEvent As ITCallStateEvent
        Set objCallStateEvent = pEvent

        Dim State As CALL_STATE
        Dim objEventCallInfo As ITCallInfo

        'extract the call object from pEvent (from its
        'ITCallStateEvent interface)
        Set objEventCallInfo = objCallStateEvent.Call
        State = objCallStateEvent.State

        DisplayCallState (State)

        If objEventCallInfo Is gobjReceivedCallInfo Then

            If State = CS_DISCONNECTED Then
                'after call is disconnected, you don't need
                'its object anymore; you can only release it
                Set gobjReceivedCallInfo = Nothing
            End If

        End If

        'release not needed objects
        Set objEventCallInfo = Nothing
        Set objCallStateEvent = Nothing
    End If

    'always release the received pEvent
    Set pEvent = Nothing
    Exit Sub
End Sub

Private Sub RegisterForReceiveCall_Click()

    If Not (gobjTapi Is Nothing) Then
        Dim strMsg
        strMsg = "You already did that :-)"
        strMsg = strMsg & Chr(13)
        strMsg = strMsg & "Restart the app if you want to register another address."
        lblStatus.Caption = strMsg
        Exit Sub
    End If

    RegisterForReceiveCall.MousePointer = vbHourglass

    'create the tapi object
    Set gobjTapi = New TAPI

    'call Initialize before calling any other tapi function
    gobjTapi.Initialize

    'pick up the collection of addresses
    Dim objcollAddress As ITCollection
    Set objcollAddress = gobjTapi.Addresses

    'search address that supports the desired address type
    Dim nAddressIndex
    nAddressIndex = FindAddressIndex(objcollAddress)

    If nAddressIndex < 1 Or nAddressIndex > objcollAddress.Count Then

        strMsg = "Could not find an appropriate address for this address type!" & _
            Chr(13) & "Need 2 media types at least to be available on that address."
        lblStatus.Caption = strMsg

        'release all objects
        Set objcollAddress = Nothing
        If Not (gobjTapi Is Nothing) Then
            gobjTapi.Shutdown
        End If
        Set gobjTapi = Nothing
        RegisterForReceiveCall.MousePointer = vbDefault
        Exit Sub
    End If

    'pick up the "N"-th address - the address on which
    'you want to receive calls
    Set gobjDestAddress = objcollAddress.Item(nAddressIndex)
    Set objcollAddress = Nothing    'no more needed, release

    '
    'pick up first 2 media types supported by this address
    '

    Dim objMediaSupport As ITMediaSupport
    Dim objCollMediaTypes As ITCollection

    'query ITMediaSupport interface from Address object
    Set objMediaSupport = gobjDestAddress

    'pick up the collection of media types
    Set objCollMediaTypes = objMediaSupport.MediaTypes

    gguidMediaType1 = objCollMediaTypes.Item(1)
    gguidMediaType2 = objCollMediaTypes.Item(2)

    'release not needed objects
    Set objMediaSupport = Nothing
    Set objCollMediaTypes = Nothing

    '
    'pick up the default terminal for each media type
    '

'    Dim objTerminalSupport As ITTerminalSupport
'
'    'query ITTerminalSupport from Address object
'    Set objTerminalSupport = gobjDestAddress
'
'    Set gobjTerminal1 = objTerminalSupport.GetDefaultTerminal(gguidMediaType1)
'    Set gobjTerminal2 = objTerminalSupport.GetDefaultTerminal(gguidMediaType2)
'
'    'release not needed objects
'    Set objTerminalSupport = Nothing
'
    'register (specify) media types for which you want
    'to receive calls;
    'only calls that have this media type will be offered to the app
    'the media types must be passed to RegisterCallTypes in
    'an array contained in a variant
    Dim arrMediaTypes(0 To 1) As String
    Dim bOutgoing, bOwner, bMonitor
    Dim MediaTypes  As Variant
    bOutgoing = False
    'bOwner = True ensures that app receives incoming calls
    'and their call state events
    bOwner = True
    bMonitor = False
    arrMediaTypes(0) = gguidMediaType1
    arrMediaTypes(1) = gguidMediaType2
    MediaTypes = arrMediaTypes
    Call gobjDestAddress.RegisterCallTypes(bOutgoing, bOwner, _
        bMonitor, MediaTypes)

    'register the outgoing interface (the one that will actually
    'receive and process the events)
    On Error Resume Next
    Set gobjAddressWithEvents = gobjDestAddress
    If Err.Number <> 0 Then
        strMsg = "Registering for receiving calls failed." & Chr(13) & _
            "If you have a data modem, replace it with a voice modem"
        strMsg = strMsg & "Quit the app and try again."
        lblStatus.Caption = strMsg
        RegisterForReceiveCall.MousePointer = vbDefault
        Exit Sub
    End If

    'from now on the app is able to receive calls made on the
    'specified address, with the specified media type

    RegisterForReceiveCall.MousePointer = vbDefault

    lblStatus.Caption = "Waiting for a call..."

End Sub

'Search through all addresses and return the index of the one that matches
'the address type selected in the combo box with address types. Also, the
'found address must support at least 2 media types (so that later we can
'safely use the first and second media type).
'Note: usually, the first 2 media types are "audio in" and "audio out".
'Return 0 if no address found. Otherwise return its index, which will be
'between 1 and Addresses.Count
Private Function FindAddressIndex(objCollAddresses As ITCollection) As Long
    Dim nSelectedType As Long
    Dim indexAddr As Long, indexType As Long
    Dim objCrtAddress As Address
    Dim objCollAddrTypes As ITCollection
    Dim objMediaSupport As ITMediaSupport
    Dim objCollMediaTypes As ITCollection
    Dim bFound As Boolean

    '
    'retrieve from combo box the type of the selected address type
    '
    nSelectedType = cmbAddressTypes.ItemData(cmbAddressTypes.ListIndex)

    '
    'search through all addresses the first one that matches this type
    'and also supports at least one media type
    '
    bFound = False

    For indexAddr = 1 To objCollAddresses.Count
        Set objCrtAddress = objCollAddresses.Item(indexAddr)
        Set objMediaSupport = objCrtAddress
        Set objCollMediaTypes = objMediaSupport.MediaTypes

        If objCollMediaTypes.Count >= 2 Then
            Set objCollAddrTypes = objCrtAddress.AddressTypes

            For indexType = 1 To objCollAddrTypes.Count
                If nSelectedType = objCollAddrTypes.Item(indexType) Then
                    bFound = True
                    Exit For
                End If
            Next indexType

            Set objCollAddrTypes = Nothing
        End If

        Set objCollMediaTypes = Nothing
        Set objMediaSupport = Nothing
        Set objCrtAddress = Nothing

        If bFound = True Then Exit For
    Next indexAddr

    '
    'return the index of the found address, or 0 if no address found
    '
    If bFound = True Then
        FindAddressIndex = indexAddr
    Else
        FindAddressIndex = 0
    End If
    Exit Function
End Function


README.TXT

TAPI 3.0
T3XDOC Sample Application

Overview:

T3XDOC is a sample ActiveX document that uses TAPI 3.0 to make an outgoing
synchronous call. This ActiveX document was developed using Visual Basic 5.

The ActiveX document must be opened with Internet Explorer and shows up
as a Visual Basic form, that lets the user select the address from which
to make the call, the media type and the terminal desired.

How to use:

To run the T3XDoc sample, use Visual Basic's Application Setup Wizard:
this will create the DLL, the CAB file as well as the HTM file that points
to the ActiveX Document. (Note: the setup wizard will suggest you to
include tapi3.dll in the cab file; you should say no, since this application
is designed to work on Windows NT 5.0, that already has tapi3.dll installed,
as well as all the other dll components of the Tapi 3.0 product).
Then launch Internet Explorer and point it to the HTM file created by the
setup wizard. This will automatically load the ActiveX document T3XDoc.
The document will show up as a VB5 form that lets you type in the
destination address and then dial by pressing Connect.
Before that, anyway, press the button "What to Use for Connect", which
will open a dialog box. This dialog box is named "Connect using" and
lets you select the address from which the call is to be made, as well
as the media type and terminal desired.

What functionality does this sample show:

The T3XDOC sample application shows how to make a synchronous outgoing
call with TAPI 3.0, from a Visual Basic ActiveX document.

What this sample does not show:

This sample does not allow the user to select more than 1 MediaTerminal
on the call. Though, Tapi3 allows selection of multiple MediaTerminals
on the call, and it's up to the telephony provider to actually support
them.
(A "MediaTerminal" means a valid pair of "media type" and "terminal".)

The sample also is not implemented to support more than one call per
address. But Tapi 3.0 is designed to allow creation of multiple calls per
address, and it's only up to the telephony provider to actually support
them.

The sample does not show how to receive incoming calls.

Hints:

1. For information about how to package ActiveX documents for Internet
distribution, see Visual Basic (5) Books Online - Component Tools Guide -
Building Internet Applications - Packaging ActiveX Components.
This will also show how to digitally sign and mark safe for scripting and
initializing the ActiveX document.
For more info about how to write an ActiveX document, see again
Visual Basic 5 Books Online - Component Tools Guide - Creating ActiveX
Components - Creating an ActiveX Document.

2. For being able to make audio calls, a sound card needs to be installed
on the machine.

3. For being able to make video calls, a video card and a camera are needed.
However, they are mandatory only for sending video streams, for receiving
video streams no special hardware is required.

4. For being able to make phone calls, a modem needs to be installed.
For this special case of phone (Unimodem) calls, depending on the terminal
selected on the call, in order to actually talk, you will use:
- the telephone set plugged into the modem - if modem terminal was selected.
In this case, the app simply dials the number for you, then you use the
phone to talk.
- the microphone (talk in microphone and listen in the phone from the other
end of the call) - if audio out + microphone were selected
- the speakers (talk in the phone set from the other end of the call and
listen in your computer's speakers) - if audio in + speakers were selected.
For data calls, you need to select the media type "data modem" and the Null
terminal.

5. The media types supported by an address are up to the telephony provider
which exposes that address. Unimodem supports "audio in/out" and "datamodem",
H323 and IPCONF support "audio in/out", "video in/out". However, for these
media types to be actually used, the correspoding devices must be installed
on the machine, i.e. sound and/or video cards.


FRMUSING.FRM

VERSION 5.00
Begin VB.Form frmUsing
   Caption         =   "Connect Using"
   ClientHeight    =   3195
   ClientLeft      =   60
   ClientTop       =   345
   ClientWidth     =   6780
   LinkTopic       =   "Form1"
   MaxButton       =   0   'False
   MinButton       =   0   'False
   ScaleHeight     =   3195
   ScaleWidth      =   6780
   StartUpPosition =   3  'Windows Default
   Begin VB.CommandButton pbCancel
      Caption         =   "Cancel"
      Height          =   375
      Left            =   5160
      TabIndex        =   4
      Top             =   1680
      Width           =   1335
   End
   Begin VB.CommandButton pbOK
      Caption         =   "OK"
      Height          =   375
      Left            =   5160
      TabIndex        =   3
      Top             =   960
      Width           =   1335
   End
   Begin VB.ComboBox cmbTerminals
      Height          =   315
      Left            =   1200
      TabIndex        =   2
      Top             =   2040
      Width           =   3615
   End
   Begin VB.ComboBox cmbMediaTypes
      Height          =   315
      Left            =   1200
      TabIndex        =   1
      Top             =   1320
      Width           =   3615
   End
   Begin VB.ComboBox cmbAddresses
      Height          =   315
      Left            =   1200
      TabIndex        =   0
      Top             =   600
      Width           =   3615
   End
   Begin VB.Label lblTerminal
      Caption         =   "Terminal"
      Height          =   255
      Left            =   240
      TabIndex        =   7
      Top             =   2160
      Width           =   855
   End
   Begin VB.Label lblMediaType
      Caption         =   "Media Type"
      Height          =   255
      Left            =   240
      TabIndex        =   6
      Top             =   1440
      Width           =   855
   End
   Begin VB.Label lblAddress
      Caption         =   "Origination Address"
      Height          =   375
      Left            =   240
      TabIndex        =   5
      Top             =   600
      Width           =   855
   End
End
Attribute VB_Name = "frmUsing"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
'
'frmUsing.frm
'Contains implementation of dialog box that allows user to make
'a selection of tapi objects that are going to be used in making
'the call: the origination address + the mediaterminal to be
'selected on call.
'
'Create/Destroy behaviour:
'This form reads all Tapi objects when it's created, and releases
'all Tapi objects (including Tapi obj itself) when it's destroyed.
'That's why I don't want this dialog to be destroyed every time
'when user closes the window (with Ok, Cancel or control menu).
'Instead, I simply hide it when it is closed by the user (by means
'of Ok, Close or control menu), and I destroy it only when parent
'ActiveX Document terminates. (It also is created by the parent
'ActiveX Document when this one is created.)
'

Option Explicit

'Global variables
Private mnTapiRefCount As Long  'Reference count: incremented when
                                'InitializeTapiObjects is called
                                'decremented when
                                'ReleaseTapiObjects is called

'
'Global variables that don't change along the form's life
'Next arrays have the same dimension = number of addresses
'Each index in each array corresponds to one address
'For address "i":
' - marrobjAddresses(i) contains the address object
' - marrMediaTypesPerAddr(i) contains an arrguidMediaTypes for that address
' - marrTerminalsPerAddr(i) contains an arrobjTerminals for that address
'
Private mobjTapi As TAPI
Private mcollobjAddresses As Collection         'collection of Address objects
Private mcollMediaTypesPerAddr As Collection    'collection of collections of guidMediaTypes
Private mcollTerminalsPerAddr As Collection     'collection of collections of Terminal objects

'
'Global variables that change depending on comboboxes selection
'
Private mobjCrtAddress As Address
Private mobjCrtMediaTerminal As ITMediaTerminal

'next function populates Tapi objects and increments reference count
'Tapi objects are populated only when RefCount becomes 1
Public Sub InitializeTapiObjects()
    mnTapiRefCount = mnTapiRefCount + 1

    If mnTapiRefCount <> 1 Then
        Exit Sub
    End If

    Call PopulateTapiObjects

    'because Tapi objects are now known, UI can also be populated
    Call PopulateCmbAddresses
End Sub

'next function releases Tapi objects and decrements reference count
'Tapi objects are released only when RefCount becomes 0
Public Sub ReleaseTapiObjects()
    mnTapiRefCount = mnTapiRefCount - 1

    If mnTapiRefCount <> 0 Then
        Exit Sub
    End If

    Call DepopulateTapiObjects

End Sub

'Next function returns the selected address
'The returned Address is Nothing if there is something wrong
'with the selection
Public Property Get Address() As Address
    Debug.Assert (mnTapiRefCount > 0)

    Set Address = mobjCrtAddress

End Property

'next function returns an array with the selected media terminals
'The returned MediaTerminal is Nothing if there is something wrong
'with the selection
Public Property Get MediaTerminals() As Variant
    Debug.Assert (mnTapiRefCount > 0)

    Dim arrMediaTerminals(1 To 1) As ITMediaTerminal
    Set arrMediaTerminals(1) = mobjCrtMediaTerminal

    MediaTerminals = arrMediaTerminals

    Set arrMediaTerminals(1) = Nothing
End Property

'When User tries to close the dialog, don't destroy the window,
'just hide it.
Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
    If UnloadMode = vbFormControlMenu Then
        Cancel = True
        Me.Hide
        Debug.Print "Form_QueryUnload: unload refused. Instead, it was just hidden."
    Else
        Cancel = False
    End If
End Sub

'When User tries to close the dialog, don't destroy the window,
'just hide it.
Private Sub pbCancel_Click()
    frmUsing.Hide
End Sub

'When User tries to close the dialog, don't destroy the window,
'just hide it.
'Check selection made by user, and if it's wrong, don't let him
'close the dialog
Private Sub pbOK_Click()
    'Calling ChangeSelectedObjects() will set new values
    'for mobjCrtAddress and mobjCrtMediaTerminal.
    'It will also display an error message if new values
    'selected by the user are invalid.
    If ChangeSelectedObjects() Then
        frmUsing.Hide
    End If
End Sub

Private Sub Form_Load()
    Debug.Print ("frmUsing: Form_Load")
    mnTapiRefCount = 0
End Sub

Private Sub Form_Unload(Cancel As Integer)
    Debug.Print ("frmUsing: Form_Unload")

    'refuse Unload if reference count <> 0, there still exists
    'an ActiveXDocument that might want to use the Tapi objects
    'from this form.
    If mnTapiRefCount <> 0 Then
        Dim strMsg
        strMsg = "Form_Unload: unload refused, "
        strMsg = strMsg & "Tapi reference count still not zero."
        Debug.Print strMsg
        Cancel = True
    End If

End Sub

Private Sub PopulateTapiObjects()
On Error GoTo ErrHandle

    'populate Tapi object
    Set mobjTapi = New TAPI
    mobjTapi.Initialize

    'populate collection of Addresses
    PopulateAddresses

    'populate collection of MediaTypes (one collection for each address)
    PopulateMediaTypes

    'populate collection of Terminals (one collection for each address)
    PopulateTerminals

cleanup:
    Exit Sub

ErrHandle:
    'On error, I Resume Next, because I want all collections to be
    'created, at least as empty collections (with count = 0)

    Call PrintError("PopulateTapiObjects", Err)
    Resume Next
End Sub

'create a collection that contains all addresses
Private Sub PopulateAddresses()
On Error GoTo ErrHandle

    Dim objcollAddresses As ITCollection
    Dim nAddrCount As Long
    Dim nIterator As Long

    'read all addresses from Tapi
    Set objcollAddresses = mobjTapi.Addresses
    nAddrCount = objcollAddresses.Count

    'populate collection of Addresses
    Set mcollobjAddresses = New Collection
    For nIterator = 1 To nAddrCount
        mcollobjAddresses.Add _
            Item:=objcollAddresses.Item(nIterator)
    Next

cleanup:
    Set objcollAddresses = Nothing
    Exit Sub

ErrHandle:
    'On error, I Resume Next, because I want all collections to be
    'created, at least as empty collections (with count = 0)

    Call PrintError("PopulateAddresses", Err)
    Resume Next
End Sub

'create a collection of collections: a collection for each
'address, each collection containing all media types for that
'address
Private Sub PopulateMediaTypes()
On Error GoTo ErrHandle

    Dim objcollAddresses As ITCollection
    Dim nAddrCount As Long, nCrtCount As Long
    Dim nAddrIndex As Long, nMTIndex As Long

    Dim objcrtMediaSupport As ITMediaSupport
    Dim collCrtMediaTypes As Collection     'collection of guids
    Dim strCrtGuidName As String

    'read all addresses from Tapi
    Set objcollAddresses = mobjTapi.Addresses
    nAddrCount = objcollAddresses.Count

    'populate collections of MediaTypes (one collection for each address)

    Set mcollMediaTypesPerAddr = New Collection
    For nAddrIndex = 1 To nAddrCount
        'create a collection with all media types for this address
        Set collCrtMediaTypes = New Collection

        Set objcrtMediaSupport = objcollAddresses.Item(nAddrIndex)
        nCrtCount = 0
        nCrtCount = objcrtMediaSupport.MediaTypes.Count
        For nMTIndex = 1 To nCrtCount

            strCrtGuidName = ""
            strCrtGuidName = MTGuidToName( _
                objcrtMediaSupport.MediaTypes.Item(nMTIndex))

            'reject unknown "guid"s (for which I don't know the name)
            If strCrtGuidName <> "" Then
                collCrtMediaTypes.Add _
                    Item:=objcrtMediaSupport.MediaTypes.Item(nMTIndex)
            End If
        Next nMTIndex

        'save collection in array of collections
        On Error Resume Next
        mcollMediaTypesPerAddr.Add Item:=collCrtMediaTypes
        On Error GoTo ErrHandle

        If Err.Number <> 0 Then
            Call PrintError("PopulateMediaTypes: adding collections", Err)
            Err.Clear

            'this collection is useless, release it, but first
            'remove its items
            nCrtCount = 0
            nCrtCount = collCrtMediaTypes.Count
            For nMTIndex = 1 To nCrtCount
                'collections are reindexed automatically,
                'so remove the 1st element on each iteration
                collCrtMediaTypes.Remove 1
            Next
        End If

        'Cleanup "crt" variables (that change in each iteration)
        'Note: don't remove items from CrtCollection, because
        'the corresponding items from "collection of collections"
        'will also be removed!
        Set collCrtMediaTypes = Nothing
        Set objcrtMediaSupport = Nothing
    Next nAddrIndex

cleanup:
    Set objcollAddresses = Nothing
    Exit Sub

ErrHandle:
    'On error, I Resume Next, because I want all collections to be
    'created, at least as empty collections (with count = 0)

    Call PrintError("PopulateMediaTypes", Err)
    Resume Next
End Sub

'create a collection of collections: a collection for each
'address, each collection containing all terminals for that
'address: static + dynamic + the Null terminal
Private Sub PopulateTerminals()
On Error GoTo ErrHandle

    Dim objcollAddresses As ITCollection
    Dim nAddrCount As Long, nCrtCount As Long
    Dim nAddrIndex As Long, nTerminalIndex As Long, nIndex As Long

    Dim objcrtTerminalSupport As ITTerminalSupport
    Dim collCrtTerminals As Collection     'collection of Terminal objects
    Dim collCrtStaticTerminals As ITCollection
    Dim objCrtTerminal As Terminal

    'read all addresses from Tapi
    Set objcollAddresses = mobjTapi.Addresses
    nAddrCount = objcollAddresses.Count

    'populate collections of Terminals (one collection for each address)

    Set mcollTerminalsPerAddr = New Collection
    For nAddrIndex = 1 To nAddrCount
        'create a collection with all terminals for this address:
        'static + dynamic + the Null terminal
        Set collCrtTerminals = New Collection

        Set objcrtTerminalSupport = objcollAddresses.Item(nAddrIndex)

        'add static terminals
        nCrtCount = 0
        Set collCrtStaticTerminals = objcrtTerminalSupport.StaticTerminals
        nCrtCount = collCrtStaticTerminals.Count
        For nTerminalIndex = 1 To nCrtCount

            Set objCrtTerminal = collCrtStaticTerminals.Item(nTerminalIndex)

            collCrtTerminals.Add Item:=objCrtTerminal

            Set objCrtTerminal = Nothing

        Next nTerminalIndex
        Set collCrtStaticTerminals = Nothing

        'add dynamic terminals: create 1 terminal for each dynamic class
        nCrtCount = 0
        nCrtCount = objcrtTerminalSupport.DynamicTerminalClasses.Count
        For nIndex = 1 To nCrtCount

            Set objCrtTerminal = _
                objcrtTerminalSupport.CreateTerminal( _
                    objcrtTerminalSupport.DynamicTerminalClasses.Item(nIndex))

            collCrtTerminals.Add Item:=objCrtTerminal

            Set objCrtTerminal = Nothing

        Next nIndex

        'add the Null Terminal
        collCrtTerminals.Add Item:=Nothing

        'save collection in array of collections
        On Error Resume Next
        mcollTerminalsPerAddr.Add Item:=collCrtTerminals
        On Error GoTo ErrHandle

        If Err.Number <> 0 Then
            Call PrintError("PopulateTerminals: adding collections", Err)
            Err.Clear

            'this collection is useless, release it, but first
            'remove its items
            nCrtCount = 0
            nCrtCount = collCrtTerminals.Count
            For nTerminalIndex = 1 To nCrtCount
                'collections are reindexed automatically,
                'so remove the 1st element on each iteration
                collCrtTerminals.Remove 1
            Next
        End If

        'Cleanup "crt" variables (that change in each iteration)
        'Note: don't remove items from CrtCollection, because
        'the corresponding items from "collection of collections"
        'will also be removed!
        Set collCrtTerminals = Nothing
        Set objcrtTerminalSupport = Nothing
    Next nAddrIndex

cleanup:
    Set objcollAddresses = Nothing
    Exit Sub

ErrHandle:
    'On error, I Resume Next, because I want all collections to be
    'created, at least as empty collections (with count = 0)

    Call PrintError("PopulateTerminals", Err)
    Resume Next
End Sub

Private Sub DepopulateTapiObjects()
On Error GoTo ErrHandle

    Dim collCrt As Collection
    Dim nIterator As Long, nIndex As Long
    Dim nCount As Long, nCrtCount As Long

    'depopulate collection of Terminals (one collection for each address)
    nCount = 0
    nCount = mcollTerminalsPerAddr.Count
    For nIterator = 1 To nCount
        'collections are reindexed automatically,
        'so remove the 1st element on each iteration

        Set collCrt = mcollTerminalsPerAddr.Item(1)
        nCrtCount = 0
        nCrtCount = collCrt.Count
        For nIndex = 1 To nCrtCount
            collCrt.Remove 1
        Next nIndex
        Set collCrt = Nothing

        mcollTerminalsPerAddr.Remove 1
    Next nIterator
    Set mcollTerminalsPerAddr = Nothing

    'depopulate collection of MediaTypes (one collection for each address)
    nCount = 0
    nCount = mcollMediaTypesPerAddr.Count
    For nIterator = 1 To nCount
        'collections are reindexed automatically,
        'so remove the 1st element on each iteration

        Set collCrt = mcollMediaTypesPerAddr.Item(1)
        nCrtCount = 0
        nCrtCount = collCrt.Count
        For nIndex = 1 To nCrtCount
            collCrt.Remove 1
        Next nIndex
        Set collCrt = Nothing

        mcollMediaTypesPerAddr.Remove 1
    Next nIterator
    Set mcollMediaTypesPerAddr = Nothing

    'depopulate collection of Addresses
    nCount = 0
    nCount = mcollobjAddresses.Count
    For nIterator = 1 To nCount
        'collections are reindexed automatically,
        'so remove the 1st element on each iteration

        mcollobjAddresses.Remove 1
    Next nIterator
    Set mcollobjAddresses = Nothing

    'depopulate Tapi object
    If Not (mobjTapi Is Nothing) Then
        mobjTapi.Shutdown
    End If
    Set mobjTapi = Nothing

cleanup:
    Exit Sub

ErrHandle:
    'On error, I Resume Next, because I want all release actions
    'to be performed

    Call PrintError("DepopulateTapiObjects", Err)
    Resume Next
End Sub

'This function is called only once: after dialog is created,
'when it is asked to load the Tapi objects.
'When this function is called, the combo cmbAddresses is
'populated, and the 1st item is selected; this determines
'automatical populate for dependent comboboxes cmbMediaTypes
'and cmbTerminals, and automatical selection of their 1st
'item.
Private Sub PopulateCmbAddresses()
On Error GoTo ErrHandle

    Dim nIterator As Long, nCount As Long

    'don't need to clean combo of previous contents,
    'because this function is called only once
    'cmbAddresses.Clear

    'populate combobox with names of all addresses from mcollobjAddresses
    'attach to each combo item the index of the corresponding address
    nCount = 0
    nCount = mcollobjAddresses.Count
    For nIterator = 1 To nCount
        On Error Resume Next
        cmbAddresses.AddItem (mcollobjAddresses.Item(nIterator).AddressName)
        On Error GoTo ErrHandle
        If Err.Number <> 0 Then
            'there was an error therefore don't call NewIndex
            Call PrintError("PopulateCmbAddresses", Err)
            Err.Clear
        Else
            cmbAddresses.ItemData(cmbAddresses.NewIndex) = nIterator
        End If
    Next nIterator

    'set selection on 1st item
    If cmbAddresses.ListCount > 0 Then
        cmbAddresses.ListIndex = 0
        'This automatically called cmbAddresses_Click

    End If

cleanup:
    Exit Sub

ErrHandle:
    'I resume next, because I want to add as many items as possible

    Call PrintError("PopulateCmbAddresses", Err)
    Resume Next
End Sub

'Note: cmbMediaTypes depends on cmbAddresses: it is always
'populated only with those media types that correspond
'to the Address currently selected in cmbAddresses
'This is why each time the selection changes in cmbAddresses
'this combo is repopulated. This means that this function is
'called each time when another address is selected; thus it must
'first clear the combo's contents before refilling it.
Private Sub PopulateCmbMediaTypes(nAddrIndex As Long)
On Error GoTo ErrHandle

    Dim collguidMediaTypes As Collection
    Dim nIterator As Long, nCount As Long

    'clean combo of all previous contents, because
    'this might be a repopulating action
    cmbMediaTypes.Clear

    'pick up the media types corresponding to
    'the address received as parameter
    Set collguidMediaTypes = mcollMediaTypesPerAddr.Item(nAddrIndex)

    'populate combobox with the names corresponding to all guids
    'from this particular collection of MediaTypes
    'attach to each combo item the index of the corresponding media type
    nCount = 0
    nCount = collguidMediaTypes.Count
    For nIterator = 1 To nCount
        On Error Resume Next
        cmbMediaTypes.AddItem ( _
            MTGuidToName(collguidMediaTypes.Item(nIterator)))
        On Error GoTo ErrHandle
        If Err.Number <> 0 Then
            'there was an error therefore don't call NewIndex
            Call PrintError("PopulateCmbMediaTypes", Err)
            Err.Clear
        Else
            cmbMediaTypes.ItemData(cmbMediaTypes.NewIndex) = nIterator
        End If
    Next nIterator

    'set selection on 1st item
    If cmbMediaTypes.ListCount > 0 Then
        cmbMediaTypes.ListIndex = 0
        'this automatically called cmbMediaTypes_Click
    End If

cleanup:
    Set collguidMediaTypes = Nothing
    Exit Sub

ErrHandle:
    'I resume next, because I want to add as many items as possible

    Call PrintError("PopulateCmbMediaTypes", Err)
    Resume Next
End Sub

'Note: cmbTerminals depends on cmbAddresses: it is always
'populated only with those terminals that correspond
'to the Address currently selected in cmbAddresses
'This is why each time the selection changes in cmbAddresses
'this combo is repopulated. This means that this function is
'called each time another address is selected; thus it must
'first clear the combo's contents before refilling it.
Private Sub PopulateCmbTerminals(nAddrIndex As Long)
On Error GoTo ErrHandle

    Dim collobjTerminals As Collection
    Dim nIterator As Long, nCount As Long
    Dim objCrtTerminal As Terminal

    'clean combo of all previous contents, because
    'this might be a repopulating action
    cmbTerminals.Clear

    'pick up the terminals corresponding to
    'the address received as parameter
    Set collobjTerminals = mcollTerminalsPerAddr.Item(nAddrIndex)

    'populate combobox with the names corresponding to all terminals
    'from this particular collection of Terminals
    'attach to each combo item the index of the corresponding terminal
    nCount = 0
    nCount = collobjTerminals.Count
    For nIterator = 1 To nCount

        'special case: the Null terminal
        '(objcrtTerminal = Nothing but Err.Number=0)

        On Error Resume Next
        Set objCrtTerminal = collobjTerminals.Item(nIterator)
        On Error GoTo ErrHandle

        If Err.Number <> 0 Then
            'there was an error when retrieving item
            'therefore there's no item to add
            Call PrintError("PopulateCmbTerminals", Err)
            Err.Clear
        Else
            'there was success when retrieving item
            'therefore there is a item to add
            On Error Resume Next
            cmbTerminals.AddItem (TerminalToCompleteName(objCrtTerminal))
            On Error GoTo ErrHandle
            If Err.Number <> 0 Then
                'there was an error therefore don't call NewIndex
                Call PrintError("PopulateCmbTerminals", Err)
                Err.Clear
            Else
                cmbTerminals.ItemData(cmbTerminals.NewIndex) = nIterator
            End If
        End If

        Set objCrtTerminal = Nothing
    Next nIterator

    'set selection on 1st item
    If cmbTerminals.ListCount > 0 Then
        cmbTerminals.ListIndex = 0
        'this automatically called cmbTerminals_Click
    End If

cleanup:
    Set collobjTerminals = Nothing
    Exit Sub

ErrHandle:
    'I resume next, because I want to add as many items as possible

    Call PrintError("PopulateCmbTerminals", Err)
    Resume Next
End Sub

Private Sub cmbAddresses_Click()
On Error GoTo ErrHandle

    Dim nSelectedAddrIndex As Long

    'each time the address changes, repopulate
    'the dependent comboboxes (cmbMediaTypes & cmbTerminals)

    'retrieve the index of the selected address
    nSelectedAddrIndex = cmbAddresses.ItemData(cmbAddresses.ListIndex)

    PopulateCmbMediaTypes (nSelectedAddrIndex)
    PopulateCmbTerminals (nSelectedAddrIndex)

cleanup:
    Exit Sub

ErrHandle:
    'I resume next, because I want to populate
    'as many controls (comboboxes) as possible

    Call PrintError("cmbAddresses_Click", Err)
    Resume Next
End Sub

'ChangeSelectedObjects
'Returns:
'True if valid new values were selected by the user, False otherwise
'Action:
'Each time when OK is pressed, it means that the user
'decided for a new OriginationAddress and MediaTerminal;
'therefore check if the comboboxes have valid values
'then set new values in mobjCrtAddress and mobjCrtMediaTerminal.
'If new values are not valid (they correspond to Null objects)
'leave the old values unchanged.
Private Function ChangeSelectedObjects() As Boolean
On Error GoTo ErrHandle

    ChangeSelectedObjects = False

    Dim nSelectedAddrIndex As Long
    Dim nSelectedMTIndex As Long
    Dim nSelectedTerminalIndex As Long

    Dim guidMediaType As String
    Dim objTerminal As Terminal

    Dim objNewAddress As Address
    Dim objNewMediaTerminal As ITMediaTerminal

    Dim strMsg As String

    'pick up the address object that has the name selected in cmbAddresses
    nSelectedAddrIndex = cmbAddresses.ItemData(cmbAddresses.ListIndex)
    Set objNewAddress = mcollobjAddresses.Item(nSelectedAddrIndex)

    'pick up the media type guid that has the name selected in cmbMediaTypes
    nSelectedMTIndex = cmbMediaTypes.ItemData(cmbMediaTypes.ListIndex)
    guidMediaType = mcollMediaTypesPerAddr.Item( _
        nSelectedAddrIndex).Item(nSelectedMTIndex)

    'pick up the terminal object that has the name selected in cmbTerminals
    nSelectedTerminalIndex = cmbTerminals.ItemData(cmbTerminals.ListIndex)
    Set objTerminal = mcollTerminalsPerAddr.Item( _
        nSelectedAddrIndex).Item(nSelectedTerminalIndex)

    Set objNewMediaTerminal = mobjTapi.CreateMediaTerminal( _
        guidMediaType, objTerminal)

    If (Not (objNewAddress Is Nothing)) And _
       (Not (objNewMediaTerminal Is Nothing)) Then

        'release old references
        Set mobjCrtAddress = Nothing
        Set mobjCrtMediaTerminal = Nothing

        'set new values
        Set mobjCrtAddress = objNewAddress
        Set mobjCrtMediaTerminal = objNewMediaTerminal

        'set True in return value
        ChangeSelectedObjects = True
    Else
        If objNewAddress Is Nothing Then
            strMsg = "A valid origination address must be selected!"
        End If
        If objNewMediaTerminal Is Nothing Then
            strMsg = "A valid pair Media+Terminal must be selected!"
        End If
        Call MessageBox(0, strMsg, "Connect Using - Error", MB_OK Or MB_USERICON)
    End If

cleanup:
    Set objNewAddress = Nothing
    Set objNewMediaTerminal = Nothing

    Exit Function

ErrHandle:
    'I resume Next, because I want to execute the
    'same code in case of both types of failures:
    'code failure OR user failure (wrong selection)
    'In both cases, the same MsgBox is displayed,
    'explaining which object could not be created
    Call PrintError("ChangeSelectedObjects", Err)
    Resume Next
End Function


T3XDOC.DOB

VERSION 5.00
Begin VB.UserDocument Tapi3XDoc
   ClientHeight    =   3600
   ClientLeft      =   0
   ClientTop       =   0
   ClientWidth     =   5085
   HScrollSmallChange=   225
   ScaleHeight     =   3600
   ScaleWidth      =   5085
   VScrollSmallChange=   225
   Begin VB.CommandButton Using
      Caption         =   "What To Use for Connect"
      Height          =   375
      Left            =   1320
      TabIndex        =   4
      Top             =   480
      Width           =   2295
   End
   Begin VB.CommandButton Disconnect
      Caption         =   "Disconnect"
      Height          =   375
      Left            =   1320
      TabIndex        =   3
      Top             =   2880
      Width           =   2295
   End
   Begin VB.CommandButton Connect
      Caption         =   "Connect Sync"
      Default         =   -1  'True
      Height          =   375
      Left            =   1320
      TabIndex        =   2
      Top             =   2160
      Width           =   2295
   End
   Begin VB.TextBox txtDestAddress
      Height          =   285
      Left            =   1320
      TabIndex        =   0
      Top             =   1320
      Width           =   2295
   End
   Begin VB.Label lblDestAddress
      Caption         =   "Destination Address"
      Height          =   375
      Left            =   240
      TabIndex        =   1
      Top             =   1320
      Width           =   855
   End
End
Attribute VB_Name = "Tapi3XDoc"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = True
'
'T3XDoc.DLL
'
'This is a sample that shows how to write an ActiveX Document
'that uses Tapi3.0 for making a synchronous outgoing call.
'
'The ActiveX document must be loaded in Internet Explorer.
'It allows the user to select the address on which to make the call,
'and indicate one terminal and one media type to be used on that call.
'After the call is connected, the user can disconnect the call.
'
'In order to obtain the ActiveX Document, the Application Setup Wizard
'must be used. It will create the dll, the cab file as
'well as the htm file that points to the ActiveXDocument.
'Internet Explorer must be pointed to the HTM file, and this
'will automatically load the ActiveX Document.
'

Option Explicit

Private mbFirstShow As Boolean
Private mobjCallControl As ITBasicCallControl

Private Sub UserDocument_Initialize()
On Error GoTo ErrHandle

    Debug.Print ("UserDocument_Initialize")

    'set initial value in global variables
    Set mobjCallControl = Nothing
    mbFirstShow = True

    'init user interface
    Load frmUsing

    'init tapi objects
    Call frmUsing.InitializeTapiObjects

cleanup:
    Exit Sub

ErrHandle:
    Debug.Print ("Error: " & Err.Number & " " & Err.Description)
    Call PrintError("UserDocument_Initialize", Err)

    GoTo cleanup

End Sub

Private Sub UserDocument_Terminate()
On Error Resume Next

    Debug.Print ("UserDocument_Terminate")

    'release tapi objects
    Set mobjCallControl = Nothing
    frmUsing.ReleaseTapiObjects     'this releases also objTapi itself

    'release user interface
    Unload frmUsing

cleanup:
    Exit Sub

End Sub

Private Sub Using_Click()
    If Not (mobjCallControl Is Nothing) Then
        'a previous call exists;
        Dim strMsg As String
        strMsg = "Current call is not yet disconnected!"
        Call MessageBox(0, strMsg, "T3XDoc", MB_OK Or MB_USERICON)
    Else
        frmUsing.Show vbModal
    End If
End Sub

'
'Connect_Click: check if connect is allowed, then make call
'
Private Sub Connect_Click()
On Error GoTo ErrHandle

    Dim strMsg As String
    Dim objAddress As Address
    Dim MediaTerminals
    Dim bSync As Boolean
    Dim objCallInfo As ITCallInfo
    Dim bConnectReturned As Boolean, bCallStateDisplayed As Boolean

    bConnectReturned = False
    bCallStateDisplayed = False

    '
    'Connect is allowed only if there is no previous call or
    'the previous call is in disconnected state
    '
    If Not (mobjCallControl Is Nothing) Then
        '
        'a previous call exists; query for its state
        '
        If CallIsConnected(mobjCallControl) Then
            strMsg = "Cannot make new call while previous one is still connected."
            Call MessageBox(0, strMsg, "T3XDoc", MB_OK Or MB_USERICON)

            Exit Sub

        Else
            '
            'Release previous call, which is not needed anymore
            '
            Set mobjCallControl = Nothing
        End If
    End If

    '
    ' Go on to create and connect a call, using the tapi objects
    ' selected by the user in the secondary form "frmUsing":
    ' user must have selected the origination address + the
    ' mediaterminal
    '

    Set objAddress = frmUsing.Address
    If objAddress Is Nothing Then
        strMsg = "No address is selected, go in What To Use for Connect, "
        strMsg = strMsg & "select something, then press OK."
        Call MessageBox(0, strMsg, "T3XDoc", MB_OK Or MB_USERICON)
        GoTo cleanup
    End If
    Set mobjCallControl = objAddress.CreateCall(txtDestAddress.Text)

    MediaTerminals = frmUsing.MediaTerminals
    mobjCallControl.SelectMediaTerminals (MediaTerminals)

    '
    'Calling Connect with bSync = True, means that the
    'call is synchronous; the function Connect will not
    'return until it has a result, i.e. until the call reaches
    'either the connected or the disconnected state.
    'This might take a while, depending on the Tapi service provider,
    'and meanwhile the calling app will appear as "hanging"
    '

    bSync = True
    mobjCallControl.Connect (bSync)
    bConnectReturned = True

    'query the ITCallInfo interface, which can tell us the state
    Set objCallInfo = mobjCallControl
    DisplayCallState (objCallInfo.CallState)
    bCallStateDisplayed = True

cleanup:
On Error Resume Next        'otherwise there would be a loop
    Set objCallInfo = Nothing

    If Not IsEmpty(MediaTerminals) Then
        Dim nIterator As Long
        For nIterator = LBound(MediaTerminals) To UBound(MediaTerminals)
            Set MediaTerminals(nIterator) = Nothing
        Next
    End If

    Set objAddress = Nothing
    Exit Sub

ErrHandle:

    Call PrintError("Connect_Click", Err)

    'display a message
    If bConnectReturned = False Then
        strMsg = "Connect didn't succeed, there was an error."
        Call MessageBox(0, strMsg, "T3XDoc", MB_OK Or MB_USERICON)
    ElseIf bCallStateDisplayed = False Then
        strMsg = "Can't find out the state of the call, there was an error."
        Call MessageBox(0, strMsg, "T3XDoc", MB_OK Or MB_USERICON)
    End If

    'finish
    GoTo cleanup
End Sub

Private Sub Disconnect_Click()
On Error GoTo ErrHandle

Dim strMsg As String

    If mobjCallControl Is Nothing Then
        strMsg = "There is no call to be disconnected."
        Call MessageBox(0, strMsg, "T3XDoc", MB_OK Or MB_USERICON)
        Exit Sub
    End If

    'disconnect the call by calling Disconnect, method
    'of ITBasicCallControl
    If CallIsConnected(mobjCallControl) Then
        mobjCallControl.Disconnect (DC_NORMAL)
    End If

    Set mobjCallControl = Nothing

cleanup:
    Exit Sub

ErrHandle:
    Call PrintError("Disconnect_Click", Err)
    GoTo cleanup
End Sub

Private Function CallIsConnected(objCall As ITBasicCallControl) As Boolean
On Error GoTo ErrHandle

    Dim objCallInfo As ITCallInfo
    Dim State As CALL_STATE

    CallIsConnected = False

    'to find out the state, we must query the ITCallInfo interface
    Set objCallInfo = objCall
    State = objCallInfo.CallState

    If State = CS_CONNECTED Then
        CallIsConnected = True
    End If

cleanup:
On Error Resume Next        'otherwise there would be a loop
    Set objCallInfo = Nothing
    Exit Function

ErrHandle:
    Call PrintError("CallIsConnected", Err)
    GoTo cleanup
End Function

'I don't want the message boxes to beep, so instead of using
'MsgBox, I call the Win32 function MessageBox with MB_USERICON
'See Tools.bas for declarations
Private Sub DisplayCallState(State As CALL_STATE)
    Select Case State
        Case CS_CONNECTED
            Call MessageBox(0, "CS_CONNECTED", "Call State", _
                MB_OK Or MB_USERICON)
        Case CS_DISCONNECTED
            Call MessageBox(0, "CS_DISCONNECTED", "Call State", _
                MB_OK Or MB_USERICON)
        Case CS_HOLD
            Call MessageBox(0, "CS_HOLD", "Call State", _
                MB_OK Or MB_USERICON)
        Case CS_IDLE
            Call MessageBox(0, "CS_IDLE", "Call State", _
                MB_OK Or MB_USERICON)
        Case CS_INPROGRESS
            Call MessageBox(0, "CS_INPROGRESS", "Call State", _
                MB_OK Or MB_USERICON)
        Case CS_OFFERING
            Call MessageBox(0, "CS_OFFERING", "Call State", _
                MB_OK Or MB_USERICON)
        Case CS_QUEUED
            Call MessageBox(0, "CS_QUEUED", "Call State", _
                MB_OK Or MB_USERICON)
        Case Else
            Call MessageBox(0, "Call state unknown!!", "Call State", _
                MB_OK Or MB_USERICON)
    End Select
End Sub


TOOLS.BAS

Attribute VB_Name = "Tools"

'
'Tools.bas
'

Option Explicit

'
'declarations needed for using win32 api MessageBox
'
Declare Function MessageBox Lib "user32" Alias "MessageBoxA" (ByVal hwnd As Long, ByVal lpText As String, ByVal lpCaption As String, ByVal wType As Long) As Long
Public Const MB_OK = &H0&
'add a new constant, that Or-ed with MB_OK will avoid the beep
Public Const MB_USERICON = &H80&

Public Sub PrintError(strTitle As String, Error As ErrObject)
    Dim strMsg
    strMsg = strTitle & ": run time error " & _
        Error.Number & " Description: " & Error.Description

    Debug.Print (strMsg)
End Sub

Public Function MTGuidToName(guidMediaType As String) As String

    Select Case guidMediaType
        Case TAPIMEDIATYPE_String_AudioIn
            MTGuidToName = "AudioIn"
        Case TAPIMEDIATYPE_String_AudioOut
            MTGuidToName = "AudioOut"
        Case TAPIMEDIATYPE_String_DataModem
            MTGuidToName = "DataModem"
        Case TAPIMEDIATYPE_String_G3Fax
            MTGuidToName = "G3Fax"
        Case TAPIMEDIATYPE_String_VideoIn
            MTGuidToName = "VideoIn"
        Case TAPIMEDIATYPE_String_VideoOut
            MTGuidToName = "VideoOut"
        Case Else
            MTGuidToName = ""
            Debug.Print ("Error: MTGuidToName: unknown Media Type!")
    End Select

    Debug.Assert (MTGuidToName <> "")

End Function

Private Function TerminalClassToName(guidTermClass As String) As String

    Select Case guidTermClass
        Case CLSID_String_FileTerminal
            TerminalClassToName = "File"
        Case CLSID_String_HandsetTerminal
            TerminalClassToName = "Handset"
        Case CLSID_String_HeadsetTerminal
            TerminalClassToName = "Headset"
        Case CLSID_String_MicrophoneTerminal
            TerminalClassToName = "Microphone"
        Case CLSID_String_SpeakerphoneTerminal
            TerminalClassToName = "Speakerphone"
        Case CLSID_String_SpeakersTerminal
            TerminalClassToName = "Speakers"
        Case CLSID_String_VideoInputTerminal
            TerminalClassToName = "VideoInput"
        Case CLSID_String_VideoWindowTerm
            TerminalClassToName = "VideoWindow"
        Case Else
            TerminalClassToName = "Unknown Class"
            Debug.Print ("Error: TerminalClassToName: unknown Terminal Class!")
    End Select

    Debug.Assert (TerminalClassToName <> "")

End Function

'This function creates the "complete" name of the terminal, by
'concatenating its name and its class. It is usefull for the cases
'when terminal names are not unique (i.e. the same name SoundBlaster
'can appear twice, once for the microphone device, once for the
'speakers device)
Public Function TerminalToCompleteName(objTerminal As Terminal) As String
On Error GoTo ErrHandle

    Dim strTerminalName As String
    Dim strTerminalClass As String

    'the received terminal can be a valid terminal or the Null terminal
    'the Null terminal is considered valid too, but I need to create its name

    If objTerminal Is Nothing Then
        strTerminalName = "No Terminal"
        strTerminalClass = ""
    Else
        strTerminalName = objTerminal.Name
        strTerminalClass = TerminalClassToName(objTerminal.TerminalClass)
    End If

cleanup:
    'append terminal name and terminal class name and return it
    TerminalToCompleteName = strTerminalName & " - " & strTerminalClass
    Exit Function

ErrHandle:
    'I resume next, because I want to retrieve as much information
    'as possible about the terminal's name. Maybe I end up only with
    'its name without the class, but it's more than nothing

    Call PrintError("TerminalToCompleteName", Err)
    Resume Next
End Function


README.TXT

This sample tree includes a number of Windows NT Networking (Lan Manager) API
related samples.

These samples illustrated user, group, and share management, in addition to
other networking related activities.

Other samples which illustrate security operations can be found in
\mstools\samples\win32\winnt\security


README.TXT

Networking (LAN Manager) API Sample

This sample changes the password for an arbitrary user on an arbitrary
target machine.

When targetting a domain controller for account update operations,
be sure to target the primary domain controller for the domain.
The account 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.

Note that admin or account operator privilege is required on the
target machine unless argv[4] is present and represents the correct
current password.

NetUserSetInfo() at info-level 1003 is appropriate for administrative
over-ride of an existing password.

NetUserChangePassword() allows for an arbitrary user to over-ride
an existing password providing that the current password is confirmed.


CHNGPASS.C

/*++

Copyright (c) 1995, 1996  Microsoft Corporation

Module Name:

    chngpass.c

Abstract:

    This sample changes the password for an arbitrary user on an arbitrary
    target machine.

    When targetting a domain controller for account update operations,
    be sure to target the primary domain controller for the domain.
    The account 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.

    Username is argv[1]
    new password is argv[2]
    optional target machine (or domain name) is argv[3]
    optional old password is argv[4].  This allows non-admin password
     changes.

    Note that admin or account operator privilege is required on the
    target machine unless argv[4] is present and represents the correct
    current password.

    NetUserSetInfo() at info-level 1003 is appropriate for administrative
    over-ride of an existing password.

    NetUserChangePassword() allows for an arbitrary user to over-ride
    an existing password providing that the current password is confirmed.

    Link with netapi32.lib

Author:

    Scott Field (sfield)    21-Dec-95

--*/

#include <windows.h>
#include <stdio.h>

#include <lm.h>

#define RTN_OK 0
#define RTN_USAGE 1
#define RTN_ERROR 13

void
DisplayErrorText(
    DWORD dwLastError
    );

//
// Unicode entry point and argv
//

int
__cdecl
wmain(
    int argc,
    wchar_t *argv[]
    )
{
    LPWSTR          wUserName;
    LPWSTR          wComputerName = NULL; // default to local machine
    LPWSTR          wOldPassword;
    LPWSTR          wNewPassword;
    USER_INFO_1003  pi1003;
    NET_API_STATUS  nas;

    if( argc < 3 ) {
        fprintf(stderr, "Usage: %ls <user> <new_password> "
                        "[\\\\machine | domain] [old_password]\n",
                        argv[0]);
        return RTN_USAGE;
    }

    //
    // process command line arguments
    //

    wUserName = argv[1];
    wNewPassword = argv[2];

    if( argc >= 4 && *argv[3] != L'\0' ) {

        //
        // obtain target machine name, if appropriate
        // always in Unicode, as that is what the API takes
        //

        if(argv[3][0] == L'\\' && argv[3][1] == L'\\') {

            //
            // target specified machine name
            //

            wComputerName = argv[3];
        }
        else {

            //
            // the user specified a domain name.  Lookup the PDC
            //

            nas = NetGetDCName(
                NULL,
                argv[3],
                (LPBYTE *)&wComputerName
                );

            if(nas != NERR_Success) {
                DisplayErrorText( nas );
                return RTN_ERROR;
            }
        }
    }

    if(argc == 5) {
        wOldPassword = argv[4];
    } else {
        wOldPassword = NULL;
    }

    if(wOldPassword == NULL) {

        //
        // administrative over-ride of existing password
        //

        pi1003.usri1003_password = wNewPassword;

        nas = NetUserSetInfo(
                wComputerName,  // computer name
                wUserName,      // username
                1003,           // info level
                (LPBYTE)&pi1003,     // new info
                NULL
                );
    } else {

        //
        // allows user to change their own password
        //

        nas = NetUserChangePassword(
                wComputerName,
                wUserName,
                wOldPassword,
                wNewPassword
                );
    }

    if(wComputerName != NULL && wComputerName != argv[3]) {

        //
        // a buffer was allocated for the PDC name, free it
        //

        NetApiBufferFree(wComputerName);
    }

    if(nas != NERR_Success) {
        DisplayErrorText( nas );
        return RTN_ERROR;
    }

    return RTN_OK;
}

void
DisplayErrorText(
    DWORD dwLastError
    )
{
    HMODULE hModule = NULL; // default to system source
    LPSTR MessageBuffer;
    DWORD dwBufferLength;
    DWORD dwFormatFlags;

    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;
    }

    //
    // 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);
}


README.TXT

Networking (LAN Manager) API Sample

The following sample illustrates how to display error text associated with
Networking related error codes, in addition to displaying error text
associated with system related error codes.

If the supplied error number is in a specific range, the netmsg.dll
message module is loaded and used to lookup the specified error number
with the FormatMessage() Win32 API.


NETMSG.C

/*++

Copyright 1996 - 1997 Microsoft Corporation

Module Name:

    netmsg.c

Abstract:

    The following sample illustrates how to display error text associated with
    Networking related error codes, in addition to displaying error text
    associated with system related error codes.

    If the supplied error number is in a specific range, the netmsg.dll
    message module is loaded and used to lookup the specified error number
    with the FormatMessage() Win32 API.

Author:

    Scott Field (sfield)    29-Mar-96

--*/

#include <windows.h>
#include <stdio.h>

#include <lmerr.h>

void
DisplayErrorText(
    DWORD dwLastError
    );

#define RTN_OK 0
#define RTN_USAGE 1
#define RTN_ERROR 13

int
__cdecl
main(
    int argc,
    char *argv[]
    )
{
    if(argc != 2) {
        fprintf(stderr,"Usage: %s <error number>\n", argv[0]);
        return RTN_USAGE;
    }

    DisplayErrorText( atoi(argv[1]) );

    return RTN_OK;
}

void
DisplayErrorText(
    DWORD dwLastError
    )
{
    HMODULE hModule = NULL; // default to system source
    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;
    }

    //
    // 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);
}


README.TXT

Networking (LAN Manager) API Sample

This module illustrates how to use the Windows NT Lan Manager API
in conjunction with the Win32 security API to create a new share
on an arbitrary machine with permissions that grant an arbitrary
user/group Full Access to the share.


NETSHARE.C

/*++

Copyright (c) 1995, 1996  Microsoft Corporation

Module Name:

    netshare.c

Abstract:

    This module illustrates how to use the Windows NT Lan Manager API
    in conjunction with the Win32 security API to create a new share
    on an arbitrary machine with permissions that grant an arbitrary
    user/group Full Access to the share.

Author:

    Scott Field (sfield)    01-Oct-95

--*/

#include <windows.h>
#include <lm.h>
#include <stdio.h>

#define RTN_OK 0
#define RTN_USAGE 1
#define RTN_ERROR 13

//
// Note: UNICODE entry point and argv.  This way, we don't need to bother
// with converting commandline args to Unicode
//

int
__cdecl
wmain(
    int argc,
    wchar_t *argv[]
    )
{
    LPWSTR DirectoryToShare;
    LPWSTR Sharename;
    LPWSTR Username;
    LPWSTR Server;

    PSID pSid = NULL;
    DWORD cbSid;

    WCHAR RefDomain[DNLEN + 1];
    DWORD cchDomain = DNLEN + 1;
    SID_NAME_USE peUse;

    SECURITY_DESCRIPTOR sd;
    PACL pDacl = NULL;
    DWORD dwAclSize;

    SHARE_INFO_502 si502;
    NET_API_STATUS nas;

    BOOL bSuccess = FALSE; // assume this function fails

    if(argc < 4) {
        printf("Usage: %ls <directory> <sharename> <user/group> [\\\\Server]\n", argv[0]);
        printf(" directory is fullpath of directory to share\n");
        printf(" sharename is name of share on server\n");
        printf(" user/group is an WinNT user/groupname (REDMOND\\sfield, Administrators, etc)\n");
        printf(" optional Server is the name of the computer to create the share on\n");
        printf("\nExample: %ls c:\\public public Everyone\n", argv[0]);
        printf("c:\\public shared as public granting Everyone full access\n");
        printf("\nExample: %ls c:\\private cool$ REDMOND\\sfield \\\\WINBASE\n", argv[0]);
        printf("c:\\private on \\\\WINBASE shared as cool$ (hidden) granting REDMOND\\sfield access\n");

        return RTN_USAGE;
    }

    //
    // since the commandline was Unicode, just provide pointers to
    // the relevant items
    //

    DirectoryToShare = argv[1];
    Sharename = argv[2];
    Username = argv[3];

    if( argc > 4 ) {
        Server = argv[4];
    } else {
        Server = NULL; // local machine
    }

    //
    // initial allocation attempt for Sid
    //
#define SID_SIZE 96
    cbSid = SID_SIZE;

    pSid = (PSID)HeapAlloc(GetProcessHeap(), 0, cbSid);
    if(pSid == NULL) {
        printf("HeapAlloc error!\n");
        return RTN_ERROR;
    }

    //
    // get the Sid associated with the supplied user/group name
    // force Unicode API since we always pass Unicode string
    //

    if(!LookupAccountNameW(
        NULL,       // default lookup logic
        Username,   // user/group of interest from commandline
        pSid,       // Sid buffer
        &cbSid,     // size of Sid
        RefDomain,  // Domain account found on (unused)
        &cchDomain, // size of domain in chars
        &peUse
        )) {

        //
        // if the buffer wasn't large enough, try again
        //

        if(GetLastError() == ERROR_INSUFFICIENT_BUFFER) {

            pSid = (PSID)HeapReAlloc(GetProcessHeap(), 0, pSid, cbSid);

            if(pSid == NULL) {
                printf("HeapReAlloc error!\n");
                goto cleanup;
            }

            cchDomain = DNLEN + 1;

            if(!LookupAccountNameW(
                NULL,       // default lookup logic
                Username,   // user/group of interest from commandline
                pSid,       // Sid buffer
                &cbSid,     // size of Sid
                RefDomain,  // Domain account found on (unused)
                &cchDomain, // size of domain in chars
                &peUse
                )) {
                    printf("LookupAccountName error! (rc=%lu)\n", GetLastError());
                    goto cleanup;
                }

        } else {
            printf("LookupAccountName error! (rc=%lu)\n", GetLastError());
            goto cleanup;
        }
    }

    //
    // compute size of new acl
    //

    dwAclSize = sizeof(ACL) +
        1 * ( sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) ) +
        GetLengthSid(pSid) ;

    //
    // allocate storage for Acl
    //

    pDacl = (PACL)HeapAlloc(GetProcessHeap(), 0, dwAclSize);
    if(pDacl == NULL) goto cleanup;

    if(!InitializeAcl(pDacl, dwAclSize, ACL_REVISION))
        goto cleanup;

    //
    // grant GENERIC_ALL (Full Control) access
    //

    if(!AddAccessAllowedAce(
        pDacl,
        ACL_REVISION,
        GENERIC_ALL,
        pSid
        )) goto cleanup;

    if(!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION))
        goto cleanup;

    if(!SetSecurityDescriptorDacl(&sd, TRUE, pDacl, FALSE)) {
        fprintf(stderr, "SetSecurityDescriptorDacl error! (rc=%lu)\n",
            GetLastError());
        goto cleanup;
    }

    //
    // setup share info structure
    //

    si502.shi502_netname = (LPTSTR) Sharename;
    si502.shi502_type = STYPE_DISKTREE;
    si502.shi502_remark = NULL;
    si502.shi502_permissions = 0;
    si502.shi502_max_uses = SHI_USES_UNLIMITED;
    si502.shi502_current_uses = 0;
    si502.shi502_path = (LPTSTR) DirectoryToShare;
    si502.shi502_passwd = NULL;
    si502.shi502_reserved = 0;
    si502.shi502_security_descriptor = &sd;

    nas = NetShareAdd(
        (LPTSTR) Server,         // share is on local machine
        502,            // info-level
        (LPBYTE)&si502, // info-buffer
        NULL            // don't bother with parm
        );

    if(nas != NO_ERROR) {
        printf("NetShareAdd error! (rc=%lu)\n", nas);
        goto cleanup;
    }

    bSuccess = TRUE; // indicate success

cleanup:

    //
    // free allocated resources
    //
    if(pDacl != NULL)
        HeapFree(GetProcessHeap(), 0, pDacl);

    if(pSid != NULL)
        HeapFree(GetProcessHeap(), 0, pSid);

    if(!bSuccess) {
        return RTN_ERROR;
    }

    return RTN_OK;
}


README.TXT

Networking (LAN Manager) API Sample

The NETAPI sample demonstrates how to create a user and a local group and
add the user to the local group under Windows NT.

The functions used include the ported LAN Manager functions NetGetDCName,
NetUserAdd, NetLocalGroupAdd, and NetLocalGroupAddMembers.


NETSMPLE.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1995 - 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.
\******************************************************************************/

/*
    Net API Example

    Create a user and a local group and add the user to the local group.

    This sample requires NT 3.51
*/

#define UNICODE 1
#include <windows.h>
#include <lmcons.h>
#include <lmaccess.h>
#include <lmerr.h>
#include <lmapibuf.h>
#include <stdio.h>
#include <stdlib.h>

int _CRTAPI1 main( int cArgs, char *pArgs[] );

NET_API_STATUS NetSample( LPWSTR lpszDomain,
                          LPWSTR lpszUser,
                          LPWSTR lpszPassword,
                          LPWSTR lpszLocalGroup )
{

    USER_INFO_1               user_info;
    LOCALGROUP_INFO_1         localgroup_info;
    LOCALGROUP_MEMBERS_INFO_3 localgroup_members;
    LPWSTR                    lpszPrimaryDC = NULL;
    NET_API_STATUS            err = 0;
    DWORD                     parm_err = 0;

/* First get the name of the Primary Domain Controller. */
/* Be sure to free the returned buffer */

    err = NetGetDCName( NULL,                        /* Local Machine */
                        lpszDomain,                  /* Domain Name */
                        (LPBYTE *)&lpszPrimaryDC );  /* returned PDC */

    if ( err != 0 )
    {
        printf( "Error getting DC name: %d\n", err );
        return( err );
    }

/* Set up the USER_INFO_1 struct */

    user_info.usri1_name = lpszUser;
    user_info.usri1_password = lpszPassword;
    user_info.usri1_priv = USER_PRIV_USER;
    user_info.usri1_home_dir = TEXT("");
    user_info.usri1_comment = TEXT("Sample User");
    user_info.usri1_flags = UF_SCRIPT;
    user_info.usri1_script_path = TEXT("");

    err = NetUserAdd( lpszPrimaryDC,       /* PDC name */
                      1,                   /* level */
                      (LPBYTE)&user_info,  /* input buffer */
                      &parm_err );         /* parameter in error */

    switch ( err )
    {
    case 0:
        printf("user successfully created.\n");
        break;
    case NERR_UserExists:
        printf("user already exists.\n");
        err = 0;
        break;
    case ERROR_INVALID_PARAMETER:
        printf("Invalid Parameter Error adding user: Parameter Index = %d\n",
                parm_err);
        NetApiBufferFree( lpszPrimaryDC );
        return( err );
    default:
        printf("Error adding user: %d\n", err);
        NetApiBufferFree( lpszPrimaryDC );
        return( err );
    }

/* Set up the LOCALGROUP_INFO_1 struct */

    localgroup_info.lgrpi1_name = lpszLocalGroup;
    localgroup_info.lgrpi1_comment = TEXT("Sample Local group.");

    err = NetLocalGroupAdd( lpszPrimaryDC,              /* PDC name */
                            1,                          /* level */
                            (LPBYTE)&localgroup_info,   /* input buffer */
                            &parm_err );                /* parm in error */

    switch ( err )
    {
    case 0:
        printf("Local Group successfully created.\n");
        break;
    case ERROR_ALIAS_EXISTS:
        printf("Local Group already exists.\n");
        err = 0;
        break;
    case ERROR_INVALID_PARAMETER:
        printf("Invalid Parameter Error adding Local Group: Parameter Index = %d\n",
                err, parm_err);
        NetApiBufferFree( lpszPrimaryDC );
        return( err );
    default:
        printf("Error adding Local Group: %d\n", err);
        NetApiBufferFree( lpszPrimaryDC );
        return( err );
    }

/* Now add the user to the local group */

    localgroup_members.lgrmi3_domainandname = lpszUser;

    err = NetLocalGroupAddMembers( lpszPrimaryDC,        /* PDC name */
                                   lpszLocalGroup,       /* group name */
                                   3,                    /* passing in name */
                                   (LPBYTE)&localgroup_members, /* Buffer */
                                   1 );                  /* count passed in */

    switch ( err )
    {
    case 0:
        printf("User successfully added to Local Group.\n");
        break;
    case ERROR_MEMBER_IN_ALIAS:
        printf("User already in Local Group.\n");
        err = 0;
        break;
    default:
        printf("Error adding User to Local Group: %d\n", err);
        break;
    }

    NetApiBufferFree( lpszPrimaryDC );
    return( err );

}

int _CRTAPI1 main( int    cArgs,
                   char * pArgs[] )
{
    NET_API_STATUS err = 0;

    printf( "Calling NetSample.\n" );
    err = NetSample( TEXT("SampleDomain"),
                     TEXT("SampleUser"),
                     TEXT("SamplePswd"),
                     TEXT("SampleLG") );
    printf( "NetSample returned %d\n", err );
    return( 0 );
}


README.TXT

Networking (LAN Manager) API Sample

This module illustrates how to use a Null session to overcome access
problems during network related query operations.

One example of the scenario this approach addresses is as follows:

User logs onto workstation A as the local administrator.

Administrator tries to query user information using the NetUserGetInfo()
API call on server B.  This call fails with ERROR_ACCESS_DENIED.

The reason this problem occurs is that the administrator password on
workstation A does not match the administrator password on server B.

During the network query operation, the default behavior is to establish
a connection to the remote server using the credentials of the logged-in
user.  In some scenarios, this behavior is not appropriate, and the
solution is to establish a connection using either known credentials,
or the Null credentials.  Null credentials are suitable for most query
operations against a remote machine.  However, if administrator
related actions are necessary, it is necessary to supply credentials
which have administrative privilege on the remote machine.  Valid
credentials consist of a username, password, and optional domain name.

Establishing a connection in this manner requires that no existing
connections exist to the remote machine tied to the current logon
session.

Note: Null sessions are those where the user credentials passed in the
session setup SMB are null.  This sample only implements a function to
establish a Null session, rather than a session with specific credentials.

Processes and Services running in the Local System account security context
have Null credentials by default, so establishing Null sessions in this
scenario is not required.

Applications that run only on Windows NT 4.0 and above can use
WNetAddConnection2() rather than NetUseAdd() to establish a Null session.


NULLSESS.C

/*++

Copyright 1996 - 1997 Microsoft Corporation

Module Name:

    nullsess.c

Abstract:

    This module illustrates how to use a Null session to overcome access
    problems during network related query operations.

    One example of the scenario this approach addresses is as follows:

    User logs onto workstation A as the local administrator.

    Administrator tries to query user information using the NetUserGetInfo()
    API call on server B.  This call fails with ERROR_ACCESS_DENIED.

    The reason this problem occurs is that the administrator password on
    workstation A does not match the administrator password on server B.

    During the network query operation, the default behavior is to establish
    a connection to the remote server using the credentials of the logged-in
    user.  In some scenarios, this behavior is not appropriate, and the
    solution is to establish a connection using either known credentials,
    or the Null credentials.  Null credentials are suitable for most query
    operations against a remote machine.  However, if administrator
    related actions are necessary, it is necessary to supply credentials
    which have administrative privilege on the remote machine.  Valid
    credentials consist of a username, password, and optional domain name.

    Establishing a connection in this manner requires that no existing
    connections exist to the remote machine tied to the current logon
    session.

    Note: Null sessions are those where the user credentials passed in the
    session setup SMB are null.  This sample only implements a function to
    establish a Null session, rather than a session with specific credentials.

    Processes and Services running in the Local System account security context
    have Null credentials by default, so establishing Null sessions in this
    scenario is not required.

    Applications that run only on Windows NT 4.0 and above can use
    WNetAddConnection2() rather than NetUseAdd() to establish a Null session.

Author:

    Scott Field (sfield)    13-Jun-96

--*/

#include <windows.h>
#include <lm.h>

#include <stdio.h>

BOOL
EstablishNullSession(
    LPCWSTR Server,
    BOOL bEstablish
    );

#define RTN_OK 0
#define RTN_USAGE 1
#define RTN_ERROR 13

int
__cdecl
wmain(
    int argc,
    wchar_t *argv[]
    )
{

    if(argc != 2) {
        printf("Usage: %ls <\\\\Server>\n", argv[0]);
        return RTN_USAGE;
    }

    //
    // try network operation here.  If this fails with an access denied
    // error message, retry the operation using a Null session.
    //

    //
    // establish a session to the target machine with Null credentials
    //

    if(EstablishNullSession( argv[1], TRUE )) {

        //
        // retry network related operation here.
        //

        //
        // break the existing connection we made
        //

        EstablishNullSession( argv[1], FALSE );
    } else {

        //
        // error occurred establishing Null session
        //

        printf("Error establishing Null session! (rc=%lu)\n", GetLastError());
        return RTN_ERROR;
    }

    return RTN_OK;
}

BOOL
EstablishNullSession(
    LPCWSTR Server,
    BOOL bEstablish
    )
{
    LPCWSTR szIpc = L"\\IPC$";
    WCHAR RemoteResource[UNCLEN + 5 + 1]; // UNC len + \IPC$ + NULL
    DWORD cchServer;

    NET_API_STATUS nas;

    //
    // do not allow NULL or empty server name
    //

    if(Server == NULL || *Server == L'\0') {
        SetLastError(ERROR_INVALID_COMPUTERNAME);
        return FALSE;
    }

    cchServer = lstrlenW( Server );

    if(Server[0] != L'\\' && Server[1] != L'\\') {

        //
        // prepend slashes and NULL terminate
        //

        RemoteResource[0] = L'\\';
        RemoteResource[1] = L'\\';
        RemoteResource[2] = L'\0';
    }
    else {
        cchServer -= 2; // drop slashes from count

        RemoteResource[0] = L'\0';
    }

    if(cchServer > CNLEN) {
        SetLastError(ERROR_INVALID_COMPUTERNAME);
        return FALSE;
    }

    if(lstrcatW(RemoteResource, Server) == NULL) return FALSE;
    if(lstrcatW(RemoteResource, szIpc) == NULL) return FALSE;

    //
    // disconnect or connect to the resource, based on bEstablish
    //

    if(bEstablish) {
        USE_INFO_2 ui2;

        ZeroMemory(&ui2, sizeof(ui2));

        ui2.ui2_local = NULL;
        ui2.ui2_remote = (LPTSTR) RemoteResource;
        ui2.ui2_asg_type = USE_IPC;
        ui2.ui2_password = ui2.ui2_username = ui2.ui2_domainname = (LPTSTR) L"";

        nas = NetUseAdd(NULL, 2, (LPBYTE)&ui2, NULL);
    }
    else {
        nas = NetUseDel(NULL, (LPTSTR) RemoteResource, 0);
    }

    if( nas == NERR_Success ) return TRUE; // indicate success

    SetLastError( nas );

    return FALSE;
}


README.TXT

Windows Sockets samples

appletalk   -- using the appletalk PAP protocol.
bcast       -- Broadcast over UDP or IPX with SO_BROADCAST
dt_dll      -- Debug Trace feature of checked build of ws2_32.dll
globchat    -- client/server chat program
iocomplt    -- Demonstrate the use of I/O Completion ports
ipxchat     -- GUI chat sample showing Windows Sockets over IPX
layered     -- Layered WinSock2 service provider
nullprov    -- NULL service provider, and sample install code
nwlink      -- using sockets over SPX.
overlap     -- overlapped I/O + AcceptEx
ping        -- demonstrates raw sockets
rnr         -- registration and name resolution
simple      -- simple client, server, and IOCTL example
sockspx     -- simple IPX sample, including enumeration of local adapters
tp4         -- using the OSI tp4 protocol, requires special run-time setup
ws2chat     -- Windows Sockets 2 sample chat application
wsock       -- GUID tutorial, includes demonstration of AcceptEx

Several of the samples demonstrate Windows Sockets 1 functionality.  They
are included here because they show good, general purpose techniques, and
because they demonstrate that building a Windows Sockets 2 application is
often as simple as re-compiling and re-linking.


README.TXT

AppleTalk PAP Protocol Sample

The PRPERF sample is a Windows Socket application that demonstrates how to
use the AppleTalk PAP Protocol. Both a server and a client application are
included in the source for this sample. The client application reads a file
and sends the contents to the server application. The server writes the
bytes received to another file.


PRPERF.C

/*++

Copyright 1994 - 1998 Microsoft Corporation

Module Name: prperf.c

Abstract: This Sample Windows Socket Application demonstrates use of
          the AppleTalk PAP Protocol. Both a server and a client
          application are included in this source file. The client
          app reads a file and sends the contents to the server. The
          server writes out the received bytes to another file.

--*/

#include <windows.h>

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <time.h>

//
// the following header contains appletalk specific definitions.
// should be included for AppleTalk Windows Socket Applications
//
#include <atalkwsh.h>
#include "prsamp.h"

#define SOCKET_COUNT                    32

#define DEF_SERVER_NAME                 "Windows Adsp"
#define DEF_SERVER_TYPE                 "Windows Sockets"
#define DEF_SERVER_ZONE                 "*"
#define BLOCKSIZE                       4096

#define DEF_QUEUE_LEN                   5
#define ENTITY_LEN                      32

CHAR    gServerName[ENTITY_LEN]         = DEF_SERVER_NAME;
CHAR    gServerType[ENTITY_LEN]         = DEF_SERVER_TYPE;
CHAR    gServerZone[ENTITY_LEN]         = DEF_SERVER_ZONE;
CHAR    gProtocolName[10]               = "ADSP";
INT     NumTimes                        = 5;
CHAR    gFileName[256];

LPSTR   pServerName   = gServerName;
LPSTR   pServerType   = gServerType;
LPSTR   pServerZone   = gServerZone;
LPSTR   pProtocolName = gProtocolName;
LPSTR   pFileName     = gFileName;

HANDLE  gFileHandle;
WSADATA WsaData;

SYSTEMTIME    SystemStartTime;
SYSTEMTIME    SystemEndTime;
FILETIME      FileTime;

BOOL fFileSpecified = FALSE;
BOOL fVerify = FALSE;
BOOL fRandom = FALSE;
BOOL fRDM    = FALSE;

int loop = 1, looptot;
double Throughput, AvgThroughPut, MinThroughPut, MaxThroughPut;

CHAR DataString[] = {"0123456789abcdefghijklmnopqrstuvwxyz~!@#$%^&*()_+"};

#define NANOPOWER    1000000000

//#define DEBUG

typedef struct Sock_Info {
   int     sock_typ;
   int     prototyp;
   SOCKET  nsock;
} *PTEST_INFO, test_info;

_cdecl main (IN SHORT argc, IN PSZ argv[])
{

   register char p;
   register char *ptr;
   int i, r;
   BOOL fClient = FALSE;
   BOOL fServer = FALSE;

   for(i = 1; i < argc; i++)
   {
       p = argv[i][0];

       if (p == '/' || p == '-')          // option string
       {
           p = argv[i][1];

           switch(tolower(p))
           {
               case 'n':
                   ptr = argv[i]+2;
                   if(*ptr == '\0')
                       usage();
                   while(*pServerName++ = *ptr++);
                   break;
               case 't':
                   ptr = argv[i]+2;
                   if(*ptr == '\0')
                       usage();
                   while(*pServerType++ = *ptr++);
                   break;
               case 'z':
                   ptr = argv[i]+2;
                   if(*ptr == '\0')
                       usage();
                   while(*pServerZone++ = *ptr++);
                   break;
               case 's':
                   ptr = argv[i]+2;
                   if(*ptr != '\0')
                       usage();
                   if(fClient == TRUE)
                       usage();
                   fServer = TRUE;
                   break;
               case 'c':
                   ptr = argv[i]+2;
                   if(*ptr != '\0')
                       usage();
                   if(fServer == TRUE)
                       usage();
                   fClient = TRUE;
                   break;
               case 'p':
                   ptr = argv[i]+2;
                   if(*ptr == '\0')
                       usage();
                   while(*pProtocolName++ = *ptr++);
                   break;
               case 'b':
                   ptr=argv[i]+2;
                   if(*ptr == '\0')
                       usage();
                   NumTimes = atoi(ptr);
                   break;
               case 'f':
                   ptr = argv[i]+2;
                   if(*ptr == '\0')
                       usage();
                   while(*pFileName++ = *ptr++);
                   fFileSpecified = TRUE;
                   break;
               case 'l':
                   ptr = argv[i]+2;
                   if(*ptr == '\0')
                       usage();
                   loop = atoi(ptr);
                   break;
               case 'v':
                   ptr = argv[i]+2;
                   if(*ptr != '\0')
                       usage();
                   fVerify = TRUE;
                   break;
               case 'r':
                   ptr = argv[i]+2;
                   if(*ptr != '\0')
                       usage();
                   fRandom = TRUE;
                   break;
               case 'y':
                   ptr = argv[i]+2;
                   if(*ptr != '\0')
                       usage();
                   fRDM = TRUE;
                   break;
               case '?':
               default:
                   usage();

           }
       }
       else
       {
           usage();
       }

   }

#ifdef DEBUG
   printf("Server Name %s\n", gServerName);
   printf("Server Type %s\n", gServerType);
   printf("Server Zone %s\n", gServerZone);
#endif
   printf("%s throughput measurement\n", gProtocolName);

   if(stricmp(gProtocolName, "ADSP") && stricmp(gProtocolName, "PAP"))
   {
       printf("Invalid Protocol Specified\n");
       exit(1);
   }

   if(!(fClient || fServer))
       usage();

   r = WSAStartup(0x0101, &WsaData);
   if (r == SOCKET_ERROR)
   {
       printf("Startup failed!\n");
       WSACleanup();
       return(0);
   }

   if(fClient)
   {
       BOOL DClnt;

       looptot = 0;
       MaxThroughPut = 0;
       AvgThroughPut = 0;
       if(fFileSpecified)
           printf("Ignoring -f option for client.\n");
       while (looptot < loop)
       {
           DClnt = DoClient();
           if(DClnt)
           {
               if ((looptot == 0) || (MinThroughPut > Throughput))
                   MinThroughPut = Throughput;

               if (MaxThroughPut < Throughput)
                   MaxThroughPut = Throughput;

               AvgThroughPut = ((AvgThroughPut * looptot) + Throughput)
                               / (looptot+1);
               printf("Loop %2d/%d Throughput Cur/Min/Max/Ave :", ++looptot, loop);
               printf(" %6.2f/%6.2f/%6.2f/%6.2f\n\n\n", Throughput,
                                                        MinThroughPut,
                                                        MaxThroughPut,
                                                        AvgThroughPut);
               Throughput = 0.0;
           }
           else
               break;
       }
   }

   if(fServer)
       DoServer();

   WSACleanup();
   return(0);
}

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

 Function - DoServer()

 Calling Function - main();

 Comments - This section of code calls the Server side of the application.

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

BOOL DoServer()
{
   BOOL   fRet = FALSE;

   do
   {

//
// register the name specified on the command line  or use the
// defaults.
//

       fRet = OpenSocketRegisterAndListen();

   }while(FALSE);

   return(fRet);

}

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

 Function - OpenSocketRegisterAndListen()

 Calling Function - DoServer();

 Comments - This section of code controls the Server side of the application.

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

BOOL OpenSocketRegisterAndListen()
{

   int                  r = 0;         // return from socket calls
   int                  pass_no = 1;
   BOOL                 fRet = TRUE;
   SOCKET               sock, newsock; // SOCKET descriptor
   SOCKADDR_AT          atalkaddr;     // See atalkwsh.h
   WSH_REGISTER_NAME    nbpname;       // structure for registering NBP name
                                       // see atalkwsh.h
   fd_set               readfds;       // fd_set strutture for select call
   int                  addrlen;
   PTEST_INFO           test;
   int                  sockettype = SOCK_STREAM;
   int                  protocoltype = ATPROTO_ADSP;
   int                  NumBytesRecvd = 0;
   unsigned long        ThreadId;

//
// open a appletalk socket. The protocol family should be AF_APPLETALK,
// the socket type can be SOCK_STREAM or SOCK_RDM, and the ProtocolType
// for PAP socket must be ATPROTO_PAP. Note that opening a socket does
// not yet create an endpoint on the AppleTalk Protocol. A bind must
// happen before this socket can be used with AppleTalk;
//

   do
   {
       if(!stricmp(gProtocolName, "PAP"))
       {
           sockettype = SOCK_RDM;
           protocoltype = ATPROTO_PAP;
       }
       else if (fRDM)
           sockettype = SOCK_RDM;

       sock = socket(AF_APPLETALK, sockettype, protocoltype);

       if(sock == INVALID_SOCKET)
       {
           printf("Open Socket: Error = %ld\n", WSAGetLastError());
           r = -1;
           break;
       }

       atalkaddr.sat_socket = 0;
       atalkaddr.sat_family = AF_APPLETALK;

       r = bind(sock, (struct sockaddr *)&atalkaddr, sizeof(SOCKADDR_AT));

       if(r < 0)
       {
           printf("Bind:Error = %d\n", WSAGetLastError());
           break;
       }

       //
       // end point has now been created on the AppleTalk Protocol
       // now register the name that the client will look up.
       //

       strcpy(nbpname.ObjectName, gServerName);
       nbpname.ObjectNameLen = strlen(gServerName);

       strcpy(nbpname.TypeName, gServerType);
       nbpname.TypeNameLen = strlen(gServerType);

       strcpy(nbpname.ZoneName, "*");
       nbpname.ZoneNameLen = strlen("*");

       printf("Registering %s:%s@%s\n", nbpname.ObjectName,
                                        nbpname.TypeName,
                                        nbpname.ZoneName);

       r = setsockopt(sock,                         // socket descriptor
                     SOL_APPLETALK,                 // socket level - always SOL_APPLETALK
                     SO_REGISTER_NAME,              // socket option
                     (char *)&nbpname,              // nbp name structure
                     sizeof(WSH_NBP_NAME));         // size of nbp name structure

       if(r < 0)
       {
           printf("Register Name: Error = %d\n", WSAGetLastError());
           break;
       }

   }while(FALSE);

   if(r == SOCKET_ERROR)
   {
       closesocket(sock);
       return(FALSE);
   }

//
// Post a listen on this socket. The default queue length is 5
//

   r =  listen(sock, DEF_QUEUE_LEN);

   if(r < 0)
   {
       printf("Listen:Error = %d\n", WSAGetLastError());
       return FALSE;
   }

//
// listen is successful. select the socket for reading
//

   do
   {

       FD_ZERO(&readfds);            // clear set
       FD_SET(sock, &readfds);       // add sock to set

//
// this is a blocking select. Select will complete when
// a client connects.
//

       r = select(0, &readfds, NULL, NULL, NULL);

       if(r == SOCKET_ERROR)
       {
           printf("Select: Error = %d\n", WSAGetLastError());
           fRet = FALSE;
           break;
       }

       if ( r == 0)
       {
           printf("Select: no sockets available\n");
           fRet = FALSE;
           break;
       }

//
// Accept an incoming request.
//

       addrlen = sizeof(SOCKADDR_AT);
       newsock = accept(sock, (struct sockaddr *)&atalkaddr, &addrlen);

       if(newsock == INVALID_SOCKET)
       {
           printf("Accept: Socket Error = %d\n", WSAGetLastError());

           fRet = FALSE;
           break;
       }

#ifdef DEBUG
//       printf("Accept Succeeded\n");
#endif

       if(r == SOCKET_ERROR)
       {
           fRet = FALSE;
           break;
       }

       test = (PTEST_INFO) LocalAlloc(LPTR, sizeof(test_info));
       test->sock_typ = sockettype;
       test->prototyp = protocoltype;
       test->nsock = newsock;

       if( ! CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)PerfThread,
                            (PTEST_INFO) test, 0, & ThreadId ) )
       {
           fprintf(stderr,"CreateThread NULL 0 PerfThread %d 0 %d 0 %lx",
                        test->sock_typ, test->prototyp, (unsigned long) & ThreadId);
           exit(1);
        }

       if (fRet == FALSE)
           break;

   }while (TRUE);

   closesocket(sock);

   //
   // send all done
   //

   return(TRUE);

}

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

 Function - PerfThread()

 Calling Function - OpenSocketRegisterAndListen();

 Comments - This section of code controls the Server side Recieve and Verification
            for the application.

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

unsigned long PerfThread( PTEST_INFO lpvTest )
{
   int     r;                          // return from socket calls
   int     recvflags = 0;              // see WSARecvEx call
   CHAR    recvbuf[4096];              // recv buffer
   long    blockmode = 1;              // see ioctlsocket call
   BOOL    fRet = TRUE;
   int     NumBytesRecvd = 0, pass_no = 1;
   SOCKET  newsock = lpvTest->nsock;   // SOCKET descriptor
   int     sockettype = lpvTest->sock_typ;
   int     protocoltype = lpvTest->prototyp;

   //
   // make socket non blocking. As far as possible, use non
   // blocking sockets to improve performance of app.
   //
   //

   r = ioctlsocket(newsock, FIONBIO, &blockmode);

   if( r == SOCKET_ERROR)
   {
       printf("ioctlsocket: error = %d\n", WSAGetLastError());
       fRet = FALSE;
       return(FALSE);
   }

   do
   {

       //
       // Prime a Read here. This will enable the receive to complete
       // This is PAP specific. For other AppleTalk protocols -
       // ADSP and DDP a recv can be posted directly
       //

       if(!stricmp(gProtocolName, "PAP"))
       {

           r = setsockopt(newsock,
                          SOL_APPLETALK,
                          SO_PAP_PRIME_READ,
                          recvbuf,
                          sizeof(recvbuf));

           if( r < 0)
           {
               printf("primeread: error = %d\n", WSAGetLastError());
               fRet = FALSE;
               break;

           }
       }

       fRet = CheckForRecv(newsock);

       if(fRet == FALSE)
           break;

       r = WSARecvEx(newsock,recvbuf,sizeof(recvbuf), &recvflags);

       if ((sockettype == SOCK_RDM) && (r < sizeof(recvbuf)))
       {
           printf("Did not receive entire message\n");
           fRet = FALSE;
           break;
       }

       if( r == SOCKET_ERROR)
       {

           if (WSAGetLastError() == WSAEWOULDBLOCK)
               continue;
           else
           {
               if(WSAGetLastError() == WSAENOTCONN || WSAGetLastError() == WSAEFAULT)
                   break;
               printf("recv: error = %d\n", WSAGetLastError());
               fRet = FALSE;
               break;
           }

       }

#ifdef DEBUG
//       printf("Recvd %d bytes\n", r);
#endif

       if (fVerify)
       {
           int rr, pos = 0;

           do
           {
               fRet = CheckForSend(newsock);

               if (fRet == FALSE)
               {
                   printf("send select error\n");
                   break;
               }

               rr = send(newsock, &recvbuf[pos], r-pos, 0);

               if ( rr < 0 )
               {
                   if(WSAGetLastError() == WSAEWOULDBLOCK)
                   {
                       printf("Send: error = WSAEWOULDBLOCK\n");
                       continue;
                   }
                   else
                   {
                       printf("send: error = %d\n", WSAGetLastError());
                       break;
                   }

               }

#ifdef DEBUG
//           printf("Bytes Sent %d\n", rr);
#endif

               pos += rr;

               if (pos >= r)
                   break;

           } while(TRUE);

       }  // endif Verify

       NumBytesRecvd += r;

       //
       // reset the recv flags for the next WSARecvEx
       //
       recvflags = 0;

   }while(TRUE);

   printf("Total Number of Bytes Received = %d \n",NumBytesRecvd);

   closesocket(newsock);
   LocalFree(lpvTest);

   return(TRUE);
}

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

 Function - DoClient()

 Calling Function - main();

 Comments - This section of code controls the Client side of the application.

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

BOOL DoClient()
{
   int                  r = 0;             // return code
   BOOL                 fRet = FALSE;      //
   int                  charcnt;           // count of bytes read from file
   CHAR                 LookupBuffer[512]; // LookUp results return buffer
   PCHAR                ptupleBuffer;
   PWSH_LOOKUP_NAME     pLookup;
   PWSH_NBP_TUPLE       pTempTuple;
   SOCKADDR_AT          ataddress;
   DWORD                WrittenSize;
   SOCKET               clntsock;
   long                 blockmode = 1;          // for ioctlsocket
   int                  BytesTransferred = 0;
   HLOCAL               memhandle;
   LPSTR                DataPtr, DataStartPtr, DataEndPtr;
   DWORD                hourdiff;          //, minutediff, seconddiff;
   double               StartSecond, EndSecond,ElapsedSeconds;
   int                  sockettype = SOCK_STREAM;
   int                  prototype = ATPROTO_ADSP;
   int                  i, rndbuf;

   if(!stricmp(gProtocolName, "PAP"))
   {
       sockettype = SOCK_RDM;
       prototype = ATPROTO_PAP;
   }
   else if (fRDM)
       sockettype = SOCK_RDM;

   if (fRandom)
       rndbuf = rand() % strlen(DataString) + 1;
   else
       rndbuf = strlen(DataString);

   do
   {
       //
       // See socket call in OpenSocketRegisterAndListen for desc
       //
       clntsock = socket(AF_APPLETALK, sockettype, prototype);

       if(clntsock == INVALID_SOCKET)
       {
           printf("Open Socket: Error = %ld\n", WSAGetLastError());
           r = -1;
           break;

       }

       ataddress.sat_socket = 0;
       ataddress.sat_family = AF_APPLETALK;

       r = bind(clntsock, (struct sockaddr *)&ataddress, sizeof(SOCKADDR_AT));

       if(r < 0)
       {
           printf("Bind:Error = %d\n", WSAGetLastError());
           break;
       }

       //
       // end point has now been created on the AppleTalk Protocol.
       // lookup the server name.
       //

       pLookup = (PWSH_LOOKUP_NAME)LookupBuffer;

       strcpy(pLookup->LookupTuple.NbpName.ObjectName, gServerName);
       pLookup->LookupTuple.NbpName.ObjectNameLen = strlen(gServerName);

       strcpy(pLookup->LookupTuple.NbpName.TypeName,  gServerType);
       pLookup->LookupTuple.NbpName.TypeNameLen = strlen(gServerType);

       strcpy(pLookup->LookupTuple.NbpName.ZoneName, gServerZone);
       pLookup->LookupTuple.NbpName.ZoneNameLen = strlen(gServerZone);

#ifdef DEBUG
       printf("Looking up %s:%s@%s\n",pLookup->LookupTuple.NbpName.ObjectName,
                                      pLookup->LookupTuple.NbpName.TypeName,
                                      pLookup->LookupTuple.NbpName.ZoneName);
#endif

       WrittenSize = sizeof(LookupBuffer);

       r = getsockopt(clntsock, SOL_APPLETALK, SO_LOOKUP_NAME,
                      (char*)LookupBuffer,
                      &WrittenSize);

       if(r != NO_ERROR)
       {
           printf("getsockopt:error = %d\n", WSAGetLastError());
           break;

       }

       if (pLookup->NoTuples <= 0)
       {
           printf("LookupName failed - no tuples found\n");
           r=-1;
           break;
       }

       ptupleBuffer = (char *)pLookup+sizeof(WSH_LOOKUP_NAME);
       pTempTuple = (PWSH_NBP_TUPLE)ptupleBuffer;

       ataddress.sat_family = AF_APPLETALK;
       ataddress.sat_net = pTempTuple[0].Address.Network;
       ataddress.sat_node = pTempTuple[0].Address.Node;
       ataddress.sat_socket = pTempTuple[0].Address.Socket;

#ifdef DEBUG
       printf("server address = %lx.%lx.%lx.\n", ataddress.sat_net,
                                                 ataddress.sat_node,
                                                 ataddress.sat_socket);
#endif

       //
       // lookup succeeded. Use the address in ataddress to connect to the
       // server
       //

       r =  connect(clntsock, (struct sockaddr *)&ataddress, sizeof(SOCKADDR_AT));

       if(r < 0)
       {
           printf("connect: error = %d\n", WSAGetLastError());
           break;
       }

#ifdef DEBUG
//       printf("Connect Succeeded\n");
#endif

   }while(FALSE);

   if(r < 0)
   {
       closesocket(clntsock);
       return(FALSE);
   }

   //
   //  Set Socket to non blocking mode
   //

   r = ioctlsocket(clntsock, FIONBIO, &blockmode);

#ifdef DEBUG
//       printf("ioctlsocket Succeeded\n");
#endif

   if( r == SOCKET_ERROR)
   {
       printf("ioctlsocket: error = %d\n", WSAGetLastError());
       return FALSE;
   }

   //
   // Fill Up Send Buffer with Data
   //
   memhandle = LocalAlloc(LPTR, BLOCKSIZE);
   if(memhandle == NULL)
   {
       printf("LocalAlloc Failed %d\n", GetLastError());
       return(FALSE);
   }
   DataPtr = LocalLock(memhandle);
   DataStartPtr = DataPtr;
   DataEndPtr = DataPtr + LocalSize(memhandle);

   //
   // fill the buffer with Data
   //
   while((DataStartPtr + rndbuf) <= DataEndPtr)
   {
       memcpy(DataStartPtr, DataString,rndbuf);
       DataStartPtr+= rndbuf;
   }

   if(DataStartPtr != DataEndPtr)
       memcpy(DataStartPtr, DataString, DataEndPtr-DataStartPtr-1);

   *DataEndPtr = '\0';

   fRet = CheckForSend(clntsock);

   if(fRet == FALSE)
       return FALSE;

   GetLocalTime(&SystemStartTime);

#ifdef DEBUG
   printf("Data Size = %d\n", lstrlen(DataPtr));
//   printf("Base Data sent: %s\n",DataStartPtr);
   printf("Start Time:%d:%2d:%2d:%2d\t", SystemStartTime.wHour,
                                         SystemStartTime.wMinute,
                                         SystemStartTime.wSecond,
                                         SystemStartTime.wMilliseconds);
#endif

   for( i= 0; i < NumTimes; i++)
   {
       //
       // Can I send - CheckForSend calls select to find if we can send
       // without blocking
       //
       DataStartPtr = DataPtr;

       do
       {
           fRet = CheckForSend(clntsock);

           if(fRet == FALSE)
           {
               printf("send select error\n");
               break;
           }

           charcnt = DataEndPtr - DataStartPtr;

           if(charcnt == 0)
               break;

           r = send(clntsock, DataStartPtr, charcnt, 0);

           if ( r < 0 )
           {

               if(WSAGetLastError() == WSAEWOULDBLOCK)
               {
                   printf("send: wouldblock\n");
                   continue;
               }
               else
               {
                   printf("send: error = %d\n", WSAGetLastError());
                   fRet = FALSE;
                   break;
               }

           }
           else
           {

#ifdef DEBUG
//               printf("Sent %d bytes\n",r);
#endif

               if ((fVerify) && (DataStartPtr + r == DataEndPtr))
               {
                   CHAR buffer[4096];
                   int recvflag = 0;
                   int rr, pos = 0;

           //
           // Prime a Read here. This will enable the receive to complete
           // This is PAP specific. For other AppleTalk protocols -
           // ADSP and DDP a recv can be posted directly
           //

                   if(!stricmp(gProtocolName, "PAP"))
                   {

                       rr = setsockopt(clntsock,
                                      SOL_APPLETALK,
                                      SO_PAP_PRIME_READ,
                                      buffer,
                                      sizeof(buffer));

                       if( rr < 0)
                       {
                           printf("primeread: error = %d\n", WSAGetLastError());
                           fRet = FALSE;
                           break;
                       }
                   }

                   do
                   {

                       fRet = CheckForRecv(clntsock);

                       if(fRet == FALSE)
                           break;

                       rr = WSARecvEx(clntsock, &buffer[pos], sizeof(buffer)-pos, &recvflag);

                       if( rr == SOCKET_ERROR)
                       {
                           if (WSAGetLastError() == WSAEWOULDBLOCK)
                               continue;
                           else
                           {
                               printf("recv: error = %d\n", WSAGetLastError());
                               fRet = FALSE;
                               break;
                           }
                       }

                       pos += rr;

#ifdef DEBUG
//                       printf("Recv'd %d bytes\n",rr);
#endif

                       if ((recvflag == 0) || (sockettype == SOCK_RDM) ||
                           (pos == sizeof(buffer)))
                           break;
                       recvflag = 0;

                   } while(TRUE);

                   if ((memcmp(DataPtr, buffer, 4096) != 0) && (fRet != FALSE))
                   {
                       printf("Strings do not compare\n");
#ifdef DEBUG
//                       printf("Data sent: \n%s\n",DataPtr);
//                       printf("Data recv'd: \n%s\n",buffer);
#endif

                   }

               } // IF (fVerify)

               BytesTransferred += r;
               DataStartPtr += r;

               if(DataStartPtr == DataEndPtr)
                   break;

           } // IF send() PASSED

       }while(TRUE);

       if(fRet == FALSE)
           break;

   } // FOR

   GetLocalTime(&SystemEndTime);

#ifdef DEBUG
   printf("End Time:%d:%2d:%2d:%2d\t", SystemEndTime.wHour,
                                       SystemEndTime.wMinute,
                                       SystemEndTime.wSecond,
                                       SystemEndTime.wMilliseconds);
#endif

   //
   // calculate the difference
   //

   hourdiff = SystemEndTime.wHour - SystemStartTime.wHour;

   StartSecond = (SystemStartTime.wHour * 3600) +
                 (SystemStartTime.wMinute * 60) +
                 SystemStartTime.wSecond +
                 (SystemStartTime.wMilliseconds * 0.001);

   EndSecond = 0;

   if(SystemEndTime.wMonth != SystemStartTime.wMonth)
   {
       EndSecond = (SystemEndTime.wDay * 24) * 3600;
       switch (SystemStartTime.wMonth)
       {
           case 1:
           case 3:
           case 5:
           case 7:
           case 8:
           case 10:
           case 12:
                   EndSecond = ((31 - SystemStartTime.wDay) * 24) + EndSecond;
                   break;
           case 4:
           case 6:
           case 9:
           case 11:
                   EndSecond = ((30 - SystemStartTime.wDay) * 24) + EndSecond;
                   break;
           case 2:
                   if((SystemStartTime.wYear % 400 == 0) ||

((SystemStartTime.wYear % 4 == 0) &&
                       (SystemStartTime.wYear % 100 != 0)))
                       EndSecond = ((29 - SystemStartTime.wDay) *
                                     24) + EndSecond;
                   else
                       EndSecond = ((28 - SystemStartTime.wDay) *
                                     24) + EndSecond;
                   break;
       }
   }
   else
       if(SystemEndTime.wDay > SystemStartTime.wDay)
       {
           EndSecond = ((SystemEndTime.wDay - SystemStartTime.wDay) * 24) * 3600;
       }

   EndSecond = EndSecond + (SystemEndTime.wHour * 3600) +
               (SystemEndTime.wMinute * 60) + SystemEndTime.wSecond +
               (SystemEndTime.wMilliseconds * 0.001);

   ElapsedSeconds = EndSecond-StartSecond;

#ifdef DEBUG
   printf("Elapsed Time (secs) = %6.3f\n", ElapsedSeconds);
   printf("Bytes Transferred = %ld\n", BytesTransferred);
   printf("Send All Done\n");
#endif

   if(ElapsedSeconds !=0)
   {
       Throughput = (BytesTransferred)/(ElapsedSeconds) ;

#ifdef DEBUG
       printf("Throughput (bytes/sec) = %6.2f\n", Throughput);
#endif

   }

   printf("\n");

   closesocket(clntsock);

   LocalFree(memhandle);
   return(TRUE);

}

BOOL CheckForSend(SOCKET s)
{

   fd_set writefds;
   int r;

#ifdef DEBUG
   printf("\t\tChecking for Send..\n");
#endif

   FD_ZERO(&writefds);
   FD_SET(s, &writefds);

   //
   // indefinite wait select
   //
   r =  select(0, NULL, &writefds, NULL, NULL);

   if( r != 1)
   {
       printf("select:error = %d\n", WSAGetLastError());
       return(FALSE);
   }
   return(TRUE);
}

BOOL CheckForRecv(SOCKET s)
{

   fd_set readfds;
   int r;

   FD_ZERO(&readfds);
   FD_SET(s, &readfds);

#ifdef DEBUG
   printf("\t\tChecking for Recv..\n");
#endif

   r =  select(0, &readfds, NULL, NULL, NULL);

   if( r != 1)
   {
       printf("select:error = %d\n", WSAGetLastError());
       return(FALSE);
   }
   return(TRUE);
}

void usage()
{

   printf("prperf -s/c <options>\n");
   printf("\t-s/c\t Server/Client\n");
   printf("\t-n\t Server Name\n");
   printf("\t-t\t Server Type \n");
   printf("\t-z\t Server Zone \n");
   printf("\t-p\t Protocol Name (ADSP/PAP)\n");
   printf("\t-f\t File Name for data to be saved (server only) \n");
   printf("\t-b\t Number of times to send a 4k buffer (Default = 5) \n");
   printf("\t-l\t Number of times to re-start prperf (Default = 1) (Client only)\n");
   printf("\t-v\t Verify Integraty of data transmission. \n");
   printf("\t-r\t Randomize the size of the base data string. \n");
   printf("\t-y\t Change ADSP to use SOCK_RDM instead of SOCK_STREAM. \n");
   printf("\n");
   exit(1);

}


PRSAMP.H

BOOL
DoServer();

BOOL
DoClient();

BOOL
OpenSocketRegisterAndListen();

unsigned long
PerfThread(LPVOID lpvTest);

BOOL
CheckForRecv(SOCKET s);

BOOL
CheckForSend(SOCKET s);

void
usage();


README.TXT

This module illustrates the Win32 Winsock and Mailslot APIs to do a generic
broadcast over IPX, UDP and Mailslot protocols.

    This example implements a client and a server. The example has a number
command line options. For example,

    -s To run the example as a server(default role).

    -c To run the example as a client.

-p <i or m or u> To specify the protocol to be used.
 i - IPX.
 m - Mailslots.
 u - UDP(default protocol).

    -e <Endpoint> To specify an end point of your choice. This is a mandatory
parameter. Servers create this endpoint and read broadcast messages. An
endpoint in case Mailslot protocol is a Mailslot name.(default is 5005).

    -d <DomainName> - To specify a domain name or a workgroup name. This is
useful for Mailslot clients, only.

To run the application as a server, the following command lines can be
specified:

bcast -s -e 8000 -p u
bcast -s -e 8000 -p i
bcast -s -e MAILSLOT1 -p m

To run the application as a client, the following command lines can be
specified:

bcast -c -e 8000 -p u
bcast -c -e 8000 -p i
bcast -c -e MAILSLOT1 -p m -d DOMAIN1
bcast -c -e MAILSLOT1 -p m -d WORKGROUP1


BCAST.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.
\******************************************************************************/

/*
Module Name:

    bcast.c

Abstract:

    This module illustrates the Win32 Winsock and Mailslot APIs to do a generic
    broadcast over IPX, UDP and Mailslot protocols.

    This example implements a client and a server. The example has a number of
    command line options. For example,

    -s To run the example as a server(default role).

-c To run the example as a client.

-p <i or m or u> To specify the protocol to be used.
 i - IPX.
 m - Mailslots.
 u - UDP(default protocol).

    -e <Endpoint> To specify an end point of your choice. This is a mandatory
parameter. Servers create this endpoint and read broadcast messages. An
endpoint in case Mailslot protocol is a Mailslot name.(default is 5005).

    -d <DomainName> - To specify a domain name or a workgroup name. This is
useful for Mailslot clients, only.

To run the application as a server, the following command lines can be
specified:

bcast -s -e 8000 -p u
bcast -s -e 8000 -p i
bcast -s -e MAILSLOT1 -p m

To run the application as a client, the following command lines can be
specified:

bcast -c -e 8000 -p u
bcast -c -e 8000 -p i
bcast -c -e MAILSLOT1 -p m -d DOMAIN1
bcast -c -e MAILSLOT1 -p m -d WORKGROUP1

Author:

    Rajesh Dadhia (rajeshd) 02-Mar-96

Revision History:

*/

#include <stdio.h>
#include <time.h>
#include <windows.h>
#include <winsock.h>
#include <wsipx.h>
#include <wsnwlink.h>

#define MAX_MSGLEN 80
#define MAX_ADDLEN 80
#define MAX_MSLOTNAME 80

typedef enum _MODE { CLIENT=0, SERVER } MODE;
typedef enum _PROTOCOL { UDP=0, IPX, MAILSLOT } PROTOCOL;

BOOL __stdcall
CtrlCHandler (
    DWORD dwEvent
    );

void __stdcall
DoMailSlot (
    MODE mRole,
    LPSTR lpsEndPoint,
    LPSTR lpsDomainName
    );

void __stdcall
DoIpx (
    MODE mRole,
    USHORT usEndPoint
    );

void __stdcall
DoUdp (
    MODE mRole,
    USHORT usEndPoint
    );

void __stdcall
DoMailSlotServer (
    LPSTR lpsEndPoint
    );

void __stdcall
DoMailSlotClient (
    LPSTR lpsEndPoint,
    LPSTR lpsDomainName
    );

void __stdcall
DoUdpServer (
    USHORT usEndPoint
    );

void __stdcall
DoUdpClient (
    USHORT usEndPoint
    );

void __stdcall
DoIpxServer (
    USHORT usEndPoint
    );

void __stdcall
DoIpxClient (
    USHORT usEndPoint
    );

void __stdcall
Usage (
    CHAR *pszProgramName
    );

void __stdcall
PrintError (
    LPSTR lpszRoutine,
    LPSTR lpszCallName,
    DWORD dwError
    );

CHAR * __stdcall
IpxnetAddr (
    CHAR *lpBuffer,
CHAR *lpsNetnum,
CHAR *lpsNodenum
);

void __stdcall
DoStartup ( void );

void __stdcall
DoCleanup ( void );

//
// Global Variables
//

// If Startup was successful, fStarted is used to keep track.
BOOL fStarted = FALSE;

// Global socket descriptor
SOCKET sock = INVALID_SOCKET;

void __cdecl
main (
    INT argc,
    CHAR **argv
    )
{

  // Default role of the application is SERVER, which means receiver of
  // the broadcast messages.
  MODE mRole = SERVER;

  // Deafult protocol used is UDP.
  PROTOCOL pProto = UDP;

  // Default Endpoint.
  USHORT usEndPoint = 5005;

  // Strings pointing to Endpoint and DomainName(necessary for Mailslots).
  LPSTR lpsEndPoint, lpsDomainName;
  INT  i;
  CHAR chProto;

    //
    // Install the CTRL+BREAK Handler
    //
    if ( FALSE == SetConsoleCtrlHandler ( (PHANDLER_ROUTINE) CtrlCHandler,
  TRUE
  ) )
    {
PrintError ( "main", "SetConsoleCtrlHandler", GetLastError ( ) );
    }

    //
    // allow the user to override settings with command line switches
    //
    for ( i = 1; i < argc; i++ )
    {
if ( ( *argv[i] == '-' ) || ( *argv[i] == '/' ) )
{
    switch ( tolower ( *( argv[i]+1 ) ) )
    {
//
// Role of the application (Client - Sender of broadcasts).
//
case 'c':
    mRole = CLIENT;
    break;

//
// Role of the application (Server - Receiver of broadcasts)
//
case 's':
    mRole = SERVER;
    break;

//
// Network protocol (Mailslots, IPX or UDP).
//
case 'p':
    chProto = tolower ( *argv[++i] );
    if ( 'm' == chProto )
    {
pProto = MAILSLOT;
    }
    else if ( 'i' == chProto )
    {
pProto = IPX;
    }
    else
pProto = UDP;
    break;

//
// EndPoint.
//
case 'e':
    lpsEndPoint = argv[++i];
    break;

//
// DomainName (Important for Mailslot broadcasts, only).
//
case 'd':
    lpsDomainName = argv[++i];
    break;

//
// Help.
//
case 'h':
case '?':
default:
    Usage ( argv[0] );
    }
}
else
    //
    // Help.
    //
    Usage ( argv[0] );
    }

    //
    // If the protocol specified is not MAILSLOT, convert the endpoint
    // information to integer format from the string format.
    //
    if ( MAILSLOT != pProto )
    {
usEndPoint = atoi ( lpsEndPoint );
    }

    //
    // Print a Summary of the switches specfied
    // Helpful for debugging
    //
    fprintf ( stdout, "SUMMARY:\n" );
    fprintf ( stdout, "\tRole-> %s\n", (CLIENT == mRole)?"Client":"Server" );
    fprintf ( stdout, "\tProtocol-> %s\n",
      ( MAILSLOT == pProto ) ? "MAILSLOT" :
      ( IPX == pProto ) ? "IPX" : "UDP" );
    fprintf ( stdout, "\tEndpoint-> %s\n", lpsEndPoint );

    //
    // Check the protocol specified.
    // Call the appropriate handler rouine. By default the protocol
    // is UDP.
    //
    switch ( pProto )
    {
case MAILSLOT :
    DoMailSlot ( mRole, lpsEndPoint, lpsDomainName );
    break;

case IPX:
    DoStartup ( );
    DoIpx ( mRole, usEndPoint );
    break;

default:
    DoStartup ( );
    DoUdp ( mRole, usEndPoint );
    break;
    }

    return;
}

//
// CtrlCHandler () intercepts the CTRL+BREAK or CTRL+C events and calls the
// cleanup routines.
//
BOOL __stdcall
CtrlCHandler (
    DWORD dwEvent
)
{
    if ( ( CTRL_C_EVENT == dwEvent ) || ( CTRL_BREAK_EVENT == dwEvent ) )
    {
DoCleanup ( );
    }

    return FALSE;
}

//
// DoMailSlot () function calls appropriate handler function (client/server),
// if protocol=MAILSLOT is specified. By default, the role of the application
// is - SERVER.
//
void __stdcall
DoMailSlot (
    MODE mRole,
    LPSTR lpsEndPoint,
    LPSTR lpsDomainName
    )
{
    switch ( mRole )
    {
case CLIENT:
    DoMailSlotClient ( lpsEndPoint, lpsDomainName );
    break;

default:
    DoMailSlotServer ( lpsEndPoint );
    }
return;
}

//
// DoIpx () function calls appropriate handler function (client/server),
// if protocol=IPX  is specified. By default, the role of the application
// is - SERVER.
//
void __stdcall
DoIpx (
    MODE mRole,
    USHORT usEndPoint
    )
{
    //
    // Initialize the global socket descriptor.
    //
    sock = socket ( AF_IPX, SOCK_DGRAM, NSPROTO_IPX );

    if ( INVALID_SOCKET ==  sock )
    {
PrintError( "DoIpx", "socket", WSAGetLastError ( ) );
    }

    switch ( mRole )
    {
case CLIENT:
    DoIpxClient ( usEndPoint );
    break;

default:
    DoIpxServer ( usEndPoint );
    }
return;
}

//
// DoUdp () function calls appropriate handler function (client/server),
// if protocol=UDP  is specified. By default, the role of the application
// is - SERVER.
//
void __stdcall
DoUdp (
    MODE mRole,
    USHORT usEndPoint
    )
{
    //
    // Initialize the global socket descriptor.
    //
    sock = socket ( AF_INET, SOCK_DGRAM, 0 );

    if ( INVALID_SOCKET ==  sock)
    {
PrintError ( "DoUdp", "socket", WSAGetLastError() );
    }

    switch ( mRole )
    {
case CLIENT:
    DoUdpClient ( usEndPoint );
    break;

default:
    DoUdpServer ( usEndPoint );
    }
    return;
}

//
// DoMailSlotServer () function receives a mailslot message on a particular
// mailslot. The function creates a mailslot, posts a ReadFile () to receive
// the message. The function then checks the first four bytes of the message
// for the message ID, and discards all the messages with same ID, in future.
//
void __stdcall
DoMailSlotServer (
    LPSTR lpsEndPoint
    )
{
  HANDLE hSlot;

  // Variables that store MessageID, previous messageID, number of bytes to
  // read/read, size of next available message and the number of messages.
  DWORD dwMessageID,
dwPrevID,
cbMessage,
cbRead,
cbToRead,
nMessages;

  BOOL fResult;

  CHAR achMailSlotName[MAX_MSLOTNAME],
       achBuffer[MAX_MSGLEN + sizeof ( DWORD )];

  // Variable that points past the message ID part in the message.
  LPSTR lpsMessage;

    //
    // Create a string for the mailslot name.
    //
    wsprintf ( achMailSlotName, "\\\\.\\mailslot\\%s", lpsEndPoint );

    //
    // Create the mailslot.
    //
    hSlot = CreateMailslot ( achMailSlotName,
     0,
     MAILSLOT_WAIT_FOREVER,
     (LPSECURITY_ATTRIBUTES) NULL
     );

    if ( INVALID_HANDLE_VALUE == hSlot )
    {
PrintError ( "DoMailSlotServer",  "CreateMailSlot", GetLastError() );
    }

    //
    // Post ReadFile() and read a message.
    //
    cbToRead = MAX_MSGLEN + sizeof (DWORD);

    fResult = ReadFile ( hSlot,
 achBuffer,
 cbToRead,
 &cbRead,
 (LPOVERLAPPED) NULL
 );

    if ( TRUE != fResult )
    {
PrintError ( "DoMailSlotServer", "ReadFile", GetLastError() );
    }

    achBuffer[cbRead] = '\0';

    //
    // Get the message ID part from the message.
    //
    memcpy ( &dwMessageID, achBuffer, sizeof ( DWORD ) );

    //
    // Adjust the actual message pointer.
    //
    lpsMessage = achBuffer + sizeof ( DWORD );

    //
    // Print the message
    //
    fprintf ( stdout,
      "A MailSlot Message of %d bytes received with ID %d\n",
              strlen (lpsMessage ),
              dwMessageID
              );

    fprintf ( stdout, "MessageText->%s\n", lpsMessage );

    //
    // Check for duplicate messages.
    //
    dwPrevID = dwMessageID;

    while ( 1 )
    {
//
// get information on pending messages.
//
fResult = GetMailslotInfo ( hSlot,
    (LPDWORD) NULL,
    &cbMessage,
    &nMessages,
    (LPDWORD) NULL
    );
if ( TRUE != fResult )
{
    PrintError ( "DoMailSlotServer",
 "GetMailSlotInfo",
 GetLastError ( )
 );
}

//
// Break if no more messages.
//
if ( MAILSLOT_NO_MESSAGE == cbMessage )
break;

//
// We now know how much to read.
//
cbToRead = cbMessage;
fResult = ReadFile ( hSlot,
     achBuffer,
     cbToRead,
     &cbRead,
     (LPOVERLAPPED) NULL
     );

if ( TRUE != fResult )
{
    PrintError ( "DoMailSlotServer",
 "ReadFile",
 GetLastError ( )
 );
}

achBuffer[cbRead] = '\0';
memcpy ( &dwMessageID, achBuffer, sizeof (DWORD) );

//
// print the message only if it is not a duplicate.
//
lpsMessage = achBuffer + sizeof (DWORD);
if ( dwMessageID != dwPrevID )
{
    fprintf ( stdout,
      "A MailSlot Message of %d bytes received with ID %d\n",
      strlen (lpsMessage ),
      dwMessageID
      );

    fprintf ( stdout, "MessageText->%s\n", achBuffer );
}

dwPrevID = dwMessageID;
    }

    //
    // Close the handle to our mailslot.
    //
    fResult = CloseHandle ( hSlot );

    if ( TRUE != fResult )
    {
PrintError ( "DoMailSlotServer", "CloseHandle", GetLastError() );
    }
    return;
}

//
// DoMailSlotClient () function implements the broadcast routine for a
// Mailslot client. The function opens handle to the mailslot using
// CreateFile (). CreateFile will fail on Windows NT for local mailslots,
// if the mailslot is not already created using CreateMailSlot () API.
//
// The function appends a message number to the message which the server uses
// to discard duplicate messages. In the event of a client runnig on a system
// with multiple transport protocols loaded, a mailsot message is sent over
// each protocol.
//
// This routine broadcasts a mailslot message to everyone on a Windows NT
// domain, it can also be used to send a mailslot message to a particular
// host or a workgroup.
//
void __stdcall
DoMailSlotClient (
    LPSTR lpsEndPoint,
    LPSTR lpsDomainName
    )
{
  HANDLE hFile;

  // Variables that store MessageID, number of bytes to write/written.
  DWORD dwMessageID,
cbWritten,
cbToWrite;

  BOOL fResult;

  CHAR achMailSlotName[MAX_MSLOTNAME],
       achBuffer[MAX_MSGLEN + sizeof ( DWORD ) ];

    if ( NULL == lpsDomainName )
    {
fprintf ( stdout,
  "Domain/Workgroup name must be specified....Exiting\n"
  );
exit ( 1 );
    }

    //
    // Create a string for the mailslot name.
    //
    wsprintf ( achMailSlotName,
       "\\\\%s\\mailslot\\%s",
       lpsDomainName,
       lpsEndPoint
       );

    //
    // Open a handle to the mailslot.
    //
    hFile = CreateFile ( achMailSlotName,
 GENERIC_WRITE,
 FILE_SHARE_READ,
 (LPSECURITY_ATTRIBUTES) NULL,
 OPEN_EXISTING,
 FILE_ATTRIBUTE_NORMAL,
 (HANDLE) NULL
 );

    if ( INVALID_HANDLE_VALUE == hFile)
    {
PrintError ( "DoMailSlotClient", "CreateFile", GetLastError ( ) );
    }

    //
    // Generate a Message ID.
    //
    srand ( (UINT) time ( NULL) );
    dwMessageID = rand ( );

    memcpy ( achBuffer, &dwMessageID, sizeof ( DWORD ) );
    lstrcpy ( achBuffer + sizeof (DWORD), "A MailSlot Broadcast Message" );

    //
    // Total number of bytes to write.
    //
    cbToWrite = sizeof ( DWORD ) + strlen ( achBuffer + sizeof ( DWORD ) );

    //
    // Send a mailslot message.
    //
    fResult = WriteFile ( hFile,
  achBuffer,
  cbToWrite,
  &cbWritten,
  (LPOVERLAPPED) NULL
  );

    if ( TRUE != fResult )
    {
PrintError ( "DoMailSlotClient", "WriteFile", GetLastError ( ) );
    }

    fprintf ( stdout,
      "%d bytes of MailSlot data broadcasted with ID %d\n",
      cbWritten,
      dwMessageID
      );

    //
    // Close the mailslot handle.
    //
    fResult = CloseHandle ( hFile );

    if ( TRUE != fResult )
    {
PrintError ( "DoMailSlotClient", "CloseHandle", GetLastError() );
    }
    return;
}

//
// DoUdpServer () function receives the broadcast on a specified port. The
// server will have to post a recv (), before the client sends the broadcast.
//
void __stdcall
DoUdpServer (
    USHORT usEndPoint
    )
{

  // IP address structures needed to bind to a local port and get the sender's
  // information.
  SOCKADDR_IN saUdpServ, saUdpCli;

  INT err, nSize;

  CHAR achBuffer[MAX_MSGLEN];

    //
    // bind to the specified port.
    //
    saUdpServ.sin_family = AF_INET;
    saUdpServ.sin_addr.s_addr = htonl ( INADDR_ANY );
    saUdpServ.sin_port = htons ( usEndPoint );

    err = bind ( sock, (SOCKADDR FAR *)&saUdpServ, sizeof ( SOCKADDR_IN ) );

    if ( SOCKET_ERROR == err )
    {
PrintError ( "DoUdpServer", "bind", WSAGetLastError ( ) );
    }

    //
    // receive a datagram on the bound port number.
    //
    nSize = sizeof ( SOCKADDR_IN );
    err = recvfrom ( sock,
     achBuffer,
     MAX_MSGLEN,
     0,
     (SOCKADDR FAR *) &saUdpCli,
     &nSize
     );

    if ( SOCKET_ERROR == err )
    {
PrintError ( "DoUdpServer", "recvfrom", WSAGetLastError ( ) );
    }

    //
    // print the sender's information.
    //
    achBuffer[err] = '\0';
    fprintf ( stdout, "A Udp Datagram of length %d bytes received from ", err );
    fprintf ( stdout, "\n\tIP Adress->%s ", inet_ntoa ( saUdpCli.sin_addr ) );
    fprintf ( stdout, "\n\tPort Number->%d\n", ntohs ( saUdpCli.sin_port ) );
    fprintf ( stdout, "MessageText->%s\n", achBuffer );

    //
    // Call the cleanup routine
    //
    DoCleanup ( );

    return;
}

//
// DoUdpClient () function implements the broadcast routine for an UDP
// client. The function sets the SO_BROADCAST option with the global socket.
// Calling this API is important. After binding to a local port, it sends an
// UDP boradcasts to the IP address INADDR_BROADCAST, with a particular
// port number.
//
void __stdcall
DoUdpClient (
    USHORT usEndPoint
    )
{

  // IP address structures needed to fill the source and destination
  // addresses.
  SOCKADDR_IN saUdpServ, saUdpCli;

  INT err;

  CHAR achMessage[MAX_MSGLEN];

  // Variable to set the broadcast option with setsockopt ().
  BOOL fBroadcast = TRUE;

    err = setsockopt ( sock,
       SOL_SOCKET,
       SO_BROADCAST,
       (CHAR *) &fBroadcast,
       sizeof ( BOOL )
       );

    if ( SOCKET_ERROR == err )
    {
PrintError ( "DoUdpClient", "setsockopt", WSAGetLastError ( )  );
    }

    //
    // bind to a local socket and an interface.
    //
    saUdpCli.sin_family = AF_INET;
    saUdpCli.sin_addr.s_addr = htonl ( INADDR_ANY );
    saUdpCli.sin_port = htons ( 0 );

    err = bind ( sock, (SOCKADDR *) &saUdpCli, sizeof (SOCKADDR_IN) );

    if ( SOCKET_ERROR == err )
    {
PrintError ( "DoUdpClient", "bind", WSAGetLastError ( ) );
    }

    //
    // Fill an IP address structure, to send an IP broadcast. The
    // packet will be broadcasted to the specified port.
    //
    saUdpServ.sin_family = AF_INET;
    saUdpServ.sin_addr.s_addr = htonl ( INADDR_BROADCAST );
    saUdpServ.sin_port = htons ( usEndPoint );

    lstrcpy ( achMessage, "A Broadcast Datagram" );

    err = sendto ( sock,
   achMessage,
   lstrlen ( achMessage ),
   0,
   (SOCKADDR *) &saUdpServ,
   sizeof ( SOCKADDR_IN )
   );

    if ( SOCKET_ERROR == err )
    {
PrintError ( "DoUdpClient", "sendto", WSAGetLastError ( ) );
    }

    fprintf ( stdout, "%d bytes of data broadcasted\n", err );

    //
    // Call the cleanup routine.
    //
    DoCleanup ( );

    return;
}

//
// DoIpxServer () function receives the broadcast on a specified socket. The
// server will have to post a recv (), before the client sends the broadcast.
// It is necessary call setsockopt () with SO_BROADCAST flag set, in order to
// receive IPX broadcasts on Windows 95.
//
void __stdcall
DoIpxServer (
    USHORT usEndPoint
    )
{

  // IPX address structures needed to bind to a local socket and get the
  // sender's information.
  SOCKADDR_IPX saIpxServ, saIpxCli;

  INT err, nSize;

  CHAR achBuffer[MAX_MSGLEN],
   achAddress[MAX_ADDLEN];

  OSVERSIONINFO osVer;

  // Variable to set the broadcast option with setsockopt ().
  BOOL fResult, fBroadcast = TRUE;

    //
    // Check the platform.
    //
    osVer.dwOSVersionInfoSize = sizeof ( OSVERSIONINFO );
    fResult  = GetVersionEx ( &osVer);

    if ( FALSE == fResult)
    {
        PrintError ( "DoIpxServer", "GetVersionEx", GetLastError ( ) );
    }

    //
    // If the platform is Windows 95, call setsockopt ().
    //
    if ( VER_PLATFORM_WIN32_WINDOWS == osVer.dwPlatformId )
    {
err = setsockopt ( sock,
   SOL_SOCKET,
   SO_BROADCAST,
   (CHAR *) &fBroadcast,
   sizeof ( BOOL )
   );

if ( SOCKET_ERROR == err )
{
    PrintError ( "DoIpxServer", "setsockopt", WSAGetLastError() );
}
    }

    //
    // bind to the specified socket.
    //
    saIpxServ.sa_family = AF_IPX;
    saIpxServ.sa_socket = usEndPoint;
    memset ( saIpxServ.sa_netnum, 0, sizeof (saIpxServ.sa_netnum ) );
    memset ( saIpxServ.sa_nodenum, 0, sizeof (saIpxServ.sa_nodenum ) );

    err = bind ( sock, (SOCKADDR *) &saIpxServ, sizeof (SOCKADDR_IPX) );

    if ( SOCKET_ERROR == err )
    {
PrintError ( "DoIpxServer", "bind", WSAGetLastError ( ) );
    }

    //
    // receive a datagram on the bound socket number.
    //
    nSize = sizeof ( SOCKADDR_IPX );
    err = recvfrom ( sock,
     achBuffer,
     MAX_MSGLEN,
     0,
     (SOCKADDR *) &saIpxCli,
     &nSize
     );

    if ( SOCKET_ERROR == err )
    {
PrintError ( "DoIpxServer", "recvfrom", WSAGetLastError ( ) );
    }

    //
    // print the sender's information.
    //
    achBuffer[err] = '\0';
    fprintf ( stdout,
      "An Ipx Datagram of length %d bytes received from ",
      err );
    fprintf ( stdout,
      "\n\tIPX Adress->%s ",
      IpxnetAddr ( achAddress,
   saIpxCli.sa_netnum,
   saIpxCli.sa_nodenum
   )
      );
    fprintf ( stdout, "\n\tSocket Number->%d\n", saIpxCli.sa_socket );
    fprintf ( stdout, "MessageText->%s\n", achBuffer );

    //
    // Call the cleanup routine.
    //
    DoCleanup ( );
    return;

}

//
// DoIpxClient () function implements the broadcast routine for a an IPX
// client. The fucntion sets the SO_BROADCAST option with the gloabal socket.
// Calling this API is important. After binding to a local port, it sends an IPX
// packet to the address with node number as all 1's and net number as all 0's,
// with a particuler socket number.
//
void __stdcall
DoIpxClient (
    USHORT usEndPoint
)
{

  // IPX address structures needed to fill the source and destination
  // addresses.
  SOCKADDR_IPX saIpxServ, saIpxCli;

  INT err;

  CHAR achMessage[MAX_MSGLEN];

  // Variable to set the broadcast option with setsockopt ().
  BOOL fBroadcast = TRUE;

  err = setsockopt ( sock,
     SOL_SOCKET,
     SO_BROADCAST,
     (CHAR *) &fBroadcast,
     sizeof ( BOOL )
     );

    if ( SOCKET_ERROR == err )
    {
PrintError ( "DoIpxClient", "setsockopt", WSAGetLastError ( ) );
    }

    //
    // bind to a local socket and an interface.
    //
    saIpxCli.sa_family = AF_IPX;
    saIpxCli.sa_socket = (USHORT) 0;
    memset ( saIpxCli.sa_netnum, 0, sizeof ( saIpxCli.sa_netnum ) );
    memset ( saIpxCli.sa_nodenum, 0, sizeof ( saIpxCli.sa_nodenum ) );

    err = bind ( sock, (SOCKADDR  *) &saIpxCli, sizeof ( SOCKADDR_IPX ) );

    if ( SOCKET_ERROR == err )
    {
PrintError ( "DoIpxClient", "bind", WSAGetLastError() );
    }

    //
    // Fill an IPX address structure, to send an IPX broadcast. The
    // packet will be broadcasted to the specified socket.
    //
    saIpxServ.sa_family = AF_IPX;
    saIpxServ.sa_socket = usEndPoint;
    memset ( saIpxServ.sa_netnum, 0, sizeof ( saIpxServ.sa_netnum ) );
    memset ( saIpxServ.sa_nodenum, 0xFF, sizeof ( saIpxServ.sa_nodenum ) );

    lstrcpy ( achMessage, "A Broadcast Datagram" );

    err = sendto ( sock,
   achMessage,
   lstrlen ( achMessage ),
   0,
   (SOCKADDR *) &saIpxServ,
   sizeof ( SOCKADDR_IPX )
   );

    if ( SOCKET_ERROR == err )
    {
PrintError ( "DoIpxClient", "sendto", WSAGetLastError ( ) );
    }

    fprintf ( stdout, "%d bytes of data broadcasted\n", err);

    //
    // Call the cleanup routine.
    //
    DoCleanup ( );

    return;
}

//
// Usage () lists the available command line options.
//
void __stdcall
Usage (
    CHAR *pszProgramName
)
{
    fprintf ( stderr, "Usage:  %s\n", pszProgramName );
    fprintf ( stderr,
"\t-s or -c (s - server, c - client, default - server)\n" );
    fprintf ( stderr,
"\t-p <i or m or u> (i - IPX, m - Mailslots, u - UDP)\n" );
    fprintf ( stderr, "\t-e <Endpoint>\n" );
    fprintf ( stderr,
"\t-d <DomainName> - needed only for a Mailslot client\n" );
    fprintf ( stderr,
"\n\tDefault Values-> Role:Server, Protocol:UDP, EndPoint:5005\n" );

exit ( 1 );
}

//
// PrintError () is a function available globally for printing the error and
// doing the cleanup.
//
void __stdcall
PrintError (
    LPSTR lpszRoutine,
LPSTR lpszCallName,
DWORD dwError
)
{

    fprintf ( stderr,
      "The Call to %s() in routine() %s failed with error %d\n",
      lpszCallName,
      lpszRoutine,
      dwError
      );

    DoCleanup ( );

    exit ( 1 );
}

//
// IpxnetAddr () function converts an IPX address address in the binary form
// to ascii format, it fills the input buffer with the address and returns a
// pointer to it.
//
CHAR * __stdcall
IpxnetAddr (
    CHAR *lpBuffer,
    CHAR *lpsNetnum,
    CHAR *lpsNodenum
    )
{
    wsprintf ( lpBuffer,
       "%02X%02X%02X%02X.%02X%02X%02X%02X%02X%02X",
       (UCHAR) lpsNetnum[0], (UCHAR) lpsNetnum[1],
       (UCHAR) lpsNetnum[2], (UCHAR) lpsNetnum[3],
       (UCHAR) lpsNodenum[0], (UCHAR) lpsNodenum[1],
       (UCHAR) lpsNodenum[2], (UCHAR) lpsNodenum[3],
       (UCHAR) lpsNodenum[4], (USHORT) lpsNodenum[5]
       );

    return ( lpBuffer);
}

//
// DoStartup () initializes the Winsock DLL with Winsock version 1.1
//
void __stdcall
DoStartup ( void )
{
  WSADATA wsaData;

  INT iRetVal;

    iRetVal = WSAStartup ( MAKEWORD ( 1,1 ), &wsaData );

    if ( 0 != iRetVal)
    {
PrintError ( "DoStartup", "WSAStartup", iRetVal );
    }

    //
    // Set the global flag.
    //
    fStarted = TRUE;

    return;
}

//
// DoCleanup () will close the global socket which was opened successfully by
// a call to socket (). Additionally, it will call WSACleanup (), if a call
// to WSAStartup () was made successfully.
//
void __stdcall
DoCleanup ( void )
{
    if ( INVALID_SOCKET != sock )
    {
closesocket ( sock );
    }

    if ( TRUE == fStarted )
    {
WSACleanup ( );
    }

    fprintf ( stdout, "DONE\n" );

    return;
}


README.TXT

The debug/trace feature of Windows Sockets 2 will only work with the debug
version of ws2_32.dll.

For documentation of the debug/trace feature, as well as this dt_dll sample
source code, consult DBGSPEC.DOC.


DT.H

/*++

  Copyright (c) 1995 Intel Corp

  Module Name:

    dt.h

  Abstract:

    Header file containing definitions, function prototypes, and other
    stuff for internal use of the debug/trace dll.

--*/

#define FILE_ONLY 1
#define WINDOW_ONLY 2
#define FILE_AND_WINDOW 3
#define DEBUGGER 4
#define EC_CHILD 1
#define TEXT_LEN 2048
#define MAX_FP   128

// resource constants
#define IDD_DIALOG1                     101
#define IDC_RADIO5                      1008
#define IDC_RADIO6                      1009
#define IDC_RADIO7                      1010
#define IDC_RADIO8                      1011
#define IDC_STATIC                      -1

// structure to hold init data
typedef struct _INITDATA {

    SYSTEMTIME LocalTime;
    DWORD      TID;
    DWORD      PID;

} INITDATA, *PINITDATA;

BOOL
DTTextOut(
    IN HWND   WindowHandle,
    IN HANDLE FileHandle,
    IN char   *String,
    IN DWORD  Style);

extern HWND   DebugWindow;
extern HANDLE LogFileHandle;
extern DWORD  OutputStyle;
extern char   Buffer[TEXT_LEN];


DT_DLL.CPP

/*++

  Copyright (c) 1995 Intel Corp

  File Name:

    dt_dll.cpp

  Abstract:

    Contains main and supporting functions for a Debug/Trace
    DLL for the WinSock2 DLL.  See the design spec
    for more information.

--*/

//
// Include Files
//

#include "nowarn.h"  /* turn off benign warnings */
#ifndef _WINSOCKAPI_
#define _WINSOCKAPI_   /* Prevent inclusion of winsock.h in windows.h */
#endif

#include "nowarn.h"  /* some warnings may have been turned back on */
#include <winsock2.h>
#include <stdarg.h>
#include <ws2spi.h>
#include <commdlg.h>

#include "dt_dll.h"
#include "cstack.h"
#include "dt.h"
#include "handlers.h"

//
// Forward References for Functions
//

LRESULT APIENTRY
DTMainWndProc(
    IN HWND   WindowHandle,
    IN UINT   Message,
    IN WPARAM WParam,
    IN LPARAM LParam);

LRESULT APIENTRY
DTEditWndProc(
    IN HWND   WindowHandle,
    IN UINT   Message,
    IN WPARAM WParam,
    IN LPARAM LParam);

BOOL WINAPI
DllMain(
    HINSTANCE DllInstHandle,
    DWORD     Reason,
    LPVOID    Reserved);

DWORD
WindowThreadFunc(LPDWORD TheParam);

BOOL APIENTRY
DebugDlgProc(
    IN HWND hwndDlg,
    IN UINT message,
    IN WPARAM wParam,
    IN LPARAM lParam);

BOOL
GetFile(
    IN  HWND   OwnerWindow,
    OUT  LPSTR Buffer,
    IN  DWORD  BufSize);

void
AbortAndClose(
    IN HANDLE FileHandle,
    IN HWND WindowHandle);

//
// Externally Visible Global Variables
//

HWND   DebugWindow;               // handle to the child edit control
HANDLE LogFileHandle;             // handle to the log file
DWORD  OutputStyle = WINDOW_ONLY; // where to put output
char   Buffer[TEXT_LEN];          // buffer for building output strings

//
// Static Global Variables
//

// name for my window class
static char             DTWndClass[] = "DTWindow";

static HWND             FrameWindow;   // handle to frame of debug window
static WNDPROC          EditWndProc;   // the edit control's window proc
static HINSTANCE        DllInstHandle; // handle to the dll instance
static DWORD            TlsIndex;      // tls index for this module
static CRITICAL_SECTION CrSec;         // critical section for text output
static HANDLE           TextOutEvent;  // set when debug window is ready

static char             LogFileName[256]; // name of the log file

// handle to and id of the main thread of the DLL which initializes
// and creates windows, etc
static HANDLE           WindowThread;
static DWORD            WindowThreadId;

// function pointer tables for handler functions.
static LPFNDTHANDLER  HdlFuncTable[MAX_DTCODE + 1];

// static strings
static char ErrStr1[] = "Couldn't open file.  Debug output will go to \
debug window.";
static char  ErrStr2[] = "An error occurred while trying to get a log \
filename.  Debug output will go to the window only.";
static char ErrStr3[] = "Had problems writing to file.  Aborting file \
ouput -- all debug output will now go to the debugging window";

//
// Function Definitions
//

BOOL WINAPI
DllMain(
    HINSTANCE InstanceHandle,
    DWORD     Reason,
    LPVOID    Reserved)
/*++

  DllMain()

  Function Description:

      Please see Windows documentation for DllEntryPoint.

  Arguments:

      Please see windows documentation.

  Return Value:

      Please see windows documentation.

--*/
{

    Cstack_c   *ThreadCstack;  // points to Cstack objects in tls
    PINITDATA  InitDataPtr;    // to pass to the window creation thread

    switch(Reason) {

    // Determine the reason for the call and act accordingly.

    case DLL_PROCESS_ATTACH:

        DllInstHandle = InstanceHandle;
        InitializeCriticalSection(&CrSec);
        TextOutEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

        // Fill in the handler function table.
        DTHandlerInit(HdlFuncTable, MAX_DTCODE);

        // Allocate a TLS index.
        TlsIndex = TlsAlloc();

        // Pop up a dialog box for the user to choose output method.
        DialogBox(DllInstHandle,
                  MAKEINTRESOURCE(IDD_DIALOG1),
                  NULL,
                  (DLGPROC)DebugDlgProc);

        if ((OutputStyle == FILE_ONLY) || (OutputStyle == FILE_AND_WINDOW)) {

            LogFileHandle = CreateFile(LogFileName,
                                       GENERIC_WRITE,
                                       FILE_SHARE_READ | FILE_SHARE_WRITE,
                                       NULL,
                                       CREATE_ALWAYS,
                                       FILE_ATTRIBUTE_NORMAL,
                                       NULL);
            if (LogFileHandle == INVALID_HANDLE_VALUE) {
                OutputStyle = WINDOW_ONLY;
                MessageBox(NULL, ErrStr1, "Error", MB_OK | MB_ICONSTOP);
            }
        }

        // Get some information for later output to the debug window
        // or file -- get the time, PID, and TID of the calling
        // process and put into a INITDATA struct.  This memory will
        // be freed by the thread it is passed to.
        InitDataPtr = (PINITDATA) LocalAlloc(0, sizeof(INITDATA));
        GetLocalTime(&(InitDataPtr->LocalTime));
        InitDataPtr->TID = GetCurrentThreadId();
        InitDataPtr->PID = GetCurrentProcessId();

        // Create the initialization/window handling thread.
        if ((OutputStyle == WINDOW_ONLY) || (OutputStyle == FILE_AND_WINDOW)) {
            WindowThread =
              CreateThread(NULL,
                           0,
                           (LPTHREAD_START_ROUTINE)WindowThreadFunc,
                           (LPVOID)InitDataPtr,
                           0,
                           &WindowThreadId);
        } else {

            // Normally the window thread does a DTTextOut of the time
            // and process info that we saved just above.  But in this
            // case,  there is no window thread so spit it out to the
            // file.

            wsprintf(Buffer, "Log initiated: %d-%d-%d, %d:%d:%d\r\n",
                     InitDataPtr->LocalTime.wMonth,
                     InitDataPtr->LocalTime.wDay,
                     InitDataPtr->LocalTime.wYear,
                     InitDataPtr->LocalTime.wHour,
                     InitDataPtr->LocalTime.wMinute,
                     InitDataPtr->LocalTime.wSecond);
            DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
            wsprintf(Buffer, "Process ID: 0x%X   Thread ID: 0x%X\r\n",
                     InitDataPtr->PID,
                     InitDataPtr->TID);
            DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);

            // Setting this event allows {Pre|Post}ApiNotify to
            // proceed.  This event isn't really needed in this case
            // (because there is only one thread, and we know the code
            // above has been executed before WSAPre|PostApiNotify).
            SetEvent(TextOutEvent);
        }

        // flow through...

    case DLL_THREAD_ATTACH:

        // Store a pointer to a new Cstack_c in the slot for this
        // thread.
        ThreadCstack = new Cstack_c();
        TlsSetValue(TlsIndex, (LPVOID)ThreadCstack);

        break;

    case DLL_PROCESS_DETACH:

        // Free up some resources.  This is like cleaning up your room
        // before the tornado strikes, but hey, it's good practice.
        TlsFree(TlsIndex);
        DeleteCriticalSection(&CrSec);

        if ((OutputStyle == FILE_ONLY) || (OutputStyle == FILE_AND_WINDOW)) {
            CloseHandle(LogFileHandle);
        }
        CloseHandle(WindowThread);

        break;

    case DLL_THREAD_DETACH:

        // Get the pointer to this thread's Cstack, and delete the
        // object.
        ThreadCstack = (Cstack_c *)TlsGetValue(TlsIndex);
        delete ThreadCstack;

        break;

    default:

        break;
    } // switch (Reason)

    return TRUE;
} // DllMain()

BOOL WINAPIV
WSAPreApiNotify(
    IN  INT    NotificationCode,
    OUT LPVOID ReturnCode,
    IN  LPSTR  LibraryName,
    ...)
/*++

  Function Description:

      Builds a string for output and passes it, along with information
      about the call, to a handler function.

  Arguments:

      NotificationCode -- specifies which API function called us.

      ReturnCode -- a generic pointer to the return value of the API
      function.  Can be used to change the return value in the
      case of a short-circuit (see how the return value from
      PreApiNotify works for more information on short-circuiting
      the API function).

      LibraryName -- a string pointing to the name of the library that
      called us.

      ...    -- variable number argument list.  These are pointers
      to the actual parameters of the API functions.

  Return Value:

      Returns TRUE if we want to short-circuit the API function;
      in other words, returning non-zero here forces the API function
      to return immediately before any other actions take place.

      Returns FALSE if we want to proceed with the API function.

--*/
{
    va_list          vl;            // used for variable arg-list parsing
    Cstack_c         *ThreadCstack; // the Cstack_c object for this thread
    int              Index = 0;     // index into string we are creating
    BOOL             ReturnValue;   // value to return
    LPFNDTHANDLER    HdlFunc;       // pointer to handler function
    int              Counter;       // counter popped off the cstack
    int              OriginalError; // any pending error is saved
    int              HandlerError;  // the error after handler returns

    EnterCriticalSection(&CrSec);
    OriginalError = GetLastError();

    // Wait until the debug window is ready to receive text for output.
    WaitForSingleObject(TextOutEvent, INFINITE);
    va_start(vl, LibraryName);

    // Get the Cstack_c object for this thread.
    ThreadCstack = (Cstack_c *)TlsGetValue(TlsIndex);
    if (!ThreadCstack){
        ThreadCstack = new Cstack_c();
        TlsSetValue(TlsIndex, (LPVOID)ThreadCstack);
        wsprintf(Buffer, "0x%X Foriegn thread\n",
                 GetCurrentThreadId());
        DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    } //if

    // Start building an output string with some info that's
    // independent of which API function called us.
    Index += wsprintf(Buffer, "TID: 0x%X   ", GetCurrentThreadId());
    Index += wsprintf(Buffer + Index, "Function call: %d   ",
                      ThreadCstack->CGetCounter());

    // Push the counter & increment.
    ThreadCstack->CPush();

    // Reset the error to what it was when the function started.
    SetLastError(OriginalError);

    // Call the appropriate handling function, output the buffer.
    if ((NotificationCode < MAX_DTCODE) && HdlFuncTable[NotificationCode]) {
        HdlFunc = HdlFuncTable[NotificationCode];
        ReturnValue = (*HdlFunc)(vl, ReturnCode,
                                 LibraryName,
                                 Buffer,
                                 Index,
                                 TEXT_LEN,
                                 TRUE);
        HandlerError = GetLastError();

    } else {

        wsprintf(Buffer + Index, "Unknown function called!\r\n");
        DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
        ReturnValue = FALSE;
    }

    // If we are returning TRUE, then the API/SPI function will be
    // short-circuited.  We must pop the thread stack, since no
    // corresponding WSAPostApiNotify will be called.
    if (ReturnValue) {
        ThreadCstack->CPop(Counter);
    }

    // In case the error has changed since the handler returned, we
    // want to set it back to that.  So if the handler set the error,
    // the function exits with that value; if not, it exits with the
    // original error.
    SetLastError(HandlerError);
    LeaveCriticalSection(&CrSec);
    return(ReturnValue);

} // WSAPreApiNotify()

BOOL WINAPIV
WSAPostApiNotify(
    IN  INT    NotificationCode,
    OUT LPVOID ReturnCode,
    IN  LPSTR  LibraryName,
    ...)
/*++

  PostApiNotify()

  Function Description:

      Like PreApiNotify, builds a string and passes it, along with
      information about the call, to a handler function.

  Arguments:

      NotificationCode  -- specifies which API function called us.

      ReturnCode -- a generic pointer to the return value of the API
      function.

      ...    -- variable number argument list.  These are pointers
      to the actual parameters of the API functions.

  Return Value:

      Returns value is currently meaningless.

--*/
{
    va_list          vl;            // used for variable arg-list parsing
    Cstack_c         *ThreadCstack; // the Cstack_c object for this thread
    int              Index = 0;     // index into string we are creating
    int              Counter;       // counter we pop off the cstack
    LPFNDTHANDLER    HdlFunc;       // pointer to handler function
    int              OriginalError; // any pending error is saved
    int              HandlerError;  // error after the handler returns

    // Lets hope EnterCriticalSection() doesn't change the error...
    EnterCriticalSection(&CrSec);
    OriginalError = GetLastError();

    // Wait until it's ok to send output.
    WaitForSingleObject(TextOutEvent, INFINITE);

va_start(vl, LibraryName);

// Get the cstack object from TLS, pop the Counter.
    ThreadCstack = (Cstack_c *) TlsGetValue(TlsIndex);

    if (!ThreadCstack){
        ThreadCstack = new Cstack_c();
        TlsSetValue(TlsIndex, (LPVOID)ThreadCstack);
        wsprintf(Buffer, "0x%X Foriegn thread\n",
                 GetCurrentThreadId());
        DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    } //if

    ThreadCstack->CPop(Counter);

    // Output some info that's independent of which API called us.
    Index += wsprintf(Buffer, "TID: 0x%X   ", GetCurrentThreadId());
    Index += wsprintf(Buffer + Index, "Function Call: %d   ", Counter);

    // Set the error to what it originally was.
    SetLastError(OriginalError);

    // Call the appropriate handling function, output the buffer.
    if ((NotificationCode < MAX_DTCODE) && HdlFuncTable[NotificationCode]) {
        HdlFunc = HdlFuncTable[NotificationCode];
        (*HdlFunc)(vl, ReturnCode,
                   LibraryName,
                   Buffer,
                   Index,
                   TEXT_LEN,
                   FALSE);
        HandlerError = GetLastError();

    } else {

        wsprintf(Buffer + Index, "Unknown function returned!\r\n");
        DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    }

    // In case the error has changed since the handler returned, we
    // want to set it back to that.  So if the handler set the error,
    // the function exits with that value; if not, it exits with the
    // original error.
    SetLastError(HandlerError);
    LeaveCriticalSection(&CrSec);
    return(FALSE);

} // WSAPostApiNotify()

LRESULT APIENTRY
DTMainWndProc(
    IN HWND   WindowHandle,
    IN UINT   Message,
    IN WPARAM WParam,
    IN LPARAM LParam)
/*++

  DTMainWndProc()

  Function Description:

      Window procedure for the main window of the Dll.  This function
      processes WM_CREATE messages in order to create a child
      edit control, which does most of the dirty work.  Also processes
      WM_COMMAND to trap notification messages from the edit control,
      as well as WM_SIZE and WM_DESTROY messages.

  Arguments:

      WindowHandle -- the window.

      Message -- the message.

      WParam -- first parameter.

      LParam -- second parameter.

  Return Value:

      Message dependent.

--*/
{

    HFONT      FixedFontHandle;   // self-explanatory
    RECT       Rect;              // specifies client area of frame window
    DWORD      CharIndex1;
    DWORD      CharIndex2;
    DWORD      LineIndex;         // indices into edit control text
    char       NullString[] = ""; // self-explanatory
    DWORD      OldOutputStyle;    // temporary storage for OutputStyle

    switch (Message) {

    case WM_CREATE:

        // Create the debug window as a multiline edit control.
        GetClientRect(WindowHandle, &Rect);
        DebugWindow = CreateWindow("EDIT",
                                   NULL,
                                   WS_CHILD | WS_VISIBLE |
                                   WS_VSCROLL | ES_LEFT  |
                                   ES_MULTILINE | ES_AUTOVSCROLL,
                                   0,
                                   0,
                                   Rect.right,
                                   Rect.bottom,
                                   WindowHandle,
                                   (HMENU)EC_CHILD,
                                   DllInstHandle,
                                   NULL);

        // Subclass the edit control's window procedure to be
        // DTEditWndProc.
        EditWndProc = (WNDPROC) SetWindowLong(DebugWindow,
                                              GWL_WNDPROC,
                                              (DWORD)DTEditWndProc);

        // Set the edit control's text size to the maximum.
        SendMessage(DebugWindow, EM_LIMITTEXT, 0, 0);

        // Set the edit control's font
        FixedFontHandle = (HFONT)GetStockObject(ANSI_FIXED_FONT);
        SendMessage(DebugWindow, WM_SETFONT, (WPARAM)FixedFontHandle,
                    MAKELPARAM(TRUE, 0));

        return(0);

    case WM_COMMAND:

        if (LOWORD(WParam) == EC_CHILD) {

            // The notification is coming from the edit-control child.
            // Determine which notification it is and act appropriately.

            switch (HIWORD(WParam)) {

            case EN_ERRSPACE:

                // Flow through

            case EN_MAXTEXT:

                // There's too much text in the edit control.  This is
                // a hack to eliminate approximately the first half of
                // the text, so we can then add more...
                CharIndex1 = GetWindowTextLength(DebugWindow) / 2;
                LineIndex = SendMessage(DebugWindow, EM_LINEFROMCHAR,
                                        (WPARAM)CharIndex1, 0);
                CharIndex2 = SendMessage(DebugWindow, EM_LINEINDEX,
                                         (WPARAM)LineIndex, 0);

                SendMessage(DebugWindow, EM_SETSEL, 0, CharIndex2);
                SendMessage(DebugWindow, EM_REPLACESEL, 0,
                            (LPARAM)NullString);

                // send this text to the window only...
                OldOutputStyle = OutputStyle;
                OutputStyle = WINDOW_ONLY;
                DTTextOut(DebugWindow, LogFileHandle,
                          "----Buffer Overflow...Resetting----\r\n",
                          OutputStyle);
                OutputStyle = OldOutputStyle;
                break;

            case EN_CHANGE:
            case EN_UPDATE:

                // Ignore these notification codes
                return 0;
                break;

            default:

                // Let the default window procedure handle it.
                return DefWindowProc(WindowHandle, Message, WParam,
                                 LParam);
            } // switch (HIWORD(WParam))

        } // if (LOWORD(WParam) == EC_CHILD)
        else {

            // The notification is coming from somewhere else!!!
            return DefWindowProc(WindowHandle, Message, WParam,
                                 LParam);
        }

        return(0);
        break;

    case WM_DESTROY:

        PostQuitMessage(0);
        return(0);

    case WM_SIZE:

        // Make the edit control the size of the window's client area.
        MoveWindow(DebugWindow, 0, 0, LOWORD(LParam), HIWORD(LParam), TRUE);
        return(0);

    default:

        // All other messages are taken care of by the default.
        return(DefWindowProc(WindowHandle, Message, WParam, LParam));

    } // switch

} // DTMainWndProc()

LRESULT APIENTRY
DTEditWndProc(
    IN HWND   WindowHandle,
    IN UINT   Message,
    IN WPARAM WParam,
    IN LPARAM LParam)
/*++

  DTEditWndProc()

  Function Description:

      Subclassed window procedure for the debug window.  This function
      disables some edit control functionality, and also responds to a
      user-defined message to print out text in the window.

  Arguments:

      WindowHandle -- the window.

      Message -- the message.

      WParam -- first parameter.

      LParam -- second parameter.

  Return Value:

      Message dependent.

--*/
{
    switch (Message) {

    case WM_CHAR:

        // Handle control-c so that copy works.  Sorry about the magic
        // number!
        if (WParam == 3) {
            return (CallWindowProc((WNDPROC)EditWndProc, WindowHandle, Message,
                                   WParam, LParam));
        } // else flows through

    case WM_KEYDOWN:    // Flow through
    case WM_UNDO:       // Flow through
    case WM_PASTE:      // Flow through
    case WM_CUT:

        return (0);     // Effectively disables the above messages

    default:

        return (CallWindowProc(EditWndProc, WindowHandle, Message,
                               WParam, LParam));
    } // switch

} // DTEditWndProc()

DWORD
WindowThreadFunc(
    LPDWORD TheParam)
/*++

  WindowThreadFunc()

  Function Description:

      Thread function for WindowThread created in DllMain during
      process attachment.  Registers a window class, creates an
      instance of that class, and goes into a message loop to retrieve
      messages for that window or it's child edit control.

  Arguments:

      TheParam -- Pointer to the parameter passed in by the function
      that called CreateThread.

  Return Value:

      Returns the wParam of the quit message that forced us out of the
      message loop.

--*/
{

    WNDCLASS  wnd_class;    // window class structure to register
    MSG       msg;          // retrieved message
    PINITDATA InitDataPtr;  // casts TheParam into a INITDATA pointer

    // Register a window class for the frame window.
    wnd_class.style         = CS_HREDRAW | CS_VREDRAW;
    wnd_class.lpfnWndProc   = DTMainWndProc;
    wnd_class.cbClsExtra    = 0;
    wnd_class.cbWndExtra    = 0;
    wnd_class.hInstance     = DllInstHandle;
    wnd_class.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
    wnd_class.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wnd_class.hbrBackground = (HBRUSH)(COLOR_APPWORKSPACE + 1);
    wnd_class.lpszMenuName  = NULL;
    wnd_class.lpszClassName = DTWndClass;
    RegisterClass(&wnd_class);

    // Create a frame window
    FrameWindow = CreateWindow(DTWndClass,
                               "Debug Window",
                               WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN |
                               WS_VISIBLE,
                               CW_USEDEFAULT,
                               CW_USEDEFAULT,
                               CW_USEDEFAULT,
                               CW_USEDEFAULT,
                               NULL,
                               NULL,
                               DllInstHandle,
                               NULL);

    // Send the initialization data to the debug window and/or file.
    InitDataPtr = (PINITDATA)TheParam;
    wsprintf(Buffer, "Log initiated: %d-%d-%d, %d:%d:%d\r\n",
             InitDataPtr->LocalTime.wMonth,
             InitDataPtr->LocalTime.wDay,
             InitDataPtr->LocalTime.wYear,
             InitDataPtr->LocalTime.wHour,
             InitDataPtr->LocalTime.wMinute,
             InitDataPtr->LocalTime.wSecond);
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    wsprintf(Buffer, "Process ID: 0x%X   Thread ID: 0x%X\r\n",
             InitDataPtr->PID,
             InitDataPtr->TID);
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    LocalFree(InitDataPtr);

    // Setting this event allows {Pre|Post}ApiNotify to proceed.  This
    // insures (ensures?  what's the difference) that any debugging
    // output by other threads is held up until after this statement.
    SetEvent(TextOutEvent);

    // Go into a message loop.
    while (GetMessage(&msg, NULL, 0 , 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return(msg.wParam);

} // WindowThreadFunc()

BOOL APIENTRY
DebugDlgProc(
    HWND DialogWindow,
    UINT Message,
    WPARAM WParam,
    LPARAM LParam)
/*++

  DebugDlgProc()

  Function Description:

      Window function for the dialog box IDC_DIALOG1, the dialog box
      that pops up when the dll is loaded and prompts the user for the
      output style of his/her choice.

  Arguments:

      DialogWindow -- handle to the dialog box window.

      Message -- the message being received.

      WParam -- first parameter.

      LParam -- second parameter.

  Return Value:

      Returns TRUE to indicate message was handled, FALSE otherwise.

--*/
{

    DWORD LogFNSize = sizeof(LogFileName); // size of the file name buffer

    switch (Message) {

    case WM_COMMAND:

        switch (LOWORD(WParam)) {

        case IDOK:

            // The user clicked the OK button...figure out his choice
            // and act appropriately.
            if (IsDlgButtonChecked(DialogWindow, IDC_RADIO5)) {

                // Radio Button 1 was clicked.
                if (!GetFile(DialogWindow, LogFileName, LogFNSize)) {

                    // Error -- OutputStyle stays WINDOW_ONLY.
                    MessageBox(DialogWindow, ErrStr2, "Error.",
                               MB_OK | MB_ICONSTOP);
                } else {
                    OutputStyle = FILE_ONLY;
                }

            } else if (IsDlgButtonChecked(DialogWindow, IDC_RADIO6)) {

                // Radio Button 2 was clicked.
                OutputStyle = WINDOW_ONLY;

            } else if (IsDlgButtonChecked(DialogWindow, IDC_RADIO7)) {

                // Radio Button 3 was clicked.
                if (!GetFile(DialogWindow, LogFileName, LogFNSize)) {

                    // Error -- OutputStyle stays WINDOW_ONLY.
                    MessageBox(DialogWindow, ErrStr2, "Error",
                               MB_OK | MB_ICONSTOP);
                } else {
                    OutputStyle = FILE_AND_WINDOW;
                }

            } else if (IsDlgButtonChecked(DialogWindow, IDC_RADIO8)) {

                // Radio Button 4 was clicked.
                OutputStyle = DEBUGGER;

            } else {

                // No radio buttons were clicked -- pop up a Message
                // box.
                MessageBox(DialogWindow, "You must choose one output method.",
                           "Choose or Die.", MB_OK | MB_ICONSTOP);
                break;

            }

            // flow through

        case IDCANCEL:

            EndDialog(DialogWindow, WParam);
            return TRUE;

        }

    case WM_INITDIALOG:

        return TRUE;

    }
    return FALSE;

} // DebugDlgProc()

BOOL
DTTextOut(
    IN HWND   WindowHandle,
    IN HANDLE FileHandle,
    IN char   *String,
    DWORD     Style)
/*++

  DTTextOut()

  Function Description:

      This function outputs a string to a debug window and/or file.

  Arguments:

      WindowHandle -- handle to an edit control for debug output.

      FileHandle -- handle to an open file for debug output.

      String -- the string to output.

      Style -- specifies whether the output should go to the window,
      the file, or both.

  Return Value:

      Returns TRUE if the output succeeds, FALSE otherwise.

--*/
{

    DWORD NumWritten;           // WriteFile takes an address to this
    DWORD Index;                // index of end of edit control text
    BOOL  Result;               // result of WriteFile
    char  Output[TEXT_LEN];     // scratch buffer

    static DWORD LineCount = 0; // text output line number
    DWORD  BufIndex = 0;        // index into output string

    // Build a new string with the line-number in front.

BufIndex += wsprintf(Output, "(%d) ", LineCount++);
    strcpy(Output + BufIndex, String);

    switch (Style) {

    case WINDOW_ONLY:

        Index = GetWindowTextLength(WindowHandle);
        SendMessage(WindowHandle, EM_SETSEL, Index, Index);
        SendMessage(WindowHandle, EM_REPLACESEL, 0, (LPARAM)Output);

        break;

    case FILE_ONLY:

        Result = WriteFile(FileHandle, (LPCVOID)Output, strlen(Output),
                           &NumWritten, NULL);
        if (!Result) {

            AbortAndClose(FileHandle, WindowHandle);
            return FALSE;
        }
        break;

    case FILE_AND_WINDOW:

        Index = GetWindowTextLength(WindowHandle);
        SendMessage(WindowHandle, EM_SETSEL, Index, Index);
        SendMessage(WindowHandle, EM_REPLACESEL, 0, (LPARAM)Output);
        Result = WriteFile(FileHandle, (LPCVOID)Output, strlen(Output),
                           &NumWritten, NULL);
        if (!Result) {

            AbortAndClose(FileHandle, WindowHandle);
            return FALSE;
        }
        break;

    case DEBUGGER:

        OutputDebugString(Output);
    }
    return TRUE;

} // DTTextOut()

void
AbortAndClose(
    IN HANDLE FileHandle,
    IN HWND WindowHandle)
/*++

  AbortAndClose()

  Function Description:

      Closes a file handle, informs the user via a message box, and
      changes the global variable OutputStyle to WINDOW_ONLY

  Arguments:

      FileHandle -- handle to a file that caused the error.

      WindowHandle -- handle to a window to be the parent of the
      Message Box.

  Return Value:

      Void.

--*/
{
    CloseHandle(FileHandle);
    MessageBox(WindowHandle, ErrStr3, "Error", MB_OK | MB_ICONSTOP);
    OutputStyle = WINDOW_ONLY;

} // AbortAndClose()

BOOL
GetFile(
    IN  HWND   OwnerWindow,
    OUT LPSTR  FileName,
    IN  DWORD  FileNameSize)
/*++

  GetFile()

  Function Description:

      Uses the predefined "Save As" dialog box style to retrieve a
      file name from the user.  The file name the user selects is
      stored in LogFileName.

  Arguments:

      OwnerWindow -- window which will own the dialog box.

      FileName -- address of a buffer in which to store the string.

      FileNameSize -- size of the FileName buffer.

  Return Value:

      Returns whatever GetSaveFileName returns; see documentation for
      that function.

--*/
{

    OPENFILENAME OpenFileName;  // common dialog box structure
    char DirName[256];          // directory string
    char FileTitle[256];        // file-title string

    FileName[0] = '\0';

    FillMemory((PVOID)&OpenFileName, sizeof(OPENFILENAME), 0);

    // Retrieve the system directory name and store it in DirName.
    GetCurrentDirectory(sizeof(DirName), DirName);

    // Set the members of the OPENFILENAME structure.
    OpenFileName.lStructSize = sizeof(OPENFILENAME);
    OpenFileName.hwndOwner = OwnerWindow;
    OpenFileName.lpstrFilter = OpenFileName.lpstrCustomFilter = NULL;
    OpenFileName.nFilterIndex = 0;
    OpenFileName.lpstrFile = FileName;
    OpenFileName.nMaxFile = FileNameSize;
    OpenFileName.lpstrFileTitle = FileTitle;
    OpenFileName.nMaxFileTitle = sizeof(FileTitle);
    OpenFileName.lpstrInitialDir = DirName;
    OpenFileName.Flags = OFN_SHOWHELP | OFN_OVERWRITEPROMPT;

    // Pop up the dialog box to get the file name.
    return GetSaveFileName(&OpenFileName);

} // GetFile()


DT_DLL.H

/*++

Copyright (c) 1995 Intel Corp

File Name:

    dt_dll.h

Abstract:

    This header describes the interface to the WinSock 2 debug/trace
    DLL.  Please see the design spec for more information.

-*/

#ifndef _DT_DLL_H
#define _DT_DLL_H

#include "nowarn.h"
#include <winsock2.h>

//
// This type defines a pointer to the Pre/PostApiNotify functions
//

typedef BOOL (WINAPIV * LPFNWSANOTIFY)(
    IN  INT    NotificationCode,
    OUT LPVOID ReturnCode,
    IN  LPSTR  LibraryName,
    ...);

//
// Function prototypes for Pre/PostApiNotify
//

BOOL WINAPIV
WSAPreApiNotify(
    IN  INT    NotificationCode,
    OUT LPVOID ReturnCode,
    IN  LPSTR  LibraryName,
    ...);

BOOL WINAPIV
WSAPostApiNotify(
    IN  INT    NotificationCode,
    OUT LPVOID ReturnCode,
    IN  LPSTR  LibraryName,
    ...);

//
// Pointer to an exception notification function.
//

typedef
VOID
(WINAPI * LPFNWSAEXCEPTIONNOTIFY)(
    IN LPEXCEPTION_POINTERS ExceptionPointers
    );

//
// Function prototype for exception notify.
//

VOID
WINAPI
WSAExceptionNotify(
    IN LPEXCEPTION_POINTERS ExceptionPointers
    );

//
// API function codes for Pre/PostApiNotify functions.  Note:  These must start
// at  1  or more and be fairly densely assigned.  Small gaps can be tolerated.
// Note  that  a  "MAX_DTCODE"  definition  should  be updated if new codes are
// added.
//

#define DTCODE_accept 1
#define DTCODE_bind 2
#define DTCODE_closesocket 3
#define DTCODE_connect 4
#define DTCODE_getpeername 5
#define DTCODE_getsockname 6
#define DTCODE_getsockopt 7
#define DTCODE_htonl 8
#define DTCODE_htons 9
#define DTCODE_ioctlsocket 10
#define DTCODE_listen 11
#define DTCODE_ntohl 12
#define DTCODE_ntohs 13
#define DTCODE_recv 14
#define DTCODE_recvfrom 15
#define DTCODE_select 16
#define DTCODE_send 17
#define DTCODE_sendto 18
#define DTCODE_setsockopt 19
#define DTCODE_shutdown 20
#define DTCODE_socket 21
#define DTCODE_WSAAccept 22
#define DTCODE_WSAAsyncSelect 23
#define DTCODE_WSACancelBlockingCall 24
#define DTCODE_WSACleanup 25
#define DTCODE_WSACloseEvent 26
#define DTCODE_WSAConnect 27
#define DTCODE_WSACreateEvent 28
#define DTCODE_WSADuplicateSocketA 29
#define DTCODE_WSAEnumNetworkEvents 30
#define DTCODE_WSAEnumProtocolsA 31
#define DTCODE_WSAEventSelect 32
#define DTCODE_WSAGetLastError 33
#define DTCODE_WSAGetOverlappedResult 34
#define DTCODE_WSAGetQOSByName 35
#define DTCODE_WSAHtonl 36
#define DTCODE_WSAHtons 37
#define DTCODE_WSAIoctl 38
#define DTCODE_WSAIsBlocking 39
#define DTCODE_WSAJoinLeaf 40
#define DTCODE_WSANtohl 41
#define DTCODE_WSANtohs 42
#define DTCODE_WSARecv 43
#define DTCODE_WSARecvDisconnect 44
#define DTCODE_WSARecvFrom 45
#define DTCODE_WSAResetEvent 46
#define DTCODE_WSASend 47
#define DTCODE_WSASendDisconnect 48
#define DTCODE_WSASendTo 49
#define DTCODE_WSASetBlockingHook 50
#define DTCODE_WSASetEvent 51
#define DTCODE_WSASetLastError 52
#define DTCODE_WSASocketA 53
#define DTCODE_WSAStartup 54
#define DTCODE_WSAUnhookBlockingHook 55
#define DTCODE_WSAWaitForMultipleEvents 56
#define DTCODE_gethostbyaddr 57
#define DTCODE_gethostbyname 58
#define DTCODE_gethostname 59
#define DTCODE_getprotobyname 60
#define DTCODE_getprotobynumber 61
#define DTCODE_getservbyname 62
#define DTCODE_getservbyport 63
#define DTCODE_inet_addr 64
#define DTCODE_inet_ntoa 65
#define DTCODE_WSAAsyncGetHostByAddr 66
#define DTCODE_WSAAsyncGetHostByName 67
#define DTCODE_WSAAsyncGetProtoByName 68
#define DTCODE_WSAAsyncGetProtoByNumber 69
#define DTCODE_WSAAsyncGetServByName 70
#define DTCODE_WSAAsyncGetServByPort 71
#define DTCODE_WSACancelAsyncRequest 72
#define DTCODE_WSPAccept 73
#define DTCODE_WSPAsyncSelect 74
#define DTCODE_WSPBind 75
#define DTCODE_WSPCancelBlockingCall 76
#define DTCODE_WSPCleanup 77
#define DTCODE_WSPCloseSocket 78
#define DTCODE_WSPConnect 79
#define DTCODE_WSPDuplicateSocket 80
#define DTCODE_WSPEnumNetworkEvents 81
#define DTCODE_WSPEventSelect 82
#define DTCODE_WSPGetOverlappedResult 83
#define DTCODE_WSPGetPeerName 84
// The  WSPGetProcTable  function  has  been removed, but the code numbers have
// been kept the same.
// #define DTCODE_WSPGetProcTable 85
#define DTCODE_WSPGetSockName 86
#define DTCODE_WSPGetSockOpt 87
#define DTCODE_WSPGetQOSByName 88
#define DTCODE_WSPIoctl 89
#define DTCODE_WSPJoinLeaf 90
#define DTCODE_WSPListen 91
#define DTCODE_WSPRecv 92
#define DTCODE_WSPRecvDisconnect 93
#define DTCODE_WSPRecvFrom 94
#define DTCODE_WSPSelect 95
#define DTCODE_WSPSend 96
#define DTCODE_WSPSendDisconnect 97
#define DTCODE_WSPSendTo 98
#define DTCODE_WSPSetSockOpt 99
#define DTCODE_WSPShutdown 100
#define DTCODE_WSPSocket 101
#define DTCODE_WSPStartup 102
#define DTCODE_WPUCloseEvent 103
#define DTCODE_WPUCloseSocketHandle 104
#define DTCODE_WPUCreateEvent 105
#define DTCODE_WPUCreateSocketHandle 106
#define DTCODE_WSCDeinstallProvider 107
#define DTCODE_WSCInstallProvider 108
#define DTCODE_WPUModifyIFSHandle 109
#define DTCODE_WPUQueryBlockingCallback 110
#define DTCODE_WPUQuerySocketHandleContext 111
#define DTCODE_WPUQueueApc 112
#define DTCODE_WPUResetEvent 113
#define DTCODE_WPUSetEvent 114
#define DTCODE_WSCEnumProtocols 115
#define DTCODE_WPUGetProviderPath 116
#define DTCODE_WPUPostMessage 117
#define DTCODE_WPUFDIsSet 118
#define DTCODE_WSADuplicateSocketW 119
#define DTCODE_WSAEnumProtocolsW 120
#define DTCODE_WSASocketW 121
#define DTCODE___WSAFDIsSet 122
#define DTCODE_WSAAddressToStringA 123
#define DTCODE_WSAAddressToStringW 124
#define DTCODE_WSAStringToAddressA 125
#define DTCODE_WSAStringToAddressW 126
#define DTCODE_WSALookupServiceBeginA 127
#define DTCODE_WSALookupServiceBeginW 128
#define DTCODE_WSALookupServiceNextA 129
#define DTCODE_WSALookupServiceNextW 130
#define DTCODE_WSALookupServiceEnd 131
//
// WSAGetAddressByName[AW] have been removed.
//
// #define DTCODE_WSAGetAddressByNameA 132
// #define DTCODE_WSAGetAddressByNameW 133
#define DTCODE_WSAInstallServiceClassA 134
#define DTCODE_WSAInstallServiceClassW 135
#define DTCODE_WSASetServiceA 136
#define DTCODE_WSASetServiceW 137
#define DTCODE_WSARemoveServiceClass 138
#define DTCODE_WSAGetServiceClassInfoA 139
#define DTCODE_WSAGetServiceClassInfoW 140
#define DTCODE_WSAEnumNameSpaceProvidersA 141
#define DTCODE_WSAEnumNameSpaceProvidersW 142
#define DTCODE_WSAGetServiceClassNameByClassIdA 143
#define DTCODE_WSAGetServiceClassNameByClassIdW 144
#define DTCODE_NSPAddressToString 145
#define DTCODE_NSPStringToAddress 146
#define DTCODE_NSPLookupServiceBegin 147
#define DTCODE_NSPLookupServiceNext 148
#define DTCODE_NSPLookupServiceEnd 149
#define DTCODE_NSPGetAddressByName 150
#define DTCODE_NSPInstallServiceClass 151
#define DTCODE_NSPSetService 152
#define DTCODE_NSPRemoveServiceClass 153
#define DTCODE_NSPGetServiceClassInfo 154
#define DTCODE_NSPEnumNameSpaceProviders 155
#define DTCODE_NSPGetServiceClassNameByClassId 156
#define DTCODE_WSCGetProviderPath 157
#define DTCODE_WSCInstallNameSpace 158
#define DTCODE_WSCUnInstallNameSpace 159
#define DTCODE_WSCEnableNSProvider 160
#define DTCODE_WSPAddressToString 161
#define DTCODE_WSPStringToAddress 162

#define MAX_DTCODE DTCODE_WSPStringToAddress

#endif


HANDLERS.CPP

/*++

  Copyright (c) 1995 Intel Corp

  Module Name:

    handlers.cpp

  Abstract:

    Contains handler functions for each possible API or SPI hook.

--*/

#include "nowarn.h"  /* turn off benign warnings */
#ifndef _WINSOCKAPI_
#define _WINSOCKAPI_   /* Prevent inclusion of winsock.h in windows.h */
#endif

#include "nowarn.h"  /* some warnings may have been turned back on */
#include <winsock2.h>
#include <ws2spi.h>

#include "handlers.h"
#include "dt_dll.h"
#include "dt.h"

// turn off unreferenced local variable warning
#pragma warning(disable: 4100)

//
// Function Definitions
//

BOOL
DTHandlerInit(
    OUT LPLPFNDTHANDLER HandlerFuncTable,
    int                    NumEntries)
/*++

  Function Description:

      Fills in the given HandlerFuncTable with pointers to all the
      appropriate handler functions, based on notification code.

  Arguments:

      HandlerFuncTable -- an uninitialized array of pointers of type
      LPFNDTHANDLER, which point to handler functions (see handlers.h)

      NumEntries -- the number of entries in the array.  Currently
      only used to zero out the array.

  Return Value:

      Always returns TRUE.

--*/
{
    memset((char *)HandlerFuncTable, 0, NumEntries * sizeof(LPFNDTHANDLER));

    HandlerFuncTable[DTCODE_accept] = DTHandler_accept;
    HandlerFuncTable[DTCODE_bind] = DTHandler_bind;
    HandlerFuncTable[DTCODE_closesocket] = DTHandler_closesocket;
    HandlerFuncTable[DTCODE_connect] = DTHandler_connect;
    HandlerFuncTable[DTCODE_getpeername] = DTHandler_getpeername;
    HandlerFuncTable[DTCODE_getsockname] = DTHandler_getsockname;
    HandlerFuncTable[DTCODE_getsockopt] = DTHandler_getsockopt;
    HandlerFuncTable[DTCODE_htonl] = DTHandler_htonl;
    HandlerFuncTable[DTCODE_htons] = DTHandler_htons;
    HandlerFuncTable[DTCODE_ioctlsocket] = DTHandler_ioctlsocket;
    HandlerFuncTable[DTCODE_listen] = DTHandler_listen;
    HandlerFuncTable[DTCODE_ntohl] = DTHandler_ntohl;
    HandlerFuncTable[DTCODE_ntohs] = DTHandler_ntohs;
    HandlerFuncTable[DTCODE_recv] = DTHandler_recv;
    HandlerFuncTable[DTCODE_recvfrom] = DTHandler_recvfrom;
    HandlerFuncTable[DTCODE_select] = DTHandler_select;
    HandlerFuncTable[DTCODE_send] = DTHandler_send;
    HandlerFuncTable[DTCODE_sendto] = DTHandler_sendto;
    HandlerFuncTable[DTCODE_setsockopt] = DTHandler_setsockopt;
    HandlerFuncTable[DTCODE_shutdown] = DTHandler_shutdown;
    HandlerFuncTable[DTCODE_socket] = DTHandler_socket;
    HandlerFuncTable[DTCODE_WSAAccept] = DTHandler_WSAAccept;
    HandlerFuncTable[DTCODE_WSAAsyncSelect] = DTHandler_WSAAsyncSelect;
    HandlerFuncTable[DTCODE_WSACancelBlockingCall] =
      DTHandler_WSACancelBlockingCall;
    HandlerFuncTable[DTCODE_WSACleanup] = DTHandler_WSACleanup;
    HandlerFuncTable[DTCODE_WSACloseEvent] = DTHandler_WSACloseEvent;
    HandlerFuncTable[DTCODE_WSAConnect] = DTHandler_WSAConnect;
    HandlerFuncTable[DTCODE_WSACreateEvent] = DTHandler_WSACreateEvent;
    HandlerFuncTable[DTCODE_WSADuplicateSocketA] =
      DTHandler_WSADuplicateSocketA;
    HandlerFuncTable[DTCODE_WSADuplicateSocketW] =
      DTHandler_WSADuplicateSocketW;
    HandlerFuncTable[DTCODE_WSAEnumNetworkEvents] =
      DTHandler_WSAEnumNetworkEvents;
    HandlerFuncTable[DTCODE_WSAEnumProtocolsA] = DTHandler_WSAEnumProtocolsA;
    HandlerFuncTable[DTCODE_WSAEnumProtocolsW] = DTHandler_WSAEnumProtocolsW;
    HandlerFuncTable[DTCODE_WSAEventSelect] = DTHandler_WSAEventSelect;
    HandlerFuncTable[DTCODE_WSAGetLastError] = DTHandler_WSAGetLastError;
    HandlerFuncTable[DTCODE_WSAGetOverlappedResult] =
      DTHandler_WSAGetOverlappedResult;
    HandlerFuncTable[DTCODE_WSAGetQOSByName] = DTHandler_WSAGetQOSByName;
    HandlerFuncTable[DTCODE_WSAHtonl] = DTHandler_WSAHtonl;
    HandlerFuncTable[DTCODE_WSAHtons] = DTHandler_WSAHtons;
    HandlerFuncTable[DTCODE_WSAIoctl] = DTHandler_WSAIoctl;
    HandlerFuncTable[DTCODE_WSAIsBlocking] = DTHandler_WSAIsBlocking;
    HandlerFuncTable[DTCODE_WSAJoinLeaf] = DTHandler_WSAJoinLeaf;
    HandlerFuncTable[DTCODE_WSANtohl] = DTHandler_WSANtohl;
    HandlerFuncTable[DTCODE_WSANtohs] = DTHandler_WSANtohs;
    HandlerFuncTable[DTCODE_WSARecv] = DTHandler_WSARecv;
    HandlerFuncTable[DTCODE_WSARecvDisconnect] =
      DTHandler_WSARecvDisconnect;
    HandlerFuncTable[DTCODE_WSARecvFrom] = DTHandler_WSARecvFrom;
    HandlerFuncTable[DTCODE_WSAResetEvent] = DTHandler_WSAResetEvent;
    HandlerFuncTable[DTCODE_WSASend] = DTHandler_WSASend;
    HandlerFuncTable[DTCODE_WSASendDisconnect] =
      DTHandler_WSASendDisconnect;
    HandlerFuncTable[DTCODE_WSASendTo] = DTHandler_WSASendTo;
    HandlerFuncTable[DTCODE_WSASetBlockingHook] =
      DTHandler_WSASetBlockingHook;
    HandlerFuncTable[DTCODE_WSASetEvent] = DTHandler_WSASetEvent;
    HandlerFuncTable[DTCODE_WSASetLastError] = DTHandler_WSASetLastError;
    HandlerFuncTable[DTCODE_WSASocketA] = DTHandler_WSASocketA;
    HandlerFuncTable[DTCODE_WSASocketW] = DTHandler_WSASocketW;
    HandlerFuncTable[DTCODE_WSAStartup] = DTHandler_WSAStartup;
    HandlerFuncTable[DTCODE_WSAUnhookBlockingHook] =
      DTHandler_WSAUnhookBlockingHook;
    HandlerFuncTable[DTCODE_WSAWaitForMultipleEvents] =
      DTHandler_WSAWaitForMultipleEvents;
    HandlerFuncTable[DTCODE_gethostbyaddr] = DTHandler_gethostbyaddr;
    HandlerFuncTable[DTCODE_gethostbyname] = DTHandler_gethostbyname;
    HandlerFuncTable[DTCODE_gethostname] = DTHandler_gethostname;
    HandlerFuncTable[DTCODE_getprotobyname] = DTHandler_getprotobyname;
    HandlerFuncTable[DTCODE_getprotobynumber] = DTHandler_getprotobynumber;
    HandlerFuncTable[DTCODE_getservbyname] = DTHandler_getservbyname;
    HandlerFuncTable[DTCODE_getservbyport] = DTHandler_getservbyport;
    HandlerFuncTable[DTCODE_inet_addr] = DTHandler_inet_addr;
    HandlerFuncTable[DTCODE_inet_ntoa] = DTHandler_inet_ntoa;
    HandlerFuncTable[DTCODE_WSAAsyncGetHostByAddr] =
      DTHandler_WSAAsyncGetHostByAddr;
    HandlerFuncTable[DTCODE_WSAAsyncGetHostByName] =
      DTHandler_WSAAsyncGetHostByName;
    HandlerFuncTable[DTCODE_WSAAsyncGetProtoByName] =
      DTHandler_WSAAsyncGetProtoByName;
    HandlerFuncTable[DTCODE_WSAAsyncGetProtoByNumber] =
      DTHandler_WSAAsyncGetProtoByNumber;
    HandlerFuncTable[DTCODE_WSAAsyncGetServByName] =
      DTHandler_WSAAsyncGetServByName;
    HandlerFuncTable[DTCODE_WSAAsyncGetServByPort] =
      DTHandler_WSAAsyncGetServByPort;
    HandlerFuncTable[DTCODE_WSACancelAsyncRequest] =
      DTHandler_WSACancelAsyncRequest;
    HandlerFuncTable[DTCODE_WSPAccept] = DTHandler_WSPAccept;
    HandlerFuncTable[DTCODE_WSPAsyncSelect] = DTHandler_WSPAsyncSelect;
    HandlerFuncTable[DTCODE_WSPBind] = DTHandler_WSPBind;
    HandlerFuncTable[DTCODE_WSPCancelBlockingCall] =
      DTHandler_WSPCancelBlockingCall;
    HandlerFuncTable[DTCODE_WSPCleanup] = DTHandler_WSPCleanup;
    HandlerFuncTable[DTCODE_WSPCloseSocket] = DTHandler_WSPCloseSocket;
    HandlerFuncTable[DTCODE_WSPConnect] = DTHandler_WSPConnect;
    HandlerFuncTable[DTCODE_WSPDuplicateSocket] =
      DTHandler_WSPDuplicateSocket;
    HandlerFuncTable[DTCODE_WSPEnumNetworkEvents] =
      DTHandler_WSPEnumNetworkEvents;
    HandlerFuncTable[DTCODE_WSPEventSelect] = DTHandler_WSPEventSelect;
    HandlerFuncTable[DTCODE_WSPGetOverlappedResult] =
      DTHandler_WSPGetOverlappedResult;
    HandlerFuncTable[DTCODE_WSPGetPeerName] = DTHandler_WSPGetPeerName;
    HandlerFuncTable[DTCODE_WSPGetSockName] = DTHandler_WSPGetSockName;
    HandlerFuncTable[DTCODE_WSPGetSockOpt] = DTHandler_WSPGetSockOpt;
    HandlerFuncTable[DTCODE_WSPGetQOSByName] = DTHandler_WSPGetQOSByName;
    HandlerFuncTable[DTCODE_WSPIoctl] = DTHandler_WSPIoctl;
    HandlerFuncTable[DTCODE_WSPJoinLeaf] = DTHandler_WSPJoinLeaf;
    HandlerFuncTable[DTCODE_WSPListen] = DTHandler_WSPListen;
    HandlerFuncTable[DTCODE_WSPRecv] = DTHandler_WSPRecv;
    HandlerFuncTable[DTCODE_WSPRecvDisconnect] =
      DTHandler_WSPRecvDisconnect;
    HandlerFuncTable[DTCODE_WSPRecvFrom] = DTHandler_WSPRecvFrom;
    HandlerFuncTable[DTCODE_WSPSelect] = DTHandler_WSPSelect;
    HandlerFuncTable[DTCODE_WSPSend] = DTHandler_WSPSend;
    HandlerFuncTable[DTCODE_WSPSendDisconnect] =
      DTHandler_WSPSendDisconnect;
    HandlerFuncTable[DTCODE_WSPSendTo] = DTHandler_WSPSendTo;
    HandlerFuncTable[DTCODE_WSPSetSockOpt] = DTHandler_WSPSetSockOpt;
    HandlerFuncTable[DTCODE_WSPShutdown] = DTHandler_WSPShutdown;
    HandlerFuncTable[DTCODE_WSPSocket] = DTHandler_WSPSocket;
    HandlerFuncTable[DTCODE_WSPStartup] = DTHandler_WSPStartup;
    HandlerFuncTable[DTCODE_WPUCloseEvent] = DTHandler_WPUCloseEvent;
    HandlerFuncTable[DTCODE_WPUCloseSocketHandle] =
      DTHandler_WPUCloseSocketHandle;
    HandlerFuncTable[DTCODE_WPUCreateEvent] = DTHandler_WPUCreateEvent;
    HandlerFuncTable[DTCODE_WPUCreateSocketHandle] =
      DTHandler_WPUCreateSocketHandle;
    HandlerFuncTable[DTCODE_WSCDeinstallProvider] =
      DTHandler_WSCDeinstallProvider;
    HandlerFuncTable[DTCODE_WSCInstallProvider] =
      DTHandler_WSCInstallProvider;
    HandlerFuncTable[DTCODE_WPUModifyIFSHandle] =
      DTHandler_WPUModifyIFSHandle;
    HandlerFuncTable[DTCODE_WPUQueryBlockingCallback] =
      DTHandler_WPUQueryBlockingCallback;
    HandlerFuncTable[DTCODE_WPUQuerySocketHandleContext] =
      DTHandler_WPUQuerySocketHandleContext;
    HandlerFuncTable[DTCODE_WPUQueueApc] = DTHandler_WPUQueueApc;
    HandlerFuncTable[DTCODE_WPUResetEvent] = DTHandler_WPUResetEvent;
    HandlerFuncTable[DTCODE_WPUSetEvent] = DTHandler_WPUSetEvent;
    HandlerFuncTable[DTCODE_WSCEnumProtocols] = DTHandler_WSCEnumProtocols;
    HandlerFuncTable[DTCODE_WPUGetProviderPath] = DTHandler_WPUGetProviderPath;
    HandlerFuncTable[DTCODE_WPUPostMessage] = DTHandler_WPUPostMessage;
    HandlerFuncTable[DTCODE_WPUFDIsSet] = DTHandler_WPUFDIsSet;
    HandlerFuncTable[DTCODE___WSAFDIsSet] = DTHandler___WSAFDIsSet;
    HandlerFuncTable[DTCODE_WSAAddressToStringA] =
        DTHandler_WSAAddressToStringA;
    HandlerFuncTable[DTCODE_WSAAddressToStringW] =
        DTHandler_WSAAddressToStringW;
    HandlerFuncTable[DTCODE_WSAStringToAddressA] =
        DTHandler_WSAStringToAddressA;
    HandlerFuncTable[DTCODE_WSAStringToAddressW] =
        DTHandler_WSAStringToAddressW;
    HandlerFuncTable[DTCODE_WSALookupServiceBeginA] =
        DTHandler_WSALookupServiceBeginA;
    HandlerFuncTable[DTCODE_WSALookupServiceBeginW] =
        DTHandler_WSALookupServiceBeginW;
    HandlerFuncTable[DTCODE_WSALookupServiceNextA] =
        DTHandler_WSALookupServiceNextA;
    HandlerFuncTable[DTCODE_WSALookupServiceNextW] =
        DTHandler_WSALookupServiceNextW;
    HandlerFuncTable[DTCODE_WSALookupServiceEnd] =
        DTHandler_WSALookupServiceEnd;
    HandlerFuncTable[DTCODE_WSAInstallServiceClassA] =
        DTHandler_WSAInstallServiceClassA;
    HandlerFuncTable[DTCODE_WSAInstallServiceClassW] =
        DTHandler_WSAInstallServiceClassW;
    HandlerFuncTable[DTCODE_WSASetServiceA] =
        DTHandler_WSASetServiceA;
    HandlerFuncTable[DTCODE_WSASetServiceW] =
        DTHandler_WSASetServiceW;
    HandlerFuncTable[DTCODE_WSARemoveServiceClass] =
        DTHandler_WSARemoveServiceClass;
    HandlerFuncTable[DTCODE_WSAGetServiceClassInfoA] =
        DTHandler_WSAGetServiceClassInfoA;
    HandlerFuncTable[DTCODE_WSAGetServiceClassInfoW] =
        DTHandler_WSAGetServiceClassInfoW;
    HandlerFuncTable[DTCODE_WSAEnumNameSpaceProvidersA] =
        DTHandler_WSAEnumNameSpaceProvidersA;
    HandlerFuncTable[DTCODE_WSAEnumNameSpaceProvidersW] =
        DTHandler_WSAEnumNameSpaceProvidersW;
    HandlerFuncTable[DTCODE_WSAGetServiceClassNameByClassIdA] =
        DTHandler_WSAGetServiceClassNameByClassIdA;
    HandlerFuncTable[DTCODE_WSAGetServiceClassNameByClassIdW] =
        DTHandler_WSAGetServiceClassNameByClassIdW;
    HandlerFuncTable[DTCODE_NSPLookupServiceBegin] =
        DTHandler_NSPLookupServiceBegin;
    HandlerFuncTable[DTCODE_NSPLookupServiceNext] =
        DTHandler_NSPLookupServiceNext;
    HandlerFuncTable[DTCODE_NSPLookupServiceEnd] =
        DTHandler_NSPLookupServiceEnd;
    HandlerFuncTable[DTCODE_NSPInstallServiceClass] =
        DTHandler_NSPInstallServiceClass;
    HandlerFuncTable[DTCODE_NSPSetService] =
        DTHandler_NSPSetService;
    HandlerFuncTable[DTCODE_NSPRemoveServiceClass] =
        DTHandler_NSPRemoveServiceClass;
    HandlerFuncTable[DTCODE_NSPGetServiceClassInfo] =
        DTHandler_NSPGetServiceClassInfo;

    return(TRUE);
}

/*++

  Note:

      This comment applies to all DTHandler_xxxx functions.

  Function Description:

      For the present, each handler function simply adds "xxxx
      called." to the end of the string passed in, and calls
      DTTextOut to output the whole string to the Debug Window.  This
      allows handler functions to be disabled easily, or to output
      more information (like the arguments) via multiple calls to
      DTTextOut, etc.  See the Debug/Trace documentation for more
      information.

  Arguments:

      vl -- Variable used to strip arguments off the stack of
      WSA[Pre|Post]ApiNotify, the calling function.

      ReturnValue -- A void pointer to the return value for the
      original API/SPI function.  Use a local variable to cast it to
      the appropriate type.

      LibraryName -- For API functions, this should just be
      "WinSock2", but for SPI functions it is the name of the service
      provider.  This is to distinguish among the multiple service
      providers that WinSock2 may be calling.

      Buffer -- A string containg some info about the thread and
      function call # of this call.

      Index -- How many characters are in the above.

      BufLen -- How big is the buffer.

      PreOrPost -- TRUE if called by a WSAPreApiNotify, FALSE if
      called by WSAPostApiNotify.

  Return Value:

      If the PreOrPost argument is FALSE, then the return value is
      ignored.  If it's TRUE, then the return value indicates to
      WSAPreApiNotify whether or not we want to short-circuit the API
      or SPI function.  TRUE means short-circuit it, FALSE mean don't.

--*/

BOOL CALLBACK
DTHandler_accept(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    SOCKET *RetVal = (SOCKET *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    struct sockaddr FAR **addr = va_arg(vl, struct sockaddr FAR **);
    int FAR **addrlen = va_arg(vl, int FAR **);

    wsprintf(Buffer + Index, "accept() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_bind(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    struct sockaddr FAR **name = va_arg(vl, struct sockaddr FAR **);
    int *namelen = va_arg(vl, int *);

    wsprintf(Buffer + Index, "bind() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_closesocket(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);

    wsprintf(Buffer + Index, "closesocket() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_connect(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    struct sockaddr FAR **name = va_arg(vl, struct sockaddr FAR **);
    int *namelen = va_arg(vl, int *);

    wsprintf(Buffer + Index, "connect() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_getpeername(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    struct sockaddr FAR **name = va_arg(vl, struct sockaddr FAR **);
    int FAR **namelen = va_arg(vl, int FAR **);

    wsprintf(Buffer + Index, "getpeername() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_getsockname(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    struct sockaddr FAR **name = va_arg(vl, struct sockaddr FAR **);
    int FAR **namelen = va_arg(vl, int FAR **);

    wsprintf(Buffer + Index, "getsockname() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_getsockopt(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    int *level = va_arg(vl, int *);
    int *optname = va_arg(vl, int *);
    char FAR **optval = va_arg(vl, char FAR **);
    int FAR **optlen = va_arg(vl, int FAR **);

    wsprintf(Buffer + Index, "getsockopt() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_htonl(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    u_long *RetVal = (u_long *)ReturnValue;
    u_long *hostlong = va_arg(vl, u_long *);

    wsprintf(Buffer + Index, "htonl() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_htons(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    u_short *RetVal = (u_short *)ReturnValue;
    u_short *hostshort = va_arg(vl, u_short *);

    wsprintf(Buffer + Index, "htons() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_ioctlsocket(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    long *cmd = va_arg(vl, long *);
    u_long FAR **argp = va_arg(vl, u_long FAR **);

    wsprintf(Buffer + Index, "ioctlsocket() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_listen(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    int *backlog = va_arg(vl, int *);

    wsprintf(Buffer + Index, "listen() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_ntohl(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    u_long *RetVal = (u_long *)ReturnValue;
    u_long *netlong = va_arg(vl, u_long *);

    wsprintf(Buffer + Index, "ntohl() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_ntohs(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    u_short *RetVal = (u_short *)ReturnValue;
    u_short *netshort = va_arg(vl, u_short *);

    wsprintf(Buffer + Index, "ntohs() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_recv(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    char FAR **buf = va_arg(vl, char FAR **);
    int *len = va_arg(vl, int *);
    int *flags = va_arg(vl, int *);

    wsprintf(Buffer + Index, "recv() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_recvfrom(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    char FAR **buf = va_arg(vl, char FAR **);
    int *len = va_arg(vl, int *);
    int *flags = va_arg(vl, int *);
    struct sockaddr FAR **from = va_arg(vl, struct sockaddr FAR **);
    int FAR **fromlen = va_arg(vl, int FAR **);

    wsprintf(Buffer + Index, "recvfrom() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_select(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    int *nfds = va_arg(vl, int *);
    fd_set FAR **readfds = va_arg(vl, fd_set FAR **);
    fd_set FAR **writefds = va_arg(vl, fd_set FAR **);
    fd_set FAR **exceptfds = va_arg(vl, fd_set FAR **);
    struct timeval FAR **timeout = va_arg(vl, struct timeval FAR **);

    wsprintf(Buffer + Index, "select() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_send(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    char FAR **buf = va_arg(vl, char FAR **);
    int *len = va_arg(vl, int *);
    int *flags = va_arg(vl, int *);

    wsprintf(Buffer + Index, "send() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_sendto(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    char FAR **buf = va_arg(vl, char FAR **);
    int *len = va_arg(vl, int *);
    int *flags = va_arg(vl, int *);
    struct sockaddr FAR **to = va_arg(vl, struct sockaddr FAR **);
    int *tolen = va_arg(vl, int *);

    wsprintf(Buffer + Index, "sendto() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_setsockopt(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    int *level = va_arg(vl, int *);
    int *optname = va_arg(vl, int *);
    char FAR **optval = va_arg(vl, char FAR **);
    int *optlen = va_arg(vl, int *);

    wsprintf(Buffer + Index, "setsockopt() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_shutdown(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    int *how = va_arg(vl, int *);

    wsprintf(Buffer + Index, "shutdown() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_socket(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    SOCKET *RetVal = (SOCKET *)ReturnValue;
    int *af = va_arg(vl, int *);
    int *type = va_arg(vl, int *);
    int *protocol = va_arg(vl, int *);

    wsprintf(Buffer + Index, "socket() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSAAccept(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    SOCKET *RetVal = (SOCKET *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    struct sockaddr FAR **addr = va_arg(vl, struct sockaddr FAR **);
    LPINT *addrlen = va_arg(vl, LPINT *);
    LPCONDITIONPROC *lpfnCondition = va_arg(vl, LPCONDITIONPROC *);
    DWORD *dwCallbackData = va_arg(vl, DWORD *);

    wsprintf(Buffer + Index, "WSAAccept() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSAAsyncSelect(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    HWND *hWnd = va_arg(vl, HWND *);
    u_int *wMsg = va_arg(vl, u_int *);
    long *lEvent = va_arg(vl, long *);

    wsprintf(Buffer + Index, "WSAAsyncSelect() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSACancelBlockingCall(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;

    wsprintf(Buffer + Index, "WSACancelBlockingCall() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSACleanup(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;

    wsprintf(Buffer + Index, "WSACleanup() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSACloseEvent(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    BOOL *RetVal = (BOOL *)ReturnValue;
    WSAEVENT *hEvent = va_arg(vl, WSAEVENT *);

    wsprintf(Buffer + Index, "WSACloseEvent() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSAConnect(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    struct sockaddr FAR **name = va_arg(vl, struct sockaddr FAR **);
    int *namelen = va_arg(vl, int *);
    LPWSABUF *lpCallerData = va_arg(vl, LPWSABUF *);
    LPWSABUF *lpCalleeData = va_arg(vl, LPWSABUF *);
    LPQOS *lpSQOS = va_arg(vl, LPQOS *);
    LPQOS *lpGQOS = va_arg(vl, LPQOS *);

    wsprintf(Buffer + Index, "WSAConnect() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSACreateEvent(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    WSAEVENT *RetVal = (WSAEVENT *)ReturnValue;

    wsprintf(Buffer + Index, "WSACreateEvent() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSADuplicateSocketA(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    DWORD *dwProcessId = va_arg(vl, DWORD *);
    LPWSAPROTOCOL_INFOA *lpProtocolInfo = va_arg(vl, LPWSAPROTOCOL_INFOA *);

    wsprintf(Buffer + Index, "WSADuplicateSocketA() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSADuplicateSocketW(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    DWORD *dwProcessId = va_arg(vl, DWORD *);
    LPWSAPROTOCOL_INFOW *lpProtocolInfo = va_arg(vl, LPWSAPROTOCOL_INFOW *);

    wsprintf(Buffer + Index, "WSADuplicateSocketW() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSAEnumNetworkEvents(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    WSAEVENT *hEventObject = va_arg(vl, WSAEVENT *);
    LPWSANETWORKEVENTS *lpNetworkEvents = va_arg(vl, LPWSANETWORKEVENTS *);

    wsprintf(Buffer + Index, "WSAEnumNetworkEvents() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSAEnumProtocolsA(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    LPINT *lpiProtocols = va_arg(vl, LPINT *);
    LPWSAPROTOCOL_INFOA *lpProtocolBuffer = va_arg(vl, LPWSAPROTOCOL_INFOA *);
    LPDWORD *lpdwBufferLength = va_arg(vl, LPDWORD *);

    wsprintf(Buffer + Index, "WSAEnumProtocolsA() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSAEnumProtocolsW(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    LPINT *lpiProtocols = va_arg(vl, LPINT *);
    LPWSAPROTOCOL_INFOW *lpProtocolBuffer = va_arg(vl, LPWSAPROTOCOL_INFOW *);
    LPDWORD *lpdwBufferLength = va_arg(vl, LPDWORD *);

    wsprintf(Buffer + Index, "WSAEnumProtocolsW() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSAEventSelect(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    WSAEVENT *hEventObject = va_arg(vl, WSAEVENT *);
    long *lNetworkEvents = va_arg(vl, long *);

    wsprintf(Buffer + Index, "WSAEventSelect() %s.\r\n",
         PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSAGetLastError(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;

    wsprintf(Buffer + Index, "WSAGetLastError() %s.\r\n",
         PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSAGetOverlappedResult(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    BOOL *RetVal = (BOOL *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    LPWSAOVERLAPPED *lpOverlapped = va_arg(vl, LPWSAOVERLAPPED *);
    LPDWORD *lpcbTransfer = va_arg(vl, LPDWORD *);
    BOOL *fWait = va_arg(vl, BOOL *);
    LPDWORD *lpdwFlags = va_arg(vl, LPDWORD *);

    wsprintf(Buffer + Index, "WSAGetOverlappedResult() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSAGetQOSByName(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    BOOL *RetVal = (BOOL *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    LPWSABUF *lpQOSName = va_arg(vl, LPWSABUF *);
    LPQOS *lpQOS = va_arg(vl, LPQOS *);

    wsprintf(Buffer + Index, "WSAGetQOSByName() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSAHtonl(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    u_long *hostlong = va_arg(vl, u_long *);
    u_long FAR **lpnetlong = va_arg(vl, u_long FAR **);

    wsprintf(Buffer + Index, "WSAHtonl() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSAHtons(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    u_short *hostshort = va_arg(vl, u_short *);
    u_short FAR **lpnetshort = va_arg(vl, u_short FAR **);

    wsprintf(Buffer + Index, "WSAHtons() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSAIoctl(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    DWORD *dwIoControlCode = va_arg(vl, DWORD *);
    LPVOID *lpvInBuffer = va_arg(vl, LPVOID *);
    DWORD *cbInBuffer = va_arg(vl, DWORD *);
    LPVOID *lpvOutBuffer = va_arg(vl, LPVOID *);
    DWORD *cbOutBuffer = va_arg(vl, DWORD *);
    LPDWORD *lpcbBytesReturned = va_arg(vl, LPDWORD *);
    LPWSAOVERLAPPED *lpOverlapped = va_arg(vl, LPWSAOVERLAPPED *);
    LPWSAOVERLAPPED_COMPLETION_ROUTINE *lpCompletionRoutine =
      va_arg(vl, LPWSAOVERLAPPED_COMPLETION_ROUTINE *);

    wsprintf(Buffer + Index, "WSAIoctl() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSAIsBlocking(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    BOOL *RetVal = (BOOL *)ReturnValue;

    wsprintf(Buffer + Index, "WSAIsBlocking() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSAJoinLeaf(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    SOCKET *RetVal = (SOCKET *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    struct sockaddr FAR **name = va_arg(vl, struct sockaddr FAR **);
    int *namelen = va_arg(vl, int *);
    LPWSABUF *lpCallerData = va_arg(vl, LPWSABUF *);
    LPWSABUF *lpCalleeData = va_arg(vl, LPWSABUF *);
    LPQOS *lpSQOS = va_arg(vl, LPQOS *);
    LPQOS *lpGQOS = va_arg(vl, LPQOS *);
    DWORD *dwFlags = va_arg(vl, DWORD *);

    wsprintf(Buffer + Index, "WSAJoinLeaf() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSANtohl(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    u_long *netlong = va_arg(vl, u_long *);
    u_long FAR **lphostlong = va_arg(vl, u_long FAR **);

    wsprintf(Buffer + Index, "WSANtohl() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSANtohs(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    u_short *netshort = va_arg(vl, u_short *);
    u_short FAR **lphostshort = va_arg(vl, u_short FAR **);

    wsprintf(Buffer + Index, "WSANtohs() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSARecv(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    LPWSABUF *lpBuffers = va_arg(vl, LPWSABUF *);
    DWORD *dwBufferCount = va_arg(vl, DWORD *);
    LPDWORD *lpNumberOfBytesRecvd = va_arg(vl, LPDWORD *);
    LPDWORD *lpFlags = va_arg(vl, LPDWORD *);
    LPWSAOVERLAPPED *lpOverlapped = va_arg(vl, LPWSAOVERLAPPED *);
    LPWSAOVERLAPPED_COMPLETION_ROUTINE *lpCompletionRoutine =
      va_arg(vl, LPWSAOVERLAPPED_COMPLETION_ROUTINE *);

    wsprintf(Buffer + Index, "WSARecv() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSARecvDisconnect(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    LPWSABUF *lpInboundDisconnectData = va_arg(vl, LPWSABUF *);

    wsprintf(Buffer + Index, "WSARecvDisconnect() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSARecvFrom(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    LPWSABUF *lpBuffers = va_arg(vl, LPWSABUF *);
    DWORD *dwBufferCount = va_arg(vl, DWORD *);
    LPDWORD *lpNumberOfBytesRecvd = va_arg(vl, LPDWORD *);
    LPDWORD *lpFlags = va_arg(vl, LPDWORD *);
    struct sockaddr FAR **lpFrom = va_arg(vl, struct sockaddr FAR **);
    LPINT *lpFromlen = va_arg(vl, LPINT *);
    LPWSAOVERLAPPED *lpOverlapped = va_arg(vl, LPWSAOVERLAPPED *);
    LPWSAOVERLAPPED_COMPLETION_ROUTINE *lpCompletionRoutine =
      va_arg(vl, LPWSAOVERLAPPED_COMPLETION_ROUTINE *);

    wsprintf(Buffer + Index, "WSARecvFrom() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSAResetEvent(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    BOOL *RetVal = (BOOL *)ReturnValue;
    WSAEVENT *hEvent = va_arg(vl, WSAEVENT *);

    wsprintf(Buffer + Index, "WSAResetEvent() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSASend(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    LPWSABUF *lpBuffers = va_arg(vl, LPWSABUF *);
    DWORD *dwBufferCount = va_arg(vl, DWORD *);
    LPDWORD *lpNumberOfBytesSent = va_arg(vl, LPDWORD *);
    DWORD *dwFlags = va_arg(vl, DWORD *);
    LPWSAOVERLAPPED *lpOverlapped = va_arg(vl, LPWSAOVERLAPPED *);
    LPWSAOVERLAPPED_COMPLETION_ROUTINE *lpCompletionRoutine =
      va_arg(vl, LPWSAOVERLAPPED_COMPLETION_ROUTINE *);

    wsprintf(Buffer + Index, "WSASend() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSASendDisconnect(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    LPWSABUF *lpOutboundDisconnectData = va_arg(vl, LPWSABUF *);

    wsprintf(Buffer + Index, "WSASendDisconnect() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSASendTo(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    LPWSABUF *lpBuffers = va_arg(vl, LPWSABUF *);
    DWORD *dwBufferCount = va_arg(vl, DWORD *);
    LPDWORD *lpNumberOfBytesSent = va_arg(vl, LPDWORD *);
    DWORD *dwFlags = va_arg(vl, DWORD *);
    struct sockaddr FAR **lpTo = va_arg(vl, struct sockaddr FAR **);
    int *iTolen = va_arg(vl, int *);
    LPWSAOVERLAPPED *lpOverlapped = va_arg(vl, LPWSAOVERLAPPED *);
    LPWSAOVERLAPPED_COMPLETION_ROUTINE *lpCompletionRoutine =
      va_arg(vl, LPWSAOVERLAPPED_COMPLETION_ROUTINE *);

    wsprintf(Buffer + Index, "WSASendTo() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSASetBlockingHook(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    FARPROC *RetVal = (FARPROC *)ReturnValue;
    FARPROC *lpBlockFunc = va_arg(vl, FARPROC *);

    wsprintf(Buffer + Index, "WSASetBlockingHook() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSASetEvent(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    BOOL *RetVal = (BOOL *)ReturnValue;
    WSAEVENT *hEvent = va_arg(vl, WSAEVENT *);

    wsprintf(Buffer + Index, "WSASetEvent() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSASetLastError(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    // WSASetLastError returns void, so ReturnValue should be NULL
    int *iError = va_arg(vl, int *);

    wsprintf(Buffer + Index, "WSASetLastError() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSASocketA(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    SOCKET *RetVal = (SOCKET *)ReturnValue;
    int *af = va_arg(vl, int *);
    int *type = va_arg(vl, int *);
    int *protocol = va_arg(vl, int *);
    LPWSAPROTOCOL_INFOA *lpProtocolInfo = va_arg(vl, LPWSAPROTOCOL_INFOA *);
    GROUP *g = va_arg(vl, GROUP *);
    DWORD *dwFlags = va_arg(vl, DWORD *);

    wsprintf(Buffer + Index, "WSASocketA() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSASocketW(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    SOCKET *RetVal = (SOCKET *)ReturnValue;
    int *af = va_arg(vl, int *);
    int *type = va_arg(vl, int *);
    int *protocol = va_arg(vl, int *);
    LPWSAPROTOCOL_INFOW *lpProtocolInfo = va_arg(vl, LPWSAPROTOCOL_INFOW *);
    GROUP *g = va_arg(vl, GROUP *);
    DWORD *dwFlags = va_arg(vl, DWORD *);

    wsprintf(Buffer + Index, "WSASocketW() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSAStartup(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    WORD *wVersionRequested = va_arg(vl, WORD *);
    LPWSADATA *lpWSAData = va_arg(vl, LPWSADATA *);

    wsprintf(Buffer + Index, "WSAStartup() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSAUnhookBlockingHook(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;

    wsprintf(Buffer + Index, "WSAUnhookBlockingHook() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSAWaitForMultipleEvents(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    DWORD *RetVal = (DWORD *)ReturnValue;
    DWORD *cEvents = va_arg(vl, DWORD *);
    WSAEVENT FAR **lphEvents = va_arg(vl, WSAEVENT FAR **);
    BOOL *fWaitAll = va_arg(vl, BOOL *);
    DWORD *dwTimeout = va_arg(vl, DWORD *);
    BOOL *fAlertable = va_arg(vl, BOOL *);

    wsprintf(Buffer + Index, "WSAWaitForMultipleEvents() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_gethostbyaddr(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    struct hostent FAR **RetVal = (struct hostent FAR **)ReturnValue;
    char FAR **addr = va_arg(vl, char FAR **);
    int *len = va_arg(vl, int *);
    int *type = va_arg(vl, int *);

    wsprintf(Buffer + Index, "gethostbyaddr() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_gethostbyname(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    struct hostent FAR **RetVal = (struct hostent FAR **)ReturnValue;
    char FAR **name = va_arg(vl, char FAR **);

    wsprintf(Buffer + Index, "gethostbyname() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_gethostname(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    char FAR **name = va_arg(vl, char FAR **);
    int *namelen = va_arg(vl, int *);

    wsprintf(Buffer + Index, "gethostname() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_getprotobyname(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    struct protoent FAR **RetVal = (struct protoent FAR **)ReturnValue;
    char FAR **name = va_arg(vl, char FAR **);

    wsprintf(Buffer + Index, "getprotobyname() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_getprotobynumber(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    struct protoent FAR **RetVal = (struct protoent FAR **)ReturnValue;
    int *number = va_arg(vl, int *);

    wsprintf(Buffer + Index, "getprotobynumber() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_getservbyname(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    struct servent FAR **RetVal = (struct servent FAR **)ReturnValue;
    char FAR **name = va_arg(vl, char FAR **);
    char FAR **proto = va_arg(vl, char FAR **);

    wsprintf(Buffer + Index, "getservbyname() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_getservbyport(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    struct servent FAR **RetVal = (struct servent FAR **)ReturnValue;
    int *port = va_arg(vl, int *);
    char FAR **proto = va_arg(vl, char FAR **);

    wsprintf(Buffer + Index, "getservbyport() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_inet_addr(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    unsigned long *RetVal = (unsigned long *)ReturnValue;
    char FAR **cp = va_arg(vl, char FAR **);

    wsprintf(Buffer + Index, "inet_addr() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_inet_ntoa(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    char FAR **RetVal = (char FAR **)ReturnValue;
    struct in_addr *in = va_arg(vl, struct in_addr *);

    wsprintf(Buffer + Index, "inet_ntoa() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);

return(FALSE);
}

BOOL CALLBACK
DTHandler_WSAAsyncGetHostByAddr(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    HANDLE *RetVal = (HANDLE *)ReturnValue;
    HWND *hWnd = va_arg(vl, HWND *);
    u_int *wMsg = va_arg(vl, u_int *);
    char FAR **addr = va_arg(vl, char FAR **);
    int *len = va_arg(vl, int *);
    int *type = va_arg(vl, int *);
    char FAR **buf = va_arg(vl, char FAR **);
    int *buflen = va_arg(vl, int *);

    wsprintf(Buffer + Index, "WSAAsyncGetHostByAddr() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSAAsyncGetHostByName(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    HANDLE *RetVal = (HANDLE *)ReturnValue;
    HWND *hWnd = va_arg(vl, HWND *);
    u_int *wMsg = va_arg(vl, u_int *);
    char FAR **name = va_arg(vl, char FAR **);
    char FAR **buf = va_arg(vl, char FAR **);
    int *buflen = va_arg(vl, int *);

    wsprintf(Buffer + Index, "WSAAsyncGetHostByName() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSAAsyncGetProtoByName(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    HANDLE *RetVal = (HANDLE *)ReturnValue;
    HWND *hWnd = va_arg(vl, HWND *);
    u_int *wMsg = va_arg(vl, u_int *);
    char FAR **name = va_arg(vl, char FAR **);
    char FAR **buf = va_arg(vl, char FAR **);
    int *buflen = va_arg(vl, int *);

    wsprintf(Buffer + Index, "WSAAsyncGetProtoByName() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSAAsyncGetProtoByNumber(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    HANDLE *RetVal = (HANDLE *)ReturnValue;
    HWND *hWnd = va_arg(vl, HWND *);
    u_int *wMsg = va_arg(vl, u_int *);
    int *number = va_arg(vl, int *);
    char FAR **buf = va_arg(vl, char FAR **);
    int *buflen = va_arg(vl, int *);

    wsprintf(Buffer + Index, "WSAAsyncGetProtoByNumber() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSAAsyncGetServByName(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    HANDLE *RetVal = (HANDLE *)ReturnValue;
    HWND *hWnd = va_arg(vl, HWND *);
    u_int *wMsg = va_arg(vl, u_int *);
    char FAR **name = va_arg(vl, char FAR **);
    char FAR **proto = va_arg(vl, char FAR **);
    char FAR **buf = va_arg(vl, char FAR **);
    int *buflen = va_arg(vl, int *);

    wsprintf(Buffer + Index, "WSAAsyncGetServByName() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSAAsyncGetServByPort(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    HANDLE *RetVal = (HANDLE *)ReturnValue;
    HWND *hWnd = va_arg(vl, HWND *);
    u_int *wMsg = va_arg(vl, u_int *);
    int *port = va_arg(vl, int *);
    char FAR **proto = va_arg(vl, char FAR **);
    char FAR **buf = va_arg(vl, char FAR **);
    int *buflen = va_arg(vl, int *);

    wsprintf(Buffer + Index, "WSAAsyncGetServByPort() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSACancelAsyncRequest(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    HANDLE *hAsyncTaskHandle = va_arg(vl, HANDLE *);

    wsprintf(Buffer + Index, "WSACancelAsyncRequest() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSPAccept(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    SOCKET *RetVal = (SOCKET *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    struct sockaddr FAR **addr = va_arg(vl, struct sockaddr FAR **);
    INT FAR **addrlen = va_arg(vl, INT FAR **);
    LPCONDITIONPROC *lpfnCondition = va_arg(vl, LPCONDITIONPROC *);
    DWORD *dwCallbackData = va_arg(vl, DWORD *);
    INT FAR **lpErrno = va_arg(vl, INT FAR **);

    wsprintf(Buffer + Index, "WSPAccept() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSPAsyncSelect(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    INT *RetVal = (INT *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    HWND *hWnd = va_arg(vl, HWND *);
    unsigned int *wMsg = va_arg(vl, unsigned int *);
    long *lEvent = va_arg(vl, long *);
    INT FAR **lpErrno = va_arg(vl, INT FAR **);

    wsprintf(Buffer + Index, "WSPAsyncSelect() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSPBind(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    INT *RetVal = (INT *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    struct sockaddr FAR **name = va_arg(vl, struct sockaddr FAR **);
    INT *namelen = va_arg(vl, INT *);
    INT FAR **lpErrno = va_arg(vl, INT FAR **);

    wsprintf(Buffer + Index, "WSPBind() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSPCancelBlockingCall(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    INT *RetVal = (INT *)ReturnValue;
    INT FAR **lpErrno = va_arg(vl, INT FAR **);

    wsprintf(Buffer + Index, "WSPCancelBlockingCall() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSPCleanup(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    INT *RetVal = (INT *)ReturnValue;
    INT FAR **lpErrno = va_arg(vl, INT FAR **);

    wsprintf(Buffer + Index, "WSPCleanup() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSPCloseSocket(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    INT *RetVal = (INT *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    INT FAR **lpErrno = va_arg(vl, INT FAR **);

    wsprintf(Buffer + Index, "WSPCloseSocket() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSPConnect(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    INT *RetVal = (INT *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    struct sockaddr FAR **name = va_arg(vl, struct sockaddr FAR **);
    INT *namelen = va_arg(vl, INT *);
    LPWSABUF *lpCallerData = va_arg(vl, LPWSABUF *);
    LPWSABUF *lpCalleeData = va_arg(vl, LPWSABUF *);
    LPQOS *lpSQOS = va_arg(vl, LPQOS *);
    LPQOS *lpGQOS = va_arg(vl, LPQOS *);
    INT FAR **lpErrno = va_arg(vl, INT FAR **);

    wsprintf(Buffer + Index, "WSPConnect() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSPDuplicateSocket(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    INT *RetVal = (INT *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    DWORD *dwProcessID = va_arg(vl, DWORD *);
    LPWSAPROTOCOL_INFO *lpProtocolInfo = va_arg(vl, LPWSAPROTOCOL_INFO *);
    INT FAR **lpErrno = va_arg(vl, INT FAR **);

    wsprintf(Buffer + Index, "WSPDuplicateSocket() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSPEnumNetworkEvents(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    INT *RetVal = (INT *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    WSAEVENT *hEventObject = va_arg(vl, WSAEVENT *);
    LPWSANETWORKEVENTS *lpNetworkEvents = va_arg(vl, LPWSANETWORKEVENTS *);
    INT FAR **lpErrno = va_arg(vl, INT FAR **);

    wsprintf(Buffer + Index, "WSPEnumNetworkEvents() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSPEventSelect(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    INT *RetVal = (INT *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    WSAEVENT *hEventObject = va_arg(vl, WSAEVENT *);
    long *lNetworkEvents = va_arg(vl, long *);
    INT FAR **lpErrno = va_arg(vl, INT FAR **);

    wsprintf(Buffer + Index, "WSPEventSelect() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSPGetOverlappedResult(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    INT *RetVal = (INT *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    LPWSAOVERLAPPED *lpOverlapped = va_arg(vl, LPWSAOVERLAPPED *);
    LPDWORD *lpcbTransfer = va_arg(vl, LPDWORD *);
    BOOL *fWait = va_arg(vl, BOOL *);
    LPDWORD *lpdwFlags = va_arg(vl, LPDWORD *);
    INT FAR **lpErrno = va_arg(vl, INT FAR **);

    wsprintf(Buffer + Index, "WSPGetOverlappedResult() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSPGetPeerName(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    INT *RetVal = (INT *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    struct sockaddr FAR **name = va_arg(vl, struct sockaddr FAR **);
    INT FAR **namelen = va_arg(vl, INT FAR **);
    INT FAR **lpErrno = va_arg(vl, INT FAR **);

    wsprintf(Buffer + Index, "WSPGetPeerName() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSPGetSockName(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    INT *RetVal = (INT *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    struct sockaddr FAR **name = va_arg(vl, struct sockaddr FAR **);
    INT FAR **namelen = va_arg(vl, INT FAR **);
    INT FAR **lpErrno = va_arg(vl, INT FAR **);

    wsprintf(Buffer + Index, "WSPGetSockName() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSPGetSockOpt(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    INT *RetVal = (INT *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    INT *level = va_arg(vl, INT *);
    INT *optname = va_arg(vl, INT *);
    char FAR **optval = va_arg(vl, char FAR **);
    INT FAR **optlen = va_arg(vl, INT FAR **);
    INT FAR **lpErrno = va_arg(vl, INT FAR **);

    wsprintf(Buffer + Index, "WSPGetSockOpt() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSPGetQOSByName(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    INT *RetVal = (INT *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    LPWSABUF *lpQOSName = va_arg(vl, LPWSABUF *);
    LPQOS *lpQOS = va_arg(vl, LPQOS *);
    INT FAR **lpErrno = va_arg(vl, INT FAR **);

    wsprintf(Buffer + Index, "WSPGetQOSByName() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSPIoctl(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    INT *RetVal = (INT *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    DWORD *dwIoControlCode = va_arg(vl, DWORD *);
    LPVOID *lpvInBuffer = va_arg(vl, LPVOID *);
    DWORD *cbInBuffer = va_arg(vl, DWORD *);
    LPVOID *lpvOutBuffer = va_arg(vl, LPVOID *);
    DWORD *cbOutBuffer = va_arg(vl, DWORD *);
    LPDWORD *lpcbBytesReturned = va_arg(vl, LPDWORD *);
    LPWSAOVERLAPPED *lpOverlapped = va_arg(vl, LPWSAOVERLAPPED *);
    LPWSAOVERLAPPED_COMPLETION_ROUTINE *lpCompletionRoutine =
      va_arg(vl, LPWSAOVERLAPPED_COMPLETION_ROUTINE *);
    LPWSATHREADID *lpThreadId = va_arg(vl, LPWSATHREADID *);
    INT FAR **lpErrno = va_arg(vl, INT FAR **);

    wsprintf(Buffer + Index, "WSPIoctl() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSPJoinLeaf(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    INT *RetVal = (INT *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    struct sockaddr FAR **name = va_arg(vl, struct sockaddr FAR **);
    INT *namelen = va_arg(vl, INT *);
    LPWSABUF *lpCallerData = va_arg(vl, LPWSABUF *);
    LPWSABUF *lpCalleeData = va_arg(vl, LPWSABUF *);
    LPQOS *lpSQOS = va_arg(vl, LPQOS *);
    LPQOS *lpGQOS = va_arg(vl, LPQOS *);
    DWORD *dwFlags = va_arg(vl, DWORD *);
    INT FAR **lpErrno = va_arg(vl, INT FAR **);

    wsprintf(Buffer + Index, "WSPJoinLeaf() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSPListen(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    INT *RetVal = (INT *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    INT *backlog = va_arg(vl, INT *);
    INT FAR **lpErrno = va_arg(vl, INT FAR **);

    wsprintf(Buffer + Index, "WSPListen() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSPRecv(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    INT *RetVal = (INT *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    LPWSABUF *lpBuffers = va_arg(vl, LPWSABUF *);
    DWORD *dwBufferCount = va_arg(vl, DWORD *);
    LPDWORD *lpNumberOfBytesRecvd = va_arg(vl, LPDWORD *);
    LPDWORD *lpFlags = va_arg(vl, LPDWORD *);
    LPWSAOVERLAPPED *lpOverlapped = va_arg(vl, LPWSAOVERLAPPED *);
    LPWSAOVERLAPPED_COMPLETION_ROUTINE *lpCompletionRoutine =
      va_arg(vl, LPWSAOVERLAPPED_COMPLETION_ROUTINE *);
    LPWSATHREADID *lpThreadId = va_arg(vl, LPWSATHREADID *);
    INT FAR **lpErrno = va_arg(vl, INT FAR **);

    wsprintf(Buffer + Index, "WSPRecv() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSPRecvDisconnect(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    INT *RetVal = (INT *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    LPWSABUF *lpInboundDisconnectData = va_arg(vl, LPWSABUF *);
    INT FAR **lpErrno = va_arg(vl, INT FAR **);

    wsprintf(Buffer + Index, "WSPRecvDisconnect() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSPRecvFrom(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    INT *RetVal = (INT *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    LPWSABUF *lpBuffers = va_arg(vl, LPWSABUF *);
    DWORD *dwBufferCount = va_arg(vl, DWORD *);
    LPDWORD *lpNumberOfBytesRecvd = va_arg(vl, LPDWORD *);
    LPDWORD *lpFlags = va_arg(vl, LPDWORD *);
    struct sockaddr FAR **lpFrom = va_arg(vl, struct sockaddr FAR **);
    LPINT *lpFromlen = va_arg(vl, LPINT *);
    LPWSAOVERLAPPED *lpOverlapped = va_arg(vl, LPWSAOVERLAPPED *);
    LPWSAOVERLAPPED_COMPLETION_ROUTINE *lpCompletionRoutine =
      va_arg(vl, LPWSAOVERLAPPED_COMPLETION_ROUTINE *);
    LPWSATHREADID *lpThreadId = va_arg(vl, LPWSATHREADID *);
    INT FAR **lpErrno = va_arg(vl, INT FAR **);

    wsprintf(Buffer + Index, "WSPRecvFrom() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSPSelect(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    INT *RetVal = (INT *)ReturnValue;
    INT *nfds = va_arg(vl, INT *);
    fd_set FAR **readfds = va_arg(vl, fd_set FAR **);
    fd_set FAR **writefds = va_arg(vl, fd_set FAR **);
    fd_set FAR **exceptfds = va_arg(vl, fd_set FAR **);
    struct timeval FAR **timeout = va_arg(vl, struct timeval FAR **);
    INT FAR **lpErrno = va_arg(vl, INT FAR **);

    wsprintf(Buffer + Index, "WSPSelect() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSPSend(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    INT *RetVal = (INT *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    LPWSABUF *lpBuffers = va_arg(vl, LPWSABUF *);
    DWORD *dwBufferCount = va_arg(vl, DWORD *);
    LPDWORD *lpNumberOfBytesSent = va_arg(vl, LPDWORD *);
    DWORD *dwFlags = va_arg(vl, DWORD *);
    LPWSAOVERLAPPED *lpOverlapped = va_arg(vl, LPWSAOVERLAPPED *);
    LPWSAOVERLAPPED_COMPLETION_ROUTINE *lpCompletionRoutine =
      va_arg(vl, LPWSAOVERLAPPED_COMPLETION_ROUTINE *);
    LPWSATHREADID *lpThreadId = va_arg(vl, LPWSATHREADID *);
    INT FAR **lpErrno = va_arg(vl, INT FAR **);

    wsprintf(Buffer + Index, "WSPSend() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSPSendDisconnect(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    INT *RetVal = (INT *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    LPWSABUF *lpOutboundDisconnectData = va_arg(vl, LPWSABUF *);
    INT FAR **lpErrno = va_arg(vl, INT FAR **);

    wsprintf(Buffer + Index, "WSPSendDisconnect() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSPSendTo(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    INT *RetVal = (INT *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    LPWSABUF *lpBuffers = va_arg(vl, LPWSABUF *);
    DWORD *dbBufferCount = va_arg(vl, DWORD *);
    LPDWORD *lpNumberOfBytesSent = va_arg(vl, LPDWORD *);
    DWORD *dwFlags = va_arg(vl, DWORD *);
    struct sockaddr FAR **lpTo = va_arg(vl, struct sockaddr FAR **);
    INT *iTolen = va_arg(vl, INT *);
    LPWSAOVERLAPPED *lpOverlapped = va_arg(vl, LPWSAOVERLAPPED *);
    LPWSAOVERLAPPED_COMPLETION_ROUTINE *lpCompletionRoutine =
      va_arg(vl, LPWSAOVERLAPPED_COMPLETION_ROUTINE *);
    LPWSATHREADID *lpThreadId = va_arg(vl, LPWSATHREADID *);
    INT FAR **lpErrno = va_arg(vl, INT FAR **);

    wsprintf(Buffer + Index, "WSPSendTo() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSPSetSockOpt(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    INT *RetVal = (INT *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    INT *level = va_arg(vl, INT *);
    INT *optname = va_arg(vl, INT *);
    char FAR **optval = va_arg(vl, char FAR **);
    INT *optlen = va_arg(vl, INT *);
    INT FAR **lpErrno = va_arg(vl, INT FAR **);

    wsprintf(Buffer + Index, "WSPSetSockOpt() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSPShutdown(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    INT *RetVal = (INT *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    INT *how = va_arg(vl, INT *);
    INT FAR **lpErrno = va_arg(vl, INT FAR **);

    wsprintf(Buffer + Index, "WSPShutdown() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSPSocket(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    INT *RetVal = (INT *)ReturnValue;
    int *af = va_arg(vl, int *);
    int *type = va_arg(vl, int *);
    int *protocol = va_arg(vl, int *);
    LPWSAPROTOCOL_INFO *lpProtocolInfo = va_arg(vl, LPWSAPROTOCOL_INFO *);
    GROUP *g = va_arg(vl, GROUP *);
    DWORD *dwFlags = va_arg(vl, DWORD *);
    INT FAR **lpErrno = va_arg(vl, INT FAR **);

    wsprintf(Buffer + Index, "WSPSocket() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSPStartup(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    WORD *wVersionRequested = va_arg(vl, WORD *);
    LPWSPDATA *lpWSPData = va_arg(vl, LPWSPDATA *);
    LPWSAPROTOCOL_INFOA *lpProtocolInfo = va_arg(vl, LPWSAPROTOCOL_INFOA *);
    WSPUPCALLTABLE *UpcallTable = va_arg(vl, WSPUPCALLTABLE *);
    LPWSPPROC_TABLE *lpProcTable = va_arg(vl, LPWSPPROC_TABLE *);

    wsprintf(Buffer + Index, "WSPStartup() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WPUCloseEvent(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    BOOL *RetVal = (BOOL *)ReturnValue;
    WSAEVENT *hEvent = va_arg(vl, WSAEVENT *);
    LPINT *lpErrno = va_arg(vl, LPINT *);

    wsprintf(Buffer + Index, "WPUCloseEvent() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WPUCloseSocketHandle(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    LPINT *lpErrno = va_arg(vl, LPINT *);

    wsprintf(Buffer + Index, "WPUCloseSocketHandle() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WPUCreateEvent(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    WSAEVENT *RetVal = (WSAEVENT *)ReturnValue;
    LPINT *lpErrno = va_arg(vl, LPINT *);

    wsprintf(Buffer + Index, "WPUCreateEvent() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WPUCreateSocketHandle(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    SOCKET *RetVal = (SOCKET *)ReturnValue;
    DWORD *dwCatalogEntryId = va_arg(vl, DWORD *);
    DWORD *dwContext = va_arg(vl, DWORD *);
    LPINT *lpErrno = va_arg(vl, LPINT *);

    wsprintf(Buffer + Index, "WPUCreateSocketHandle() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSCDeinstallProvider(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    DWORD *dwProviderId = va_arg(vl, DWORD *);
    LPINT *lpErrno = va_arg(vl, LPINT *);

    wsprintf(Buffer + Index, "WSCDeinstallProvider() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSCInstallProvider(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    char FAR **lpszProviderName = va_arg(vl, char FAR **);
    char FAR **lpszProviderDllPath = va_arg(vl, char FAR **);
    LPWSAPROTOCOL_INFO *lpProtocolInfoList = va_arg(vl, LPWSAPROTOCOL_INFO *);
    DWORD *dwNumberOfEntries = va_arg(vl, DWORD *);
    LPDWORD *lpdwProviderId = va_arg(vl, LPDWORD *);
    LPINT *lpErrno = va_arg(vl, LPINT *);

    wsprintf(Buffer + Index, "WSCInstallProvider() %s.\r\n",
         PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WPUModifyIFSHandle(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    SOCKET *RetVal = (SOCKET *)ReturnValue;
    DWORD *dwCatalogEntryId = va_arg(vl, DWORD *);
    SOCKET *ProposedHandle = va_arg(vl, SOCKET *);
    LPINT *lpErrno = va_arg(vl, LPINT *);

    wsprintf(Buffer + Index, "WPUModifyIFSHandle() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WPUQueryBlockingCallback(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    DWORD *dwCatalogEntryId = va_arg(vl, DWORD *);
    LPBLOCKINGCALLBACK FAR **lplpfnCallback =
      va_arg(vl, LPBLOCKINGCALLBACK FAR **);
    LPDWORD *lpdwContext = va_arg(vl, LPDWORD *);
    LPINT *lpErrno = va_arg(vl, LPINT *);

    wsprintf(Buffer + Index, "WPUQueryBlockingCallback() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WPUQuerySocketHandleContext(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    LPDWORD *lpContext = va_arg(vl, LPDWORD *);
    LPINT *lpErrno = va_arg(vl, LPINT *);

    wsprintf(Buffer + Index, "WPUQuerySocketHandleContext() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WPUQueueApc(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    LPWSATHREADID *lpThreadId = va_arg(vl, LPWSATHREADID *);
    LPWSAUSERAPC *lpfnUserApc = va_arg(vl, LPWSAUSERAPC *);
    DWORD *dwContext = va_arg(vl, DWORD *);
    LPINT *lpErrno = va_arg(vl, LPINT *);

    wsprintf(Buffer + Index, "WPUQueueApc() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WPUResetEvent(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    BOOL *RetVal = (BOOL *)ReturnValue;
    WSAEVENT *hEvent = va_arg(vl, WSAEVENT *);
    LPINT *lpErrno = va_arg(vl, LPINT *);

    wsprintf(Buffer + Index, "WPUResetEvent() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WPUSetEvent(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    BOOL *RetVal = (BOOL *)ReturnValue;
    WSAEVENT *hEvent = va_arg(vl, WSAEVENT *);
    LPINT *lpErrno = va_arg(vl, LPINT *);

    wsprintf(Buffer + Index, "WPUSetEvent() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WPUFDIsSet(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    fd_set FAR **set = va_arg(vl, fd_set FAR **);

    wsprintf(Buffer + Index, "WPUFDIsSet() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WPUGetProviderPath(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    DWORD *dwProviderId = va_arg(vl, DWORD *);
    char FAR **lpszProviderDllPath = va_arg(vl, char FAR **);
    LPINT *lpProviderDllPathLen = va_arg(vl, LPINT *);
    LPINT *lpErrno = va_arg(vl, LPINT *);

    wsprintf(Buffer + Index, "WPUGetProviderPath() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WPUPostMessage(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    BOOL *RetVal = (BOOL *)ReturnValue;
    HWND *hWnd = va_arg(vl, HWND *);
    UINT *Msg = va_arg(vl, UINT *);
    WPARAM *wParam = va_arg(vl, WPARAM *);
    LPARAM *lParam = va_arg(vl, LPARAM *);

    wsprintf(Buffer + Index, "WPUPostMessage() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSCEnumProtocols(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    LPINT *lpiProtocols = va_arg(vl, LPINT *);
    LPWSAPROTOCOL_INFO *lpProtocolBuffer = va_arg(vl, LPWSAPROTOCOL_INFO *);
    LPDWORD *lpdwBufferLength = va_arg(vl, LPDWORD *);
    LPINT *lpErrno = va_arg(vl, LPINT *);

    wsprintf(Buffer + Index, "WSCEnumProtocols() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler___WSAFDIsSet(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    SOCKET *s = va_arg(vl, SOCKET *);
    fd_set FAR **set = va_arg(vl, fd_set FAR **);

    wsprintf(Buffer + Index, "__WSAFDIsSet() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSAAddressToStringA(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    LPSOCKADDR *lpsaAddress = va_arg(vl, LPSOCKADDR *);
    DWORD *dwAddressLength= va_arg(vl, DWORD *);
    LPWSAPROTOCOL_INFOA *lpProtocolInfo= va_arg(vl, LPWSAPROTOCOL_INFOA *);
    LPSTR *lpszAddressString= va_arg(vl, LPSTR *);
    LPDWORD *lpdwAddressStringLength= va_arg(vl, LPDWORD *);

    wsprintf(Buffer + Index, "WSAAddressToStringA() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSAAddressToStringW(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    LPSOCKADDR *lpsaAddress = va_arg(vl, LPSOCKADDR *);
    DWORD *dwAddressLength= va_arg(vl, DWORD *);
    LPWSAPROTOCOL_INFOW *lpProtocolInfo= va_arg(vl, LPWSAPROTOCOL_INFOW *);
    LPWSTR *lpszAddressString= va_arg(vl, LPWSTR *);
    LPDWORD *lpdwAddressStringLength= va_arg(vl, LPDWORD *);

    wsprintf(Buffer + Index, "WSAAddressToStringW() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSAStringToAddressA(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    LPSTR *AddressString = va_arg(vl, LPSTR*);
    INT *AddressFamily = va_arg(vl, INT *);
    LPWSAPROTOCOL_INFOA *lpProtocolInfo = va_arg(vl, LPWSAPROTOCOL_INFOA*);
    LPSOCKADDR *lpAddress = va_arg(vl, LPSOCKADDR*);
    LPINT *lpAddressLength = va_arg(vl, LPINT*);

    wsprintf(Buffer + Index, "WSAStringToAddressA() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSAStringToAddressW(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    LPWSTR *AddressString = va_arg(vl, LPWSTR*);
    INT *AddressFamily = va_arg(vl, INT *);
    LPWSAPROTOCOL_INFOW *lpProtocolInfo = va_arg(vl, LPWSAPROTOCOL_INFOW*);
    LPSOCKADDR *lpAddress = va_arg(vl, LPSOCKADDR*);
    LPINT *lpAddressLength = va_arg(vl, LPINT*);

    wsprintf(Buffer + Index, "WSAStringToAddressW() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSALookupServiceBeginA(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    LPWSAQUERYSETA *lpqsRestrictions = va_arg(vl, LPWSAQUERYSETA*);
    DWORD *dwControlFlags = va_arg(vl, DWORD*);
    LPHANDLE *lphLookup  = va_arg(vl, LPHANDLE*);

    wsprintf(Buffer + Index, "WSALookupServiceBeginA() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSALookupServiceBeginW(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    LPWSAQUERYSETW *lpqsRestrictions = va_arg(vl, LPWSAQUERYSETW*);
    DWORD *dwControlFlags = va_arg(vl, DWORD*);
    LPHANDLE *lphLookup  = va_arg(vl, LPHANDLE*);

    wsprintf(Buffer + Index, "WSALookupServiceBeginW() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSALookupServiceNextA(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    HANDLE *hLookup = va_arg(vl, HANDLE*);
    DWORD *dwControlFlags = va_arg(vl, DWORD*);
    LPDWORD *lpdwBufferLength = va_arg(vl, LPDWORD*);
    LPWSAQUERYSETA *lpqsResults = va_arg(vl, LPWSAQUERYSETA*);

    wsprintf(Buffer + Index, "WSALookupServiceNextA() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSALookupServiceNextW(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    HANDLE *hLookup = va_arg(vl, HANDLE*);
    DWORD *dwControlFlags = va_arg(vl, DWORD*);
    LPDWORD *lpdwBufferLength = va_arg(vl, LPDWORD*);
    LPWSAQUERYSETW *lpqsResults = va_arg(vl, LPWSAQUERYSETW*);

    wsprintf(Buffer + Index, "WSALookupServiceNextW() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSALookupServiceEnd(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    HANDLE *hLookup = va_arg(vl, HANDLE*);

    wsprintf(Buffer + Index, "WSALookupServiceEnd() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSAGetAddressByNameA(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    LPSTR *lpszServiceInstanceName = va_arg(vl, LPSTR*);
    LPGUID *lpServiceClassId = va_arg(vl, LPGUID*);
    DWORD *dwNameSpace = va_arg(vl, DWORD*);
    LPDWORD *lpdwBufferLength = va_arg(vl, LPDWORD*);
    LPWSAQUERYSETA *lpqsResults = va_arg(vl, LPWSAQUERYSETA*);
    DWORD *dwResolution = va_arg(vl, DWORD*);
    LPSTR *lpszAliasBuffer = va_arg(vl, LPSTR*);
    LPDWORD *lpdwAliasBufferLength  = va_arg(vl, LPDWORD*);

    wsprintf(Buffer + Index, "WSAGetAddressByNameA() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSAGetAddressByNameW(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    LPWSTR *lpszServiceInstanceName = va_arg(vl, LPWSTR*);
    LPGUID *lpServiceClassId = va_arg(vl, LPGUID*);
    DWORD *dwNameSpace = va_arg(vl, DWORD*);
    LPDWORD *lpdwBufferLength = va_arg(vl, LPDWORD*);
    LPWSAQUERYSETW *lpqsResults = va_arg(vl, LPWSAQUERYSETW*);
    DWORD *dwResolution = va_arg(vl, DWORD*);
    LPWSTR *lpszAliasBuffer = va_arg(vl, LPWSTR*);
    LPDWORD *lpdwAliasBufferLength  = va_arg(vl, LPDWORD*);

    wsprintf(Buffer + Index, "WSAGetAddressByNameW() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSAInstallServiceClassA(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    LPWSASERVICECLASSINFOA *lpServiceClassInfo =
        va_arg(vl, LPWSASERVICECLASSINFOA*);

    wsprintf(Buffer + Index, "WSAInstallServiceClassA() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSAInstallServiceClassW(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    LPWSASERVICECLASSINFOW *lpServiceClassInfo =
        va_arg(vl, LPWSASERVICECLASSINFOW*);

    wsprintf(Buffer + Index, "WSAInstallServiceClassW() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSASetServiceA(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    LPWSAQUERYSETA *lpqsRegInfo = va_arg(vl, LPWSAQUERYSETA*);
    WSAESETSERVICEOP *essOperation = va_arg(vl, WSAESETSERVICEOP*);
    DWORD *dwControlFlags = va_arg(vl, DWORD*);

    wsprintf(Buffer + Index, "WSASetServiceA() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSASetServiceW(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    LPWSAQUERYSETW *lpqsRegInfo = va_arg(vl, LPWSAQUERYSETW*);
    WSAESETSERVICEOP *essOperation = va_arg(vl, WSAESETSERVICEOP*);
    DWORD *dwControlFlags = va_arg(vl, DWORD*);

    wsprintf(Buffer + Index, "WSASetServiceW() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSARemoveServiceClass(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    LPGUID *lpServiceClassId = va_arg(vl, LPGUID*);

    wsprintf(Buffer + Index, "WSARemoveServiceClass() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSAGetServiceClassInfoA(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    LPGUID *lpProviderId = va_arg(vl, LPGUID*);
    LPGUID *lpServiceClassId = va_arg(vl, LPGUID*);
    LPDWORD *lpdwBufSize = va_arg(vl, LPDWORD*);
    LPWSASERVICECLASSINFOA *lpServiceClassInfo =
        va_arg(vl, LPWSASERVICECLASSINFOA*);

    wsprintf(Buffer + Index, "WSAGetServiceClassInfoA() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSAGetServiceClassInfoW(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    LPGUID *lpProviderId = va_arg(vl, LPGUID*);
    LPGUID *lpServiceClassId = va_arg(vl, LPGUID*);
    LPDWORD *lpdwBufSize = va_arg(vl, LPDWORD*);
    LPWSASERVICECLASSINFOW *lpServiceClassInfo =
        va_arg(vl, LPWSASERVICECLASSINFOW*);

    wsprintf(Buffer + Index, "WSAGetServiceClassInfoW() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSAEnumNameSpaceProvidersA(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    LPDWORD *lpdwBufferLength = va_arg(vl, LPDWORD*);
    LPWSANAMESPACE_INFOA *Lpnspbuffer = va_arg(vl,  LPWSANAMESPACE_INFOA*);

    wsprintf(Buffer + Index, "WSAEnumNameSpaceProvidersA() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSAEnumNameSpaceProvidersW(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    LPDWORD *lpdwBufferLength = va_arg(vl, LPDWORD*);
    LPWSANAMESPACE_INFOW *Lpnspbuffer = va_arg(vl,  LPWSANAMESPACE_INFOW*);

    wsprintf(Buffer + Index, "WSAEnumNameSpaceProvidersW() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSAGetServiceClassNameByClassIdA(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    LPGUID *lpServiceClassId = va_arg(vl, LPGUID*);
    LPSTR *lpszServiceClassName = va_arg(vl, LPSTR*);
    LPDWORD *lpdwBufferLength = va_arg(vl, LPDWORD*);

    wsprintf(Buffer + Index, "WSAGetServiceClassNameByClassIdA() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_WSAGetServiceClassNameByClassIdW(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    LPGUID *lpServiceClassId = va_arg(vl, LPGUID*);
    LPWSTR *lpszServiceClassName = va_arg(vl, LPWSTR*);
    LPDWORD *lpdwBufferLength = va_arg(vl, LPDWORD*);

    wsprintf(Buffer + Index, "WSAGetServiceClassNameByClassIdW() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_NSPAddressToString(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    LPSOCKADDR *lpsaAddress = va_arg(vl, LPSOCKADDR *);
    DWORD *dwAddressLength= va_arg(vl, DWORD *);
    LPWSAPROTOCOL_INFOW *lpProtocolInfo= va_arg(vl, LPWSAPROTOCOL_INFOW *);
    LPWSTR *lpszAddressString= va_arg(vl, LPWSTR *);
    LPDWORD *lpdwAddressStringLength= va_arg(vl, LPDWORD *);

    wsprintf(Buffer + Index, "NSPAddressToString() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_NSPStringToAddress(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    LPWSTR *AddressString = va_arg(vl, LPWSTR*);
    INT *AddressFamily = va_arg(vl, INT *);
    LPWSAPROTOCOL_INFOW *lpProtocolInfo = va_arg(vl, LPWSAPROTOCOL_INFOW*);
    LPSOCKADDR *lpAddress = va_arg(vl, LPSOCKADDR*);
    LPINT *lpAddressLength = va_arg(vl, LPINT*);

    wsprintf(Buffer + Index, "NSPStringToAddress() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_NSPLookupServiceBegin(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    LPWSAQUERYSETW *lpqsRestrictions = va_arg(vl, LPWSAQUERYSETW*);
    DWORD *dwControlFlags = va_arg(vl, DWORD*);
    LPHANDLE *lphLookup  = va_arg(vl, LPHANDLE*);

    wsprintf(Buffer + Index, "NSPLookupServiceBegin() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_NSPLookupServiceNext(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    HANDLE *hLookup = va_arg(vl, HANDLE*);
    DWORD *dwControlFlags = va_arg(vl, DWORD*);
    LPDWORD *lpdwBufferLength = va_arg(vl, LPDWORD*);
    LPWSAQUERYSETW *lpqsResults = va_arg(vl, LPWSAQUERYSETW*);

    wsprintf(Buffer + Index, "NSPLookupServiceNext() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_NSPLookupServiceEnd(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    HANDLE *hLookup = va_arg(vl, HANDLE*);

    wsprintf(Buffer + Index, "NSPLookupServiceEnd() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_NSPGetAddressByName(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;

LPWSTR *lpszServiceInstanceName = va_arg(vl, LPWSTR*);
    LPGUID *lpServiceClassId = va_arg(vl, LPGUID*);
    DWORD *dwNameSpace = va_arg(vl, DWORD*);
    LPDWORD *lpdwBufferLength = va_arg(vl, LPDWORD*);
    LPWSAQUERYSETW *lpqsResults = va_arg(vl, LPWSAQUERYSETW*);
    DWORD *dwResolution = va_arg(vl, DWORD*);
    LPWSTR *lpszAliasBuffer = va_arg(vl, LPWSTR*);
    LPDWORD *lpdwAliasBufferLength  = va_arg(vl, LPDWORD*);

    wsprintf(Buffer + Index, "NSPGetAddressByName() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_NSPInstallServiceClass(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    LPWSASERVICECLASSINFOW *lpServiceClassInfo =
        va_arg(vl, LPWSASERVICECLASSINFOW*);

    wsprintf(Buffer + Index, "NSPInstallServiceClass() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_NSPSetService(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    LPWSAQUERYSETW *lpqsRegInfo = va_arg(vl, LPWSAQUERYSETW*);
    WSAESETSERVICEOP *essOperation = va_arg(vl, WSAESETSERVICEOP*);
    DWORD *dwControlFlags = va_arg(vl, DWORD*);

    wsprintf(Buffer + Index, "NSPSetService() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_NSPRemoveServiceClass(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    LPGUID *lpServiceClassId = va_arg(vl, LPGUID*);

    wsprintf(Buffer + Index, "NSPRemoveServiceClass() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_NSPGetServiceClassInfo(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    LPGUID *lpProviderId = va_arg(vl, LPGUID*);
    LPGUID *lpServiceClassId = va_arg(vl, LPGUID*);
    LPDWORD *lpdwBufSize = va_arg(vl, LPDWORD*);
    LPWSASERVICECLASSINFOW *lpServiceClassInfo =
        va_arg(vl, LPWSASERVICECLASSINFOW*);

    wsprintf(Buffer + Index, "NSPGetServiceClassInfo() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}

BOOL CALLBACK
DTHandler_NSPGetServiceClassNameByClassId(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost)
{
    int *RetVal = (int *)ReturnValue;
    LPGUID *lpServiceClassId = va_arg(vl, LPGUID*);
    LPWSTR *lpszServiceClassName = va_arg(vl, LPWSTR*);
    LPDWORD *lpdwBufferLength = va_arg(vl, LPDWORD*);

    wsprintf(Buffer + Index, "NSPGetServiceClassNameByClassId() %s.\r\n",
             PreOrPost ? "called" : "returned");
    DTTextOut(DebugWindow, LogFileHandle, Buffer, OutputStyle);
    return(FALSE);
}


HANDLERS.H

/*++

  Copyright (c) 1995 Intel Corp

  Module Name:

    handlers.h

  Abstract:

    Contains handler function prototypes and typedefs for handlers.cpp.

--*/

//
// Typedefs
//

// This typedef defines a pointer to a handler function.  See
// dt_dll.cpp for examples of how this is used.
typedef BOOL (CALLBACK * LPFNDTHANDLER)(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

typedef LPFNDTHANDLER *LPLPFNDTHANDLER;

//
// Function Prototypes
//

BOOL
DTHandlerInit(
    OUT LPLPFNDTHANDLER HandlerFuncTable,
    int                 NumEntries);

BOOL CALLBACK
DTHandler_accept(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_bind(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_closesocket(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_connect(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_getpeername(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_getsockname(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_getsockopt(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_htonl(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_htons(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_ioctlsocket(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_listen(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_ntohl(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_ntohs(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_recv(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_recvfrom(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_select(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_send(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_sendto(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_setsockopt(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_shutdown(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_socket(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSAAccept(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSAAsyncSelect(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSACancelBlockingCall(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSACleanup(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSACloseEvent(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSAConnect(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSACreateEvent(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSADuplicateSocketA(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSADuplicateSocketW(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSAEnumNetworkEvents(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSAEnumProtocolsA(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSAEnumProtocolsW(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSAEventSelect(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSAGetLastError(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSAGetOverlappedResult(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSAGetQOSByName(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSAHtonl(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSAHtons(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSAIoctl(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSAIsBlocking(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSAJoinLeaf(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSANtohl(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSANtohs(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSARecv(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSARecvDisconnect(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSARecvFrom(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSAResetEvent(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSASend(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSASendDisconnect(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSASendTo(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSASetBlockingHook(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSASetEvent(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSASetLastError(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSASocketA(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSASocketW(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSAStartup(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSAUnhookBlockingHook(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSAWaitForMultipleEvents(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_gethostbyaddr(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_gethostbyname(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_gethostname(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_getprotobyname(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_getprotobynumber(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_getservbyname(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_getservbyport(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_inet_addr(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_inet_ntoa(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSAAsyncGetHostByAddr(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSAAsyncGetHostByName(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSAAsyncGetProtoByName(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSAAsyncGetProtoByNumber(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSAAsyncGetServByName(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSAAsyncGetServByPort(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSACancelAsyncRequest(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSPAccept(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSPAsyncSelect(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSPBind(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSPCancelBlockingCall(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSPCleanup(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSPCloseSocket(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSPConnect(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSPDuplicateSocket(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSPEnumNetworkEvents(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSPEventSelect(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSPGetOverlappedResult(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSPGetPeerName(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSPGetSockName(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSPGetSockOpt(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSPGetQOSByName(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSPIoctl(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSPJoinLeaf(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSPListen(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSPRecv(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSPRecvDisconnect(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSPRecvFrom(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSPSelect(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSPSend(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSPSendDisconnect(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSPSendTo(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSPSetSockOpt(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSPShutdown(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSPSocket(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSPStartup(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WPUCloseEvent(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WPUCloseSocketHandle(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WPUCreateEvent(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WPUCreateSocketHandle(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSCDeinstallProvider(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSCInstallProvider(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WPUModifyIFSHandle(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WPUQueryBlockingCallback(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,

IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WPUQuerySocketHandleContext(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WPUQueueApc(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WPUResetEvent(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WPUSetEvent(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WPUFDIsSet(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WPUGetProviderPath(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WPUPostMessage(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler___WSAFDIsSet(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSCEnumProtocols(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSAAddressToStringA(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSAAddressToStringW(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSAStringToAddressA(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSAStringToAddressW(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSALookupServiceBeginA(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSALookupServiceBeginW(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSALookupServiceNextA(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSALookupServiceNextW(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSALookupServiceEnd(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSAGetAddressByNameA(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSAGetAddressByNameW(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSAInstallServiceClassA(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSAInstallServiceClassW(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSASetServiceA(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSASetServiceW(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSARemoveServiceClass(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSAGetServiceClassInfoA(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSAGetServiceClassInfoW(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSAEnumNameSpaceProvidersA(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSAEnumNameSpaceProvidersW(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSAGetServiceClassNameByClassIdA(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_WSAGetServiceClassNameByClassIdW(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_NSPCleanup(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_NSPLookupServiceBegin(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_NSPLookupServiceNext(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_NSPLookupServiceEnd(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_NSPSetService(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_NSPInstallServiceClass(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_NSPRemoveServiceClass(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);

BOOL CALLBACK
DTHandler_NSPGetServiceClassInfo(
    IN     va_list vl,
    IN OUT LPVOID  ReturnValue,
    IN     LPSTR   LibraryName,
    OUT    char    *Buffer,
    IN     int     Index,
    IN     int     BufLen,
    IN     BOOL    PreOrPost);


HUERROR.CPP

/*++

  Copyright (c) 1995 Intel Corp

  File Name:

    huerror.cpp

  Abstract:

    Error functions.

--*/

#include "nowarn.h"  /* turn off benign warnings */
#include <stdio.h>
#include "huerror.h"

static ErrorCode_e HULastError= ENONE;

void HUSetLastError(ErrorCode_e ErrorCode)
{
  HULastError = ErrorCode;
}

ErrorCode_e HUGetLastError()
{
  return HULastError;
}

void HUPrintError(char *func,ErrorCode_e ErrorCode)
{
  if(func == NULL){
    printf("Error: - - ");
  }else{
    printf("Error: %s - - ",func);
  }
  switch(ErrorCode){
    case ENONE:
printf("No error\n");
break;
    case ALLOCERROR:
printf("Allocation error\n");
break;
    case INVALIDARG:
printf("Invalid arguement passed in\n");
break;
    case OBJNOTINIT:
printf("Object not initialized\n");
break;
    case OBJEFFERROR:
printf("Object becoming ineffecient. Trying making it large\n");
break;
    case ALREADYCONN:
printf("Already connected\n");
break;
    default:
      printf("ErrorCode not defined\n");
  }
}


HUERROR.H

/*++

  Copyright (c) 1995 Intel Corp

  File Name:

    huerror.cpp

  Abstract:

    Header file for error functions.

--*/

#ifndef _ERRORH_
#define _ERRORH_

#include <stdarg.h>

const int ERRORSTART = 0x100;

typedef enum {  ENONE     = ERRORSTART,
                OTHERERROR,
                ALLOCERROR,
                INVALIDARG,
                OBJNOTINIT,
OBJEFFERROR,  // Object effeciency error.
ALREADYCONN,
ALREADYLIST
             } ErrorCode_e;

void        HUSetLastError(ErrorCode_e ErrorCode);
ErrorCode_e HUGetLastError();
void        HUPrintError(char *func,ErrorCode_e ErrorCode = ENONE);

#endif


NIDEQUE.H

/*++

  Copyright 1996 - 1998 Microsoft Corporation.

  Copyright (c) 1995 Intel Corp

  File Name:

    nideque.h

  Abstract:

    Implements Deque structures.  These are double linked lists  that
    can place and remove data from the beginning and end. This file
    also contains an iterator for Deques.  Important to notice is that
    these are non-intrusive deques.  Meaning the use of these classes
    does not need to insert pointers into there classes to use these.

--*/

#ifndef _NIDEQUE_H_
#define _NIDEQUE_H_

#include "nowarn.h"  /* turn off benign warnings */
#ifndef _WINSOCKAPI_
#define _WINSOCKAPI_   /* Prevent inclusion of winsock.h in windows.h */
#endif
#include <windows.h>
#include "nowarn.h"  /* some warnings may have been turned back on */
#include "huerror.h"

template<class T> class NIDeque_c;
template<class T> class NIDequeIter_c;

// Class Name:     NILNode_c
// Purpose:  Simply holds data and pointer to form a double linked
// list.
// Context:  Can be used anywhere.
template<class T> class NILNode_c {
    friend class NIDeque_c<T>;
    friend class NIDequeIter_c<T>;

    private:
        T           Data;
        NILNode_c   *Next,
                    *Back;

    public:
        NILNode_c();
};

template<class T> NILNode_c<T>::NILNode_c()
{
    // Make sure the members are correctly initialized.
    Next = NULL;
    Back = NULL;
} // NILNode_c::NILNode_c

// Class Name:     NIDeque_c
// Purpose:  Non-intrusize deque
// Context:  Can be used anywhere.
template<class T> class NIDeque_c {
    friend class NIDequeIter_c<T>;

    private:
      NILNode_c<T>  *Root,
                    *Tail;
    public:
                NIDeque_c();
            ~NIDeque_c();
        BOOL    RemoveFromFront(T &data);
        BOOL    RemoveFromBack(T &data);
        BOOL    InsertIntoFront(T data);
        BOOL    InsertIntoBack(T data);
        BOOL    GetFromFront(T &data);
    BOOL    GetFromBack(T &data);
    BOOL    IsEmpty();
};

/*++

  NIDeque_c::NIDeque_c()

  Function Description:

      Constructor

  Arguments:

      None.

  Return Value:

      None.

--*/
template<class T> NIDeque_c<T>::NIDeque_c()
{
    Root = NULL;
    Tail = NULL;
} // End of NINIDeque_c::NIDeque_c

/*++

  NIDeque_c::~NIDeque_c()

  Function Description:

      Destructor

  Arguments:

      None.

  Return Value:

      None.

--*/
template<class T> NIDeque_c<T>::~NIDeque_c()
{
    NILNode_c<T>    *bptr    = NULL,
                    *cptr    = NULL;

    for(bptr=NULL,cptr=Root;cptr;bptr=cptr,cptr=cptr->Next){
        delete bptr;
    }
    delete bptr;
}

/*++

  NIDeque_c::InsertIntoFront()

  Function Description:

      Inserts a piece of data onto the linked list.

  Arguments:

      Data -- Data to put on the linked list.

  Return Value:

      None.

--*/
template<class T> BOOL NIDeque_c<T>::InsertIntoFront(T Data)
{
    NILNode_c<T>    *nptr    = NULL;

    /*  If the list contains something then move the root pointer down.
    //  Otherwise, put this new object on the root.
    */
    if(!(nptr = new NILNode_c<T>)){
         HUSetLastError(ALLOCERROR);
         return FALSE;
    }

    nptr->Data = Data;

    if(Root){
        nptr->Next = Root;
        Root->Back = nptr;
        Root = nptr;
    }else{
        Root = nptr;
        Tail = nptr;
    }
    return TRUE;
} // End of NIDeque_c::InsFront

/*++

  NIDeque_c::RemoveFromFront()

  Function Description:

      Removes a piece of data from the front of the linked list.

  Arguments:

      Data -- Data to be taken off of the linked list.

  Return Value:

      None.

--*/
template<class T> BOOL NIDeque_c<T>::RemoveFromFront(T &Data)
{
    NILNode_c<T>    *nptr    = NULL;

    if(Root){
        nptr = Root;
        Root = Root->Next;
        if(Root){
            Root->Back = NULL;
        }else{
            Tail = NULL;
        }
        nptr->Next = NULL;
        nptr->Back = NULL;
        Data = nptr->Data;
        delete nptr;
        return TRUE;
    }else{
        Data = NULL;
        return FALSE;
    }
} // End of NIDeque_c::RemFront

/*++

  NIDeque_c::InsertIntoBack()

  Function Description:

      Inserts a piece of data onto the back of the linked list.

  Arguments:

      Data -- Data to be taken off of the linked list.

  Return Value:

      None.

--*/
template<class T> BOOL NIDeque_c<T>::InsertIntoBack(T Data)
{
    NILNode_c<T>    *nptr    = NULL;
    if(!(nptr = new NILNode_c<T>)){
        HUSetLastError(ALLOCERROR);
        return FALSE;
    }

    nptr->Data = Data;

    if(Root){
        nptr->Back = Tail;
        Tail->Next = nptr;
        Tail = nptr;
    }else{
        Root = nptr;
        Tail = nptr;
    }
    return TRUE;
} // End of NIDeque_c::InsBack

/*++

  NIDeque_c::RemoveFromBack()

  Function Description:

      Removes a piece of data from the back of the linked list.

  Arguments:

      Data -- Data to be taken off of the linked list.

  Return Value:

      None.

--*/
template<class T> BOOL NIDeque_c<T>::RemoveFromBack(T &Data)
{
    NILNode_c<T>    *nptr    = NULL;

    if(Tail){
        nptr = Tail;
        Tail = Tail->Back;
        if(Tail){
            Tail->Next = NULL;
        }else{
        Root = NULL;
    }
        nptr->Next = NULL;
        nptr->Back = NULL;
        Data = nptr->Data;
        delete nptr;
        return TRUE;
    }else{
        Data = NULL;
        return FALSE;
    }
}

/*++

  NIDeque_c::GetFromFront()

  Function Description:

      Gets a piece of data from the front of the linked list.

  Arguments:

      Data -- Data to be taken off of the linked list.

  Return Value:

      None.

--*/
template<class T> BOOL NIDeque_c<T>::GetFromFront(T &Data)
{
    NILNode_c<T>    *nptr    = NULL;

    if(Root){
        Data = Root->Data;
        return TRUE;
    }else{
        Data = NULL;
        return FALSE;
    }
} // End of NIDeque_c::GetFromFront

/*++

  NIDeque_c::GetFromBack()

  Function Description:

      Gets a piece of data from the back of the linked list.

  Arguments:

      Data -- Data to be taken off of the linked list.

  Return Value:

      None.

--*/
template<class T> BOOL NIDeque_c<T>::GetFromBack(T &Data)
{
    NILNode_c<T>    *nptr    = NULL;

    if(Tail){
        Data = Tail->Data;
        return TRUE;
    }else{
        Data = NULL;
        return FALSE;
    }
} // End of NIDeque_c::RemFront

/*++

  NIDeque_c::IsEmpty()

  Function Description:

      Determines whether a deques is empty

  Arguments:

      None.

  Return Value:

      None.

--*/
template<class T> BOOL NIDeque_c<T>::IsEmpty()
{
    NILNode_c<T>    *nptr    = NULL;

    if(Root){
        return FALSE;
    }else{
        return TRUE;
    }
} // End of NIDeque_c::IsEmpty

// Name:     NIDequeIter_c
// Purpose:  An iterator for the Deque_c class.
// Context:  Of course only with Deque_c
template<class T> class NIDequeIter_c {
    private:
        NIDeque_c<T>  *NIDeque;
        NILNode_c<T>  *Current;

    private:
        void    RemoveData(NILNode_c<T> *cptr,T &data);

    protected: // Derived class interface
    inline NILNode_c<T> *GetCurrent();
    inline NIDeque_c<T> *GetNIDeque();

    public:
                NIDequeIter_c();
                NIDequeIter_c(NIDeque_c<T> &ANIDeque);
                ~NIDequeIter_c();
        BOOL    Initialize(NIDeque_c<T> &ANIDeque);
        BOOL    First(T &data);
        BOOL    Next(T &data);
        BOOL    Last(T &data);
        BOOL    Back(T &data);
        BOOL    Remove(T &data);
        BOOL    Replace(T &src,T chg);
};

//
// Private members
//

/*++

  NIDequeIter_c::RemoveData()

  Function Description:

      To remove all of the data from the linked list.

  Arguments:

      cptr -- Current pointer in the linked list.

      ret_data -- Return the data before deleting.

  Return Value:

      None.

--*/
template<class T> void NIDequeIter_c<T>::RemoveData(NILNode_c<T> *cptr,
                                                    T &ret_data)
{
    NILNode_c<T>  *bptr    = NULL,
                  *nptr    = NULL,
                  *fptr    = NULL;

    if(Current == cptr){
        Current = cptr->Next;
    }
    fptr = cptr;
    bptr = cptr->Back;
    nptr = cptr->Next;
    if(bptr != NULL){
        bptr->Next = nptr;
    }
    if(nptr != NULL){
        nptr->Back = bptr;
    }
    if(fptr == NIDeque->Root){
        NIDeque->Root = nptr;
    }
    if(fptr == NIDeque->Tail){
        NIDeque->Tail = bptr;
    }
    if(ret_data){
        ret_data = fptr->Data;
    }
    delete fptr;
} // End of NINIDeque_c::RemoveData

//
// Public members
//

/*++

  NIDequeIter_c::NIDequeIter_c()

  Function Description:

      Constructor.

  Arguments:

      ANIDeque -- The deque to iterate over.

  Return Value:

      None.

--*/
template<class T> NIDequeIter_c<T>::NIDequeIter_c(NIDeque_c<T> &ANIDeque)
{
    NIDeque = &ANIDeque;
    Current = NULL;
}

template<class T> NIDequeIter_c<T>::NIDequeIter_c()
{
    NIDeque = NULL;
    Current = NULL;
}

/*++

  NIDequeIter_c::~NIDequeIter_c()

  Function Description:

      Destructor.

  Arguments:

      None.

  Return Value:

      None.

--*/
template<class T> NIDequeIter_c<T>::~NIDequeIter_c()
{
}

/*++

  NIDequeIter_c::Initialize()

  Function Description:

      Initializes the iterator.

  Arguments:

      ANIDeque -> The deque to iterate over.

  Return Value:

      None.

--*/
template<class T> BOOL NIDequeIter_c<T>::Initialize(NIDeque_c<T> &ANIDeque)
{
    NIDeque = &ANIDeque;
    Current = NULL;
    return TRUE;
}

/*++

  NIDequeIter_c::First()

  Function Description:

      Returns the First data in the linked list.  This primes the
      current pointer so that next time the Next method is used to get
      more linked list data.

  Arguments:

      data -- The data from the linked list.

  Return Value:

      TRUE -- If data is valid.

      FALSE -- If data is not valid.

--*/
template<class T> BOOL NIDequeIter_c<T>::First(T &Data)
{
    Current = NIDeque->Root;
    if(Current != NULL){
        Data = Current->Data;
        return TRUE;
    }else{
        return FALSE;
    }
} // End of NIDeque_c::First

/*++

  NIDequeIter_c::Next()

  Function Description:

      Returns the Next data in the linked list.

  Arguments:

      data -- The data from the linked list.

  Return Value:

      TRUE -- If data is valid.

      FALSE -- If data is not valid.

--*/
template<class T> BOOL NIDequeIter_c<T>::Next(T &Data)
{
    if(Current){
        Current = Current->Next;
        if(Current){
            Data = Current->Data;
            return TRUE;
        }
    }
    Data = NULL;
    return FALSE;
} // End of NIDeque_c::Next

/*++

  NIDequeIter_c::Last()

  Function Description:

      Returns the Last data in the linked list.

  Arguments:

      data -- The data from the linked list.

  Return Value:

      TRUE -- If data is valid.

      FALSE -- If data is not valid.

--*/
template<class T> BOOL NIDequeIter_c<T>::Last(T &Data)
{
    Current = NIDeque->Tail;
    if(Current != NULL){
        Data = Current->Data;
        return TRUE;
    }else{
        return FALSE;
    }
} // End of NIDeque_c::First

/*++

  NIDequeIter_c::Back()

  Function Description:

      Returns the previous data in the linked list.

  Arguments:

      data -- The data from the linked list.

  Return Value:

      TRUE -- If data is valid.

      FALSE -- If data is not valid.

--*/
template<class T> BOOL NIDequeIter_c<T>::Back(T &Data)
{
    if(Current && (Current = Current->Back)){
        Data = Current->Data;
        return TRUE;
    }else{
        Data = NULL;
        return FALSE;
    }
} // End of NIDeque_c::Next

/*++

  NIDequeIter_c::Replace()

  Function Description:

      Replaces src by chg.

  Arguments:

      src -- Data to search for.

      chg -- The new data.

  Return Value:

      TRUE -- If data is valid.

      FALSE -- If data is not valid.

--*/
template<class T> BOOL NIDequeIter_c<T>::Replace(T &src,T chg)
{
    NILNode_c<T> *cptr    = NULL;

    if(Current != NULL){
        src = Current->Data;
        Current->Data = chg;
        return TRUE;
    }
    return FALSE;
} // End of NIDeque_c::Replace

/*++

  NIDequeIter_c::Remove()

  Function Description:

      Removes whatever the current pointer is pointing at.

  Arguments:

      data -- Data removed.

  Return Value:

      TRUE -- If data is valid.

      FALSE -- If data is not valid.

--*/
template<class T> BOOL NIDequeIter_c<T>::Remove(T &data)
{
    NILNode_c<T>    *cptr   = NULL;
    T               *vdata  = NULL;

    if(Current != NULL){
        RemoveData(Current,data);
        return TRUE;
    }
    return FALSE;
} // End of NIDeque_c<T>::Remove

#endif


NOWARN.H

/*++

    Copyright (c) 1995 Intel Corporation

Module Name:

    nowarn.h

Abstract:

    This header contains pragmas to turn off compiler warnings that
    may be safely ignored.

--*/

/* nonstandard extension 'single line comment' was used */
#pragma warning(disable: 4001)

// argument differs in indirection to slightly different base types
#pragma warning(disable: 4057)

// unreferenced formal parameter
#pragma warning(disable: 4100)

// named type definition in parentheses
#pragma warning(disable: 4115)

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

// nonstandard extension used : benign typedef redefinition
#pragma warning(disable: 4209)

// nonstandard extension used : bit field types other than int
#pragma warning(disable: 4214)

// unreferenced inline function has been removed
#pragma warning(disable: 4514)

// Note: Creating precompiled header
#pragma warning(disable: 4699)


CSTACK.H

/*++

  Copyright (c) 1995 Intel Corp

  Module Name:

    cstack.h

  Abstract:

    Creates a subclass of Stack_c that can push and pop an integer
    counter onto or off a stack.

--*/
#ifndef _CSTACKH_
#define _CSTACKH_

#include "nowarn.h"  /* turn off benign warnings */
#ifndef _WINSOCKAPI_
#define _WINSOCKAPI_   /* Prevent inclusion of winsock.h in windows.h */
#endif
#include <winsock2.h>
#include "nowarn.h"  /* some warnings may have been turned back on */
#include "nideque.h"
#include "stack.h"

class Cstack_c : private Stack_c<int> {

public:
    Cstack_c() : Stack_c<int>() { counter = 0; }
    ~Cstack_c()                 {}
    inline BOOL CPush()         {return Stack_c<int>::Push(counter++);}
    inline BOOL CPop(int &Data) {return Stack_c<int>::Pop(Data);}
    inline int  CGetCounter()   {return counter;}
    inline BOOL IsEmpty()       {return Stack_c<int>::IsEmpty();}

private:
    int counter;
};

#endif


STACK.H

/*++

  Copyright (c) 1995 Intel Corp

  File Name:

    stack.h

  Abstract:

    Implements stack structure.

--*/

#ifndef _STACKH_
#define _STACKH_

#include "nowarn.h"  /* turn off benign warnings */
#ifndef _WINSOCKAPI_
#define _WINSOCKAPI_   /* Prevent inclusion of winsock.h in windows.h */
#endif
#include <winsock2.h>
#include "nowarn.h"  /* some warnings may have been turned back on */
#include "nideque.h"

// Name:     Stack_c
// Purpose:  A general purpose stack.
// Context:  Can be used anywhere.
template<class T> class Stack_c : private NIDeque_c<T> {

    public:
                Stack_c() : NIDeque_c<T>() {}
            ~Stack_c()       {}
        inline BOOL Push(T Data) {return NIDeque_c<T>::InsertIntoFront(Data);}
        inline BOOLPop(T &Data) {return
                                  NIDeque_c<T>::RemoveFromFront(Data);}
    inline BOOL IsEmpty()    {return NIDeque_c<T>::IsEmpty();}
};

#endif


README.TXT

Global Chat Sample for Windows Sockets

SUMMARY
=======

The GLOBCHAT sample allows two clients to carry on a conversation using a
server as the intermediary. By using the Name Resolution and Registration
functions, the clients need not even have the same transport protocols
loaded. The server acts as a gateway between the two clients, forwarding
messages over the appropriate Windows Sockets interface.

MORE INFORMATION
================

To run Global Chat:

  - Load the server side, GLOBCHAT.EXE, on a Windows NT server. This machine
    should run every protocol that a potential client might run.

  - On a client machine, run GLOBCL.EXE. Choose Connect from the Options
    menu to get to the dialog box for selecting the protocol of your choice
    and give yourself a name to be known by.

  - Once you enter the protocol and your name, the Global Chat Client finds
    the server and gives you a list of other users who you can chat with.
    Pick one, wait for them to accept your offer of a chat, and start
    typing.

The Global Chat Server displays the protocols it is using, the number of
client machines connected using each protocol, a list of all the client
names currently connected, and the status of each.

Note: Some of the supported protocols simply use the first Global Chat
Server that they find. This means that if more than one Global Chat Server
is running on your network, you may not connect to the right one.


ABOUT.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:   about.c
//
//  PURPOSE:   Displays the "About" dialog box
//
//  FUNCTIONS:
//    CmdAbout        - Displays the "About" dialog box
//    About           - Processes messages for "About" dialog box.
//    MsgAboutInit    - To initialize the about box with version info
//                      from resources.
//    MsgAboutCommand - Process WM_COMMAND message sent to the about box.
//    CmdAboutDone    - Free the about box and related data.
//
//  COMMENTS:
//
//

#include <windows.h>            // required for all Windows applications
#include <windowsx.h>
#include <wsipx.h>              // IPX Sockets defs
#include <nspapi.h>
#include <wsnetbs.h>
#include "globals.h"            // prototypes specific to this application

LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM);
LRESULT MsgAboutInit(HWND, UINT, WPARAM, LPARAM);
LRESULT MsgAboutCommand(HWND, UINT, WPARAM, LPARAM);
LRESULT CmdAboutDone(HWND, WORD, WORD, HWND);

// About dialog message table definition.
MSD rgmsdAbout[] =
{
    {WM_COMMAND,    MsgAboutCommand},
    {WM_INITDIALOG, MsgAboutInit}
};

MSDI msdiAbout =
{
    sizeof(rgmsdAbout) / sizeof(MSD),
    rgmsdAbout,
    edwpNone
};

// About dialog command table definition.
CMD rgcmdAbout[] =
{
    {IDOK,     CmdAboutDone},
    {IDCANCEL, CmdAboutDone}
};

CMDI cmdiAbout =
{
    sizeof(rgcmdAbout) / sizeof(CMD),
    rgcmdAbout,
    edwpNone
};

// Module specific "globals"  Used when a variable needs to be
// accessed in more than on handler function.

HFONT hFontCopyright;

//
//  FUNCTION: CmdAbout(HWND, WORD, WORD, HWND)
//
//  PURPOSE: Displays the "About" dialog box
//
//  PARAMETERS:
//    hwnd      - Window handle
//    wCommand  - IDM_ABOUT (unused)
//    wNotify   - Notification number (unused)
//    hwndCtrl  - NULL (unused)
//
//  RETURN VALUE:
//
//    Always returns 0 - Message handled
//
//  COMMENTS:
//    To process the IDM_ABOUT message, call DialogBox() to display the
//    about dialog box.

LRESULT CmdAbout(HWND hwnd, WORD wCommand, WORD wNotify, HWND hwndCtrl)
{
    DialogBox(hInst, "AboutBox", hwnd, (DLGPROC)About);
    return 0;
}

//
//  FUNCTION: About(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE:  Processes messages for "About" dialog box.
//
//  PARAMETERS:
//    hdlg - window handle of the dialog box
//    wMessage - type of message
//    wparam - message-specific information
//    lparam - message-specific information
//
//  RETURN VALUE:
//    TRUE - message handled
//    FALSE - message not handled
//
//  COMMENTS:
//
//     Display version information from the version section of the
//     application resource.
//
//     Wait for user to click on "Ok" button, then close the dialog box.
//

LRESULT CALLBACK About(HWND hdlg, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    return DispMessage(&msdiAbout, hdlg, uMessage, wparam, lparam);
}

//
//  FUNCTION: MsgAboutInit(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: To initialize the about box with version info from resources.
//
//  PARAMETERS:
//    hwnd - The window handing the message.
//    uMessage - The message number. (unused).
//    wparam - Message specific data (unused).
//    lparam - Message specific data (unused).
//
//  RETURN VALUE:
//    Always returns 0 - message handled.
//
//  COMMENTS:
//    Uses the version apis to retrieve version information for
//    each of the static text boxes in the about box.
//

LRESULT MsgAboutInit(HWND hdlg, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    #define POINTSIZE 8

    char  szFullPath[256];
    DWORD dwVerHnd;
    DWORD dwVerInfoSize;
    HDC   hDC;
    int   iLogPixelsY, iPointSize;

    // Center the dialog over the application window
    CenterWindow(hdlg, GetWindow(hdlg, GW_OWNER));

    // Set the copyright font to something smaller than default
    hDC = GetDC(hdlg);
    iLogPixelsY = GetDeviceCaps(hDC, LOGPIXELSY);
    ReleaseDC(hdlg, hDC);
    iPointSize = MulDiv(iLogPixelsY, POINTSIZE, 72);
    iPointSize *= -1;

    hFontCopyright = CreateFont(iPointSize,
                                0, 0, 0,
                                FW_BOLD,
                                0, 0, 0, 0,
                                0, 0, 0, 0,
                                "Arial");

    SendDlgItemMessage(hdlg,
                       IDD_VERLAST,
                       WM_SETFONT,
                       (WPARAM)hFontCopyright,
                       0L);

    // Get version information from the application
    GetModuleFileName(hInst, szFullPath, sizeof(szFullPath));
    dwVerInfoSize = GetFileVersionInfoSize(szFullPath, &dwVerHnd);
    if (dwVerInfoSize)
    {
        // If we were able to get the information, process it:
        HANDLE  hMem;
        LPVOID  lpvMem;
        char    szGetName[256];
        int     cchRoot;
        int     i;

        hMem = GlobalAlloc(GMEM_MOVEABLE, dwVerInfoSize);
        lpvMem = GlobalLock(hMem);
        GetFileVersionInfo(szFullPath, dwVerHnd, dwVerInfoSize, lpvMem);

        lstrcpy(szGetName, GetStringRes (IDS_FILEINFOLANG));

        cchRoot = lstrlen(szGetName);

        // Walk through the dialog items that we want to replace:
        for (i = IDD_VERFIRST; i <= IDD_VERLAST; i++)
        {
            BOOL  fRet;
            UINT  cchVer = 0;
            LPSTR lszVer = NULL;
            char  szResult[256];

            GetDlgItemText(hdlg, i, szResult, sizeof(szResult));
            lstrcpy(&szGetName[cchRoot], szResult);
            fRet = VerQueryValue(lpvMem, szGetName, &lszVer, &cchVer);

            if (fRet && cchVer && lszVer)
            {
                // Replace dialog item text with version info
                lstrcpy(szResult, lszVer);
                SetDlgItemText(hdlg, i, szResult);
            }
        }
        GlobalUnlock(hMem);
        GlobalFree(hMem);
    }
    return TRUE;
}

//
//  FUNCTION: MsgAboutCommand(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Process WM_COMMAND message sent to the about box.
//
//  PARAMETERS:
//    hwnd - The window handing the message.
//    uMessage - The message number. (unused).
//    wparam - Message specific data (unused).
//    lparam - Message specific data (unused).
//
//  RETURN VALUE:
//    Always returns 0 - message handled.
//
//  COMMENTS:
//    Uses this DipsCommand function defined in wndproc.c combined
//    with the cmdiAbout structure defined in this file to handle
//    the command messages for the about dialog box.
//

LRESULT MsgAboutCommand(HWND   hwnd,
                        UINT   uMessage,
                        WPARAM wparam,
                        LPARAM lparam)
{
    return DispCommand(&cmdiAbout, hwnd, wparam, lparam);
}

//
//  FUNCTION: CmdAboutDone(HWND, WORD, HWND)
//
//  PURPOSE: Free the about box and related data.
//
//  PARAMETERS:
//    hwnd - The window handling the command.
//    wCommand - The command to be handled (unused).
//    wNotify   - Notification number (unused)
//    hwndCtrl - NULL (unused).
//
//  RETURN VALUE:
//    Always returns TRUE.
//
//  COMMENTS:
//    Calls EndDialog to finish the dialog session.
//

LRESULT CmdAboutDone(HWND hdlg, WORD wCommand, WORD wNotify, HWND hwndCtrl)
{
    if (hFontCopyright)
       DeleteObject(hFontCopyright);

    EndDialog(hdlg, TRUE);          // Exit the dialog
    return TRUE;
}


CONNECT.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:   connect.c
//
//  PURPOSE:   Displays the "Connect" dialog box
//
//  FUNCTIONS:
//    CmdConnect              - Displays the "Connect" dialog box
//    Connect                 - Processes messages for "Connect" dialog box.
//    MsgConnectInit          - Initializes edit controls
//    MsgConnectReadyForWrite - Sends name registration message when connection
//                              established with server
//    MsgConnectCommand       - Process WM_COMMAND message sent to the connect box.
//    CmdConnectDone          - Free the connect box and related data.
//    CmdConnectNow           - Establishes connection to server.
//    CmdConnectEnableOK      - Enables/Disables OK button when input data is
//                              valid/invalid
//
//  COMMENTS:
//
//

#include <windows.h>            // required for all Windows applications
#include <windowsx.h>
#include <wsipx.h>              // IPX sockets
#include <wsnetbs.h>            // NetBIOS sockets
#include <stdlib.h>
#include <stdio.h>
#include <nspapi.h>
#include <svcguid.h>
#include "globals.h"            // prototypes specific to this application

#define WSN_READYFORWRITE   700 // dlg window message indicating the connection is ready to send on

//   Function Definitions

LRESULT CALLBACK Connect(HWND, UINT, WPARAM, LPARAM);
LRESULT MsgConnectInit(HWND, UINT, WPARAM, LPARAM);
LRESULT MsgConnectReadyForWrite(HWND, UINT, WPARAM, LPARAM);
LRESULT MsgConnectCommand(HWND, UINT, WPARAM, LPARAM);
LRESULT CmdConnectNow(HWND, WORD, WORD, HWND);
LRESULT CmdConnectDone(HWND, WORD, WORD, HWND);
LRESULT CmdConnectEnableOK(HWND, WORD, WORD, HWND);

// Connect dialog message table definition.
MSD rgmsdConnect[] =
{
    {WM_COMMAND,        MsgConnectCommand},
    {WM_INITDIALOG,     MsgConnectInit},
    {WSN_READYFORWRITE,  MsgConnectReadyForWrite}
};

MSDI msdiConnect =
{
    sizeof(rgmsdConnect) / sizeof(MSD),
    rgmsdConnect,
    edwpNone
};

// Connect dialog command table definition.
CMD rgcmdConnect[] =
{
    {IDOK,        CmdConnectNow},
    {IDCANCEL,    CmdConnectDone},
    {CD_NAME,     CmdConnectEnableOK},
    {CD_PROTOCOL, CmdConnectEnableOK},
   {CD_SERVER,   CmdConnectEnableOK}
};

CMDI cmdiConnect =
{
    sizeof(rgcmdConnect) / sizeof(CMD),
    rgcmdConnect,
    edwpNone
};

// Module specific "globals"  Used when a variable needs to be
// accessed in more than one handler function.

HFONT hfontDlg;

//
//  FUNCTION: CmdConnect(HWND, WORD, WORD, HWND)
//
//  PURPOSE: Displays the "Connect" dialog box
//
//  PARAMETERS:
//    hwnd      - Window handle
//    wCommand  - IDM_CONNECT (unused)
//    wNotify   - Notification number (unused)
//    hwndCtrl  - NULL (unused)
//
//  RETURN VALUE:
//
//    Always returns 0 - Message handled
//
//  COMMENTS:
//    To process the IDM_CONNECT message, call DialogBox() to display the
//    Connect dialog box.

LRESULT CmdConnect(HWND hwnd, WORD wCommand, WORD wNotify, HWND hwndCtrl)
{

    // Start dialog box
    if(DialogBox(hInst, "ConnectBox", hwnd, (DLGPROC)Connect))
    {
        // We have a connection and have registered our name.  Set up Select dialog.
        PostMessage(hwnd, WM_COMMAND, MAKELONG(IDM_SELECT,0), 0);
        return 0;
    }

    // Connection failed - reset window title
    SetWindowText(hwnd, szTitle);
    return 0;
}

//
//  FUNCTION: Connect(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE:  Processes messages for "Connect" dialog box.
//
//  PARAMETERS:
//    hdlg - window handle of the dialog box
//    wMessage - type of message
//    wparam - message-specific information
//    lparam - message-specific information
//
//  RETURN VALUE:
//    TRUE - message handled
//    FALSE - message not handled
//
//  COMMENTS:
//
//     Gets connection information from user and then establishes a connection.
//
//     Connect when user clicks on the OK button.  Kill Dialog when connection
//     established.
//

LRESULT CALLBACK Connect(HWND hdlg, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    return DispMessage(&msdiConnect, hdlg, uMessage, wparam, lparam);
}

//
//  FUNCTION: MsgConnectInit(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: To center dialog, limit size of edit controls, and set up available protocols
//           to connect with
//
//  PARAMETERS:
//    hdlg - The window handling the message.
//    uMessage - The message number. (unused).
//    wparam - Message specific data (unused).
//    lparam - Message specific data (unused).
//
//  RETURN VALUE:
//    Always returns 0 - message handled.
//
//  COMMENTS:
//    Set size of edit controls for the following
//           Network  8  chars (4 2-digit hex numbers)
//           Node     12 chars (6 2-digit hex numbers)
//           Socket   4  chars (2 2-digit hex numbers)
//

LRESULT MsgConnectInit(HWND hdlg, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    int numStrucs;
    int j, k;
    int iProtBufSize = 0;
   char outtext[80];

    // Create a font to use
    hfontDlg = CreateFont(14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                          VARIABLE_PITCH | FF_SWISS, "");

    // Center the dialog over the application window
    CenterWindow (hdlg, GetWindow (hdlg, GW_OWNER));

   // Call EnumProtocols with NULL buffer in order to determine size of buffer required
   EnumProtocols(NULL, NULL, &iProtBufSize);

    if (((LPVOID)lpProtBuf = VirtualAlloc(NULL,
                                          iProtBufSize,
                                          MEM_COMMIT,
                                          PAGE_READWRITE)) == NULL)
    {
        // ERROR -- abort
        return 0;
    }

    // Calling EnumProtocols with large enough buffer
    if ((numStrucs = EnumProtocols(NULL, lpProtBuf, &iProtBufSize)) == SOCKET_ERROR)
    {
        // Error -- abort
        return 0;
    }

   // Add every connection oriented protocol to protocol combo box
    for (j = 0, k = 0; j < numStrucs; j++)
    {
        if ((lpProtBuf[j].dwServiceFlags & XP_CONNECTIONLESS) == 0)
        {
         wsprintf(outtext, "%2d: %s", k, lpProtBuf[j].lpProtocol);
            SendMessage(GetDlgItem(hdlg, CD_PROTOCOL), CB_ADDSTRING, 0, (LPARAM)outtext);
            goodprots[k++] = j; // Keep track of good protocols
        }
    }

    // Limit size of edit controls
    SendDlgItemMessage(hdlg, CD_NAME, EM_LIMITTEXT, 15, 0);

    // Initialize Edit Controls
    SetDlgItemText(hdlg, CD_NAME, szConnectName);

    return (TRUE);
}

//
//  FUNCTION: MsgConnectReadyForWrite(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Receive notification of connected socket and register name
//
//  PARAMETERS:
//    hwnd - The window handing the message.
//    uMessage - The message number. (unused).
//    wparam - Message specific data (unused).
//    lparam - Message specific data (unused).
//
//  RETURN VALUE:
//    Always returns 0 - message handled.
//
//  COMMENTS:
//    Uses this DispCommand function defined in wndproc.c combined
//    with the cmdiConnect structure defined in this file to handle
//    the command messages for the Connect dialog box.
//

LRESULT MsgConnectReadyForWrite(HWND   hwnd,
                                UINT   uMessage,
                                WPARAM wparam,
                                LPARAM lparam)
{
    char outtext[80];

    // Build our name
    GetDlgItemText(hwnd, CD_NAME, MySock.name, sizeof(MySock.name));

    // Build name registration packet
    xferbuf.hdr.signature = MYSIGNATURE;                 // signature
    xferbuf.hdr.length = REALLEN(MySock.name) + HDRSIZE;
    lstrcpy(xferbuf.data, MySock.name);                  // copy name
    xferbuf.hdr.command = REGISTER_NAME;                 // REGISTER_NAME command

    // Lets send it
    senddatamessage(MySock.sock, &xferbuf);

    wsprintf(outtext, GetStringRes(IDS_CHAT_AVAIL), MySock.name);

    SetWindowText(GetParent(hwnd), outtext);

    EndDialog(hwnd, TRUE);          // Exit the dialog
    DeleteObject (hfontDlg);
    return (TRUE);
}

//
//  FUNCTION: MsgConnectCommand(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Process WM_COMMAND message sent to the Connect box.
//
//  PARAMETERS:
//    hwnd - The window handing the message.
//    uMessage - The message number. (unused).
//    wparam - Message specific data (unused).
//    lparam - Message specific data (unused).
//
//  RETURN VALUE:
//    Always returns 0 - message handled.
//
//  COMMENTS:
//    Uses this DispCommand function defined in wndproc.c combined
//    with the cmdiConnect structure defined in this file to handle
//    the command messages for the Connect dialog box.
//

LRESULT MsgConnectCommand(HWND   hwnd,
                          UINT   uMessage,
                          WPARAM wparam,
                          LPARAM lparam)
{
    return DispCommand(&cmdiConnect, hwnd, wparam, lparam);
}

//
//  FUNCTION: CmdConnectDone(HWND, WORD, HWND)
//
//  PURPOSE: Free the Connect box and related data.
//
//  PARAMETERS:
//    hdlg - The window handling the command.
//    wCommand - The command to be handled (unused).
//    wNotify   - Notification number (unused)
//    hwndCtrl - NULL (unused).
//
//  RETURN VALUE:
//    Always returns TRUE.
//
//  COMMENTS:
//    Cleans up sockets then calls EndDialog to finish the dialog session.
//

LRESULT CmdConnectDone(HWND hdlg, WORD wCommand, WORD wNotify, HWND hwndCtrl)
{

    closesocket(MySock.sock);      // Free any aborted socket resources
    EndDialog(hdlg, FALSE);        // Exit Dialog -- rtrn false since no connection
    return TRUE;
}

//
//  FUNCTION: CmdConnectNow(HWND, WORD, HWND)
//
//  PURPOSE: Establish the connection
//
//  PARAMETERS:
//    hdlg - The window handling the command.
//    wCommand - The command to be handled (unused).
//    wNotify   - Notification number (unused)
//    hwndCtrl - NULL (unused).
//
//  RETURN VALUE:
//    Always returns TRUE.
//
//  COMMENTS:
//    Makes Connection calls
//

LRESULT CmdConnectNow(HWND hdlg, WORD wCommand, WORD wNotify, HWND hwndCtrl)
{
    int iIndex;
    char outtext[80];
    int numStrucs;
   GUID guidNW = SVCID_NETWARE(NWCHATID);
   GUID guidDNS = SVCID_TCP(DNSCHATID);

    // Get protocol selected
    iIndex = SendMessage(GetDlgItem(hdlg, CD_PROTOCOL), CB_GETCURSEL, 0, 0);

   // ************************************************************************
   //
   //    Below we will be calling GetAddressByName with the RES_FIND_MULTIPLE
   //    option in order to find a remote address to connect() to.  At this
   //    point, GetAddressByName() only supports the DNS and the SAP/Bindery
   //    name spaces.  Ultimately a single call to GetAddressByName will
   //    query all available protocols and name spaces, but this requires a
   //    central database which is currently not available.  In the mean time
   //    we will make name space specific calls to GetAddressByName.
   //
   // ***********************************************************************

    switch (lpProtBuf[goodprots[iIndex]].iAddressFamily)
    {
        case AF_IPX:
          //  SAP/Bindery Name Space
          dwCSABufsize = sizeof(CSABuf);
            if (((numStrucs = GetAddressByName(0,  // GUID indicates name space so we don't need to specify
                                               &guidNW, // GUID generated from NetWare service type
                                               "GLOBCHAT", // Name of service to look for
                                               NULL, // GUID implies IPX/SPX protocols
                                               RES_FIND_MULTIPLE, // We might get multiple responses
                                               NULL, // Not currently supported
                                               CSABuf, // results buffer
                                               &dwCSABufsize,  // size of results buffer
                                               NULL,  // not supported
                                               NULL)  // not supported
                                               ) == SOCKET_ERROR) || (numStrucs == 0))
            {
                // Error -- try another protocol.  We've got lots!

                MessageBox(hdlg, GetStringRes(IDS_ERR_SERVER_NOT_FOUND), NULL, MB_OK);
                EnableWindow(GetDlgItem(hdlg, IDOK), FALSE);
                SetFocus(GetDlgItem(hdlg, CD_PROTOCOL));
                return 0;
            }
            break;

        case AF_INET:
            //  DNS Name Space

            // Static Name Space requires us to specify the host name
            GetDlgItemText(hdlg, CD_SERVER, lpServName, sizeof(lpServName));
         dwCSABufsize = sizeof(CSABuf);
            if (((numStrucs = GetAddressByName(0,    // GUID indicates name space so we don't need to specify
                                             &guidDNS, // GUID generated from TCP port number
                                             lpServName, // Name of host to look for
                                             NULL, // GUID implies UDP/TCP protocols
                                             RES_FIND_MULTIPLE, // We might get multiple responses
                                             NULL, // Not currently supported
                                             CSABuf, // results buffer
                                             &dwCSABufsize, // size of results buffer
                                             NULL, // not supported
                                             NULL) // not supported
                                             ) == SOCKET_ERROR) || (numStrucs == 0))
            {
                // Error -- try another protocol.  We've got lots!

                MessageBox(hdlg, GetStringRes(IDS_ERR_SERVER_NOT_FOUND), NULL, MB_OK);

                EnableWindow(GetDlgItem(hdlg, IDOK), FALSE);
                SetFocus(GetDlgItem(hdlg, CD_PROTOCOL));
                return 0;
            }
            break;

        case AF_NETBIOS:
         // NetBIOS name space???

            // no netbios name space provider so fill in lpCSABuf ourselves
            numStrucs = 1;
            CSABuf[0].iSocketType = lpProtBuf[goodprots[iIndex]].iSocketType;
            CSABuf[0].iProtocol = lpProtBuf[goodprots[iIndex]].iProtocol;

            SET_NETBIOS_SOCKADDR(&NBAddr,
                                 NETBIOS_GROUP_NAME,
                                 "GLOBSERV",
                                 0);
           CSABuf[0].RemoteAddr.lpSockaddr = (LPSOCKADDR)&NBAddr;
         CSABuf[0].RemoteAddr.iSockaddrLength = sizeof(NBAddr);
            break;

        default:
          // We don't support anything else
            MessageBox(hdlg,
                       GetStringRes(IDS_ERR_NAMESPACE_NOT_SUPPORTED),
                       NULL, MB_OK);

            EnableWindow(GetDlgItem(hdlg, IDOK), FALSE);
            SetFocus(GetDlgItem(hdlg, CD_PROTOCOL));
            return 0;
    }

   // Populate SOCKDATA struct
    MySock.type = lpProtBuf[goodprots[iIndex]].iSocketType;
    MySock.protocol = lpProtBuf[goodprots[iIndex]].iProtocol;

    SetDlgItemText(hdlg, CD_HELP, GetStringRes(IDS_CALLING_SOCKET));

   // Call socket() using triple provided by EnumProtocols()
    if((MySock.sock = socket(lpProtBuf[goodprots[iIndex]].iAddressFamily,
                             lpProtBuf[goodprots[iIndex]].iSocketType,
                             lpProtBuf[goodprots[iIndex]].iProtocol)) == INVALID_SOCKET)
    {
        // ERROR
        wsprintf(outtext, GetStringRes(IDS_ERR_SOCKET_FAILED), WSAGetLastError());
        SetDlgItemText(hdlg, CD_HELP, outtext);
        return 0;
    }

    SetDlgItemText(hdlg, CD_HELP, GetStringRes(IDS_CALLING_CONNECT));

   // Call connect() using info from GetAddressByName()
    if (connect(MySock.sock,
                CSABuf[0].RemoteAddr.lpSockaddr,
                CSABuf[0].RemoteAddr.iSockaddrLength) == SOCKET_ERROR)
    {
        // ERROR
        wsprintf(outtext,
                 GetStringRes(IDS_ERR_CONNECT_FAILED), WSAGetLastError());

        SetDlgItemText(hdlg, CD_HELP, outtext);
        return 0;
    }

    // Signal for when we are ready for writing

    SetDlgItemText(hdlg, CD_HELP, GetStringRes(IDS_WAITING_FOR_ACCEPT));

   // Specify message to signal accepted socket
    if (WSAAsyncSelect(MySock.sock, hdlg, WSN_READYFORWRITE, FD_WRITE) == SOCKET_ERROR)
    {
        wsprintf(outtext,
                 GetStringRes(IDS_ERR_WSAASYNCSELECT), WSAGetLastError());

        SetDlgItemText(hdlg, CD_HELP, outtext);
        return 0;
    }
    return 0;

}

//
//  FUNCTION: CmdConnectEnableOK(HWND, WORD, WORD, HWND)
//
//  PURPOSE: Enable/Disable OK button when input data is valid/invalid
//
//  PARAMETERS:
//    hdlg - The window handling the command.
//    wCommand - The command to be handled (unused).
//    wNotify   - Notification number (unused)
//    hwndCtrl - NULL (unused).
//
//  RETURN VALUE:
//    Always returns TRUE.
//
//  COMMENTS:
//    Checks for acceptable name and protocol.  We also need an acceptable
//    machine name for TCP/IP protocol since DNS name space is static
//

LRESULT CmdConnectEnableOK(HWND hdlg, WORD wCommand, WORD wNotify, HWND hwndCtrl)
{
    char buf[128];
   int iIndex;

    // Is message a change notification?
    if ((wNotify == CBN_SELCHANGE) || (wNotify == EN_CHANGE))
    {
        // Yes!, get protocol
        iIndex = SendMessage(GetDlgItem(hdlg, CD_PROTOCOL), CB_GETCURSEL, 0, 0);
      if (iIndex == LB_ERR)
      {
          // No protocol selected...OK disabled
          EnableWindow(GetDlgItem(hdlg, IDOK), FALSE);
          return TRUE;
      }
        // Protocol selected!  Check which one...
        switch(lpProtBuf[goodprots[iIndex]].iAddressFamily)
        {
            case AF_IPX:
            case AF_NETBIOS:
                // For IPX and NetBIOS, we don't need the machine name
            // so hide that edit control
                ShowWindow(GetDlgItem(hdlg, CD_SERVER), SW_HIDE);
                ShowWindow(GetDlgItem(hdlg, CD_SERVER_TEXT), SW_HIDE);

            // We already have a protocol selected.  Do we have a name?
                if(GetDlgItemText(hdlg, CD_NAME, buf, sizeof(buf)) >= 1)
                {
                    // Yes, we have a name and a protocol.  Enable OK.
                    EnableWindow(GetDlgItem(hdlg, IDOK), TRUE);
                }
            else
            {
                // We don't have a name.  Disable OK.
                EnableWindow(GetDlgItem(hdlg, IDOK), FALSE);
            }
                return TRUE;

            case AF_INET:
             // For TCP/IP we need a protocol, name, and machine name
            // in order for data to be valid

            // Enable Machine name edit control and text
                ShowWindow(GetDlgItem(hdlg, CD_SERVER), SW_SHOW);
            ShowWindow(GetDlgItem(hdlg, CD_SERVER_TEXT), SW_SHOW);
            EnableWindow(GetDlgItem(hdlg, CD_SERVER), TRUE);
            EnableWindow(GetDlgItem(hdlg, CD_SERVER_TEXT), TRUE);

                // We have a protocol selected.  Do we have a name and a server name
                if((GetDlgItemText(hdlg, CD_NAME, buf, sizeof(buf)) >= 1) &&
                   (GetDlgItemText(hdlg, CD_SERVER, buf, sizeof(buf)) >= 1))
                {
                    // YES!  Enable OK.
                    EnableWindow(GetDlgItem(hdlg, IDOK), TRUE);
               SendMessage(GetDlgItem(hdlg, IDOK), BM_SETSTYLE, (WPARAM) LOWORD(BS_DEFPUSHBUTTON), MAKELPARAM(TRUE, 0));
                }
            else
            {
                // No name or server name.  Disable OK.
                EnableWindow(GetDlgItem(hdlg, IDOK), FALSE);
            }
                return TRUE;

            default:
                // We only support DNS, SAP/Bindery, and we simulate NetBIOS name spaces.

                SetDlgItemText(hdlg, CD_HELP,
                               GetStringRes(IDS_ERR_UNSUPPORTED_PROTOCOL));

                EnableWindow(GetDlgItem(hdlg, IDOK), FALSE);
                return TRUE;
        }
    }

    // When protocol combo box gets the focus, set help text at bottom of dialog
    if (wNotify == CBN_SETFOCUS)
    {
        SetDlgItemText(hdlg, CD_HELP,
                       GetStringRes(IDS_SELECT_PROTOCOL));
    }

    if (wNotify == EN_SETFOCUS)
    {
        if (hwndCtrl == GetDlgItem(hdlg, CD_NAME))
        {
            // When name edit control gets the focus, set help text at bottom of dialog.

            SetDlgItemText(hdlg, CD_HELP, GetStringRes(IDS_ENTER_NAME));
        }
        else
        {
            // When server edit control gets the focus, set help text at bottom of dialog.

            SetDlgItemText(hdlg, CD_HELP, GetStringRes(IDS_ENTER_MACHINE_NAME));
        }
    }
    return TRUE;
}


DISPATCH.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:   dispatch.c
//
//  PURPOSE:  Implement the generic message and command dispatchers.
//
//
//  FUNCTIONS:
//    DispMessage - Call the function associated with a message.
//    DispCommand - Call the function associated with a command.
//    DispDefault - Call the appropriate default window procedure.
//
//  COMMENTS:
//

#include <windows.h>            // required for all Windows applications
#include <windowsx.h>
#include <wsipx.h>              // IPX Sockets defs
#include <wsnetbs.h>
#include <nspapi.h>
#include "globals.h"            // prototypes specific to this application

LRESULT DispDefault(EDWP, HWND, UINT, WPARAM, LPARAM);

//
//  FUNCTION: DispMessage(LPMSDI, HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Call the function associated with a message.
//
//  PARAMETERS:
//    lpmsdi - Structure containing the message dispatch information.
//    hwnd - The window handle
//    uMessage - The message number
//    wparam - Message specific data
//    lparam - Message specific data
//
//  RETURN VALUE:
//    The value returned by the message function that was called.
//
//  COMMENTS:
//    Runs the table of messages stored in lpmsdi->rgmsd searching
//    for a message number that matches uMessage.  If a match is found,
//    call the associated function.  Otherwise, call DispDefault to
//    call the default function, if any, associated with the message
//    structure.  In either case, return the value recieved from the
//    message or default function.
//

LRESULT DispMessage(LPMSDI lpmsdi,
                    HWND   hwnd,
                    UINT   uMessage,
                    WPARAM wparam,
                    LPARAM lparam)
{
    int  imsd = 0;

    MSD *rgmsd = lpmsdi->rgmsd;
    int  cmsd  = lpmsdi->cmsd;

    for (imsd = 0; imsd < cmsd; imsd++)
    {
        if (rgmsd[imsd].uMessage == uMessage)
            return rgmsd[imsd].pfnmsg(hwnd, uMessage, wparam, lparam);
    }

    return DispDefault(lpmsdi->edwp, hwnd, uMessage, wparam, lparam);
}

//
//  FUNCTION: DispCommand(LPCMDI, HWND, WPARAM, LPARAM)
//
//  PURPOSE: Call the function associated with a command.
//
//  PARAMETERS:
//    lpcmdi - Structure containing the command dispatch information.
//    hwnd - The window handle
//    GET_WM_COMMAND_ID(wparam, lparam) - Identifier of the menu item,
//      control, or accelerator.
//    GET_WM_COMMAND_CMD(wparam, lparam) - Notification code.
//    GET_WM_COMMAND_HWND(wparam, lparam) - The control handle or NULL.
//
//  RETURN VALUE:
//    The value returned by the command function that was called.
//
//  COMMENTS:
//    Runs the table of commands stored in lpcmdi->rgcmd searching
//    for a command number that matches wCommand.  If a match is found,
//    call the associated function.  Otherwise, call DispDefault to
//    call the default function, if any, associated with the command
//    structure.  In either case, return the value recieved from the
//    command or default function.
//

LRESULT DispCommand(LPCMDI lpcmdi,
                    HWND   hwnd,
                    WPARAM wparam,
                    LPARAM lparam)
{
    LRESULT lRet = 0;
    WORD    wCommand = GET_WM_COMMAND_ID(wparam, lparam);
    int     icmd;

    CMD    *rgcmd = lpcmdi->rgcmd;
    int     ccmd  = lpcmdi->ccmd;

    // Message packing of wparam and lparam have changed for Win32,
    // so use the GET_WM_COMMAND macro to unpack the commnad

    for (icmd = 0; icmd < ccmd; icmd++)
    {
        if (rgcmd[icmd].wCommand == wCommand)
        {
            return rgcmd[icmd].pfncmd(hwnd,
                                      wCommand,
                                      GET_WM_COMMAND_CMD(wparam, lparam),
                                      GET_WM_COMMAND_HWND(wparam, lparam));
        }
    }

    return DispDefault(lpcmdi->edwp, hwnd, WM_COMMAND, wparam, lparam);
}

//
//  FUNCTION: DispDefault(EDWP, HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Call the appropriate default window procedure.
//
//  PARAMETERS:
//    edwp - Enumerate specifying the appropriate default winow procedure.
//    hwnd - The window handle
//    uMessage - The message number
//    wparam - Message specific data
//    lparam - Message specific data
//
//  RETURN VALUE:
//    If there is a default proc, return the value returned by the
//    default proc.  Otherwise, return 0.
//
//  COMMENTS:
//    Calls the default procedure associated with edwp using the specified
//    parameters.
//

LRESULT DispDefault(EDWP   edwp,
                    HWND   hwnd,
                    UINT   uMessage,
                    WPARAM wparam,
                    LPARAM lparam)
{
    switch (edwp)
    {
        case edwpNone:
            return 0;
        case edwpWindow:
            return DefWindowProc(hwnd, uMessage, wparam, lparam);
        case edwpDialog:
            return DefDlgProc(hwnd, uMessage, wparam, lparam);
        case edwpMDIFrame:
            return DefFrameProc(hwnd, hwndMDIGlobCl, uMessage, wparam, lparam);
        case edwpMDIChild:
            return DefMDIChildProc(hwnd, uMessage, wparam, lparam);
    }
    return 0;
}


GLOBALS.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.
//
// PURPOSE:
//    Contains declarations for all globally scoped names in the program.
//

//-------------------------------------------------------------------------
// Product identifier string defines

#define APPNAME       GlobCl

//-------------------------------------------------------------------------
// Functions for handling main window messages.  The message-dispatching
// mechanism expects all message-handling functions to have the following
// prototype:
//
//     LRESULT FunctionName(HWND, UINT, WPARAM, LPARAM);

LRESULT MsgCreate(HWND, UINT, WPARAM, LPARAM);
LRESULT MsgSize(HWND, UINT, WPARAM, LPARAM);
LRESULT MsgSetfocus(HWND, UINT, WPARAM, LPARAM);
LRESULT MsgCommand(HWND, UINT, WPARAM, LPARAM);
LRESULT MsgDataready(HWND, UINT, WPARAM, LPARAM);
LRESULT MsgRefreshdisplay(HWND, UINT, WPARAM, LPARAM);
LRESULT MsgDisconnected(HWND, UINT, WPARAM, LPARAM);
LRESULT MsgDestroy(HWND, UINT, WPARAM, LPARAM);

//-------------------------------------------------------------------------
// Functions for handling main window commands--ie. functions for
// processing WM_COMMAND messages based on the wParam value.
// The message-dispatching mechanism expects all command-handling
// functions to have the following prototype:
//
//     LRESULT FunctionName(HWND, WORD, WORD, HWND);

LRESULT CmdConnect(HWND, WORD, WORD, HWND);
LRESULT CmdSelect(HWND, WORD, WORD, HWND);
LRESULT CmdExit(HWND, WORD, WORD, HWND);
LRESULT CmdAbout(HWND, WORD, WORD, HWND);
LRESULT CmdOutbox(HWND, WORD, WORD, HWND);
LRESULT CmdDisconnect(HWND, WORD, WORD, HWND);
LRESULT CmdEndChat(HWND, WORD, WORD, HWND);

//
//   Socket Data transfer Message definitions
//
typedef struct _MSGHDR            // Message Header
{
    BYTE            signature;    // Identifies start of a message
    WORD            length;       // size of message
    BYTE            command;      // message command
} MSGHDR, *LPMSGHDR;

typedef struct _DATAMSG            // Message--contains header + data
{
    MSGHDR          hdr;
    BYTE            data[0x8000];  // data
} DATAMSG, *LPDATAMSG;

DATAMSG xferbuf;

// Message constants
#define HDRSIZE      sizeof(MSGHDR)    // Size of Message header
#define REALLEN(x)   lstrlen(x) + 1    // REALLEN will now count terminating NULL of x

//    Globchat socket data structure to hold all necessary socket information
//
typedef struct _SOCKDATA
{
    SOCKET           sock;         // socket handle
    int              status;       // Socket's status
    struct sockaddr  addr;         // Address structure
    char             reserved[10]; // Generic sockaddr struct is not big enough for all
                                   // addresses (namely netbios addresses) so adding some
                                   // space at the end.
    int              addrlen;      // Address length
    int              type;         // Socket type (from socket() call)
    int              protocol;     // protocol (from socket() call)
    int              currconnects; // Number of current connections on this socket
    SOCKET           peer;         // Handle of server's peer socket we are connected to
    char             name[16];      // Our name
} SOCKDATA, *LPSOCKDATA;

SOCKDATA MySock;

//  SOCKDATA structure status options
#define SOCKSTAT_INIT        1
#define SOCKSTAT_LISTENING   2
#define SOCKSTAT_ACCEPTING   3
#define SOCKSTAT_CLOSED      4
#define SOCKSTAT_CONNECTED   5
#define SOCKSTAT_AVAILABLE   6
#define SOCKSTAT_INSESSION   7
#define SOCKSTAT_REQSESSION  8

//-------------------------------------------------------------------------
// Global function prototypes.

BOOL InitApplication(HINSTANCE, int);
BOOL CenterWindow(HWND, HWND);
BOOL ReceiveInBox(HWND, WPARAM, LPARAM, char *, int);
void SendOutBox(char *, int);
void AtoH(char *, char *, int);
unsigned char BtoH(char);
void CleanUp(void);
void GetAddrString(PSOCKADDR_IPX, char *);
void HtoA(char *, char *, int);
char HtoB(UCHAR);
BOOL senddatamessage (SOCKET, LPDATAMSG);
BOOL recvdatamessage (LPSOCKDATA, LPDATAMSG);
LPTSTR GetStringRes (int id);

// Callback functions.  These are called by Windows.

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

//-------------------------------------------------------------------------
// Command ID definitions.  These definitions are used to associate menu
// items with commands.

// Options menu
#define IDM_CONNECT           1000
#define IDM_SELECT            1001
#define IDM_END_CHAT          1002
#define IDM_DISCONNECT_SERVER 1003
#define IDM_EXIT              1004

// Help menu
#define IDM_ABOUT      1100

//-------------------------------------------------------------------------
// User Defined Messages.  These definitions are used for indicating
// network events.

#define MW_DATAREADY       501
#define MW_DISPLAYREFRESH  502
#define MW_DISCONNECTED    503
#define LDM_CONNECTED      504

//-------------------------------------------------------------------------
// String Table ID definitions.

#define IDS_APPNAME                       1
#define IDS_DESCRIPTION                   2
#define IDS_FILEINFOLANG                  3
#define IDS_CHAT_AVAIL                    4
#define IDS_ERR_SERVER_NOT_FOUND          5
#define IDS_ERR_NAMESPACE_NOT_SUPPORTED   6
#define IDS_ERR_SOCKET_FAILED             7
#define IDS_ERR_CONNECT_FAILED            8
#define IDS_ERR_WSAASYNCSELECT            9
#define IDS_ERR_UNSUPPORTED_PROTOCOL      10
#define IDS_ERR_CONNECTION_DROPPED        11
#define IDS_CALLING_SOCKET                12
#define IDS_CALLING_CONNECT               13
#define IDS_WAITING_FOR_ACCEPT            14
#define IDS_SELECT_PROTOCOL               15
#define IDS_ENTER_NAME                    16
#define IDS_ENTER_MACHINE_NAME            17
#define IDS_CONNECTION_STOPPED            18
#define IDS_CHAT_DLG_STOPPED              19
#define IDS_CHAT_AVAILABLE                20
#define IDS_CHATTING_REMOTE               21
#define IDS_REQUESTS_CHAT                 22
#define IDS_SESSION_REQUEST               23

//-------------------------------------------------------------------------
//  Main Window Edit Control defines.

#define ID_OUTBOX          601
#define ID_INBOX           602

//-------------------------------------------------------------------------
//  About dialog defines.

#define IDD_VERFIRST    100
#define IDD_VERLAST     104

//-------------------------------------------------------------------------
//  Connect dialog defines.

#define CD_NAME        200
#define CD_PROTOCOL    201
#define CD_SERVER      202
#define CD_SERVER_TEXT 203
#define CD_HELP        204

//-------------------------------------------------------------------------
//  Select dialog defines.

#define SD_LIST        300

//-------------------------------------------------------------------------
//  Globchat socket message commands

#define REGISTER_NAME             1
#define XFER_DATA                 2
#define REQUEST_SESSION           3
#define SESSION_REQUEST_RESPONSE  4
#define SESSION_CLOSE             5
#define DEREGISTER_NAME           6

//-------------------------------------------------------------------------
//  Socket constants
#define MYSIGNATURE               0xCC        // Starts every globchat socket message header
#define NWCHATID                  0x0756      // 0x0756 (network order) was assigned by Novell
                                              // for this specific application
#define DNSCHATID                 0x555       // Well known socket for DNS GUID generation

//-------------------------------------------------------------------------
// Global variable declarations.

extern HINSTANCE hInst;          // The current instance handle
extern char      szAppName[];    // The name of this application
extern char      szTitle[];      // The title bar text
SOCKET sock, SrvSock;
SOCKADDR_IPX RemAddr;
PSOCKADDR_IPX pSockAddr, pRemAddr;
struct sockaddr addr;
int addrlen;
HWND hOutWnd, hInWnd;
char szRcvBuf[0x8000];
char szSndBuf[0x8000];
extern char szConnectName[];
extern char szConnectServer[];
CSADDR_INFO CSAInfo[10];
CSADDR_INFO CSABuf[20];
DWORD dwCSABufsize;
char peername[16];
LPPROTOCOL_INFO lpProtBuf;
int goodprots[30];
char lpServName[256];
SOCKADDR_NB NBAddr;
SOCKADDR_IPX IPXAddr;

#define hwndMDIGlobCl NULL        /* Stub for NON-MDI applications. */

//-------------------------------------------------------------------------
// Message and command dispatch infrastructure.  The following type
// definitions and functions are used by the message and command dispatching
// mechanism and do not need to be changed.

    // Function pointer prototype for message handling functions.
typedef LRESULT (*PFNMSG)(HWND,UINT,WPARAM,LPARAM);

    // Function pointer prototype for command handling functions.
typedef LRESULT (*PFNCMD)(HWND,WORD,WORD,HWND);

    // Enumerated type used to determine which default window procedure
    // should be called by the message- and command-dispatching mechanism
    // if a message or command is not handled explicitly.
typedef enum
{
   edwpNone,            // Do not call any default procedure.
   edwpWindow,          // Call DefWindowProc.
   edwpDialog,          // Call DefDlgProc (This should be used only for
                        // custom dialogs - standard dialog use edwpNone).
   edwpMDIChild,        // Call DefMDIChildProc.
   edwpMDIFrame         // Call DefFrameProc.
} EDWP;                // Enumeration for Default Window Procedures

    // This structure maps messages to message handling functions.
typedef struct _MSD
{
    UINT   uMessage;
    PFNMSG pfnmsg;
} MSD;                 // MeSsage Dispatch structure

    // This structure contains all of the information that a window
    // procedure passes to DispMessage in order to define the message
    // dispatching behavior for the window.
typedef struct _MSDI
{
    int  cmsd;          // Number of message dispatch structs in rgmsd
    MSD *rgmsd;         // Table of message dispatch structures
    EDWP edwp;          // Type of default window handler needed.
} MSDI, FAR *LPMSDI;   // MeSsage Dipatch Information

    // This structure maps command IDs to command handling functions.
typedef struct _CMD
{
    WORD   wCommand;
    PFNCMD pfncmd;
} CMD;                 // CoMmand Dispatch structure

    // This structure contains all of the information that a command
    // message procedure passes to DispCommand in order to define the
    // command dispatching behavior for the window.
typedef struct _CMDI
{
    int  ccmd;          // Number of command dispatch structs in rgcmd
    CMD *rgcmd;         // Table of command dispatch structures
    EDWP edwp;          // Type of default window handler needed.
} CMDI, FAR *LPCMDI;   // CoMmand Dispatch Information

    // Message and command dispatching functions.  They look up messages
    // and commands in the dispatch tables and call the appropriate handler
    // function.
LRESULT DispMessage(LPMSDI, HWND, UINT, WPARAM, LPARAM);
LRESULT DispCommand(LPCMDI, HWND, WPARAM, LPARAM);

    // Message dispatch information for the main window
extern MSDI msdiMain;
    // Command dispatch information for the main window
extern CMDI cmdiMain;

//-------------------------------------------------------------------------
// Version string definitions--Leave these alone.

#define SZRCOMPANYNAME "CompanyName"
#define SZRDESCRIPTION "FileDescription"
#define SZRVERSION     "FileVersion"
#define SZRAPPNAME     "InternalName"
#define SZRCOPYRIGHT   "LegalCopyright"
#define SZRTRADEMARK   "LegalTrademarks"
#define SZRPRODNAME    "ProductName"
#define SZRPRODVER     "ProuctVersion"


GLOBCL.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:   globcl.c
//
//  PURPOSE:   Implement the windows procedure for the main application
//    windows.  Also implement the generic message and command dispatchers.
//
//  FUNCTIONS:
//    WndProc           - Processes messages for the main window.
//    MsgCreate         - Initializes Edit Controls for text input/output.
//    MsgSize           - Adjusts size of Edit Controls when window is resized.
//    MsgSetfocus       - Keeps window focus on edit control instead of parent.
//    MsgDataready      - Reads data from incoming IPC mechanism.
//    MsgRefreshdisplay - Refills Inbox edit control text contents
//    MsgDisconnected   - Cleans up connection killed by other side
//    MsgCommand        - Handle the WM_COMMAND messages for the main window.
//    MsgDestroy        - Handles the WM_DESTROY message by calling
//                        PostQuitMessage().
//    CmdOutbox         - Handles messages from Outbox edit control.
//    CmdDisconnected   - Disconnects current connection
//    CmdEndChat        - Ends Peer to Peer session
//    CmdExit           - Handles the file exit command by calling destory
//                        window on the main window.
//
//  COMMENTS:
//

#include <windows.h>            // required for all Windows applications
#include <windowsx.h>
#include <wsipx.h>
#include <wsnetbs.h>
#include <nspapi.h>
#include "globals.h"            // prototypes specific to this application

// Main window message table definition.
MSD rgmsd[] =
{
    {WM_CREATE,         MsgCreate},
    {WM_SIZE,           MsgSize},
    {WM_SETFOCUS,       MsgSetfocus},
    {WM_COMMAND,        MsgCommand},
    {WM_DESTROY,        MsgDestroy},
    {MW_DATAREADY,      MsgDataready},
    {MW_DISCONNECTED,   MsgDisconnected},
    {MW_DISPLAYREFRESH, MsgRefreshdisplay}
};

MSDI msdiMain =
{
    sizeof(rgmsd) / sizeof(MSD),
    rgmsd,
    edwpWindow
};

// Main window command table definition.
CMD rgcmd[] =
{
    {IDM_CONNECT,           CmdConnect},
    {IDM_SELECT,            CmdSelect},
    {IDM_EXIT,              CmdExit},
    {IDM_ABOUT,             CmdAbout},
    {ID_OUTBOX,             CmdOutbox},
    {IDM_DISCONNECT_SERVER, CmdDisconnect},
    {IDM_END_CHAT,          CmdEndChat}
};

CMDI cmdiMain =
{
    sizeof(rgcmd) / sizeof(CMD),
    rgcmd,
    edwpWindow
};

//
//  FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE:  Processes messages for the main window.
//
//  PARAMETERS:
//    hwnd     - window handle
//    uMessage - message number
//    wparam   - additional information (dependant on message number)
//    lparam   - additional information (dependant on message number)
//
//  RETURN VALUE:
//    The return value depends on the message number.  If the message
//    is implemented in the message dispatch table, the return value is
//    the value returned by the message handling function.  Otherwise,
//    the return value is the value returned by the default window procedure.
//
//  COMMENTS:
//    Call the DispMessage() function with the main window's message dispatch
//    information (msdiMain) and the message specific information.
//

LRESULT CALLBACK WndProc(HWND   hwnd,
                         UINT   uMessage,
                         WPARAM wparam,
                         LPARAM lparam)
{
    return DispMessage(&msdiMain, hwnd, uMessage, wparam, lparam);
}

//
//  FUNCTION: MsgCreate(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Creates Inbox and Outbox Edit controls
//
//  PARAMETERS:
//
//    hwnd      - Window handle
//    uMessage  - Message number (Unused)
//    wparam    - Extra data     (Unused)
//    lparam    - Extra data     (Unused)
//
//  RETURN VALUE:
//
//    Always returns 0 - Message handled
//
//  COMMENTS:
//
//

LRESULT MsgCreate(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    // Create Edit control for typing to be sent to server
    hOutWnd = CreateWindow("EDIT",
                           NULL,
                           WS_BORDER | WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_LEFT |
                           ES_MULTILINE | ES_AUTOVSCROLL,
                           0,0,0,0,
                           hwnd,
                           (HMENU) ID_OUTBOX,
                           (HINSTANCE) GetWindowLong(hwnd, GWL_HINSTANCE),
                           0);
    // Create Edit control for typing to be received from server
    hInWnd = CreateWindow("EDIT",
                          NULL,
                          WS_BORDER | WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_LEFT |
                          ES_MULTILINE | ES_AUTOVSCROLL,
                          0,0,0,0,
                          hwnd,
                          (HMENU) ID_INBOX,
                          (HINSTANCE) GetWindowLong(hwnd, GWL_HINSTANCE),
                          0);

    return (TRUE);
}

//
//  FUNCTION: MsgSize(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Adjust Size of Inbox and Outbox Edit controls
//
//  PARAMETERS:
//
//    hwnd      - Window handle
//    uMessage  - Message number (Unused)
//    wparam    - Extra data     (Unused)
//    lparam    - Parent Window Size
//
//  RETURN VALUE:
//
//    Always returns 0 - Message handled
//
//  COMMENTS:
//
//

LRESULT MsgSize(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    // Size OutBox Edit Control
    MoveWindow(hOutWnd,
               1,1,                    // Upper Left Corner
               LOWORD(lparam) - 2,         // Width of Parent Window
               (HIWORD(lparam) / 2) - 2 ,     // Half the height of Parent
               TRUE);                  // repaint

    // Size Inbox Edit Control
    MoveWindow(hInWnd,
               1, (HIWORD(lparam) / 2) + 1,  // Half Way down right side
               LOWORD(lparam) - 2,         // Width of Parent Window
               (HIWORD(lparam) / 2) - 2,      // Half the height of Parent
               TRUE);                  // repaint
    return (TRUE);
}

//
//  FUNCTION: MsgSetfocus(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Keeps Window focus on edit control
//
//  PARAMETERS:
//
//    hwnd      - Window handle
//    uMessage  - Message number (Unused)
//    wparam    - Extra data     (Unused)
//    lparam    - Extra data     (Unused)
//
//  RETURN VALUE:
//
//    Always returns 0 - Message handled
//
//  COMMENTS:
//
//

LRESULT MsgSetfocus(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    SetFocus(hOutWnd);  // Don't let main window have focus
    return (TRUE);
}

//
//  FUNCTION: MsgDataready(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Read data and post message to display data
//
//  PARAMETERS:
//
//    hwnd      - Window handle
//    uMessage  - Message number (Unused)
//    wparam    - forwarded to ReceiveInBox
//    lparam    - forwarded to ReceiveInBox
//
//  RETURN VALUE:
//
//    Always returns 0 - Message handled
//
//  COMMENTS:
//
//

LRESULT MsgDataready(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    // Call protocol specific function to Read data and put in szRcvBuf
    if(ReceiveInBox(hwnd, wparam, lparam, szRcvBuf, sizeof(szRcvBuf)))
    {
        PostMessage(hwnd, MW_DISPLAYREFRESH, 0, 0);
    }
    else
    {
        PostMessage(hwnd, IDM_SELECT, 0, 0);
    }
    return(TRUE);
}

//
//  FUNCTION: MsgRefreshdisplay(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Display socket data in inbox
//
//  PARAMETERS:
//
//    hwnd      - Window handle
//    uMessage  - Message number (Unused)
//    wparam    - Extra Data     (Unused)
//    lparam    - Extra Data     (Unused)
//
//  RETURN VALUE:
//
//    Always returns 0 - Message handled
//
//  COMMENTS:
//
//

LRESULT MsgRefreshdisplay(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    MSG peekmsg;

    // Don't bother displaying if there is already another update queued
    if(PeekMessage(&peekmsg, hwnd, MW_DATAREADY, MW_DISPLAYREFRESH, PM_NOREMOVE))
    {
       return(TRUE);
    }
    // Put data in szRcvBuf in InBox
    SendDlgItemMessage(hwnd,
                       ID_INBOX,
                       EM_SETSEL,
                       0,-1);
    SendDlgItemMessage(hwnd,
                       ID_INBOX,
                       EM_REPLACESEL,
                       0,
                       (LPARAM)szRcvBuf);
    return(TRUE);
}

//
//  FUNCTION: MsgDisconnected(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Cleanup connection dropped from other side
//
//  PARAMETERS:
//
//    hwnd      - Window handle
//    uMessage  - Message number (Unused)
//    wparam    - Extra data     (Unused)
//    lparam    - Extra data     (Unused)
//
//  RETURN VALUE:
//
//    Always returns 0 - Message handled
//
//  COMMENTS:
//
//

LRESULT MsgDisconnected(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    HMENU hmenu;

    // Let the user know

    MessageBox(hwnd,
               GetStringRes(IDS_CONNECTION_STOPPED),
               GetStringRes(IDS_CHAT_DLG_STOPPED),
               MB_OK);

    // cleanup socket
    closesocket(MySock.sock);
    // Fix menus
    hmenu = GetMenu(hwnd);
    EnableMenuItem(hmenu, IDM_CONNECT, MF_ENABLED);
    EnableMenuItem(hmenu, IDM_DISCONNECT_SERVER, MF_GRAYED);
    EnableMenuItem(hmenu, IDM_END_CHAT, MF_GRAYED);
    SetWindowText(hwnd, szTitle);
    return(TRUE);
}

//
//  FUNCTION: MsgCommand(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Handle the WM_COMMAND messages for the main window.
//
//  PARAMETERS:
//    hwnd     - window handle
//    uMessage - WM_COMMAND (Unused)
//    GET_WM_COMMAND_ID(wparam, lparam)   - Command identifier
//    GET_WM_COMMAND_HWND(wparam, lparam) - Control handle
//
//  RETURN VALUE:
//    The return value depends on the message number.  If the message
//    is implemented in the message dispatch table, the return value is
//    the value returned by the message handling function.  Otherwise,
//    the return value is the value returned by the default window procedure.
//
//  COMMENTS:
//    Call the DispCommand() function with the main window's command dispatch
//    information (cmdiMain) and the command specific information.
//

LRESULT MsgCommand(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    return DispCommand(&cmdiMain, hwnd, wparam, lparam);
}

//
//  FUNCTION: MsgDestroy(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Calls PostQuitMessage().
//
//  PARAMETERS:
//
//    hwnd      - Window handle  (Unused)
//    uMessage  - Message number (Unused)
//    wparam    - Extra data     (Unused)
//    lparam    - Extra data     (Unused)
//
//  RETURN VALUE:
//
//    Always returns 0 - Message handled
//
//  COMMENTS:
//
//

LRESULT MsgDestroy(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    PostQuitMessage(0);
    return 0;
}

//
//  FUNCTION: CmdOutbox(HWND, WORD, WORD, HWND)
//
//  PURPOSE: Handle messages from Outbox--Send data if EN_CHANGE
//
//  PARAMETERS:
//    hwnd     - The window.
//    wCommand - ID_OUTBOX (unused)
//    wNotify  - Notification number (unused)
//    hwndCtrl - NULL (unused)
//
//  RETURN VALUE:
//    Always returns 0 - command handled.
//
//  COMMENTS:
//
//

LRESULT CmdOutbox(HWND hwnd, WORD wCommand, WORD wNotify, HWND hwndCtrl)
{
    int cSendLen;

    if (wNotify != EN_CHANGE)  // We only care if text has changed
    {
        return 0;
    }
    // Text has changed!  Put OutBox's text in szSndBuf
    cSendLen = GetDlgItemText(hwnd,
                              ID_OUTBOX,
                              szSndBuf,
                              sizeof(szSndBuf));

    cSendLen++;   // Add a byte for the NULL terminator

    // Do a send
    SendOutBox(szSndBuf, cSendLen);
    return 0;
}

//
//  FUNCTION: CmdDisconnect(HWND, WORD, WORD, HWND)
//
//  PURPOSE: Cut off session and fix menus appropriately
//
//  PARAMETERS:
//    hwnd     - The window.
//    wCommand - ID_OUTBOX (unused)
//    wNotify  - Notification number (unused)
//    hwndCtrl - NULL (unused)
//
//  RETURN VALUE:
//    Always returns 0 - command handled.
//
//  COMMENTS:
//
//

LRESULT CmdDisconnect(HWND hwnd, WORD wCommand, WORD wNotify, HWND hwndCtrl)
{
    HMENU hmenu;

    closesocket(MySock.sock);

    // Fix menus
    hmenu = GetMenu(hwnd);
    EnableMenuItem(hmenu, IDM_CONNECT, MF_ENABLED);
    EnableMenuItem(hmenu, IDM_DISCONNECT_SERVER, MF_GRAYED);
    EnableMenuItem(hmenu, IDM_END_CHAT, MF_GRAYED);
    SetWindowText(hwnd, szTitle);
    return 0;
}

//
//  FUNCTION: CmdEndChat(HWND, WORD, WORD, HWND)
//
//  PURPOSE: End Peer to Peer session
//
//  PARAMETERS:
//    hwnd     - The window.
//    wCommand - IDM_END_CHAT (unused)
//    wNotify  - Notification number (unused)
//    hwndCtrl - NULL (unused)
//
//  RETURN VALUE:
//    Always returns 0 - command handled.
//
//  COMMENTS:
//
//

LRESULT CmdEndChat(HWND hwnd, WORD wCommand, WORD wNotify, HWND hwndCtrl)
{
    HMENU hmenu;
    char outtext[80];

    // Send SESSION_CLOSE message
    xferbuf.hdr.signature = MYSIGNATURE;
    xferbuf.hdr.length = HDRSIZE;
    xferbuf.hdr.command = SESSION_CLOSE;
    senddatamessage(MySock.sock, &xferbuf);

    // Currently socket is set to notify this parent window of events.
   // We need to stop this so that the dialog that we are about to
   // display will get events instead.
    if (WSAAsyncSelect(MySock.sock, hwnd, 0, 0) == SOCKET_ERROR)
    {
        // Oops
        return 0;
    }

    // Put up select dialog
    PostMessage(hwnd, WM_COMMAND, MAKELONG(IDM_SELECT, 0), 0);

    // Fix menus
    hmenu = GetMenu(hwnd);
    EnableMenuItem(hmenu, IDM_CONNECT, MF_ENABLED);
    EnableMenuItem(hmenu, IDM_DISCONNECT_SERVER, MF_GRAYED);
    EnableMenuItem(hmenu, IDM_END_CHAT, MF_GRAYED);

    wsprintf(outtext, GetStringRes(IDS_CHAT_AVAILABLE), MySock.name);
    SetWindowText(hwnd, outtext);
    return 0;
}

//
//  FUNCTION: CmdExit(HWND, WORD, WORD, HWND)
//
//  PURPOSE: Exit the application.
//
//  PARAMETERS:
//    hwnd     - The window.
//    wCommand - IDM_EXIT            (unused)
//    wNotify  - Notification number (unused)
//    hwndCtrl - NULL                (unused)
//
//  RETURN VALUE:
//    Always returns 0 - command handled.
//
//  COMMENTS:
//
//

LRESULT CmdExit(HWND hwnd, WORD wCommand, WORD wNotify, HWND hwndCtrl)
{
    closesocket(MySock.sock);
    WSACleanup();
    DestroyWindow(hwnd);
    return 0;
}


INIT.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:   init.c
//
//  PURPOSE:   Performs application and instance specific initialization.
//
//  FUNCTIONS:
//    InitApplication() - Initializes window data and registers window.
//
//  COMMENTS:
//

#include <windows.h>            // required for all Windows applications
#include <wsipx.h>              // IPX Sockets defs
#include <wsnetbs.h>
#include <nspapi.h>
#include "globals.h"            // prototypes specific to this application

HINSTANCE hInst;                // current instance

char szAppName[9];              // The name of this application
char szTitle[40];               // The title bar text

char szConnectName[16];
char szConnectServer[16];

BOOL i_should_sleep = TRUE;

//
//  FUNCTION: InitApplication(HINSTANCE, int)
//
//  PURPOSE: Initializes window data and registers window class.
//
//  PARAMETERS:
//    hInstance - The handle to the instance of this application that
//                is currently being executed.
//    nCmdShow  - Specifies how the main window is to be displayed.
//
//  RETURN VALUE:
//    TRUE  - Success
//    FALSE - Initialization failed
//
//  COMMENTS:
//
//    This function is called at application initialization time.  It
//    performs initialization tasks for the current application instance.
//    Unlike Win16, in Win32, each instance of an application must register
//    window classes.
//
//    In this function, we initialize a window class by filling out a data
//    structure of type WNDCLASS and calling the Windows RegisterClass()
//    function.  Then we create the main window and show it.
//
//

BOOL InitApplication(HINSTANCE hInstance, int nCmdShow)
{
    WNDCLASS  wc;
    HWND      hwnd; // Main window handle.
    WORD      wVersionRequested;
    WSADATA   wsaData;

    // Load the application name and description strings.

    LoadString(hInstance, IDS_APPNAME, szAppName, sizeof(szAppName));
    LoadString(hInstance, IDS_DESCRIPTION, szTitle, sizeof(szTitle));

    // Save the instance handle in static variable, which will be used in
    // many subsequence calls from this application to Windows.

    hInst = hInstance; // Store instance handle in our global variable

    // Fill in window class structure with parameters that describe the
    // main window.

    wc.style         = CS_HREDRAW | CS_VREDRAW; // Class style(s).
    wc.lpfnWndProc   = (WNDPROC)WndProc;        // Window Procedure
    wc.cbClsExtra    = 0;                       // No per-class extra data.
    wc.cbWndExtra    = 0;                       // No per-window extra data.
    wc.hInstance     = hInstance;               // Owner of this class
    wc.hIcon         = LoadIcon (hInstance, szAppName); // Icon name from .RC
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW); // Cursor
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); // Default color
    wc.lpszMenuName  = szAppName;               // Menu name from .RC
    wc.lpszClassName = szAppName;               // Name to register as

    // Register the window class and return FALSE if unsuccesful.

    if (!RegisterClass(&wc))
    {
        return FALSE;
    }

    // Create a main window for this application instance.
    hwnd = CreateWindow(szAppName,           // See RegisterClass() call
                        szTitle,             // Text for window title bar
                        WS_OVERLAPPEDWINDOW, // Window style
                        CW_USEDEFAULT, 0,    // Use default positioning
                        CW_USEDEFAULT, 0,    // Use default size
                        NULL,                // Overlapped has no parent
                        NULL,                // Use the window class menu
                        hInstance,           // This instance owns this window
                        NULL                 // Don't need data in WM_CREATE
    );

    // If window could not be created, return "failure"
    if (!hwnd)
        return FALSE;

    // Make the window visible; update its client area; and return "success"
    ShowWindow(hwnd, nCmdShow);  // Show the window
    UpdateWindow(hwnd);          // Sends WM_PAINT message

    // Link in Winsock DLL
    wVersionRequested = MAKEWORD(1,1);
    if (WSAStartup(wVersionRequested, &wsaData) != 0)
    {
        return FALSE;
    }

    return TRUE;                 // We succeeded...
}


MISC.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:   misc.c
//
//  PURPOSE:  Contains all helper functions "global" to the application.
//
//  FUNCTIONS:
//    CenterWindow    - Center one window over another.
//    ReceiveInBox    - Reads incoming socket data.
//    SendOutBox      - Writes outgoing socket data.
//    AtoH            - Converts ascii string to network order hex
//    BtoH            - Converts ascii byte to hex
//    senddatamessage - Sends a message to the server
//    recvdatamessage - Receives a message from the server
//
//  COMMENTS:
//
//

#include <windows.h>            // required for all Windows applications
#include <windowsx.h>
#include <wsipx.h>
#include <wsnetbs.h>
#include <nspapi.h>
#include "globals.h"            // prototypes specific to this application

//
//  FUNCTION: CenterWindow(HWND, HWND)
//
//  PURPOSE:  Center one window over another.
//
//  PARAMETERS:
//    hwndChild - The handle of the window to be centered.
//    hwndParent- The handle of the window to center on.
//
//  RETURN VALUE:
//
//    TRUE  - Success
//    FALSE - Failure
//
//  COMMENTS:
//
//    Dialog boxes take on the screen position that they were designed
//    at, which is not always appropriate. Centering the dialog over a
//    particular window usually results in a better position.
//

BOOL CenterWindow(HWND hwndChild, HWND hwndParent)
{
    RECT    rcChild, rcParent;
    int     cxChild, cyChild, cxParent, cyParent;
    int     cxScreen, cyScreen, xNew, yNew;
    HDC     hdc;

    // Get the Height and Width of the child window
    GetWindowRect(hwndChild, &rcChild);
    cxChild = rcChild.right - rcChild.left;
    cyChild = rcChild.bottom - rcChild.top;

    // Get the Height and Width of the parent window
    GetWindowRect(hwndParent, &rcParent);
    cxParent = rcParent.right - rcParent.left;
    cyParent = rcParent.bottom - rcParent.top;

    // Get the display limits
    hdc = GetDC(hwndChild);
    cxScreen = GetDeviceCaps(hdc, HORZRES);
    cyScreen = GetDeviceCaps(hdc, VERTRES);
    ReleaseDC(hwndChild, hdc);

    // Calculate new X position, then adjust for screen
    xNew = rcParent.left + ((cxParent - cxChild) / 2);
    if (xNew < 0)
    {
        xNew = 0;
    }
    else if ((xNew + cxChild) > cxScreen)
    {
        xNew = cxScreen - cxChild;
    }

    // Calculate new Y position, then adjust for screen
    yNew = rcParent.top  + ((cyParent - cyChild) / 2);
    if (yNew < 0)
    {
        yNew = 0;
    }
    else if ((yNew + cyChild) > cyScreen)
    {
        yNew = cyScreen - cyChild;
    }

    // Set it, and return
    return SetWindowPos(hwndChild,
                        NULL,
                        xNew, yNew,
                        0, 0,
                        SWP_NOSIZE | SWP_NOZORDER);
}

//
//  FUNCTION: ReceiveInBox(HWND, WPARAM, LPARAM, char *, int)
//
//  PURPOSE:  Reads incoming data from socket and process message
//
//  PARAMETERS:
//    hWnd      - Handle to current window
//    uParam    - WPARAM (unused)
//    lParam    - LPARAM contains event (FD_READ or FD_CLOSE).
//    szRBuf    - Receive Buffer
//    cRBufLen  - size of Receive Buffer
//
//  RETURN VALUE:
//
//    TRUE  - Data Read
//    FALSE - If FD_CLOSE message or recv failed
//
//  COMMENTS:
//
//    Called if socket has data OR if it is closed.  If closed post
//    WM_DISCONNECTED message.  Else read data and make sure it is
//    NULL terminated.
//

BOOL ReceiveInBox(HWND hWnd, WPARAM uParam, LPARAM lParam, char * szRBuf, int cRBufLen)
{
    char outtext[80];

    if (LOWORD(lParam) == FD_CLOSE)                   // Is this a FD_CLOSE event?
    {
        SendMessage(hWnd, MW_DISCONNECTED, 0, 0);     // Yes, post message
        return(FALSE);
    }

    if (!recvdatamessage(&MySock, &xferbuf))         // Receive data
    {
        return(TRUE);
    }

    // Switch on message command
    switch (xferbuf.hdr.command)
    {
        case XFER_DATA:   // Chat data...put it in buffer
            lstrcpy(szRBuf, xferbuf.data);
            cRBufLen = xferbuf.hdr.length - HDRSIZE;
            break;

        case SESSION_CLOSE:  // Peer killed us...cleanup
            MySock.status = SOCKSTAT_AVAILABLE;

            // Clear event indicator since we need to transfer message
         // indications to the dialog we are about to create
            if (WSAAsyncSelect(MySock.sock, hWnd, 0, 0) == SOCKET_ERROR)
            {
                // oops
                return FALSE;
            }
            PostMessage(hWnd, WM_COMMAND, MAKELONG(IDM_SELECT, 0), 0);

            wsprintf(outtext, GetStringRes(IDS_CHAT_AVAILABLE), MySock.name);
            SetWindowText(hWnd, outtext);
            return FALSE;

        default:
            break;
            // unexpected meesage...drop it
    }
    return TRUE;
}

//
//  FUNCTION: SendOutBox(char *, int)
//
//  PURPOSE:  Sends outbox text via data message
//
//  PARAMETERS:
//    szSBuf    - Send Buffer
//    cSBufLen  - size of Send Buffer
//
//  COMMENTS:
//
//    Writes send buffer to socket
//

void SendOutBox(char * szSBuf, int cSBufLen)
{
    xferbuf.hdr.signature = MYSIGNATURE;
    xferbuf.hdr.length = cSBufLen + HDRSIZE;
    xferbuf.hdr.command = XFER_DATA;
    lstrcpy(xferbuf.data, szSBuf);
    senddatamessage(MySock.sock, &xferbuf);
    return;
}

//
//  FUNCTION: AtoH(char *, char *, int)
//
//  PURPOSE:  Converts ascii string to network order hex
//
//  PARAMETERS:
//    src    - pointer to input ascii string
//    dest   - pointer to output hex
//    destlen - size of dest
//
//  COMMENTS:
//
//    2 ascii bytes make a hex byte so must put 1st ascii byte of pair
//    into upper nibble and 2nd ascii byte of pair into lower nibble.
//

void AtoH(char * src, char * dest, int destlen)
{
    char * srcptr;

    srcptr = src;

    while(destlen--)
    {
    *dest = BtoH(*srcptr++) << 4;    // Put 1st ascii byte in upper nibble.
    *dest++ += BtoH(*srcptr++);      // Add 2nd ascii byte to above.
    }
}

//
//  FUNCTION: BtoH(char *, char *, int)
//
//  PURPOSE:  Converts ascii byte to numeric
//
//  PARAMETERS:
//    ch - ascii byte to convert
//
//  RETURNS:
//    associated numeric value
//
//  COMMENTS:
//
//    Will convert any hex ascii digit to its numeric counterpart.
//    Puts in 0xff if not a valid hex digit.
//

unsigned char BtoH(char ch)
{
    if (ch >= '0' && ch <= '9') return (ch - '0');        // Handle numerals
    if (ch >= 'A' && ch <= 'F') return (ch - 'A' + 0xA);  // Handle capitol hex digits
    if (ch >= 'a' && ch <= 'f') return (ch - 'a' + 0xA);  // Handle small hex digits
    return(255);
}

//
//  FUNCTION: senddatamessage(SOCKET, LPDATAMSG)
//
//  PURPOSE:  sends a chat message to the server
//
//  PARAMETERS:
//    sock    - our socket handle
//    sendbuf - the message to send
//
//  RETURNS:
//    ascii byte
//
//  COMMENTS:
//    Send() may not be able to send entire message continue to send until
//    entire message is sent
//

BOOL senddatamessage (SOCKET sock, LPDATAMSG sendbuf)
{
    int totalbytes, bytessent;

    bytessent = 0;                          // Set initial count to zero
    totalbytes = sendbuf->hdr.length;     // Set total bytes to send

   // loop until entire message is sent
    while(bytessent < totalbytes)
    {
        bytessent += send(sock,
                          (char *)sendbuf + bytessent,
                          totalbytes - bytessent,
                          0);
    }

    return TRUE;
}

//
//  FUNCTION: recvdatamessage(LPSOCKDATA, LPDATAMSG)
//
//  PURPOSE:  receives chat message from server
//
//  PARAMETERS:
//    lpSockdat - SOCKDATA structure
//    recvbuf   - buffer for incoming data
//
//  RETURNS:
//    TRUE  - Message received
//    FALSE - Message not received
//
//  COMMENTS:
//   For stream sockets we will need to make sure that we read
//    an entire message--no more, no less.  Find message size from
//    message header.
//

BOOL recvdatamessage (LPSOCKDATA lpSockdat, LPDATAMSG recvbuf)
{
    int readsize, totalbytesread, msgsize, bytesread;

    if (lpSockdat->type == SOCK_SEQPACKET)
    {
        // Message mode protocol!!  Just post one big read.
        readsize = sizeof(*recvbuf);
    }
    else
    {
        // Stream mode protocol!!  Just read header...then read data (data size determined
        // from header)
        readsize = HDRSIZE;
    }
    if((totalbytesread = recv(lpSockdat->sock, (char *)recvbuf, readsize, 0)) == SOCKET_ERROR)
    {
        // ERROR  -- just return
        return FALSE;
    }

    // Check for my signature at the beginning of the message
    if(recvbuf->hdr.signature != MYSIGNATURE)
    {
        // I've received some data that's in mid message--drop it
        return FALSE;
    }

    // Read size of message
    msgsize = recvbuf->hdr.length;
    readsize = msgsize - totalbytesread;

    while(totalbytesread < msgsize)
    {
        // we should only get hear for stream sockets
        if((bytesread = recv(lpSockdat->sock,
                             (char *)recvbuf + totalbytesread,
                             readsize,
                             0)) == SOCKET_ERROR)
        {
            if (WSAGetLastError() != WSAEWOULDBLOCK)
            {
                // ERROR -- throw out message
                return FALSE;
            }
            // If you got a WSAWOULDBLOCK error, just keep trying...it shouldn't take
            // too much longer for the rest of the message to get here.  Let's hope
            // we don't hog the CPU so the data doesn't get to us.
        }
        totalbytesread += bytesread;
        readsize -= bytesread;
    }

    return TRUE;
}

//---------------------------------------------------------------------------
//
// 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;
}


SELECT.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:    select.c
//
//  PURPOSE:   Displays the "Select" dialog box
//
//  FUNCTIONS:
//    CmdSelect          - Displays the "Select" dialog box
//    Select             - Processes messages for "Select" dialog box.
//    MsgSelectInit      - Sets up event message notification for socket
//    MsgSelectDataReady - Process socket event message notifications
//    MsgSelectCommand   - Process WM_COMMAND message sent to the select box.
//    CmdSelectDone      - Free the select box and related data.
//    CmdSelectOK        - Request Peer session
//    CmdSelectList      - Enables/Disables OK button
//
//  COMMENTS:
//
//

#include <windows.h>            // required for all Windows applications
#include <windowsx.h>
#include <wsipx.h>              // IPX Sockets defs
#include <wsnetbs.h>
#include <nspapi.h>
#include "globals.h"            // prototypes specific to this application

LRESULT CALLBACK Select(HWND, UINT, WPARAM, LPARAM);
LRESULT MsgSelectInit(HWND, UINT, WPARAM, LPARAM);
LRESULT MsgSelectCommand(HWND, UINT, WPARAM, LPARAM);
LRESULT MsgSelectDataReady(HWND, UINT, WPARAM, LPARAM);
LRESULT CmdSelectDone(HWND, WORD, WORD, HWND);
LRESULT CmdSelectOK(HWND, WORD, WORD, HWND);
LRESULT CmdSelectList(HWND, WORD, WORD, HWND);

// Select dialog message table definition.
MSD rgmsdSelect[] =
{
    {WM_COMMAND,    MsgSelectCommand},
    {WM_INITDIALOG, MsgSelectInit},
    {MW_DATAREADY,  MsgSelectDataReady}
};

MSDI msdiSelect =
{
    sizeof(rgmsdSelect) / sizeof(MSD),
    rgmsdSelect,
    edwpNone
};

// Select dialog command table definition.
CMD rgcmdSelect[] =
{
    {IDOK,     CmdSelectOK},
    {IDCANCEL, CmdSelectDone},
    {SD_LIST,  CmdSelectList}
};

CMDI cmdiSelect =
{
    sizeof(rgcmdSelect) / sizeof(CMD),
    rgcmdSelect,
    edwpNone
};

// Module specific "globals"  Used when a variable needs to be
// accessed in more than on handler function.

HFONT hFontCopyright;

//
//  FUNCTION: CmdSelect(HWND, WORD, WORD, HWND)
//
//  PURPOSE: Displays the "Select" dialog box
//
//  PARAMETERS:
//    hwnd      - Window handle
//    wCommand  - IDM_Select (unused)
//    wNotify   - Notification number (unused)
//    hwndCtrl  - NULL (unused)
//
//  RETURN VALUE:
//
//    Always returns 0 - Message handled
//
//  COMMENTS:
//    To process the IDM_Select message, call DialogBox() to display the
//    Select dialog box.

LRESULT CmdSelect(HWND hwnd, WORD wCommand, WORD wNotify, HWND hwndCtrl)
{
    char outtext[80];

    if (DialogBox(hInst, "SelectBox", hwnd, (DLGPROC)Select))
    {
        // reset socket event messaging to point at the main window
        if (WSAAsyncSelect(MySock.sock, hwnd, MW_DATAREADY, FD_READ | FD_CLOSE) == SOCKET_ERROR)
        {
            // I hate it when this happens!
            wsprintf(outtext, GetStringRes(IDS_ERR_WSAASYNCSELECT),
                     WSAGetLastError());
            MessageBox(hwnd, outtext, NULL, MB_OK);
            return 0;
        }

        // We've got a connection...set up menu and title bar
        EnableMenuItem(GetMenu(hwnd), IDM_END_CHAT, MF_ENABLED);
        EnableMenuItem(GetMenu(hwnd), IDM_DISCONNECT_SERVER, MF_ENABLED);
        EnableMenuItem(GetMenu(hwnd), IDM_CONNECT, MF_GRAYED);

        wsprintf(outtext, GetStringRes(IDS_CHATTING_REMOTE), MySock.name, peername);
        SetWindowText(hwnd, outtext);
        // we're now ready to rumble
    }
    else
    {
        // clean up
        closesocket(MySock.sock);
        EnableMenuItem(GetMenu(hwnd), IDM_END_CHAT, MF_GRAYED);
        EnableMenuItem(GetMenu(hwnd), IDM_DISCONNECT_SERVER, MF_GRAYED);
        EnableMenuItem(GetMenu(hwnd), IDM_CONNECT, MF_ENABLED);
        SetWindowText(hwnd, szTitle);
    }
    return 0;
}

//
//  FUNCTION: Select(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE:  Processes messages for "Select" dialog box.
//
//  PARAMETERS:
//    hdlg - window handle of the dialog box
//    wMessage - type of message
//    wparam - message-specific information
//    lparam - message-specific information
//
//  RETURN VALUE:
//    TRUE - message handled
//    FALSE - message not handled
//
//  COMMENTS:
//
//     Display version information from the version section of the
//     application resource.
//
//     Wait for user to click on "Ok" button, then close the dialog box.
//

LRESULT CALLBACK Select(HWND hdlg, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    return DispMessage(&msdiSelect, hdlg, uMessage, wparam, lparam);
}

//
//  FUNCTION: MsgSelectInit(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Sets up socket message notifications
//
//  PARAMETERS:
//    hwnd - The window handing the message.
//    uMessage - The message number. (unused).
//    wparam - Message specific data (unused).
//    lparam - Message specific data (unused).
//
//  RETURN VALUE:
//    Always returns 0 - message handled.
//
//  COMMENTS:
//    Uses WSAAsyncSelect() to have socket event notifications sent
//    to the Select Dialog Window
//

LRESULT MsgSelectInit(HWND hdlg, UINT uMessage, WPARAM wparam, LPARAM lparam)
{

    // Center the dialog over the application window
    CenterWindow(hdlg, GetWindow(hdlg, GW_OWNER));

    if(WSAAsyncSelect(MySock.sock, hdlg, MW_DATAREADY, FD_READ | FD_CLOSE) != 0)
    {
        // ERROR  do something
    }
    return TRUE;
}

//
//  FUNCTION: MsgSelectDataReady(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Processes incoming messages from server
//
//  PARAMETERS:
//    hwnd - The window handing the message.
//    uMessage - The message number. (unused).
//    wparam - Message specific data (unused).
//    lparam - Message specific data (unused).
//
//  RETURN VALUE:
//    Always returns 0 - message handled.
//
//  COMMENTS:
//    Receive message and then process command in message header
//

LRESULT MsgSelectDataReady(HWND hdlg, UINT uMessage, WPARAM wparam, LPARAM lparam)
{

    int iIndex;
    char displaytext[128];

    // First check to see if connection was closed
    if(LOWORD(lparam) == FD_CLOSE)
    {
        // Connection closed kill our socket
        closesocket(MySock.sock);

        MessageBox(hdlg, GetStringRes(IDS_ERR_CONNECTION_DROPPED), NULL, MB_OK);

        // Fix menus
        EnableMenuItem(GetMenu(GetParent(hdlg)), IDM_CONNECT, MF_ENABLED);
        EnableMenuItem(GetMenu(GetParent(hdlg)), IDM_DISCONNECT_SERVER, MF_GRAYED);
        EnableMenuItem(GetMenu(GetParent(hdlg)), IDM_END_CHAT, MF_GRAYED);

        EndDialog(hdlg, FALSE);
        return 0;
    }

    // receive the message
    if(!recvdatamessage(&MySock, &xferbuf))
    {
        return 0;
    }

    // We've got our whole message!  Now switch on the command flag
    switch(xferbuf.hdr.command)
    {
        case REGISTER_NAME:
            // Add name to list box
            SendMessage(GetDlgItem(hdlg, SD_LIST), LB_ADDSTRING, 0, (LPARAM)&xferbuf.data);
            break;

        case DEREGISTER_NAME:
            // Remove name from list box
            if((iIndex = SendMessage(GetDlgItem(hdlg, SD_LIST),
                                     LB_FINDSTRING,
                                     (UINT)-1, (LPARAM)&xferbuf.data)) != LB_ERR)
            {
                // found the index to the item...delete it!
                SendMessage(GetDlgItem(hdlg, SD_LIST), LB_DELETESTRING, iIndex, 0);
            }
            break;

        case REQUEST_SESSION:
          // Someone is asking us for a chat session
            lstrcpy(displaytext, xferbuf.data);

            lstrcat(displaytext,
                    GetStringRes(IDS_REQUESTS_CHAT));

            if(MessageBox(hdlg, displaytext,
                          GetStringRes(IDS_SESSION_REQUEST), MB_OKCANCEL) == IDOK)
            {
                // save the name of the peer
                lstrcpy(peername, xferbuf.data);
                // We've got ourselves a session!  Send response
                xferbuf.hdr.command = SESSION_REQUEST_RESPONSE;
                *(xferbuf.data) = 1;    // Flag to say we accept this session
                senddatamessage(MySock.sock, &xferbuf);
                EndDialog(hdlg, TRUE);  // kill the dialog
                return 0;
            }
            else
            {
                // We're obviously to stuck up to accept a session
            // from this lowlife...send the denial message
                xferbuf.hdr.command = SESSION_REQUEST_RESPONSE;
                *(xferbuf.data) = 0;    // Flag to deny session
                xferbuf.hdr.length = HDRSIZE;
                senddatamessage(MySock.sock, &xferbuf);
                return 0;
            }
            break;  // should never get here

        case SESSION_REQUEST_RESPONSE:
          // Someone responded to our session request!

            if (MySock.status != SOCKSTAT_REQSESSION)
            {
                return 0;  // we didn't expect this...drop packet
            }
            if (*xferbuf.data == 1)
            {
                // Oh JOY!  Acceptance by our peer!
                EndDialog(hdlg, TRUE);
                return 0;
            }
            else
            {
                // Depression...nobody loves us: reenable listbox so we
                // can just get rejected again.
                EnableWindow(GetDlgItem(hdlg, SD_LIST), TRUE);
                EnableWindow(GetDlgItem(hdlg, IDOK), TRUE);
                return 0;
            }

        default:
            return 0; // This is unexpected... drop packet
    }

    return TRUE;
}

//
//  FUNCTION: MsgSelectCommand(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Process WM_COMMAND message sent to the Select box.
//
//  PARAMETERS:
//    hwnd - The window handing the message.
//    uMessage - The message number. (unused).
//    wparam - Message specific data (unused).
//    lparam - Message specific data (unused).
//
//  RETURN VALUE:
//    Always returns 0 - message handled.
//
//  COMMENTS:
//    Uses this DipsCommand function defined in wndproc.c combined
//    with the cmdiSelect structure defined in this file to handle
//    the command messages for the Select dialog box.
//

LRESULT MsgSelectCommand(HWND   hwnd,
                         UINT   uMessage,
                         WPARAM wparam,
                         LPARAM lparam)
{
    return DispCommand(&cmdiSelect, hwnd, wparam, lparam);
}

//
//  FUNCTION: CmdSelectDone(HWND, WORD, HWND)
//
//  PURPOSE: Free the Select box and related data.
//
//  PARAMETERS:
//    hwnd - The window handling the command.
//    wCommand - The command to be handled (unused).
//    wNotify   - Notification number (unused)
//    hwndCtrl - NULL (unused).
//
//  RETURN VALUE:
//    Always returns TRUE.
//
//  COMMENTS:
//    Calls EndDialog to finish the dialog session.
//

LRESULT CmdSelectDone(HWND hdlg, WORD wCommand, WORD wNotify, HWND hwndCtrl)
{
    if (hFontCopyright)
       DeleteObject(hFontCopyright);

    EndDialog(hdlg, FALSE);          // Exit the dialog
    return TRUE;
}

//
//  FUNCTION: CmdSelectOK(HWND, WORD, HWND)
//
//  PURPOSE: Requests session with peer
//
//  PARAMETERS:
//    hwnd - The window handling the command.
//    wCommand - The command to be handled (unused).
//    wNotify   - Notification number (unused)
//    hwndCtrl - NULL (unused).
//
//  RETURN VALUE:
//    Always returns TRUE.
//
//  COMMENTS:
//    Sends session request message to selected peer
//

LRESULT CmdSelectOK(HWND hdlg, WORD wCommand, WORD wNotify, HWND hwndCtrl)
{
    int iIndex;

    // Get selected peer name
    iIndex = SendMessage(GetDlgItem(hdlg, SD_LIST), LB_GETCURSEL, 0, 0);
    SendMessage(GetDlgItem(hdlg, SD_LIST), LB_GETTEXT, iIndex, (LPARAM)&peername);

    // Build session request message
    MySock.status = SOCKSTAT_REQSESSION;
    xferbuf.hdr.signature = MYSIGNATURE;
    xferbuf.hdr.length = REALLEN(peername) + HDRSIZE;
    xferbuf.hdr.command = REQUEST_SESSION;
    lstrcpy(xferbuf.data, peername);
    senddatamessage(MySock.sock, &xferbuf);       // Send it!

    EnableWindow(GetDlgItem(hdlg, SD_LIST), FALSE);
    EnableWindow(GetDlgItem(hdlg, IDOK), FALSE);

    return TRUE;
}

//
//  FUNCTION: CmdSelectList(HWND, WORD, HWND)
//
//  PURPOSE: Enables OK button
//
//  PARAMETERS:
//    hwnd - The window handling the command.
//    wCommand - The command to be handled (unused).
//    wNotify   - Notification number (unused)
//    hwndCtrl - NULL (unused).
//
//  RETURN VALUE:
//    Always returns TRUE.
//
//  COMMENTS:
//    When a peer is selected, enable OK button
//

LRESULT CmdSelectList(HWND hdlg, WORD wCommand, WORD wNotify, HWND hwndCtrl)
{
    if (wNotify == LBN_SELCHANGE)  // A peer has been selected!
    {
        EnableWindow(GetDlgItem(hdlg, IDOK), TRUE);     // Enable that button!
    }
    return TRUE;
}


WINMAIN.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:   winmain.c
//
//  PURPOSE:   Calls initialization functions and processes the message loop
//
//
//  PLATFORMS: Windows 95, Windows NT
//
//  FUNCTIONS:
//    WinMain() - calls initialization functions, processes message loop
//
//  COMMENTS:
//
//

#include <windows.h>            // required for all Windows applications
#include <wsipx.h>              // IPX Socket defs
#include <wsnetbs.h>
#include <nspapi.h>
#include "globals.h"            // prototypes specific to this application

//
//  FUNCTION: WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
//
//  PURPOSE: calls initialization function, processes message loop
//
//  PARAMETERS:
//
//    hInstance - The handle to the instance of this application that
//          is currently being executed.
//
//    hPrevInstance - This parameter is always NULL in Win32
//          applications.
//
//    lpCmdLine - A pointer to a null terminated string specifying the
//          command line of the application.
//
//    nCmdShow - Specifies how the main window is to be diplayed.
//
//  RETURN VALUE:
//    If the function terminates before entering the message loop,
//    return FALSE.
//    Otherwise, return the WPARAM value sent by the WM_QUIT message.
//
//
//  COMMENTS:
//
//    Windows recognizes this function by name as the initial entry point
//    for the program.  This function calls the 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 APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
    MSG msg;
    HANDLE hAccelTable;

    // Initialize application by setting up the main window.
    if (!InitApplication(hInstance, nCmdShow))
    {
        return FALSE;           // Exits if unable to initialize
    }

    hAccelTable = LoadAccelerators(hInstance, szAppName);

    // Acquire and dispatch messages until a WM_QUIT message is received.
    while (GetMessage(&msg, NULL, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);  // Translates virtual key codes
            DispatchMessage(&msg);   // Dispatches message to window
        }
    }
    return msg.wParam;  // Returns the value from PostQuitMessage
}


ABOUT.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:   about.c
//
//  PURPOSE:   Displays the "About" dialog box
//
//  FUNCTIONS:
//    CmdAbout        - Displays the "About" dialog box
//    About           - Processes messages for "About" dialog box.
//    MsgAboutInit    - To initialize the about box with version info
//                      from resources.
//    MsgAboutCommand - Process WM_COMMAND message sent to the about box.
//    CmdAboutDone    - Free the about box and related data.
//
//  COMMENTS:
//
//

#include <windows.h>            // required for all Windows applications
#include <windowsx.h>
#include "globals.h"            // prototypes specific to this application

LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM);
LRESULT MsgAboutInit(HWND, UINT, WPARAM, LPARAM);
LRESULT MsgAboutCommand(HWND, UINT, WPARAM, LPARAM);
LRESULT CmdAboutDone(HWND, WORD, WORD, HWND);

// About dialog message table definition.
MSD rgmsdAbout[] =
{
    {WM_COMMAND,    MsgAboutCommand},
    {WM_INITDIALOG, MsgAboutInit}
};

MSDI msdiAbout =
{
    sizeof(rgmsdAbout) / sizeof(MSD),
    rgmsdAbout,
    edwpNone
};

// About dialog command table definition.
CMD rgcmdAbout[] =
{
    {IDOK,     CmdAboutDone},
    {IDCANCEL, CmdAboutDone}
};

CMDI cmdiAbout =
{
    sizeof(rgcmdAbout) / sizeof(CMD),
    rgcmdAbout,
    edwpNone
};

// Module specific "globals"  Used when a variable needs to be
// accessed in more than on handler function.

HFONT hFontCopyright;

//
//  FUNCTION: CmdAbout(HWND, WORD, WORD, HWND)
//
//  PURPOSE: Displays the "About" dialog box
//
//  PARAMETERS:
//    hwnd      - Window handle
//    wCommand  - IDM_ABOUT (unused)
//    wNotify   - Notification number (unused)
//    hwndCtrl  - NULL (unused)
//
//  RETURN VALUE:
//
//    Always returns 0 - Message handled
//
//  COMMENTS:
//    To process the IDM_ABOUT message, call DialogBox() to display the
//    about dialog box.

LRESULT CmdAbout(HWND hwnd, WORD wCommand, WORD wNotify, HWND hwndCtrl)
{
    DialogBox(hInst, "AboutBox", hwnd, (DLGPROC)About);
    return 0;
}

//
//  FUNCTION: About(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE:  Processes messages for "About" dialog box.
//
//  PARAMETERS:
//    hdlg - window handle of the dialog box
//    wMessage - type of message
//    wparam - message-specific information
//    lparam - message-specific information
//
//  RETURN VALUE:
//    TRUE - message handled
//    FALSE - message not handled
//
//  COMMENTS:
//
//     Display version information from the version section of the
//     application resource.
//
//     Wait for user to click on "Ok" button, then close the dialog box.
//

LRESULT CALLBACK About(HWND hdlg, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    return DispMessage(&msdiAbout, hdlg, uMessage, wparam, lparam);
}

//
//  FUNCTION: MsgAboutInit(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: To initialize the about box with version info from resources.
//
//  PARAMETERS:
//    hwnd - The window handing the message.
//    uMessage - The message number. (unused).
//    wparam - Message specific data (unused).
//    lparam - Message specific data (unused).
//
//  RETURN VALUE:
//    Always returns 0 - message handled.
//
//  COMMENTS:
//    Uses the version apis to retrieve version information for
//    each of the static text boxes in the about box.
//

LRESULT MsgAboutInit(HWND hdlg, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    #define POINTSIZE 8

    char  szFullPath[256];
    DWORD dwVerHnd;
    DWORD dwVerInfoSize;
    HDC   hDC;
    int   iLogPixelsY, iPointSize;

    // Center the dialog over the application window
    CenterWindow(hdlg, GetWindow(hdlg, GW_OWNER));

    // Set the copyright font to something smaller than default
    hDC = GetDC(hdlg);
    iLogPixelsY = GetDeviceCaps(hDC, LOGPIXELSY);
    ReleaseDC(hdlg, hDC);
    iPointSize = MulDiv(iLogPixelsY, POINTSIZE, 72);
    iPointSize *= -1;

    hFontCopyright = CreateFont(iPointSize,
                                0, 0, 0,
                                FW_BOLD,
                                0, 0, 0, 0,
                                0, 0, 0, 0,
                                "Arial");

    SendDlgItemMessage(hdlg,
                       IDD_VERLAST,
                       WM_SETFONT,
                       (WPARAM)hFontCopyright,
                       0L);

    // Get version information from the application
    GetModuleFileName(hInst, szFullPath, sizeof(szFullPath));
    dwVerInfoSize = GetFileVersionInfoSize(szFullPath, &dwVerHnd);
    if (dwVerInfoSize)
    {
        // If we were able to get the information, process it:
        HANDLE  hMem;
        LPVOID  lpvMem;
        char    szGetName[256];
        int     cchRoot;
        int     i;

        hMem = GlobalAlloc(GMEM_MOVEABLE, dwVerInfoSize);
        lpvMem = GlobalLock(hMem);
        GetFileVersionInfo(szFullPath, dwVerHnd, dwVerInfoSize, lpvMem);

        lstrcpy(szGetName, GetStringRes(IDS_LANGVERINFO));

        cchRoot = lstrlen(szGetName);

        // Walk through the dialog items that we want to replace:
        for (i = IDD_VERFIRST; i <= IDD_VERLAST; i++)
        {
            BOOL  fRet;
            UINT  cchVer = 0;
            LPSTR lszVer = NULL;
            char  szResult[256];

            GetDlgItemText(hdlg, i, szResult, sizeof(szResult));
            lstrcpy(&szGetName[cchRoot], szResult);
            fRet = VerQueryValue(lpvMem, szGetName, &lszVer, &cchVer);

            if (fRet && cchVer && lszVer)
            {
                // Replace dialog item text with version info
                lstrcpy(szResult, lszVer);
                SetDlgItemText(hdlg, i, szResult);
            }
        }
        GlobalUnlock(hMem);
        GlobalFree(hMem);
    }
    return TRUE;
}

//
//  FUNCTION: MsgAboutCommand(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Process WM_COMMAND message sent to the about box.
//
//  PARAMETERS:
//    hwnd - The window handing the message.
//    uMessage - The message number. (unused).
//    wparam - Message specific data (unused).
//    lparam - Message specific data (unused).
//
//  RETURN VALUE:
//    Always returns 0 - message handled.
//
//  COMMENTS:
//    Uses this DipsCommand function defined in wndproc.c combined
//    with the cmdiAbout structure defined in this file to handle
//    the command messages for the about dialog box.
//

LRESULT MsgAboutCommand(HWND   hwnd,
                        UINT   uMessage,
                        WPARAM wparam,
                        LPARAM lparam)
{
    return DispCommand(&cmdiAbout, hwnd, wparam, lparam);
}

//
//  FUNCTION: CmdAboutDone(HWND, WORD, HWND)
//
//  PURPOSE: Free the about box and related data.
//
//  PARAMETERS:
//    hwnd - The window handling the command.
//    wCommand - The command to be handled (unused).
//    wNotify   - Notification number (unused)
//    hwndCtrl - NULL (unused).
//
//  RETURN VALUE:
//    Always returns TRUE.
//
//  COMMENTS:
//    Calls EndDialog to finish the dialog session.
//

LRESULT CmdAboutDone(HWND hdlg, WORD wCommand, WORD wNotify, HWND hwndCtrl)
{
    if (hFontCopyright)
       DeleteObject(hFontCopyright);

    EndDialog(hdlg, TRUE);          // Exit the dialog
    return TRUE;
}


DISPATCH.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:   dispatch.c
//
//  PURPOSE:  Implement the generic message and command dispatchers.
//
//
//  FUNCTIONS:
//    DispMessage - Call the function associated with a message.
//    DispCommand - Call the function associated with a command.
//    DispDefault - Call the appropriate default window procedure.
//
//  COMMENTS:
//

#include <windows.h>            // required for all Windows applications
#include <windowsx.h>
#include "globals.h"            // prototypes specific to this application

LRESULT DispDefault(EDWP, HWND, UINT, WPARAM, LPARAM);

//
//  FUNCTION: DispMessage(LPMSDI, HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Call the function associated with a message.
//
//  PARAMETERS:
//    lpmsdi - Structure containing the message dispatch information.
//    hwnd - The window handle
//    uMessage - The message number
//    wparam - Message specific data
//    lparam - Message specific data
//
//  RETURN VALUE:
//    The value returned by the message function that was called.
//
//  COMMENTS:
//    Runs the table of messages stored in lpmsdi->rgmsd searching
//    for a message number that matches uMessage.  If a match is found,
//    call the associated function.  Otherwise, call DispDefault to
//    call the default function, if any, associated with the message
//    structure.  In either case, return the value recieved from the
//    message or default function.
//

LRESULT DispMessage(LPMSDI lpmsdi,
                    HWND   hwnd,
                    UINT   uMessage,
                    WPARAM wparam,
                    LPARAM lparam)
{
    int  imsd = 0;

    MSD *rgmsd = lpmsdi->rgmsd;
    int  cmsd  = lpmsdi->cmsd;

    for (imsd = 0; imsd < cmsd; imsd++)
    {
        if (rgmsd[imsd].uMessage == uMessage)
            return rgmsd[imsd].pfnmsg(hwnd, uMessage, wparam, lparam);
    }

    return DispDefault(lpmsdi->edwp, hwnd, uMessage, wparam, lparam);
}

//
//  FUNCTION: DispCommand(LPCMDI, HWND, WPARAM, LPARAM)
//
//  PURPOSE: Call the function associated with a command.
//
//  PARAMETERS:
//    lpcmdi - Structure containing the command dispatch information.
//    hwnd - The window handle
//    GET_WM_COMMAND_ID(wparam, lparam) - Identifier of the menu item,
//      control, or accelerator.
//    GET_WM_COMMAND_CMD(wparam, lparam) - Notification code.
//    GET_WM_COMMAND_HWND(wparam, lparam) - The control handle or NULL.
//
//  RETURN VALUE:
//    The value returned by the command function that was called.
//
//  COMMENTS:
//    Runs the table of commands stored in lpcmdi->rgcmd searching
//    for a command number that matches wCommand.  If a match is found,
//    call the associated function.  Otherwise, call DispDefault to
//    call the default function, if any, associated with the command
//    structure.  In either case, return the value recieved from the
//    command or default function.
//

LRESULT DispCommand(LPCMDI lpcmdi,
                    HWND   hwnd,
                    WPARAM wparam,
                    LPARAM lparam)
{
    LRESULT lRet = 0;
    WORD    wCommand = GET_WM_COMMAND_ID(wparam, lparam);
    int     icmd;

    CMD    *rgcmd = lpcmdi->rgcmd;
    int     ccmd  = lpcmdi->ccmd;

    // Message packing of wparam and lparam have changed for Win32,
    // so use the GET_WM_COMMAND macro to unpack the commnad

    for (icmd = 0; icmd < ccmd; icmd++)
    {
        if (rgcmd[icmd].wCommand == wCommand)
        {
            return rgcmd[icmd].pfncmd(hwnd,
                                      wCommand,
                                      GET_WM_COMMAND_CMD(wparam, lparam),
                                      GET_WM_COMMAND_HWND(wparam, lparam));
        }
    }

    return DispDefault(lpcmdi->edwp, hwnd, WM_COMMAND, wparam, lparam);
}

//
//  FUNCTION: DispDefault(EDWP, HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Call the appropriate default window procedure.
//
//  PARAMETERS:
//    edwp - Enumerate specifying the appropriate default winow procedure.
//    hwnd - The window handle
//    uMessage - The message number
//    wparam - Message specific data
//    lparam - Message specific data
//
//  RETURN VALUE:
//    If there is a default proc, return the value returned by the
//    default proc.  Otherwise, return 0.
//
//  COMMENTS:
//    Calls the default procedure associated with edwp using the specified
//    parameters.
//

LRESULT DispDefault(EDWP   edwp,
                    HWND   hwnd,
                    UINT   uMessage,
                    WPARAM wparam,
                    LPARAM lparam)
{
    switch (edwp)
    {
        case edwpNone:
            return 0;
        case edwpWindow:
            return DefWindowProc(hwnd, uMessage, wparam, lparam);
        case edwpDialog:
            return DefDlgProc(hwnd, uMessage, wparam, lparam);
        case edwpMDIFrame:
            return DefFrameProc(hwnd, hwndMDIClient, uMessage, wparam, lparam);
        case edwpMDIChild:
            return DefMDIChildProc(hwnd, uMessage, wparam, lparam);
    }
    return 0;
}


GLOBALS.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.
//
// PURPOSE:
//    Contains declarations for all globally scoped names in the program.
//

#include <nspapi.h>
#include <wsipx.h>

//-------------------------------------------------------------------------
// Product identifier string defines

#define APPNAME       GlobChat

//-------------------------------------------------------------------------
// Functions for handling main window messages.  The message-dispatching
// mechanism expects all message-handling functions to have the following
// prototype:
//
//     LRESULT FunctionName(HWND, UINT, WPARAM, LPARAM);

LRESULT MsgCreate(HWND, UINT, WPARAM, LPARAM);
LRESULT MsgSize(HWND, UINT, WPARAM, LPARAM);
LRESULT MsgTimer(HWND, UINT, WPARAM, LPARAM);
LRESULT MsgCommand(HWND, UINT, WPARAM, LPARAM);
LRESULT MsgDestroy(HWND, UINT, WPARAM, LPARAM);
LRESULT MsgConnected(HWND, UINT, WPARAM, LPARAM);
LRESULT MsgDataready(HWND, UINT, WPARAM, LPARAM);

//-------------------------------------------------------------------------
// Functions for handling main window commands--ie. functions for
// processing WM_COMMAND messages based on the wParam value.
// The message-dispatching mechanism expects all command-handling
// functions to have the following prototype:
//
//     LRESULT FunctionName(HWND, WORD, WORD, HWND);

LRESULT CmdExit(HWND, WORD, WORD, HWND);
LRESULT CmdAbout(HWND, WORD, WORD, HWND);

//-------------------------------------------------------------------------
//  Globchat socket message definitions
//
typedef struct _MSGHDR            // Message header
{
    BYTE            signature;    // Identifies start of a message
    WORD            length;       // size of message
    BYTE            command;      // message command
} MSGHDR, *LPMSGHDR;

typedef struct _DATAMSG            // Message = message header + data
{
    MSGHDR          hdr;
    BYTE            data[0x8000];  // data
} DATAMSG, *LPDATAMSG;

DATAMSG xferbuf;

#define HDRSIZE      sizeof(MSGHDR)    // Message header size

#define REALLEN(x)   lstrlen(x) + 1    // REALLEN will now count terminating NULL of x

//-------------------------------------------------------------------------
// My personal SOCKDATA structure for storing info on my sockets

typedef struct _SOCKDATA
{
    SOCKET           sock;           // socket handle
    int              status;         // Socket's status
    struct sockaddr  addr;           // Address structure
    char             reserved[10];   // Generic sockaddr struct is not big enough for all
                                     // addresses (namely netbios addresses) so adding some
                                     // space at the end.
    int              addrlen;        // Address length
    int              type;           // Socket type (from socket() call)
    int              protocol;       // protocol (from socket() call)
    int              currconnects;   // Number of current connections on this socket
    LPTSTR           lpProtocolName; // Pointer to protocol name socket is running on
    int              servsockindex;  // Index to array of server sockets
    SOCKET           peer;           // Associated peer socket which this socket is in session with
    char             name[16];       // User name
} SOCKDATA, *LPSOCKDATA;

LPSOCKDATA ServerSockets;        // Listening (server) sockets
LPSOCKDATA ConnectedSockets;   // Accepted (client) sockets

//  SOCKDATA structure status options
#define SOCKSTAT_INIT        1
#define SOCKSTAT_LISTENING   2
#define SOCKSTAT_ACCEPTING   3
#define SOCKSTAT_CLOSED      4
#define SOCKSTAT_CONNECTED   5
#define SOCKSTAT_AVAILABLE   6
#define SOCKSTAT_INSESSION   7
#define SOCKSTAT_REQSESSION  8

//-------------------------------------------------------------------------
// Global function prototypes.

BOOL InitApplication(HINSTANCE, int);
BOOL CenterWindow(HWND, HWND);
void AtoH(char *, char *, int);
unsigned char BtoH(char);
void deregistername(char *);
BOOL senddatamessage(SOCKET, LPDATAMSG);
BOOL recvdatamessage (LPSOCKDATA, LPDATAMSG);
void UpdateClientList(char *, int, char *);
BOOL MakeServSock(HWND, LPSOCKDATA, LPPROTOCOL_INFO);
LPTSTR GetStringRes (int id);

// Callback functions.  These are called by Windows.

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

//-------------------------------------------------------------------------
// Command ID definitions.  These definitions are used to associate menu
// items with commands.

// File menu
#define IDM_EXIT    1000

// Help menu
#define IDM_ABOUT   1100

// Status List boxes
#define ID_PROTOCOLBOX 400
#define ID_CLIENTBOX   401

//-------------------------------------------------------------------------
// String Table ID definitions.

#define IDS_APPNAME         1
#define IDS_DESCRIPTION     2
#define IDS_AVAILABLE       3
#define IDS_SESSION         4
#define IDS_SESSION_SETUP   5
#define IDS_LANGVERINFO     6

//-------------------------------------------------------------------------
//  About dialog defines.

#define IDD_VERFIRST    100
#define IDD_VERLAST     104

//-------------------------------------------------------------------------
// Application Specific Messages.

#define MW_CONNECTED    200
#define MW_DATAREADY    201

#define SAPTIMER        300

//--------------------------------------------------------------------------
//  Other constants

#define NWCHATID        0x5607      // 0x0756 (network order) was assigned by Novell
                                    // for this specific application
#define DNSCHATID       0x555       // TCP well known port

// Globchat socket message commands
#define REGISTER_NAME             1
#define XFER_DATA                 2
#define REQUEST_SESSION           3
#define SESSION_REQUEST_RESPONSE  4
#define SESSION_CLOSE             5
#define DEREGISTER_NAME           6

#define MYSIGNATURE               0xCC   // First byte in every message header

//-------------------------------------------------------------------------
// Global variable declarations.

extern HINSTANCE hInst;          // The current instance handle
extern char      szAppName[];    // The name of this application
extern char      szTitle[];      // The title bar text
HANDLE ConnectHeap;
LPPROTOCOL_INFO lpProtBuf;
SOCKET SAPSocket;
SOCKADDR_IPX SAPSockAddr, SAPDestSockAddr;
int NextFree, MaxConnects;
char aliasbuf[512];
int sizealiasbuf;
HWND hwndProtocolList;
HWND hwndClientList;

#define hwndMDIClient NULL

//-------------------------------------------------------------------------
// Message and command dispatch infrastructure.  The following type
// definitions and functions are used by the message and command dispatching
// mechanism and do not need to be changed.

    // Function pointer prototype for message handling functions.
typedef LRESULT (*PFNMSG)(HWND,UINT,WPARAM,LPARAM);

    // Function pointer prototype for command handling functions.
typedef LRESULT (*PFNCMD)(HWND,WORD,WORD,HWND);

    // Enumerated type used to determine which default window procedure
    // should be called by the message- and command-dispatching mechanism
    // if a message or command is not handled explicitly.
typedef enum
{
   edwpNone,            // Do not call any default procedure.
   edwpWindow,          // Call DefWindowProc.
   edwpDialog,          // Call DefDlgProc (This should be used only for
                        // custom dialogs - standard dialog use edwpNone).
   edwpMDIChild,        // Call DefMDIChildProc.
   edwpMDIFrame         // Call DefFrameProc.
} EDWP;                // Enumeration for Default Window Procedures

    // This structure maps messages to message handling functions.
typedef struct _MSD
{
    UINT   uMessage;
    PFNMSG pfnmsg;
} MSD;                 // MeSsage Dispatch structure

    // This structure contains all of the information that a window
    // procedure passes to DispMessage in order to define the message
    // dispatching behavior for the window.
typedef struct _MSDI
{
    int  cmsd;          // Number of message dispatch structs in rgmsd
    MSD *rgmsd;         // Table of message dispatch structures
    EDWP edwp;          // Type of default window handler needed.
} MSDI, FAR *LPMSDI;   // MeSsage Dipatch Information

    // This structure maps command IDs to command handling functions.
typedef struct _CMD
{
    WORD   wCommand;
    PFNCMD pfncmd;
} CMD;                 // CoMmand Dispatch structure

    // This structure contains all of the information that a command
    // message procedure passes to DispCommand in order to define the
    // command dispatching behavior for the window.
typedef struct _CMDI
{
    int  ccmd;          // Number of command dispatch structs in rgcmd
    CMD *rgcmd;         // Table of command dispatch structures
    EDWP edwp;          // Type of default window handler needed.
} CMDI, FAR *LPCMDI;   // CoMmand Dispatch Information

    // Message and command dispatching functions.  They look up messages
    // and commands in the dispatch tables and call the appropriate handler
    // function.
LRESULT DispMessage(LPMSDI, HWND, UINT, WPARAM, LPARAM);
LRESULT DispCommand(LPCMDI, HWND, WPARAM, LPARAM);

    // Message dispatch information for the main window
extern MSDI msdiMain;
    // Command dispatch information for the main window
extern CMDI cmdiMain;

//-------------------------------------------------------------------------
// Version string definitions--Leave these alone.

#define SZRCOMPANYNAME "CompanyName"
#define SZRDESCRIPTION "FileDescription"
#define SZRVERSION     "FileVersion"
#define SZRAPPNAME     "InternalName"
#define SZRCOPYRIGHT   "LegalCopyright"
#define SZRTRADEMARK   "LegalTrademarks"
#define SZRPRODNAME    "ProductName"
#define SZRPRODVER     "ProuctVersion"

//-------------------------------------------------------------------------
// SAP structure definition for a single entry SAP packet.

typedef struct _SAPHDR
{
    WORD     operation;    // 1 = Req., 2 = Resp., 3 = Get Nearest Req, 4 = Get Nearest Resp.
    WORD     servicetype;  // kind of service
    char     name[48];         // Name of Server
    char     network[4];      // network number
    char     node[6];         // node number
    char     socket[2];       // socket number
    WORD     hops;         // Number of hops to server
} SAPHDR, *LPSAPHDR;  // SAP structure, pointer to SAP structure

SAPHDR SAPData;


GLOBCHAT.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:   generic.c
//
//  PURPOSE:   Implement the windows procedure for the main application
//    windows.  Also implement the generic message and command dispatchers.
//
//  FUNCTIONS:
//    WndProc      - Processes messages for the main window.
//    MsgCreate    - Handles the WM_CREATE message.  Sets up Display and
//                   initializes listening sockets.  Also advertises the
//                   service
//    MsgSize      - Resizes the Listboxes to fit nicely in main window.
//    MsgTimer     - Handles the WM_TIMER messages which alert us to the
//                   fact that its time to send a SAP packet
//    MsgConnected - Handles new connection notifications
//    MsgDataready - Handles recv data notifications and close socket
//                   notifications
//    MsgCommand   - Handle the WM_COMMAND messages for the main window.
//    MsgDestroy   - Handles the WM_DESTROY message by calling
//                   PostQuitMessage().
//    CmdExit      - Handles the file exit command by calling destory
//                   window on the main window.
//
//  COMMENTS:
//

#include <windows.h>            // required for all Windows applications
#include <windowsx.h>
#include <wsipx.h>
#include <wsnetbs.h>
#include <svcguid.h>
#include "globals.h"            // prototypes specific to this application

// Main window message table definition.
MSD rgmsd[] =
{
    {WM_CREATE,    MsgCreate},
    {WM_SIZE,      MsgSize},
    {WM_TIMER,     MsgTimer},
    {WM_COMMAND,   MsgCommand},
    {WM_DESTROY,   MsgDestroy},
    {MW_CONNECTED, MsgConnected},
    {MW_DATAREADY, MsgDataready}
};

MSDI msdiMain =
{
    sizeof(rgmsd) / sizeof(MSD),
    rgmsd,
    edwpWindow
};

// Main window command table definition.
CMD rgcmd[] =
{
    {IDM_EXIT,  CmdExit},
    {IDM_ABOUT, CmdAbout}
};

CMDI cmdiMain =
{
    sizeof(rgcmd) / sizeof(CMD),
    rgcmd,
    edwpWindow
};

//
//  FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE:  Processes messages for the main window.
//
//  PARAMETERS:
//    hwnd     - window handle
//    uMessage - message number
//    wparam   - additional information (dependant on message number)
//    lparam   - additional information (dependant on message number)
//
//  RETURN VALUE:
//    The return value depends on the message number.  If the message
//    is implemented in the message dispatch table, the return value is
//    the value returned by the message handling function.  Otherwise,
//    the return value is the value returned by the default window procedure.
//
//  COMMENTS:
//    Call the DispMessage() function with the main window's message dispatch
//    information (msdiMain) and the message specific information.
//

LRESULT CALLBACK WndProc(HWND   hwnd,
                         UINT   uMessage,
                         WPARAM wparam,
                         LPARAM lparam)
{
    return DispMessage(&msdiMain, hwnd, uMessage, wparam, lparam);
}

//
//  FUNCTION: MsgCreate(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE:  Sets up child windows, initializes listening sockets, and advertises
//            the existence of the service
//
//  PARAMETERS:
//
//    hwnd      - Window handle
//    uMessage  - Message number (Unused)
//    wparam    - Extra data     (Unused)
//    lparam    - Extra data     (Unused)
//
//  RETURN VALUE:
//
//    Always returns 0 - Message handled
//
//  COMMENTS:
//
//

LRESULT MsgCreate(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    int sizeProtBuf = 0;
    int cNumProt;
    int j, k;
    int GoodProts[40];
    WORD wVersionRequested;
    WSADATA wsaData;
    char outtext[80];
    int tabwidth[1] = {100};
    BOOL bcaststat;
    int addrlen;

    // Create Protocol listbox
    hwndProtocolList = CreateWindow("LISTBOX",
                                    NULL,
                                    WS_BORDER | WS_CHILD | WS_VISIBLE | LBS_STANDARD | LBS_USETABSTOPS,
                                    0, 0, 0, 0,
                                    hwnd,
                                    (HMENU)ID_PROTOCOLBOX,
                                    (HINSTANCE) GetWindowLong(hwnd, GWL_HINSTANCE),
                                    0);
    // Set tabs
    SendMessage(hwndProtocolList, LB_SETTABSTOPS, 1, (LPARAM)tabwidth);

    // Create Client List listbox
    hwndClientList = CreateWindow("LISTBOX",
                                  NULL,
                                  WS_BORDER | WS_CHILD | WS_VISIBLE | LBS_STANDARD | LBS_USETABSTOPS | LBS_HASSTRINGS,
                                  0, 0, 0, 0,
                                  hwnd,
                                  (HMENU)ID_CLIENTBOX,
                                  (HINSTANCE) GetWindowLong(hwnd, GWL_HINSTANCE),
                                  0);
    // Set tab stops
    SendMessage(hwndClientList, LB_SETTABSTOPS, 1, (LPARAM)tabwidth);

    // First allocate Connected Sockets Heap
    if((ConnectHeap = HeapCreate(0, sizeof(SOCKDATA) * 5, sizeof(SOCKDATA) * 100)) == NULL)
    {
        // ERROR - abort
return 0;
    }
    if(((LPVOID)ConnectedSockets = HeapAlloc(ConnectHeap,0, sizeof(SOCKDATA) * 5)) == NULL)
    {
        // ERROR - abort
        return 0;
    }
    NextFree = 0;  // First available SOCKDATA struct
    MaxConnects = 5; // Allocate only 5 structures at a time

    // Load winsock dll
    wVersionRequested = MAKEWORD(1, 1);
    if (WSAStartup(wVersionRequested, &wsaData) != 0)
    {
        // ERROR -abort
return 0;
    }

    // Determine size of buffer needed
    if (EnumProtocols(NULL, NULL, &sizeProtBuf) != SOCKET_ERROR)
    {
        // ERROR
        return 0;
    }

    // Allocate buffer of appropriate size
    if (((LPVOID)lpProtBuf = VirtualAlloc(NULL,
                                          sizeProtBuf,
                                          MEM_COMMIT,
                                          PAGE_READWRITE)) == NULL)
    {
        // ERROR
        return 0;
    }

    // Do the real EnumProtocols call--note that there is no GETNEXT mechanism here.
// We have to allocate a single buffer big enough to hold everything.
    if ((cNumProt = EnumProtocols(NULL, lpProtBuf, &sizeProtBuf)) == SOCKET_ERROR)
    {
        // ERROR
        return 0;
    }

    // Now that we know how many protocols we have, allocate array of globchat
// SOCKDATA structures to hold info on each.
    if (((LPVOID)ServerSockets = VirtualAlloc(NULL,
                                              cNumProt * sizeof(SOCKDATA),
                                              MEM_COMMIT,
                                              PAGE_READWRITE)) == NULL)
    {
        // ERROR
        return 0;
    }

    // Walk through available protocols, j is index to list of all protocols, k is index to list of
// protocols we can use.
    for (j = 0, k = 0; j < cNumProt; j++)
    {
        // Only want connection oriented protocols
        if((lpProtBuf[j].dwServiceFlags & XP_CONNECTIONLESS) == 0)
        {
// Attempt to setup listen and fill in SOCKDATA struct
if (MakeServSock(hwnd, &ServerSockets[k], &lpProtBuf[j]))
{
            // Successfully using protocol...add it to our list box
            wsprintf(outtext, "%s\t%d", ServerSockets[k].lpProtocolName, ServerSockets[k].currconnects);
            SendMessage(hwndProtocolList, LB_ADDSTRING, 0, (LPARAM)&outtext);

            // Found a good protocol!
            GoodProts[k++] = j;
}
        }
    }

    cNumProt = k;  // Number of good protocols

    // Register with Name Spaces

    // In the future this will be done transparently, but for now must do it
    // manually for specific protocols.  The only NSPs we have are DNS and SAP.
    // Since DNS is static, that just leaves us with SAP to do.

    if((SAPSocket = socket(AF_IPX,                // IPX family
                           SOCK_DGRAM,   // SAPs are broadcasted
                           NSPROTO_IPX + 4)  // Uses IPX packet type 4
                           ) != INVALID_SOCKET)
    {
        // Find IPX address which we are bound to
        for(k = 0;k < cNumProt; k++)
        {
            if(ServerSockets[k].protocol == NSPROTO_SPXII)
            {
        // Initialize SAP data
        SAPData.operation = 0x200;         // 2 = SAP response(network order)
        SAPData.servicetype = 0x5607;  // 0756 Assigned by Novell specifically for GLOBCHAT only!!!!
        strcpy(SAPData.name, "GLOBCHAT");  // My service name
        memcpy((void *)SAPData.network,    // Copy address into SAP body
               (void *)(ServerSockets[k].addr.sa_data),
               12);
        SAPData.hops = 0;                 // Hop count starts at zero

        SAPSockAddr.sa_family = AF_IPX;   // SAP packets sent over IPX
        AtoH((char *)&SAPSockAddr.sa_socket, "0452", 2);  // SAP destination socket address is 0452.

// Bind to SAP socket
        if (bind(SAPSocket, (PSOCKADDR) &SAPSockAddr, sizeof(SOCKADDR_IPX)) == SOCKET_ERROR)
        {
            // Error binding to SAP socket
return 0;
        }
        else
        {
            // build destination address for SAP's IPX header (broadcast)
            SAPDestSockAddr.sa_family = AF_IPX;
            AtoH("00000000",SAPDestSockAddr.sa_netnum, 4);
            AtoH("FFFFFFFFFFFF", SAPDestSockAddr.sa_nodenum, 6);
            AtoH("0452", (char *)&SAPDestSockAddr.sa_socket, 2);

                // Setup socket to allow broadcasts
                bcaststat = TRUE;
                addrlen = sizeof(bcaststat);
                setsockopt(SAPSocket, SOL_SOCKET, SO_BROADCAST, (char *)&bcaststat, sizeof(bcaststat));
            sendto(SAPSocket, (char *)&SAPData, sizeof(SAPData), MSG_DONTROUTE, (struct sockaddr *)&SAPDestSockAddr, sizeof(SAPDestSockAddr));

            // Set timer to remind us to send SAP packet every 60 seconds.
            SetTimer(hwnd, SAPTIMER, 60000, NULL);
        }
                break;
            }
        }
    }
    return 0;
}

//
//  FUNCTION: MsgSize(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE:  Resizes child listboxes to fit nicely within parent
//
//  PARAMETERS:
//
//    hwnd      - Window handle
//    uMessage  - Message number (Unused)
//    wparam    - Extra data (Unused)
//    lparam    - Window dimensions
//
//  RETURN VALUE:
//
//    Always returns 0 - Message handled
//
//  COMMENTS:
//
//

LRESULT MsgSize(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    MoveWindow(hwndProtocolList,
               1, (HIWORD(lparam) / 4),
               (LOWORD(lparam) / 2),
               (HIWORD(lparam) * 3) / 4,
               TRUE);

    MoveWindow(hwndClientList,
               LOWORD(lparam) /2,
               HIWORD(lparam) / 4,
               LOWORD(lparam) / 2,
               (HIWORD(lparam) * 3) / 4,
               TRUE);
    return 0;
}

//
//  FUNCTION: MsgTimer(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Sends out SAP packet every 60 seconds
//
//  PARAMETERS:
//
//    hwnd      - Window handle
//    uMessage  - Message number (Unused)
//    wparam    - Should be = SAPTIMER
//    lparam    - Extra data     (Unused)
//
//  RETURN VALUE:
//
//    Always returns 0 - Message handled
//
//  COMMENTS:
//
//

LRESULT MsgTimer(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    sendto(SAPSocket, (char *) &SAPData, sizeof(SAPData), MSG_DONTROUTE, (struct sockaddr *)&SAPDestSockAddr, sizeof(SAPDestSockAddr));
    return 0;
}

//
//  FUNCTION: MsgConnected(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Handles new connection notifications
//
//  PARAMETERS:
//
//    hwnd      - Window handle
//    uMessage  - Message number (Unused)
//    wparam    - socket which has the connection
//    lparam    - loword = event, hiword = error code
//
//  RETURN VALUE:
//
//    Always returns 0 - Message handled
//
//  COMMENTS:
//
//

LRESULT MsgConnected(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    int k, addrlen;
    int index;
    char outtext[80];

    // May have to increase our heap size
    if(NextFree >= MaxConnects)
    {
        if(HeapAlloc(ConnectHeap, 0, sizeof(SOCKDATA) * 5) == NULL)
        {
           // ERROR on heap allocation...don't accept new connections
   return 0;
        }
        MaxConnects += 5;  // We've got five more connect entries available
    }

    // find socket that connect is refering to
    k = 0;
    while(ServerSockets[k].sock != wparam) k++;

    // Lets keep track of our progress
    ServerSockets[k].status = SOCKSTAT_ACCEPTING;

    // Some address our bigger than a sockaddr struct.  I threw in some reserved bytes to cover it.
    addrlen = sizeof(ConnectedSockets[NextFree].addr) + sizeof(ConnectedSockets[NextFree].reserved);

    // Accept the connect request
    if ((ConnectedSockets[NextFree].sock = accept(ServerSockets[k].sock,
                                                 &ConnectedSockets[NextFree].addr,
                                                 &addrlen)) == INVALID_SOCKET)
    {
        // ERROR clean up ConnectedSockets (that is don't increment NextFree) and go on our way
        return 0;
    }
    if(WSAAsyncSelect(ConnectedSockets[NextFree].sock, hwnd, MW_DATAREADY, FD_READ | FD_CLOSE) == SOCKET_ERROR)
    {
        // ERROR clean up connection
closesocket(ConnectedSockets[NextFree].sock);
        return 0;
    }
    // Fill in SOCKDATA structure for client socket
    ConnectedSockets[NextFree].protocol = ServerSockets[k].protocol;
    ConnectedSockets[NextFree].type = ServerSockets[k].type;
    ConnectedSockets[NextFree].servsockindex = k;
    ConnectedSockets[NextFree++].status = SOCKSTAT_CONNECTED;
// Increment protocol connection count and display
    ServerSockets[k].currconnects ++;
    index = SendMessage(hwndProtocolList, LB_FINDSTRING, (WPARAM)-1, (LPARAM)ServerSockets[k].lpProtocolName);
    SendMessage(hwndProtocolList, LB_DELETESTRING, index, 0);
    wsprintf(outtext, "%s\t%d", ServerSockets[k].lpProtocolName, ServerSockets[k].currconnects);
    SendMessage(hwndProtocolList, LB_ADDSTRING, 0, (LPARAM)&outtext);

    // Don't forget to set server socket status back to listening
    ServerSockets[k].status = SOCKSTAT_LISTENING;

    return 0;
}

//
//  FUNCTION: MsgDataready(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Handles incoming socket data notifications and close socket
//           notifications
//
//  PARAMETERS:
//
//    hwnd      - Window handle
//    uMessage  - Message number
//    wparam    - socket which has the data
//    lparam    - loword = event, hiword = error code
//
//  RETURN VALUE:
//
//    Always returns 0 - Message handled
//
//  COMMENTS:
//
//

LRESULT MsgDataready(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    int k, j, l;
    int totalbytesread;
    int index;
    char outtext[80];

    // Find the appropriate socket...allow for the reuse of a closed socket handle
    for (k = 0; k < NextFree; k++)
    {
        if((ConnectedSockets[k].sock == wparam) && (ConnectedSockets[k].status != SOCKSTAT_CLOSED))
            break;
    }

    if(LOWORD(lparam) == FD_CLOSE)
    {
        // Socket closed notification--cleanup!
        closesocket(ConnectedSockets[k].sock);

        // Delete name from client list display
        UpdateClientList(ConnectedSockets[k].name, SOCKSTAT_CLOSED, NULL);

        // Take name off of other clients' available list
        if (ConnectedSockets[k].status == SOCKSTAT_AVAILABLE)
        {
            deregistername(ConnectedSockets[k].name);
        }

        // if this connection was in session with another peer...notify peer of disconnect

        // First, find peer
        for (j = 0; j < NextFree; j++)
        {
            if((ConnectedSockets[j].sock == ConnectedSockets[k].peer) &&
               (ConnectedSockets[j].status == SOCKSTAT_INSESSION))
            {
                break;
            }
        }
        if (j < NextFree)  // Did we find a peer?
        {
            // Yes, build message header
            xferbuf.hdr.signature = MYSIGNATURE;
            xferbuf.hdr.length = totalbytesread = HDRSIZE;
            xferbuf.hdr.command = SESSION_CLOSE;
            // Send session close message
            senddatamessage(ConnectedSockets[j].sock, &xferbuf);
// Update Peer's displayed status
            ConnectedSockets[j].status = SOCKSTAT_AVAILABLE;
            UpdateClientList(ConnectedSockets[j].name, SOCKSTAT_AVAILABLE, NULL);
// Propagate the fact that the peer is now available for other chats
// and give peer list of other available clients
            for (l = 0; l < NextFree; l++)
            {
                if ((l != j) && (ConnectedSockets[l].status == SOCKSTAT_AVAILABLE))
                {
                    xferbuf.hdr.command = REGISTER_NAME;
                    lstrcpy(xferbuf.data, ConnectedSockets[l].name);
                    xferbuf.hdr.length = REALLEN(ConnectedSockets[l].name) + HDRSIZE;
                    senddatamessage(ConnectedSockets[j].sock, &xferbuf);

                    lstrcpy(xferbuf.data, ConnectedSockets[j].name);
                    xferbuf.hdr.length = REALLEN(ConnectedSockets[j].name) + HDRSIZE;
                    senddatamessage(ConnectedSockets[l].sock, &xferbuf);
                }
            }
        }
        // Cleanup ConnectedSockets array
        ConnectedSockets[k].status = SOCKSTAT_CLOSED;
        j = ConnectedSockets[k].servsockindex;
    // Fix protocol connection count display
    ServerSockets[j].currconnects --;
    index = SendMessage(hwndProtocolList, LB_FINDSTRING, (WPARAM)-1, (LPARAM)ServerSockets[j].lpProtocolName);
    SendMessage(hwndProtocolList, LB_DELETESTRING, index, 0);
    wsprintf(outtext, "%s\t%d", ServerSockets[j].lpProtocolName, ServerSockets[j].currconnects);
    SendMessage(hwndProtocolList, LB_ADDSTRING, 0, (LPARAM)&outtext);
        return 0;
    }

    // There's data to read...read it!

    if(!recvdatamessage(&ConnectedSockets[k], &xferbuf))
    {
        return 0;
    }

    // We've got our whole message!  Now switch() on the command flag

    switch(xferbuf.hdr.command)
    {
        case REGISTER_NAME:  // First message we should receive on a connection
            if(ConnectedSockets[k].status != SOCKSTAT_CONNECTED)
            {
                // ERROR -- we weren't expecting this...drop it
                return 0;
            }
            // Get name and add to internal structs and display
            lstrcpy(ConnectedSockets[k].name, xferbuf.data);
            ConnectedSockets[k].status = SOCKSTAT_AVAILABLE;
            UpdateClientList(ConnectedSockets[k].name, SOCKSTAT_AVAILABLE, NULL);

            // Send notification to other "AVAILABLE" sockets that we have a new peer available
            for (j = 0; j < NextFree; j++)
            {
                if((j != k) && (ConnectedSockets[j].status == SOCKSTAT_AVAILABLE))
                {
                    // message in xferbuf should be able to be sent just like it is
                    senddatamessage(ConnectedSockets[j].sock, &xferbuf);
                }
            }

            // Send notifications back to registering peer of all the currently available peers

            for (j = 0; j < NextFree; j++)
            {
                if((j != k) && (ConnectedSockets[j].status == SOCKSTAT_AVAILABLE))
                {
                    // found one...build message and send it
                    xferbuf.hdr.signature = MYSIGNATURE;
                    xferbuf.hdr.length = REALLEN(ConnectedSockets[j].name) + HDRSIZE;
                    xferbuf.hdr.command = REGISTER_NAME;
                    lstrcpy(xferbuf.data, ConnectedSockets[j].name);
                    senddatamessage(ConnectedSockets[k].sock, &xferbuf);
                }
            }

            return 0;

        case XFER_DATA:   // For passing data between two insession peers
            if(ConnectedSockets[k].status != SOCKSTAT_INSESSION)
            {
                // ERROR -- we weren't expecting this...drop data
                return 0;
            }

            // forward the message to peer...should be able transfer
            // message without modification
            senddatamessage(ConnectedSockets[k].peer, &xferbuf);
            return 0;

        case REQUEST_SESSION:  // Client is asking another peer for a chat
            if(ConnectedSockets[k].status != SOCKSTAT_AVAILABLE)
            {
                // ERROR -- we weren't expecting this...drop data
                return 0;
            }

            ConnectedSockets[k].status = SOCKSTAT_REQSESSION;
            UpdateClientList(ConnectedSockets[k].name, SOCKSTAT_REQSESSION, xferbuf.data);

            // Find the socket which corresponds to the name

            for(j = 0; j < NextFree; j++)
            {
                if(lstrcmp(ConnectedSockets[j].name, xferbuf.data) == 0)
                {
                    if(ConnectedSockets[j].status == SOCKSTAT_AVAILABLE)
                    {
                        // Found It!
                        break;
                    }
                }
            }

            if (j == NextFree) return 0;

            // Copy requester's name into send data buffer
            lstrcpy(xferbuf.data, ConnectedSockets[k].name);
            xferbuf.hdr.length = HDRSIZE + REALLEN(ConnectedSockets[k].name);
            senddatamessage(ConnectedSockets[j].sock, &xferbuf);

            // Update connected sockets structures
            ConnectedSockets[j].status = SOCKSTAT_REQSESSION;
            UpdateClientList(ConnectedSockets[j].name, SOCKSTAT_REQSESSION, ConnectedSockets[k].name);
            ConnectedSockets[j].peer = ConnectedSockets[k].sock;
            ConnectedSockets[k].peer = ConnectedSockets[j].sock;
            return 0;

        case SESSION_REQUEST_RESPONSE:  // Response to session request

            if(ConnectedSockets[k].status != SOCKSTAT_REQSESSION)
            {
                // ERROR -- not expecting this...drop packet
                return 0;
            }

            // find peer entry
            for (j = 0; j < NextFree; j++)
            {
                if ((ConnectedSockets[j].sock == ConnectedSockets[k].peer) &&
                    (ConnectedSockets[j].status == SOCKSTAT_REQSESSION))
                {
                    // Found it!
                    break;
                }
            }

            if (j == NextFree)
            {
                // ERROR -- couldn't find peer...drop packet
                return 0;
            }

            // forward response to requester
            senddatamessage(ConnectedSockets[k].peer, &xferbuf);

            if(*(xferbuf.data) == 1)
            {
                // Session accepted, change status of sockets
                ConnectedSockets[k].status = SOCKSTAT_INSESSION;
                ConnectedSockets[j].status = SOCKSTAT_INSESSION;
                UpdateClientList(ConnectedSockets[k].name, SOCKSTAT_INSESSION, ConnectedSockets[j].name);
                UpdateClientList(ConnectedSockets[j].name, SOCKSTAT_INSESSION, ConnectedSockets[k].name);
                deregistername(ConnectedSockets[k].name);
                deregistername(ConnectedSockets[j].name);
            }
            else
            {
                // Session not accepted, make sockets available
                ConnectedSockets[k].status = SOCKSTAT_AVAILABLE;
                ConnectedSockets[j].status = SOCKSTAT_AVAILABLE;
                UpdateClientList(ConnectedSockets[k].name, SOCKSTAT_AVAILABLE, NULL);
                UpdateClientList(ConnectedSockets[j].name, SOCKSTAT_AVAILABLE, NULL);
            }

            return 0;

        case SESSION_CLOSE:  // Insession client chose "End Chat" option
            if (ConnectedSockets[k].status != SOCKSTAT_INSESSION)
            {
                // We weren't expecting this...drop packet
                return 0;
            }

            // Find Peer
            for (j = 0; j < NextFree; j++)
            {
                if((ConnectedSockets[j].sock == ConnectedSockets[k].peer) &&
                   (ConnectedSockets[j].status == SOCKSTAT_INSESSION))
                {
                    // Found it
                    break;
                }
            }

            if(j == NextFree)
            {
                // ERROR - couldn't find peer...drop message
                return 0;
            }

            // forward message
            senddatamessage(ConnectedSockets[k].peer,&xferbuf);

            // Change Status
            ConnectedSockets[k].status = SOCKSTAT_AVAILABLE;
            ConnectedSockets[j].status = SOCKSTAT_AVAILABLE;
            UpdateClientList(ConnectedSockets[k].name, SOCKSTAT_AVAILABLE, NULL);
            UpdateClientList(ConnectedSockets[j].name, SOCKSTAT_AVAILABLE, NULL);

            // register names of both peers with other available clients.  Also
// provide current available client names to both peers

            for (l = 0; l < NextFree; l++)  // l = index to all connected sockets
                                // k = index of current peer
// j = index of socket who generated message
            {
                if (ConnectedSockets[l].status == SOCKSTAT_AVAILABLE)
                {
                    if ( l != k)
                    {
                        xferbuf.hdr.signature = MYSIGNATURE;
                        xferbuf.hdr.length = REALLEN(ConnectedSockets[k].name) + HDRSIZE;
                        xferbuf.hdr.command = REGISTER_NAME;
                        lstrcpy(xferbuf.data, ConnectedSockets[k].name);
                        senddatamessage(ConnectedSockets[l].sock, &xferbuf);

                        if (l != j)
                        {
                            xferbuf.hdr.length = REALLEN(ConnectedSockets[l].name) + HDRSIZE;
                            lstrcpy(xferbuf.data, ConnectedSockets[l].name);
                            senddatamessage(ConnectedSockets[k].sock, &xferbuf);
                        }
                    }
                    if (l != j)
                    {
                        xferbuf.hdr.signature = MYSIGNATURE;
                        xferbuf.hdr.length = REALLEN(ConnectedSockets[j].name) + HDRSIZE;
                        xferbuf.hdr.command = REGISTER_NAME;
                        lstrcpy(xferbuf.data, ConnectedSockets[j].name);
                        senddatamessage(ConnectedSockets[l].sock, &xferbuf);

                        if (l != k)
                        {
                            xferbuf.hdr.length = REALLEN(ConnectedSockets[l].name) + HDRSIZE;
                            lstrcpy(xferbuf.data, ConnectedSockets[l].name);
                            senddatamessage(ConnectedSockets[j].sock, &xferbuf);
                        }
                    }
                }
            }
            return 0;

    } // End message command switch

    // ERROR -- If we get here, we had an invalid message... drop it
    return 0;
}

//
//  FUNCTION: MsgCommand(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Handle the WM_COMMAND messages for the main window.
//
//  PARAMETERS:
//    hwnd     - window handle
//    uMessage - WM_COMMAND (Unused)
//    GET_WM_COMMAND_ID(wparam, lparam)   - Command identifier
//    GET_WM_COMMAND_HWND(wparam, lparam) - Control handle
//
//  RETURN VALUE:
//    The return value depends on the message number.  If the message
//    is implemented in the message dispatch table, the return value is
//    the value returned by the message handling function.  Otherwise,
//    the return value is the value returned by the default window procedure.
//
//  COMMENTS:
//    Call the DispCommand() function with the main window's command dispatch
//    information (cmdiMain) and the command specific information.
//

LRESULT MsgCommand(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    return DispCommand(&cmdiMain, hwnd, wparam, lparam);
}

//
//  FUNCTION: MsgDestroy(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Calls PostQuitMessage().
//
//  PARAMETERS:
//
//    hwnd      - Window handle  (Unused)
//    uMessage  - Message number (Unused)
//    wparam    - Extra data     (Unused)
//    lparam    - Extra data     (Unused)
//
//  RETURN VALUE:
//

//    Always returns 0 - Message handled
//
//  COMMENTS:
//
//

LRESULT MsgDestroy(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    PostQuitMessage(0);
    return 0;
}

//
//  FUNCTION: CmdExit(HWND, WORD, WORD, HWND)
//
//  PURPOSE: Exit the application.
//
//  PARAMETERS:
//    hwnd     - The window.
//    wCommand - IDM_EXIT (unused)
//    wNotify  - Notification number (unused)
//    hwndCtrl - NULL (unused)
//
//  RETURN VALUE:
//    Always returns 0 - command handled.
//
//  COMMENTS:
//
//

LRESULT CmdExit(HWND hwnd, WORD wCommand, WORD wNotify, HWND hwndCtrl)
{
    DestroyWindow(hwnd);
    return 0;
}


INIT.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:   init.c
//
//  PURPOSE:   Performs application and instance specific initialization.
//
//  FUNCTIONS:
//    InitApplication() - Initializes window data and registers window.
//
//  COMMENTS:
//

#include <windows.h>            // required for all Windows applications
#include "globals.h"            // prototypes specific to this application

HINSTANCE hInst;                // current instance

char szAppName[9];              // The name of this application
char szTitle[40];               // The title bar text

//
//  FUNCTION: InitApplication(HINSTANCE, int)
//
//  PURPOSE: Initializes window data and registers window class.
//
//  PARAMETERS:
//    hInstance - The handle to the instance of this application that
//                is currently being executed.
//    nCmdShow  - Specifies how the main window is to be displayed.
//
//  RETURN VALUE:
//    TRUE  - Success
//    FALSE - Initialization failed
//
//  COMMENTS:
//
//    This function is called at application initialization time.  It
//    performs initialization tasks for the current application instance.
//    Unlike Win16, in Win32, each instance of an application must register
//    window classes.
//
//    In this function, we initialize a window class by filling out a data
//    structure of type WNDCLASS and calling the Windows RegisterClass()
//    function.  Then we create the main window and show it.
//
//

BOOL InitApplication(HINSTANCE hInstance, int nCmdShow)
{
    WNDCLASS  wc;
    HWND      hwnd; // Main window handle.

    // Load the application name and description strings.

    LoadString(hInstance, IDS_APPNAME, szAppName, sizeof(szAppName));
    LoadString(hInstance, IDS_DESCRIPTION, szTitle, sizeof(szTitle));

    // Save the instance handle in static variable, which will be used in
    // many subsequence calls from this application to Windows.

    hInst = hInstance; // Store instance handle in our global variable

    // Fill in window class structure with parameters that describe the
    // main window.

    wc.style         = CS_HREDRAW | CS_VREDRAW; // Class style(s).
    wc.lpfnWndProc   = (WNDPROC)WndProc;        // Window Procedure
    wc.cbClsExtra    = 0;                       // No per-class extra data.
    wc.cbWndExtra    = 0;                       // No per-window extra data.
    wc.hInstance     = hInstance;               // Owner of this class
    wc.hIcon         = LoadIcon (hInstance, szAppName); // Icon name from .RC
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW); // Cursor
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); // Default color
    wc.lpszMenuName  = szAppName;               // Menu name from .RC
    wc.lpszClassName = szAppName;               // Name to register as

    // Register the window class and return FALSE if unsuccesful.

    if (!RegisterClass(&wc))
    {
        return FALSE;
    }

    // Create a main window for this application instance.
    hwnd = CreateWindow(szAppName,           // See RegisterClass() call
                        szTitle,             // Text for window title bar
                        WS_OVERLAPPEDWINDOW, // Window style
                        CW_USEDEFAULT, 0,    // Use default positioning
                        CW_USEDEFAULT, 0,    // Use default size
                        NULL,                // Overlapped has no parent
                        NULL,                // Use the window class menu
                        hInstance,           // This instance owns this window
                        NULL                 // Don't need data in WM_CREATE
    );

    // If window could not be created, return "failure"
    if (!hwnd)
        return FALSE;

    // Make the window visible; update its client area; and return "success"
    ShowWindow(hwnd, nCmdShow);  // Show the window
    UpdateWindow(hwnd);          // Sends WM_PAINT message

    return TRUE;                 // We succeeded...
}


MISC.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:   misc.c
//
//  PURPOSE:  Contains all helper functions "global" to the application.
//
//  FUNCTIONS:
//    CenterWindow - Center one window over another.
//    AtoH - Converts ascii to network order hex
//    BtoH - Converts ascii byte to numeric
//    deregistername - Notifies "Available" peers of clients non-availability
//    senddatamessage - Sends a message to a client node
//    recvdatamessage - reads an entire message
//    UpdateClientList - Updates Client Status Listbox
//    MakeServSock - Attempts to initialize socket for listening
//
//  COMMENTS:
//
//

#include <windows.h>            // required for all Windows applications
#include <windowsx.h>
#include <nspapi.h>
#include <svcguid.h>
#include <wsipx.h>
#include <wsnetbs.h>
#include "globals.h"            // prototypes specific to this application

//
//  FUNCTION: CenterWindow(HWND, HWND)
//
//  PURPOSE:  Center one window over another.
//
//  PARAMETERS:
//    hwndChild - The handle of the window to be centered.
//    hwndParent- The handle of the window to center on.
//
//  RETURN VALUE:
//
//    TRUE  - Success
//    FALSE - Failure
//
//  COMMENTS:
//
//    Dialog boxes take on the screen position that they were designed
//    at, which is not always appropriate. Centering the dialog over a
//    particular window usually results in a better position.
//

BOOL CenterWindow(HWND hwndChild, HWND hwndParent)
{
    RECT    rcChild, rcParent;
    int     cxChild, cyChild, cxParent, cyParent;
    int     cxScreen, cyScreen, xNew, yNew;
    HDC     hdc;

    // Get the Height and Width of the child window
    GetWindowRect(hwndChild, &rcChild);
    cxChild = rcChild.right - rcChild.left;
    cyChild = rcChild.bottom - rcChild.top;

    // Get the Height and Width of the parent window
    GetWindowRect(hwndParent, &rcParent);
    cxParent = rcParent.right - rcParent.left;
    cyParent = rcParent.bottom - rcParent.top;

    // Get the display limits
    hdc = GetDC(hwndChild);
    cxScreen = GetDeviceCaps(hdc, HORZRES);
    cyScreen = GetDeviceCaps(hdc, VERTRES);
    ReleaseDC(hwndChild, hdc);

    // Calculate new X position, then adjust for screen
    xNew = rcParent.left + ((cxParent - cxChild) / 2);
    if (xNew < 0)
    {
        xNew = 0;
    }
    else if ((xNew + cxChild) > cxScreen)
    {
        xNew = cxScreen - cxChild;
    }

    // Calculate new Y position, then adjust for screen
    yNew = rcParent.top  + ((cyParent - cyChild) / 2);
    if (yNew < 0)
    {
        yNew = 0;
    }
    else if ((yNew + cyChild) > cyScreen)
    {
        yNew = cyScreen - cyChild;
    }

    // Set it, and return
    return SetWindowPos(hwndChild,
                        NULL,
                        xNew, yNew,
                        0, 0,
                        SWP_NOSIZE | SWP_NOZORDER);
}

//
//  FUNCTION: AtoH(char *, char *, int)
//
//  PURPOSE:  Converts ascii string to network order hex
//
//  PARAMETERS:
//    src    - pointer to input ascii string
//    dest   - pointer to output hex
//    destlen - size of dest
//
//  COMMENTS:
//
//    2 ascii bytes make a hex byte so must put 1st ascii byte of pair
//    into upper nibble and 2nd ascii byte of pair into lower nibble.
//

void AtoH(char * src, char * dest, int destlen)
{
    char * srcptr;

    srcptr = src;

    while(destlen--)
    {
    *dest = BtoH(*srcptr++) << 4;    // Put 1st ascii byte in upper nibble.
    *dest++ += BtoH(*srcptr++);      // Add 2nd ascii byte to above.
    }
}

//
//  FUNCTION: BtoH(char *, char *, int)
//
//  PURPOSE:  Converts ascii byte to numeric
//
//  PARAMETERS:
//    ch - ascii byte to convert
//
//  RETURNS:
//    associated numeric value
//
//  COMMENTS:
//
//    Will convert any hex ascii digit to its numeric counterpart.
//    Puts in 0xff if not a valid hex digit.
//

unsigned char BtoH(char ch)
{
    if (ch >= '0' && ch <= '9') return (ch - '0');        // Handle numerals
    if (ch >= 'A' && ch <= 'F') return (ch - 'A' + 0xA);  // Handle capitol hex digits
    if (ch >= 'a' && ch <= 'f') return (ch - 'a' + 0xA);  // Handle small hex digits
    return(255);
}

//
//  FUNCTION: deregistername(char *)
//
//  PURPOSE:  Removes client's availability from other "AVAILABLE" clients
//
//  PARAMETERS:
//    name - name to deregister
//
//  RETURNS:
//    nothing
//
//  COMMENTS:
//
//    Between the time when a client connects to the server and when it starts
//    a chat, the client is in "AVAILABLE" state.  It keeps of list of other
//    available peers.  deregistername removes a previously available client
//    from the lists of all the other available clients
//

void deregistername(char * name)
{
    int j;

    // Set up message header
    xferbuf.hdr.signature = MYSIGNATURE;
    xferbuf.hdr.length = REALLEN(name) + HDRSIZE;
    xferbuf.hdr.command = DEREGISTER_NAME;
    // Message data is simply the name
    lstrcpy(xferbuf.data, name);

    // Send to every connected client whose status is SOCKET_AVAILABLE
    for (j = 0; j < NextFree; j++)
    {
        if (ConnectedSockets[j].status == SOCKSTAT_AVAILABLE)
        {
            senddatamessage(ConnectedSockets[j].sock, &xferbuf);
        }
    }
    return;
}

//
//  FUNCTION: senddatamessage(SOCKET, LPDATAMSG)
//
//  PURPOSE:  Sends a message to a client
//
//  PARAMETERS:
//    sock - Client socket to send data on
//    sendbuf - message to send
//
//  RETURNS:
//    TRUE - message sent
//
//  COMMENTS:
//
//    Performs send() until entire message is sent
//

BOOL senddatamessage (SOCKET sock, LPDATAMSG sendbuf)
{
    int totalbytes, bytessent;
   int thissendsize;

    bytessent = 0;                    // start count of bytessent
    totalbytes = sendbuf->hdr.length; // read total bytes from message header
    while(bytessent < totalbytes)
    {
        // We may not be able to send the entire message with a single
      // send.  Therefore continue sending until the whole thing
      // is gone.
        if ((thissendsize = send(sock,
                                (char *)sendbuf + bytessent,
                                totalbytes - bytessent,
                                0)) == SOCKET_ERROR)
      {
           // Error
          return FALSE;
       }
        bytessent += thissendsize;
    }

    return TRUE;
}

//
//  FUNCTION: recvdatamessage(LPSOCKDATA, LPDATAMSG)
//
//  PURPOSE:  Receives a message from a client
//
//  PARAMETERS:
//    lpSockdat - pointer to sockdata struct of socket we are receiving data on.
//    recvbuf - buffer to receive data into.
//
//  RETURNS:
//    TRUE - message received
//    FALSE - message not received
//
//  COMMENTS:
//    Performs recv() until entire message is received
//

BOOL recvdatamessage (LPSOCKDATA lpSockdat, LPDATAMSG recvbuf)
{
    int readsize, totalbytesread, msgsize, bytesread;

    if (lpSockdat->type == SOCK_SEQPACKET)
    {
        // Message mode protocol!!  Just post one big read.
        readsize = sizeof(*recvbuf);
    }
    else
    {
        // Stream mode protocol!!  Just read header...then read data (data size determined
        // from header)
        readsize = HDRSIZE;
    }
    if((totalbytesread = recv(lpSockdat->sock, (char *)recvbuf, readsize, 0)) == SOCKET_ERROR)
    {
        // ERROR  -- just return dropping message
        return FALSE;
    }

    // Check for my signature at the beginning of the message
    if(recvbuf->hdr.signature != MYSIGNATURE)
    {
        // I've received some data that's in mid message--drop it
        return FALSE;
    }

    // Read size of message
    msgsize = recvbuf->hdr.length;
    readsize = msgsize - totalbytesread;

    while(totalbytesread < msgsize)
    {
        // we should only get hear for stream sockets
        if((bytesread = recv(lpSockdat->sock,
                             (char *)recvbuf + totalbytesread,
                             readsize,
                             0)) == SOCKET_ERROR)
        {
            if (WSAGetLastError() != WSAEWOULDBLOCK)
            {
                // ERROR -- throw out message
                return FALSE;
            }
            // If you got a WSAWOULDBLOCK error, just keep trying...it shouldn't take
            // too much longer for the rest of the message to get here.  Let's hope
            // we don't hog the CPU so the data doesn't get to us.
        }
        totalbytesread += bytesread;
        readsize -= bytesread;
    }

    return TRUE;
}

//
//  FUNCTION: UpdateClientList(char *, int, char *)
//
//  PURPOSE:  Updates Client Status List box
//
//  PARAMETERS:
//    name - name of client to update
//    status - status of client to display
//    peername - connected peer for status = IN_SESSION
//
//  RETURNS:
//    nothing
//
//  COMMENTS:
//    Finds and updates client status in listbox, or adds
//    it if client is new
//

void UpdateClientList(char * name, int status, char * peername)
{
    int index;
    char outtext[80];

    // Find list box entry with name in it
    index = SendMessage(hwndClientList,
                        LB_FINDSTRING,
                        (WPARAM)-1,
                        (LPARAM)name);

    // Delete the entry--we'll add a new entry below
    SendMessage(hwndClientList, LB_DELETESTRING, index, 0);

    switch (status)
    {
        case SOCKSTAT_CLOSED:  // No new entry required.  Just return.
            return;

       case SOCKSTAT_AVAILABLE:  // build available entry
            wsprintf(outtext, GetStringRes(IDS_AVAILABLE), name);
       break;

       case SOCKSTAT_INSESSION:  // build in_session entry
            wsprintf(outtext, GetStringRes (IDS_SESSION), name, peername);
       break;

       case SOCKSTAT_REQSESSION: // build reqsession entry
            wsprintf(outtext, GetStringRes(IDS_SESSION_SETUP), name, peername);
    }
   // Add the new list box entry to the client status list box
    SendMessage(hwndClientList, LB_ADDSTRING, 0, (LPARAM)&outtext);
}

//
//  FUNCTION: MakeServSock(HWND, LPSOCKDATA, LPPROTOCOL_INFO)
//
//  PURPOSE:  Attempts to initialize server side socket for listening
//
//  PARAMETERS:
//    hwnd - handle to main window
//    ServSocks - SOCKDATA struct to hold socket specific information
//    lpProtBuf - PROTOCOL_INFO struct describing protocol to
//                attempt listen on
//
//  RETURNS:
//    TRUE - Listen successfully posted
//    FALSE - Initialization failed
//
//  COMMENTS:
//    Given the protocol triple in the PROTOCOL_INFO struct, we call
//    socket(), then GetAddressByName(), then bind(), listen(), and
//    finally WSAAsyncSelect() to get socket it appropriate state
//    for listening.  If we fail at any point, then cleanup and do
//    not use protocol
//

BOOL MakeServSock(HWND hwnd, LPSOCKDATA ServSocks, LPPROTOCOL_INFO lpProtBuf)
{
   CSADDR_INFO CSABuf[10];
   DWORD dwCSABufsize = sizeof(CSABuf);
   GUID guidNW =  SVCID_NETWARE(NWCHATID);  // Macro generates GUID using hard coded NetWare Service Type
   GUID guidDNS = SVCID_TCP(DNSCHATID);    // Macro generates GUID using hard coded port
   LPSOCKADDR_NB lpNBaddr;

   // open socket using protocol defined by PROTOCOL_INFO structure
   if ((ServSocks->sock = socket(lpProtBuf->iAddressFamily,
                                       lpProtBuf->iSocketType,
                                       lpProtBuf->iProtocol)) == SOCKET_ERROR)
   {
       // ERROR
      return FALSE;  // This will reuse the current ServSocks structure
   }

   // Populate SOCKDATA structure
   ServSocks->status = SOCKSTAT_INIT;
   ServSocks->type = lpProtBuf->iSocketType;
   ServSocks->protocol = lpProtBuf->iProtocol;
   ServSocks->currconnects = 0;
   ServSocks->lpProtocolName = lpProtBuf->lpProtocol;

   // ***********************************************************************
   //
   //    Below we will be calling GetAddressByName with the RES_SERVICE
   //    option in order to find the necessary sockaddr structure to use
   //    for the following bind() call.  At this point, GetAddressByName()
   //    only supports the DNS and the SAP/Bindery name spaces.  Ultimately
   //    a single call to GetAddressByName will return information on
   //    all available protocols, but this requires a central database
   //    which is currently not available.  In the mean time we will make
   //    name space specific calls to GetAddressByName.
   //
   // ***********************************************************************

   switch (lpProtBuf->iAddressFamily)
   {
       case AF_IPX:
           // Use SAP/Bindery Name Space
           if (GetAddressByName(0,    // Since GUID is name space specific, we don't need to specify distinct name space.
                                &guidNW,  // GUID defined by NetWare service type.
                                "GLOBAL CHAT SERVER",  // This parameter is actually not used for RES_SERVICE calls.
                                NULL,    // Since GUID implies the protocol, we don't need to specify a distinct one.
                                RES_SERVICE,  // Specifies that we are just looking for local address to bind to.
                                NULL,   // Currently not supported
                                CSABuf, // Structure which will hold information for bind() call
                                &dwCSABufsize,  // Size of CSABuf
                                NULL,   //  Currently not supported
                                NULL)   // Currently not supported
                                < 1)    // We need at least one address returned in order to bind()
           {
               // Error--cleanup
            closesocket(ServSocks->sock);
            return FALSE;
           }

         // Copy sockaddr info and addresslength
         memcpy(&ServSocks->addr, CSABuf[0].LocalAddr.lpSockaddr, sizeof(struct sockaddr));
         ServSocks->addrlen = CSABuf[0].LocalAddr.iSockaddrLength;
           break;

      case AF_INET:
           // Use DNS Name Space
           if (GetAddressByName(0,   // Since GUID is name space specific, we don't need to specify
                                &guidDNS,  // GUID defined by TCP port number
                                "globchat",  // This parameter is actually not used for RES_SERVICE calls
                                NULL,    // GUID implies protocol so no need to specify
                                RES_SERVICE,  // Specifies that we are trying to find local address to bind to
                                NULL,  // Currently not supported
                                CSABuf, // Results buffer
                                &dwCSABufsize,   // Size of results buffer
                                NULL,    // Not supported
                                NULL)    // Not supported
                                < 1)   // Need at least one address returned in order to bind()
           {
               // Error -- cleanup
            closesocket(ServSocks->sock);
            return FALSE;
           }
         // Copy sockaddr info and address length
         memcpy(&ServSocks->addr, CSABuf[0].LocalAddr.lpSockaddr, sizeof(struct sockaddr));
         ServSocks->addrlen = CSABuf[0].LocalAddr.iSockaddrLength;
           break;

       case AF_NETBIOS:
           // no netbios name space provider so we will simulate GetAddressByName() results
         lpNBaddr = (LPSOCKADDR_NB)&ServSocks->addr;
           SET_NETBIOS_SOCKADDR(lpNBaddr,
                                NETBIOS_GROUP_NAME,
                                "GLOBSERV",
                                0);
         ServSocks->addrlen = lpProtBuf->iMaxSockAddr;
           break;

   }

   // We have an address to bind() to, so do it!
   if (bind(ServSocks->sock,
            &ServSocks->addr,
           ServSocks->addrlen) == SOCKET_ERROR)
   {
       // Error -- cleanup
      closesocket(ServSocks->sock);
      return FALSE;
   }

   // Find out the specific local address
   if (getsockname(ServSocks->sock,
                   &ServSocks->addr,
                   &ServSocks->addrlen) == SOCKET_ERROR)
   {
       // Error -- better cleanup
       closesocket(ServSocks->sock);
      return FALSE;
   }

   // Listen on the socket
   if (listen(ServSocks->sock, 5) == SOCKET_ERROR)
   {
       // Error -- cleanup
       closesocket(ServSocks->sock);
      return FALSE;
   }

   // Set up window message to indicate connection
   if (WSAAsyncSelect(ServSocks->sock,
                      hwnd,
                     MW_CONNECTED,
                  FD_ACCEPT) == SOCKET_ERROR)
   {
       // Error -- cleanup
      closesocket(ServSocks->sock);
      return FALSE;
   }

   // Success!!!
   return TRUE;
}

//---------------------------------------------------------------------------
//
// 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;
}


WINMAIN.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:   winmain.c
//
//  PURPOSE:   Calls initialization functions and processes the message loop
//
//
//  PLATFORMS: Windows 95, Windows NT
//
//  FUNCTIONS:
//    WinMain() - calls initialization functions, processes message loop
//
//  COMMENTS:
//
//

#include <windows.h>            // required for all Windows applications
#include "globals.h"            // prototypes specific to this application

//
//  FUNCTION: WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
//
//  PURPOSE: calls initialization function, processes message loop
//
//  PARAMETERS:
//
//    hInstance - The handle to the instance of this application that
//          is currently being executed.
//
//    hPrevInstance - This parameter is always NULL in Win32
//          applications.
//
//    lpCmdLine - A pointer to a null terminated string specifying the
//          command line of the application.
//
//    nCmdShow - Specifies how the main window is to be diplayed.
//
//  RETURN VALUE:
//    If the function terminates before entering the message loop,
//    return FALSE.
//    Otherwise, return the WPARAM value sent by the WM_QUIT message.
//
//
//  COMMENTS:
//
//    Windows recognizes this function by name as the initial entry point
//    for the program.  This function calls the 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 APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
    MSG msg;
    HANDLE hAccelTable;

    // Initialize application by setting up the main window.
    if (!InitApplication(hInstance, nCmdShow))
    {
        return FALSE;           // Exits if unable to initialize
    }

    hAccelTable = LoadAccelerators(hInstance, szAppName);

    // Acquire and dispatch messages until a WM_QUIT message is received.
    while (GetMessage(&msg, NULL, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);  // Translates virtual key codes
            DispatchMessage(&msg);   // Dispatches message to window
        }
    }
    return msg.wParam;  // Returns the value from PostQuitMessage
}


README.TXT

Overlapped I/O Using I/O Completion Ports

The SOCKETS sample contains a simple Windows NT server that uses Windows
sockets. It demonstrates overlapped I/O using I/O completion ports to
receive ReadFile completion notification. An I/O completion port is the most
efficient form of overlapped I/O. I/O completion ports were introduced in
Windows NT 3.5.

CreateIoCompletionPort, as used in the server half of this sample, is only
supported on Windows NT.


COMMDEF.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:   commdef.h
//
//  PURPOSE:  Define types and values for communication.
//

typedef struct _CLIENT_IO_RETURN_DATA
{
   DWORD ByteCount;
}
CLIENT_IO_RETURN_DATA, *PCLIENT_IO_RETURN_DATA;

typedef struct _CLIENT_IO_I_AM_DONE
{
   DWORD TotalTicks;
   DWORD TotalIterations;
   DWORD TotalBytesTransferred;
}
CLIENT_IO_I_AM_DONE, *PCLIENT_IO_I_AM_DONE;

//
// Message types
//
#define CLIENT_IO_MT_RETURN_DATA    1
#define CLIENT_IO_MT_I_AM_DONE      2

typedef struct _CLIENT_IO_BUFFER
{
   DWORD MessageType;
   union
   {
      CLIENT_IO_RETURN_DATA ReturnData;
      CLIENT_IO_I_AM_DONE IAmDone;
   }
   u;
}
CLIENT_IO_BUFFER, *PCLIENT_IO_BUFFER;

#define CLIENT_OUTBOUND_BUFFER_MAX  1024

//
// Choose an arbitrary port number.  May need to be changed if
// it conflicts with an existing application.
//
#define SERVPORT 12396


SOCKCLI.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:   sockcli.c
//
//  PURPOSE:  Generate a simulated load for a sockets server.
//
//  FUNCTIONS:
// main   - Entry point for the program.
// CompleteBenchmark - Work routine for communication with server.
// ShowUsage   - Display usage help.
// ParseSwitch   - Process a command line argument.
//
//  COMMENTS:
//
//

#include "sockcli.h"

int _CRTAPI1
main (
        int argc,
        char *argv[],
        char *envp[]
)
{

   char chChar, *pchChar;
   INT err;
   WSADATA WsaData;

   fVerbose = FALSE;
   dwIterations = 1000;
   dwTransferSize = 512;
   fRandom = FALSE;

   //
   // Initialize Windows Sockets and request version 1.1
   //
   err = WSAStartup (0x0101, &WsaData);
   if (err == SOCKET_ERROR)
   {
      fprintf (stdout, "WSAStartup() failed: %ld\n", GetLastError ());
      return 1;
   }

   //
   // Default to the loopback address for the Benchmark
   //
   RemoteIpAddress.s_addr = htonl (INADDR_LOOPBACK);

   while (--argc)
   {
      pchChar = *++argv;
      if (*pchChar == '/' || *pchChar == '-')
      {
         while (chChar = *++pchChar)
         {
            ParseSwitch (chChar, &argc, &argv);
         }
      }
   }

   CompleteBenchmark ();

   return 1;
}

VOID
WINAPI
CompleteBenchmark (
                     VOID
)
{
   SOCKET s;
   SOCKADDR_IN remoteAddr;
   INT err;
   INT bytesReceived;
   DWORD i;
   DWORD startTime;
   DWORD endTime;
   DWORD totalTime;
   DWORD thisTransferSize;
   DWORD bytesTransferred = 0;
   INT ReceiveBufferSize;
   INT SendBufferSize;

   ReceiveBufferSize = CLIENT_OUTBOUND_BUFFER_MAX;
   SendBufferSize = sizeof (CLIENT_IO_BUFFER);

   //
   // Open a socket using the Internet Address family and TCP
   //
   s = socket (AF_INET, SOCK_STREAM, 0);
   if (s == INVALID_SOCKET)
   {
      printf ("DoEcho: socket failed: %ld\n", GetLastError ());
   }

   //
   // Set the receive buffer size...
   //
   err = setsockopt (s, SOL_SOCKET, SO_RCVBUF, (char *) &ReceiveBufferSize, sizeof (ReceiveBufferSize));
   if (err == SOCKET_ERROR)
   {
      printf ("DoEcho: setsockopt( SO_RCVBUF ) failed: %ld\n", GetLastError ());
      closesocket (s);
      return;
   }

   //
   // ...and the send buffer size for our new socket
   //
   err = setsockopt (s, SOL_SOCKET, SO_SNDBUF, (char *) &SendBufferSize, sizeof (SendBufferSize));
   if (err == SOCKET_ERROR)
   {
      printf ("DoEcho: setsockopt( SO_SNDBUF ) failed: %ld\n", GetLastError ());
      closesocket (s);
      return;
   }

   //
   // Connect to an agreed upon port on the host.  See the
   // commdef.h file for the actual port number
   //
   ZeroMemory (&remoteAddr, sizeof (remoteAddr));

   remoteAddr.sin_family = AF_INET;
   remoteAddr.sin_port = htons (SERVPORT);
   remoteAddr.sin_addr = RemoteIpAddress;

   err = connect (s, (PSOCKADDR) & remoteAddr, sizeof (remoteAddr));
   if (err == SOCKET_ERROR)
   {
      printf ("DoEcho: connect failed: %ld\n", GetLastError ());
      closesocket (s);
      return;
   }

   for (i = 0; i < dwIterations; i++)
   {

      if (fRandom)
      {
         thisTransferSize = (rand () * dwTransferSize) / RAND_MAX;
      }
      else
      {
         thisTransferSize = dwTransferSize;
      }

      SendBuffer.MessageType = CLIENT_IO_MT_RETURN_DATA;
      SendBuffer.u.ReturnData.ByteCount = thisTransferSize;

      //
      // Send "echo request" to remote host
      //
      err = send (s, (CHAR *) & SendBuffer, sizeof (SendBuffer), 0);
      if (err != sizeof (SendBuffer))
      {
         printf ("send didn't work, ret = %ld, error = %ld\n", err, GetLastError ());
         closesocket (s);
         return;
      }

      //
      // Read as much as the remote host should echo
      //
      bytesReceived = 0;
      do
      {
         err = recv (s, ReceiveBuffer, thisTransferSize, 0);
         if (i == 0)
         {
            startTime = GetTickCount ();
         }
         if (err == SOCKET_ERROR)
         {
            printf ("recv failed: %ld\n", GetLastError ());
            closesocket (s);
            return;
         }
         else if (err == 0 && thisTransferSize != 0)
         {
            printf ("socket closed prematurely by remote.\n");
            return;
         }
         bytesReceived += err;
      }
      while (bytesReceived < (INT) thisTransferSize);

      bytesTransferred += thisTransferSize;
   }

   endTime = GetTickCount ();
   totalTime = endTime - startTime;

   //
   // Send final packet to remote host
   //
   SendBuffer.MessageType = CLIENT_IO_MT_I_AM_DONE;
   SendBuffer.u.IAmDone.TotalTicks = totalTime;
   SendBuffer.u.IAmDone.TotalIterations = dwIterations;
   SendBuffer.u.IAmDone.TotalBytesTransferred = bytesTransferred;

   send (s, (CHAR *) & SendBuffer, sizeof (SendBuffer), 0);

   //
   // Display benchmark summary
   //
   printf ("\n%ld bytes transferred in %ld iterations, time = %ld ms\n",
           bytesTransferred, dwIterations, totalTime);
   printf ("Rate = %ld KB/s, %ld T/S, %ld ms/iteration\n",
           (bytesTransferred / totalTime) * 2,
           (dwIterations * 1000) / totalTime,
           totalTime / dwIterations);

   //
   // Close connection to remote host
   //
   err = closesocket (s);
   if (err == SOCKET_ERROR)
   {
      printf ("closesocket failed: %ld\n", GetLastError ());
      return;
   }

   return;
}

VOID
WINAPI
ShowUsage (
             VOID
)
{
   fputs ("usage: SOCKCLI [switches]\n"
  "               [-?] show this message\n"
  "               [-r] use random transfer sizes\n"
  "               [-i number-of-iterations] specify the number of iterations\n"
  "               [-s transfer-size] specify the fixed transfer size\n"
  "               [-h hostname] specify the remote server\n"
          ,stderr);

   exit (1);
}

VOID
WINAPI
ParseSwitch (
               CHAR chSwitch,
               int *pArgc,
               char **pArgv[]
)
{
   PHOSTENT host;

   switch (toupper (chSwitch))
   {

   case '?':
      ShowUsage ();
      break;

   case 'R':
      fRandom = TRUE;
      srand (22);
      break;

   case 'I':
      if (!--(*pArgc))
      {
         ShowUsage ();
      }
      (*pArgv)++;
      dwIterations = strtoul (*(*pArgv), NULL, 10);
      break;

   case 'S':
      if (!--(*pArgc))
      {
         ShowUsage ();
      }
      (*pArgv)++;
      dwTransferSize = strtoul (*(*pArgv), NULL, 10);
      if (dwTransferSize > CLIENT_OUTBOUND_BUFFER_MAX)
      {
         dwTransferSize = CLIENT_OUTBOUND_BUFFER_MAX;
      }
      fRandom = FALSE;
      break;

   case 'H':
      if (!--(*pArgc))
      {
         ShowUsage ();
      }
      (*pArgv)++;

      //
      // Assumed host is specified by name
      //
      host = gethostbyname (*(*pArgv));
      if (host == NULL)
      {
         //
         // See if the host is specified in "dot address" form
         //
         RemoteIpAddress.s_addr = inet_addr (*(*pArgv));
         if (RemoteIpAddress.s_addr == -1)
         {
            fprintf (stdout, "Unknown remote host: %s\n", *(*pArgv));
            exit (1);
         }
      }
      else
      {
         CopyMemory ((char *) &RemoteIpAddress, host->h_addr, host->h_length);
      }
      break;

   default:
      fprintf (stderr, "SOCKCLI: Invalid switch - /%c\n", chSwitch);
      ShowUsage ();
      break;

   }
}


SOCKCLI.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:   sockcli.h
//
//  PURPOSE:  Declare globals and prototypes for sockcli.c
//

#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <wsipx.h>
#include "commdef.h"

BOOL fVerbose;
BOOL fRandom;
DWORD dwIterations;
DWORD dwTransferSize;
IN_ADDR RemoteIpAddress;

CLIENT_IO_BUFFER SendBuffer;
CHAR ReceiveBuffer[CLIENT_OUTBOUND_BUFFER_MAX];

VOID
WINAPI
ShowUsage (
             VOID
);

VOID
WINAPI
ParseSwitch (
               CHAR chSwitch,
               int *pArgc,
               char **pArgv[]
);

VOID
WINAPI
CompleteBenchmark (
                     VOID
);


SOCKSRV.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:   socksrv.c
//
//  PURPOSE:  Handle and Benchmark client transactions.
//
//  FUNCTIONS:
//     main      - SockSrv entry point
//     SortTheBuffer      - Create a sorted copy of a buffer
//     CompleteBenchmark      - Time the client transactions
//     WorkerThread      - Process client requests
//     CreateWorkers      - Create worker threads
//     CreateNetConnections   - Establish client connections
//     ParseSwitch      - Process command line option
//     ShowUsage      - Display usage help
//     Random      - Generate a random # within a given range
//
//

#include "socksrv.h"

int _CRTAPI1
main (
        int argc,
        char *argv[],
        char *envp[]
)
{

   char chChar, *pchChar;
   DWORD lc;
   BOOL b;
   int WriteCount;

   //
   // try to get timing more accurate... Avoid context
   // switch that could occur when threads are released
   //

   SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_TIME_CRITICAL);

   //
   // Figure out how many processors we have to size the minimum
   // number of worker threads and concurrency
   //

   GetSystemInfo (&SystemInfo);

   fVerbose = FALSE;
   dwNumberOfClients = 1;
   dwNumberOfWorkers = 2 * SystemInfo.dwNumberOfProcessors;
   dwConcurrency = SystemInfo.dwNumberOfProcessors;
   fTcp = TRUE;
   dwWorkIndex = 4;

   while (--argc)
   {
      pchChar = *++argv;
      if (*pchChar == '/' || *pchChar == '-')
      {
         while (chChar = *++pchChar)
         {
            ParseSwitch (chChar, &argc, &argv);
         }
      }
   }

   //
   // If we are doing file I/O, then create a large file that clients
   // can randomly seek and read from
   //

   srand (1);

   //
   // Create a new file and make it the correct size
   //

   DeleteFile ("socksrv.dat");

   hFile = CreateFile (
                         "socksrv.dat",
                         GENERIC_READ | GENERIC_WRITE,
                         FILE_SHARE_READ | FILE_SHARE_WRITE,
                         NULL,
                         OPEN_ALWAYS,
   FILE_FLAG_DELETE_ON_CLOSE | FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
                         NULL
      );
   if (hFile == INVALID_HANDLE_VALUE)
   {
      fprintf (stderr, "SOCKSRV: Error opening file %d\n", GetLastError ());
      exit (1);
   }

   //
   // Initialize the file with random data
   //

   ClientData[0].Overlapped.Offset = 0;
   for (WriteCount = 0; WriteCount < NUMBER_OF_WRITES; WriteCount++)
   {

      for (lc = 0; lc < SIXTEEN_K; lc++)
      {
         InitialBuffer[lc] = (DWORD) rand ();
      }

      b = WriteFile (hFile, InitialBuffer, sizeof (InitialBuffer), &lc, &ClientData[0].Overlapped);

      if (!b && GetLastError () != ERROR_IO_PENDING)
      {
         fprintf (stderr, "SOCKSRV: Error in pre-write %d\n", GetLastError ());
         exit (1);
      }
      b = GetOverlappedResult (
                                 hFile,
                                 &ClientData[0].Overlapped,
                                 &lc,
                                 TRUE
         );
      if (!b || lc != sizeof (InitialBuffer))
      {
         fprintf (stderr, "SOCKSRV: Wait for pre-write failed %d\n", GetLastError ());
         exit (1);
      }
      ClientData[0].Overlapped.Offset += lc;
   }

   srand (1);
   fprintf (stdout, "SOCKSRV: %2d Clients %2d Workers Concurrency %d Using %s WorkIndex %d\n",
            dwNumberOfClients,
            dwNumberOfWorkers,
            dwConcurrency,
            fTcp ? "TCP" : "IPX",
            dwWorkIndex
      );

   if (!CreateNetConnections ())
   {
      exit (1);
   }

   if (!CreateWorkers ())
   {
      exit (1);
   }

   if (fVerbose)
   {
      fprintf (stdout, "Workers Created, Waiting for Clients to complete\n");
   }

   CompleteBenchmark ();

   exit (1);
   return 1;
}

VOID
WINAPI
CompleteBenchmark (
                     VOID
)
{
   DWORD LowestTicks;
   DWORD HighestTicks;
   DWORD TotalTicks;
   DWORD TotalIterations;
   DWORD TotalBytesTransferred;
   DWORD i;

   StartTime = GetTickCount ();
   SetEvent (hBenchmarkStart);
   WaitForSingleObject (hBenchmarkComplete, INFINITE);
   EndTime = GetTickCount ();

   LowestTicks = ClientData[0].IoBuffer.u.IAmDone.TotalTicks;
   HighestTicks = ClientData[0].IoBuffer.u.IAmDone.TotalTicks;
   TotalTicks = EndTime - StartTime;
   TotalIterations = 0;
   TotalBytesTransferred = 0;

   for (i = 0; i < dwNumberOfClients; i++)
   {
      TotalIterations += ClientData[i].IoBuffer.u.IAmDone.TotalIterations;
      TotalBytesTransferred += ClientData[i].IoBuffer.u.IAmDone.TotalBytesTransferred;

      //
      // find fastest client
      //
      if (LowestTicks > ClientData[i].IoBuffer.u.IAmDone.TotalTicks)
      {
         LowestTicks = ClientData[i].IoBuffer.u.IAmDone.TotalTicks;
      }

      //
      // find slowest client
      //
      if (HighestTicks < ClientData[i].IoBuffer.u.IAmDone.TotalTicks)
      {
         HighestTicks = ClientData[i].IoBuffer.u.IAmDone.TotalTicks;
      }
   }

   fprintf (stdout, "\nSOCKSRV:TPS, %ld (Fastest Client %dms Slowest Client %dms)\n",
            (TotalIterations * 1000) / TotalTicks,
            LowestTicks,
            HighestTicks
      );

   if (fVerbose)
   {
      fprintf (stdout, "\n%ld bytes transferred in %ld iterations, time = %ld ms\n",
               TotalBytesTransferred,
               TotalIterations,
               TotalTicks
         );

      for (i = 0; i < dwNumberOfWorkers; i++)
      {
         fprintf (stdout, "Thread[%2d] %d Transactions %d Bytes\n",
                  i,
                  ThreadData[i].TotalTransactions,
                  ThreadData[i].TotalBytesTransferred
            );
      }
   }
}

BOOL
WINAPI
CreateNetConnections (
                        void
)
{
   DWORD i;
   SOCKET listener;
   INT err;
   WSADATA WsaData;
   DWORD nbytes;
   BOOL b;

   err = WSAStartup (0x0101, &WsaData);
   if (err == SOCKET_ERROR)
   {
      fprintf (stdout, "WSAStartup Failed\n");
      return FALSE;
   }

   //
   // Open a socket to listen for incoming connections.
   //

   if (fTcp)
   {

      SOCKADDR_IN localAddr;

      listener = socket (AF_INET, SOCK_STREAM, 0);
      if (listener == INVALID_SOCKET)
      {
         fprintf (stdout, "Socket Create Failed\n");
         return FALSE;
      }

      //
      // Bind our server to the agreed upon port number.  See
      // commdef.h for the actual port number.
      //
      ZeroMemory (&localAddr, sizeof (localAddr));
      localAddr.sin_port = htons (SERVPORT);
      localAddr.sin_family = AF_INET;

      err = bind (listener, (PSOCKADDR) & localAddr, sizeof (localAddr));
      if (err == SOCKET_ERROR)
      {
         fprintf (stdout, "Socket Bind Failed\n");
         if (WSAGetLastError () == WSAEADDRINUSE)
            fprintf (stdout, "The port number may already be in use.\n");
         return FALSE;
      }

   }
   else
   {

      SOCKADDR_IPX localAddr;

      listener = socket (AF_IPX, SOCK_STREAM, NSPROTO_SPX);
      if (listener == INVALID_SOCKET)
      {
         fprintf (stdout, "Socket Create Failed\n");
         return FALSE;
      }

      ZeroMemory (&localAddr, sizeof (localAddr));
      localAddr.sa_socket = htons (7);
      localAddr.sa_family = AF_IPX;

      err = bind (listener, (PSOCKADDR) & localAddr, sizeof (localAddr));
      if (err == SOCKET_ERROR)
      {
         fprintf (stdout, "Socket Bind Failed\n");
         return FALSE;
      }
   }

   //
   // Prepare to accept client connections.  Allow up to 5 pending
   // connections.
   //
   err = listen (listener, 5);
   if (err == SOCKET_ERROR)
   {
      fprintf (stdout, "Socket Listen Failed\n");
      return FALSE;
   }

   //
   // Only Handle a single Queue
   //

   for (i = 0; i < dwNumberOfClients; i++)
   {

      //
      // Accept incoming connect requests and create wait events for each.
      //

      //
      // If we are doing file I/O, each client will need its own event
      // to use for async file I/O
      //

      if (hFile)
      {
         ClientData[i].hEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
         if (!ClientData[i].hEvent)
         {
            fprintf (stdout, "Client Event Create Failed\n");
            return FALSE;
         }
      }

      ClientData[i].Socket = accept (listener, NULL, NULL);
      if (ClientData[i].Socket == INVALID_SOCKET)
      {
         fprintf (stdout, "Accept Failed\n");
         return FALSE;
      }

      if (fVerbose)
      {
         fprintf (stdout, "Client Connection Accepted\n");
      }

      //
      // note the 16 says how many concurrent cpu bound threads to allow thru
      // this should be tunable based on the requests. CPU bound requests will
      // really really honor this.
      //

      CompletionPort = CreateIoCompletionPort (
                                              (HANDLE) ClientData[i].Socket,
                                                 CompletionPort,
                                                 (DWORD) i,
                                                 dwConcurrency
         );

      if (!CompletionPort)
      {
         fprintf (stdout, "CompletionPort Create Failed\n");
         return FALSE;
      }

      ClientData[i].Flags = CLIENT_CONNECTED;

      //
      // Start off an asynchronous read on the socket.
      //

      ClientData[i].Overlapped.hEvent = NULL;

      b = ReadFile (
                      (HANDLE) ClientData[i].Socket,
                      &ClientData[i].IoBuffer,
                      sizeof (CLIENT_IO_BUFFER),
                      &nbytes,
                      &ClientData[i].Overlapped
         );

      if (!b && GetLastError () != ERROR_IO_PENDING)
      {
         fprintf (stdout, "ReadFile Failed\n");
         return FALSE;
      }
   }

   dwActiveClientCount = dwNumberOfClients;
   hBenchmarkComplete = CreateEvent (NULL, TRUE, FALSE, NULL);
   if (!hBenchmarkComplete)
   {
      fprintf (stdout, "Create Benchmark Complete Event Failed\n");
      return FALSE;
   }
   hBenchmarkStart = CreateEvent (NULL, TRUE, FALSE, NULL);
   if (!hBenchmarkStart)
   {
      fprintf (stdout, "Create Benchmark Start Event Failed\n");
      return FALSE;
   }

   return TRUE;
}

BOOL
WINAPI
CreateWorkers (
                 void
)
{
   DWORD ThreadId;
   HANDLE ThreadHandle;
   DWORD i;

   for (i = 0; i < dwNumberOfWorkers; i++)
   {

      ThreadHandle = CreateThread (
                                     NULL,
                                     0,
                                     WorkerThread,
                                     &ThreadData[i],
                                     0,
                                     &ThreadId
         );
      if (!ThreadHandle)
      {
         fprintf (stdout, "Create Worker Thread Failed\n");
         return FALSE;
      }

      CloseHandle (ThreadHandle);
   }

   return TRUE;
}

DWORD
WINAPI
WorkerThread (
                LPVOID WorkContext
)
{
   PPER_THREAD_DATA Me;
   DWORD WorkIndex;
   INT err;
   DWORD ResponseLength;
   BOOL b;
   LPOVERLAPPED lpo;
   DWORD nbytes;
   PPER_CLIENT_DATA CurrentClient;
   CHAR ReadBuffer[CLIENT_OUTBOUND_BUFFER_MAX];

   WaitForSingleObject (hBenchmarkStart, INFINITE);

   Me = (PPER_THREAD_DATA) WorkContext;

   for (;;)
   {

      b = GetQueuedCompletionStatus (
                                       CompletionPort,
                                       &nbytes,
                                       &WorkIndex,
                                       &lpo,
                                       INFINITE
         );

      if (b || lpo)
      {

         if (b)
         {

            CurrentClient = &ClientData[WorkIndex];

            switch (CurrentClient->IoBuffer.MessageType)
            {
            case CLIENT_IO_MT_RETURN_DATA:

               //
               // Determine how long a response was desired by the client.
               //

               ResponseLength = CurrentClient->IoBuffer.u.ReturnData.ByteCount;
               if (ResponseLength > CLIENT_OUTBOUND_BUFFER_MAX)
               {
                  ResponseLength = CLIENT_OUTBOUND_BUFFER_MAX;
               }

               //
               // If we are running in I/O mode, do a random read
               // Otherwise, use fill memory to supply the data
               //
               if (hFile)
               {

                  CurrentClient->Overlapped.Offset = Random (FILE_SIZE / CLIENT_OUTBOUND_BUFFER_MAX) * CLIENT_OUTBOUND_BUFFER_MAX;
                  CurrentClient->Overlapped.hEvent = CurrentClient->hEvent;
                  b = ReadFile (
                                  hFile,
                                  ReadBuffer,
                                  ResponseLength,
                                  &nbytes,
                                  &CurrentClient->Overlapped
                     );
                  if (!b && GetLastError () != ERROR_IO_PENDING)
                  {
                     fprintf (stderr, "SOCKSRV: Error in client read %d\n", GetLastError ());
                     exit (1);
                  }
                  b = GetOverlappedResult (
                                             hFile,
                                             &CurrentClient->Overlapped,
                                             &nbytes,
                                             TRUE
                     );
                  if (!b)
                  {
                     fprintf (stderr, "SOCKSRV: Wait for pre-write failed %d\n", GetLastError ());
                     exit (1);
                  }
                  CurrentClient->Overlapped.hEvent = NULL;
               }
               else
               {
                  FillMemory (CurrentClient->OutboundBuffer, ResponseLength, 0xfe);
               }

               //
               // Simulate a small compute bound workload
               //
               SortTheBuffer ((LPDWORD) CurrentClient->OutboundBuffer, (LPDWORD) ReadBuffer, nbytes >> 2);

               //
               // Send a response and post another asynchronous read on the
               // socket.
               //

               err = send (CurrentClient->Socket,
                           CurrentClient->OutboundBuffer,
                           ResponseLength,
                           0
                  );

               if (err == SOCKET_ERROR)
               {
                  fprintf (stdout, "Send Failed\n");
                  exit (1);
               }

               Me->TotalTransactions++;
               Me->TotalBytesTransferred += ResponseLength;

               //
               // reprime this client by posting another asynch read
               //

               b = ReadFile (
                               (HANDLE) CurrentClient->Socket,
                               &CurrentClient->IoBuffer,
                               sizeof (CLIENT_IO_BUFFER),
                               &nbytes,
                               &CurrentClient->Overlapped
                  );

               if (!b && GetLastError () != ERROR_IO_PENDING)
               {
                  fprintf (stdout, "ReadFile Failed\n");
                  exit (1);
               }
               break;

            case CLIENT_IO_MT_I_AM_DONE:
               CurrentClient->Flags |= CLIENT_DONE;
               if (fVerbose)
               {
                  fprintf (stdout, "Client Has Completed\n");
               }
               if (!InterlockedDecrement (&dwActiveClientCount))
               {
                  SetEvent (hBenchmarkComplete);
               }
               break;

            default:
               fprintf (stdout, "Invalid MessageType %x\n", CurrentClient->IoBuffer.MessageType);
               exit (1);
            }
         }
      }
      else
      {
         fprintf (stdout, "WorkThread Wait Failed\n");
         exit (1);
      }
   }
   return 1;
}

VOID
WINAPI
ShowUsage (
             VOID
)
{
   fputs ("usage: SOCKSRV [switches]\n"
          "               [-?] show this message\n"
          "               [-v] verbose output\n"
          "               [-t number-of-threads] specify the number of worker threads\n"
     "               [-c number-of-clients] specify the number of clients\n"
          "               [-p concurrency-value] specify the concurrency\n"
          "               [-i ] use IPX instead of TCP\n"
          "               [-w work-index] specify how much compute to do\n"
          ,stderr);

   exit (1);
}

VOID
WINAPI
ParseSwitch (
               CHAR chSwitch,
               int *pArgc,
               char **pArgv[]
)
{

   switch (toupper (chSwitch))
   {

   case '?':
      ShowUsage ();
      break;

   case 'T':
      if (!--(*pArgc))
      {
         ShowUsage ();
      }
      (*pArgv)++;
      dwNumberOfWorkers = strtoul (*(*pArgv), NULL, 10);
      if (dwNumberOfWorkers > MAXIMUM_NUMBER_OF_WORKERS)
      {
         dwNumberOfWorkers = MAXIMUM_NUMBER_OF_WORKERS;
      }
      break;

   case 'C':
      if (!--(*pArgc))
      {
         ShowUsage ();
      }
      (*pArgv)++;
      dwNumberOfClients = strtoul (*(*pArgv), NULL, 10);
      if (dwNumberOfClients > MAXIMUM_NUMBER_OF_CLIENTS)
      {
         dwNumberOfClients = MAXIMUM_NUMBER_OF_CLIENTS;
      }
      break;

   case 'P':
      if (!--(*pArgc))
      {
         ShowUsage ();
      }
      (*pArgv)++;
      dwConcurrency = strtoul (*(*pArgv), NULL, 10);
      break;

   case 'W':
      if (!--(*pArgc))
      {
         ShowUsage ();
      }
      (*pArgv)++;
      dwWorkIndex = strtoul (*(*pArgv), NULL, 10);
      break;

   case 'V':
      fVerbose = TRUE;
      break;

   case 'I':
      fTcp = FALSE;
      break;

   default:
      fprintf (stderr, "SOCKSRV: Invalid switch - /%c\n", chSwitch);
      ShowUsage ();
      break;

   }
}

DWORD
WINAPI
Random (
          DWORD nMaxValue
)
{
   return (((2 * rand () * nMaxValue + RAND_MAX) / RAND_MAX - 1) / 2);
}

int _CRTAPI1
DwordComp (const void *e1, const void *e2)
{
   PULONG p1;
   PULONG p2;

   p1 = (PULONG) e1;
   p2 = (PULONG) e2;

   return (*p1 - *p2);
}

VOID
WINAPI
SortTheBuffer (
                 LPDWORD Destination,
                 LPDWORD Source,
                 int DwordCount
)

{
   DWORD i;

   for (i = 0; i < 2 * dwWorkIndex; i++)
   {
      CopyMemory (Destination, Source, DwordCount << 2);
      qsort ((void *) Destination, (size_t) DwordCount, (size_t) sizeof (DWORD), DwordComp);
   }
}


SOCKSRV.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:   socksrv.h
//
//  PURPOSE:  Definitions and prototypes for socksrv.c
//

#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <wsipx.h>
#include "commdef.h"

#define MAXIMUM_NUMBER_OF_CLIENTS   64
#define MAXIMUM_NUMBER_OF_WORKERS   32
#define FILE_SIZE ((1024*1024*8)-CLIENT_OUTBOUND_BUFFER_MAX)

#define SIXTY_FOUR_K    (64*1024)
#define SIXTEEN_K       (16*1024)
DWORD InitialBuffer[SIXTY_FOUR_K/sizeof(DWORD)];
#define NUMBER_OF_WRITES ((FILE_SIZE+CLIENT_OUTBOUND_BUFFER_MAX)/SIXTY_FOUR_K)

#define CLIENT_CONNECTED    0x00000001
#define CLIENT_DONE         0x00000002

typedef struct _PER_CLIENT_DATA {
    SOCKET Socket;
    OVERLAPPED Overlapped;
    CLIENT_IO_BUFFER IoBuffer;
    CHAR OutboundBuffer[CLIENT_OUTBOUND_BUFFER_MAX];
    DWORD Flags;
    HANDLE hEvent;
} PER_CLIENT_DATA, *PPER_CLIENT_DATA;

typedef struct _PER_THREAD_DATA {
    DWORD TotalTransactions;
    DWORD TotalBytesTransferred;
} PER_THREAD_DATA, *PPER_THREAD_DATA;

PER_THREAD_DATA ThreadData[MAXIMUM_NUMBER_OF_WORKERS];
PER_CLIENT_DATA ClientData[MAXIMUM_NUMBER_OF_CLIENTS];
BOOL fVerbose;
BOOL fTcp;
DWORD dwNumberOfClients;
DWORD dwNumberOfWorkers;
DWORD dwConcurrency;
DWORD dwWorkIndex;
SYSTEM_INFO SystemInfo;
HANDLE CompletionPort;
DWORD dwActiveClientCount;
HANDLE hBenchmarkComplete;
HANDLE hBenchmarkStart;
DWORD StartTime;
DWORD EndTime;
HANDLE hFile;

DWORD
WINAPI
Random (
    DWORD nMaxValue
    );

VOID
WINAPI
ShowUsage(
    VOID
    );

VOID
WINAPI
ParseSwitch(
    CHAR chSwitch,
    int *pArgc,
    char **pArgv[]
    );

BOOL
WINAPI
CreateNetConnections(
    VOID
    );

BOOL
WINAPI
CreateWorkers(
    VOID
    );

DWORD
WINAPI
WorkerThread(
    LPVOID WorkContext
    );

VOID
WINAPI
CompleteBenchmark(
    VOID
    );

VOID
WINAPI
SortTheBuffer(
    LPDWORD Destination,
    LPDWORD Source,
    int DwordCount
    );


README.TXT

IPXCHAT Example Application

This program demonstrates the use of Windows Sockets to communicate
over the IPX/SPX protocol.

Setup:

IPXCHAT requires that an IPX protocol is loaded.  The "IPX/SPX
Compatible Transport" shippd in the box is the protocol which was
used for testing this application.  Make sure that the protocol is
configured correctly for the same frame type on the two machines
which are talking to each other.

To use IPXCHAT:

There are two sides to a sockets application, the server side (the
listening side), and the client side (the calling side).  On the first
machine you will need to set up IPXCHAT as the server.

Start IPXCHAT and select Options->Listen.

You will be prompted to enter a socket number.  A default of 2FFF will be
offered to you although you may change it to anything you want.  Be aware
that there are well known XNS sockets which you should stay away from.  See
Novell's "IPX Router Specification" for a list of these.

When you select OK, IPXCHAT will initialize Windows Sockets and start
listening on the specified socket.  In the status box of the dialog you
will see a message "listening on <netnum>.<nodenum>.<socknum>" where
netnum will be a number specifying the network number, nodenum specifies
the node number, and socknum specifies the socket number.  You will need
these numbers to enter on the client side in order to connect.

On the second machine (actually it can be the same machine as the server):

Start IPXCHAT and select Options->Connect.

You will be prompted to enter the Network Number, Node Number, and Socket
Number indicated on the server (the three numbers in the status box of
the server's listen dialog).  Once you connect you should be able to
type in the top edit control and it should appear on the bottom of your
counterpart's edit control.


CONNECT.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:   connect.c
//
//  PURPOSE:   Displays the "Connect" dialog box
//
//  FUNCTIONS:
//    CmdConnect        - Displays the "Connect" dialog box
//    Connect           - Processes messages for "Connect" dialog box.
//    MsgConnectInit    - Initializes edit controls
//    MsgConnectCommand - Process WM_COMMAND message sent to the connect box.
//    CmdConnectDone    - Free the connect box and related data.
//    CmdConnectNow     - Establishes connection to specified address. Kills
//                        dialog if successful.
//
//  COMMENTS:
//
//

#include <windows.h>            // required for all Windows applications
#include <windowsx.h>
#include <wsipx.h>              // IPX sockets
#include <stdlib.h>
#include <stdio.h>
#include "globals.h"            // prototypes specific to this application

//   Function Definitions

LRESULT CALLBACK Connect(HWND, UINT, WPARAM, LPARAM);
LRESULT MsgConnectInit(HWND, UINT, WPARAM, LPARAM);
LRESULT MsgConnectCommand(HWND, UINT, WPARAM, LPARAM);
LRESULT CmdConnectNow(HWND, WORD, WORD, HWND);
LRESULT CmdConnectDone(HWND, WORD, WORD, HWND);

// Connect dialog message table definition.
MSD rgmsdConnect[] =
{
    {WM_COMMAND,    MsgConnectCommand},
    {WM_INITDIALOG, MsgConnectInit}
};

MSDI msdiConnect =
{
    sizeof(rgmsdConnect) / sizeof(MSD),
    rgmsdConnect,
    edwpNone
};

// Connect dialog command table definition.
CMD rgcmdConnect[] =
{
    {IDOK,     CmdConnectNow},
    {IDCANCEL, CmdConnectDone}
};

CMDI cmdiConnect =
{
    sizeof(rgcmdConnect) / sizeof(CMD),
    rgcmdConnect,
    edwpNone
};

// Module specific "globals"  Used when a variable needs to be
// accessed in more than one handler function.

HFONT hfontDlg;

//
//  FUNCTION: CmdConnect(HWND, WORD, WORD, HWND)
//
//  PURPOSE: Displays the "Connect" dialog box
//
//  PARAMETERS:
//    hwnd      - Window handle
//    wCommand  - IDM_CONNECT (unused)
//    wNotify   - Notification number (unused)
//    hwndCtrl  - NULL (unused)
//
//  RETURN VALUE:
//
//    Always returns 0 - Message handled
//
//  COMMENTS:
//    To process the IDM_CONNECT message, call DialogBox() to display the
//    Connect dialog box.

LRESULT CmdConnect(HWND hwnd, WORD wCommand, WORD wNotify, HWND hwndCtrl)
{
    HMENU hmenu;

    SetWindowText(hwnd, "IPX Chat Client");    // Change Window Title

    // Start dialog box
    if(DialogBox(hInst, "ConnectBox", hwnd, (DLGPROC)Connect))
    {

        // We have a connection!  Set up message notification if
        // data ready to be read or if connection is closed on us
        if (WSAAsyncSelect(sock,
                           hwnd,
                           MW_DATAREADY,
                           FD_READ | FD_CLOSE) == SOCKET_ERROR)
        {
            MessageBox(hwnd, "WSAAsyncSelect Failed!", NULL, MB_OK);
            CleanUp();
            return 0;
        }

        // Connect has succeeded!  Fix menus
        hmenu = GetMenu(hwnd);
        EnableMenuItem(hmenu, IDM_CONNECT, MF_GRAYED);
        EnableMenuItem(hmenu, IDM_LISTEN, MF_GRAYED);
        EnableMenuItem(hmenu, IDM_DISCONNECT, MF_ENABLED);
        return 0;
    }

    // Connection failed - reset window title
    SetWindowText(hwnd, szTitle);
    return 0;
}

//
//  FUNCTION: Connect(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE:  Processes messages for "Connect" dialog box.
//
//  PARAMETERS:
//    hdlg - window handle of the dialog box
//    wMessage - type of message
//    wparam - message-specific information
//    lparam - message-specific information
//
//  RETURN VALUE:
//    TRUE - message handled
//    FALSE - message not handled
//
//  COMMENTS:
//
//     Gets connection information from user and then establishes a connection.
//
//     Connect when user clicks on the OK button.  Kill Dialog when connection
//     established.
//

LRESULT CALLBACK Connect(HWND hdlg, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    return DispMessage(&msdiConnect, hdlg, uMessage, wparam, lparam);
}

//
//  FUNCTION: MsgConnectInit(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: To center dialog and limit size of edit controls
//
//  PARAMETERS:
//    hdlg - The window handing the message.
//    uMessage - The message number. (unused).
//    wparam - Message specific data (unused).
//    lparam - Message specific data (unused).
//
//  RETURN VALUE:
//    Always returns 0 - message handled.
//
//  COMMENTS:
//    Set size of edit controls for the following
//           Network  8  chars (4 2-digit hex numbers)
//           Node     12 chars (6 2-digit hex numbers)
//           Socket   4  chars (2 2-digit hex numbers)
//

LRESULT MsgConnectInit(HWND hdlg, UINT uMessage, WPARAM wparam, LPARAM lparam)
{

    // Create a font to use
    hfontDlg = CreateFont(14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                          VARIABLE_PITCH | FF_SWISS, "");

    // Center the dialog over the application window
    CenterWindow (hdlg, GetWindow (hdlg, GW_OWNER));

    // Limit size of edit controls
    SendDlgItemMessage(hdlg, CD_NETWORK, EM_LIMITTEXT, 8, 0);
    SendDlgItemMessage(hdlg, CD_NODE, EM_LIMITTEXT, 12, 0);
    SendDlgItemMessage(hdlg, CD_SOCKET, EM_LIMITTEXT, 4, 0);

    // Initialize Edit Controls
    SetDlgItemText(hdlg, CD_NETWORK, szConnectNetwork);
    SetDlgItemText(hdlg, CD_NODE, szConnectNode);
    SetDlgItemText(hdlg, CD_SOCKET, szConnectSocket);

    return (TRUE);
}

//
//  FUNCTION: MsgConnectCommand(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Process WM_COMMAND message sent to the Connect box.
//
//  PARAMETERS:
//    hwnd - The window handing the message.
//    uMessage - The message number. (unused).
//    wparam - Message specific data (unused).
//    lparam - Message specific data (unused).
//
//  RETURN VALUE:
//    Always returns 0 - message handled.
//
//  COMMENTS:
//    Uses this DispCommand function defined in wndproc.c combined
//    with the cmdiConnect structure defined in this file to handle
//    the command messages for the Connect dialog box.
//

LRESULT MsgConnectCommand(HWND   hwnd,
                        UINT   uMessage,
                        WPARAM wparam,
                        LPARAM lparam)
{
    return DispCommand(&cmdiConnect, hwnd, wparam, lparam);
}

//
//  FUNCTION: CmdConnectDone(HWND, WORD, HWND)
//
//  PURPOSE: Free the Connect box and related data.
//
//  PARAMETERS:
//    hdlg - The window handling the command.
//    wCommand - The command to be handled (unused).
//    wNotify   - Notification number (unused)
//    hwndCtrl - NULL (unused).
//
//  RETURN VALUE:
//    Always returns TRUE.
//
//  COMMENTS:
//    Cleans up sockets then calls EndDialog to finish the dialog session.
//

LRESULT CmdConnectDone(HWND hdlg, WORD wCommand, WORD wNotify, HWND hwndCtrl)
{

    CleanUp();               // Free any aborted socket resources
    EndDialog(hdlg, FALSE);  // Exit Dialog -- rtrn false since no connection
    return TRUE;
}

//
//  FUNCTION: CmdConnectNow(HWND, WORD, HWND)
//
//  PURPOSE: Establish the connection
//
//  PARAMETERS:
//    hdlg - The window handling the command.
//    wCommand - The command to be handled (unused).
//    wNotify   - Notification number (unused)
//    hwndCtrl - NULL (unused).
//
//  RETURN VALUE:
//    Always returns TRUE.
//
//  COMMENTS:
//    Makes Connection calls
//

LRESULT CmdConnectNow(HWND hdlg, WORD wCommand, WORD wNotify, HWND hwndCtrl)
{
    char outtext[128];
    WORD wVersionRequested;
    WSADATA wsaData;

    GetDlgItemText(hdlg, CD_NETWORK, szConnectNetwork, 9);        // Get Network Number
    AtoH(szConnectNetwork, (char *)&RemAddr.sa_netnum, 4);        // Network order

    GetDlgItemText(hdlg, CD_NODE, szConnectNode, 13);             // Get Node Number
    AtoH((char *)szConnectNode, (char *)&RemAddr.sa_nodenum, 6);  // Network order

    GetDlgItemText(hdlg, CD_SOCKET, szConnectSocket, 5);          // Get Socket Number
    AtoH((char *)szConnectSocket, (char *)&RemAddr.sa_socket, 2); // Network order

    RemAddr.sa_family = AF_IPX;                                   // IPX Family
    wVersionRequested = MAKEWORD(1, 1);

    SetDlgItemText(hdlg, CD_STATUS, "Calling WSAStartup");

    // Initialize winsock dll
    if(WSAStartup(wVersionRequested, &wsaData) == SOCKET_ERROR)
    {
        SetDlgItemText(hdlg, CD_STATUS, "WSAStartup failed");
        return TRUE;
    }

    SetDlgItemText(hdlg, CD_STATUS, "WSAStartup Succeeded");

    SetDlgItemText(hdlg, CD_STATUS, "Calling socket()");

    // get socket handle
    sock = socket(AF_IPX,            // IPX family
                  SOCK_SEQPACKET,    // Message mode data xfers
                  NSPROTO_SPX);      // SPX = Connection Oriented

    if(sock == INVALID_SOCKET) {
        SetDlgItemText(hdlg, CD_STATUS, "ERROR on socket()");
        WSACleanup();
        return TRUE;
    }

    SetDlgItemText(hdlg, CD_STATUS, "socket() Succeeded");

    SetDlgItemText(hdlg, CD_STATUS, "Calling connect()");

    // Call specified address (we block here since I don't have anything better
    // to do).
    if(connect(sock, (struct sockaddr *)&RemAddr, sizeof(RemAddr)) == SOCKET_ERROR)
    {
        // Failed!  Post status and cleanup
        sprintf(outtext, "connect() failed, error %u", WSAGetLastError());
        SetDlgItemText(hdlg, CD_STATUS, outtext);
        closesocket(sock);
        WSACleanup();
        return TRUE;
    }

    // We've got a connection!  Kill the dialog
    SetDlgItemText(hdlg, CD_STATUS, "connect() succeeded!");

    EndDialog(hdlg, TRUE);          // Exit the dialog
    DeleteObject (hfontDlg);
    return (TRUE);
}


DISPATCH.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:   dispatch.c
//
//  PURPOSE:  Implement the generic message and command dispatchers.
//
//
//  FUNCTIONS:
//    DispMessage - Call the function associated with a message.
//    DispCommand - Call the function associated with a command.
//    DispDefault - Call the appropriate default window procedure.
//
//  COMMENTS:
//

#include <windows.h>            // required for all Windows applications
#include <windowsx.h>
#include <wsipx.h>              // IPX Sockets defs
#include "globals.h"            // prototypes specific to this application

LRESULT DispDefault(EDWP, HWND, UINT, WPARAM, LPARAM);

//
//  FUNCTION: DispMessage(LPMSDI, HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Call the function associated with a message.
//
//  PARAMETERS:
//    lpmsdi - Structure containing the message dispatch information.
//    hwnd - The window handle
//    uMessage - The message number
//    wparam - Message specific data
//    lparam - Message specific data
//
//  RETURN VALUE:
//    The value returned by the message function that was called.
//
//  COMMENTS:
//    Runs the table of messages stored in lpmsdi->rgmsd searching
//    for a message number that matches uMessage.  If a match is found,
//    call the associated function.  Otherwise, call DispDefault to
//    call the default function, if any, associated with the message
//    structure.  In either case, return the value recieved from the
//    message or default function.
//

LRESULT DispMessage(LPMSDI lpmsdi,
                    HWND   hwnd,
                    UINT   uMessage,
                    WPARAM wparam,
                    LPARAM lparam)
{
    int  imsd = 0;

    MSD *rgmsd = lpmsdi->rgmsd;
    int  cmsd  = lpmsdi->cmsd;

    for (imsd = 0; imsd < cmsd; imsd++)
    {
        if (rgmsd[imsd].uMessage == uMessage)
            return rgmsd[imsd].pfnmsg(hwnd, uMessage, wparam, lparam);
    }

    return DispDefault(lpmsdi->edwp, hwnd, uMessage, wparam, lparam);
}

//
//  FUNCTION: DispCommand(LPCMDI, HWND, WPARAM, LPARAM)
//
//  PURPOSE: Call the function associated with a command.
//
//  PARAMETERS:
//    lpcmdi - Structure containing the command dispatch information.
//    hwnd - The window handle
//    GET_WM_COMMAND_ID(wparam, lparam) - Identifier of the menu item,
//      control, or accelerator.
//    GET_WM_COMMAND_CMD(wparam, lparam) - Notification code.
//    GET_WM_COMMAND_HWND(wparam, lparam) - The control handle or NULL.
//
//  RETURN VALUE:
//    The value returned by the command function that was called.
//
//  COMMENTS:
//    Runs the table of commands stored in lpcmdi->rgcmd searching
//    for a command number that matches wCommand.  If a match is found,
//    call the associated function.  Otherwise, call DispDefault to
//    call the default function, if any, associated with the command
//    structure.  In either case, return the value recieved from the
//    command or default function.
//

LRESULT DispCommand(LPCMDI lpcmdi,
                    HWND   hwnd,
                    WPARAM wparam,
                    LPARAM lparam)
{
    LRESULT lRet = 0;
    WORD    wCommand = GET_WM_COMMAND_ID(wparam, lparam);
    int     icmd;

    CMD    *rgcmd = lpcmdi->rgcmd;
    int     ccmd  = lpcmdi->ccmd;

    // Message packing of wparam and lparam have changed for Win32,
    // so use the GET_WM_COMMAND macro to unpack the commnad

    for (icmd = 0; icmd < ccmd; icmd++)
    {
        if (rgcmd[icmd].wCommand == wCommand)
        {
            return rgcmd[icmd].pfncmd(hwnd,
                                      wCommand,
                                      GET_WM_COMMAND_CMD(wparam, lparam),
                                      GET_WM_COMMAND_HWND(wparam, lparam));
        }
    }

    return DispDefault(lpcmdi->edwp, hwnd, WM_COMMAND, wparam, lparam);
}

//
//  FUNCTION: DispDefault(EDWP, HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Call the appropriate default window procedure.
//
//  PARAMETERS:
//    edwp - Enumerate specifying the appropriate default winow procedure.
//    hwnd - The window handle
//    uMessage - The message number
//    wparam - Message specific data
//    lparam - Message specific data
//
//  RETURN VALUE:
//    If there is a default proc, return the value returned by the
//    default proc.  Otherwise, return 0.
//
//  COMMENTS:
//    Calls the default procedure associated with edwp using the specified
//    parameters.
//

LRESULT DispDefault(EDWP   edwp,
                    HWND   hwnd,
                    UINT   uMessage,
                    WPARAM wparam,
                    LPARAM lparam)
{
    switch (edwp)
    {
        case edwpNone:
            return 0;
        case edwpWindow:
            return DefWindowProc(hwnd, uMessage, wparam, lparam);
        case edwpDialog:
            return DefDlgProc(hwnd, uMessage, wparam, lparam);
        case edwpMDIFrame:
            return DefFrameProc(hwnd, hwndMDIIPXChat, uMessage, wparam, lparam);
        case edwpMDIChild:
            return DefMDIChildProc(hwnd, uMessage, wparam, lparam);
    }
    return 0;
}


GLOBALS.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.
//
// PURPOSE:
//    Contains declarations for all globally scoped names in the program.
//

//-------------------------------------------------------------------------
// Product identifier string defines

// #define APPNAME       IPXChat
// #define SZVERSION     "Version 4.0"

//-------------------------------------------------------------------------
// Functions for handling main window messages.  The message-dispatching
// mechanism expects all message-handling functions to have the following
// prototype:
//
//     LRESULT FunctionName(HWND, UINT, WPARAM, LPARAM);

LRESULT MsgCreate(HWND, UINT, WPARAM, LPARAM);
LRESULT MsgSize(HWND, UINT, WPARAM, LPARAM);
LRESULT MsgSetfocus(HWND, UINT, WPARAM, LPARAM);
LRESULT MsgCommand(HWND, UINT, WPARAM, LPARAM);
LRESULT MsgDataready(HWND, UINT, WPARAM, LPARAM);
LRESULT MsgRefreshdisplay(HWND, UINT, WPARAM, LPARAM);
LRESULT MsgDisconnected(HWND, UINT, WPARAM, LPARAM);
LRESULT MsgDestroy(HWND, UINT, WPARAM, LPARAM);

//-------------------------------------------------------------------------
// Functions for handling main window commands--ie. functions for
// processing WM_COMMAND messages based on the wParam value.
// The message-dispatching mechanism expects all command-handling
// functions to have the following prototype:
//
//     LRESULT FunctionName(HWND, WORD, WORD, HWND);

LRESULT CmdConnect(HWND, WORD, WORD, HWND);
LRESULT CmdListen(HWND, WORD, WORD, HWND);
LRESULT CmdExit(HWND, WORD, WORD, HWND);
LRESULT CmdAbout(HWND, WORD, WORD, HWND);
LRESULT CmdOutbox(HWND, WORD, WORD, HWND);
LRESULT CmdDisconnect(HWND, WORD, WORD, HWND);

//-------------------------------------------------------------------------
// Global function prototypes.

BOOL InitApplication(HINSTANCE, int);
BOOL CenterWindow(HWND, HWND);
BOOL ReceiveInBox(HWND, WPARAM, LPARAM, char *, int);
void SendOutBox(char *, int);
void AtoH(char *, char *, int);
unsigned char BtoH(char);
void CleanUp(void);
void GetAddrString(PSOCKADDR_IPX, char *);
void HtoA(char *, char *, int);
char HtoB(UCHAR);
LPTSTR GetStringRes (int id);

// Callback functions.  These are called by Windows.

// **TODO**  Add new callback function prototypes here.

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

//-------------------------------------------------------------------------
// Command ID definitions.  These definitions are used to associate menu
// items with commands.

// Options menu
#define IDM_CONNECT    1000
#define IDM_LISTEN     1001
#define IDM_DISCONNECT 1002
#define IDM_EXIT       1003

// Help menu
#define IDM_ABOUT      1100

//-------------------------------------------------------------------------
// User Defined Messages.  These definitions are used for indicating
// network events.

#define MW_DATAREADY       501
#define MW_DISPLAYREFRESH  502
#define MW_DISCONNECTED    503
#define LDM_CONNECTED      504

//-------------------------------------------------------------------------
// String Table ID definitions.

#define IDS_APPNAME     1
#define IDS_DESCRIPTION 2

//-------------------------------------------------------------------------
//  Main Window Edit Control defines.

#define ID_OUTBOX          601
#define ID_INBOX           602

//-------------------------------------------------------------------------
//  About dialog defines.

#define IDD_VERFIRST    100
#define IDD_VERLAST     104

//-------------------------------------------------------------------------
//  Connect dialog defines.

#define CD_STATUS       200
#define CD_NETWORK      201
#define CD_NODE         202
#define CD_SOCKET       203

//-------------------------------------------------------------------------
//  Listen dialog defines.

#define LD_SOCKET       300
#define LD_STATUS       301

//-------------------------------------------------------------------------
// Global variable declarations.

extern HINSTANCE hInst;          // The current instance handle
extern char      szAppName[];    // The name of this application
extern char      szTitle[];      // The title bar text
SOCKET sock, SrvSock;
SOCKADDR_IPX RemAddr;
PSOCKADDR_IPX pSockAddr, pRemAddr;
struct sockaddr addr;
int addrlen;
HWND hOutWnd, hInWnd;
char szRcvBuf[0x8000];
char szSndBuf[0x8000];
extern char szConnectNetwork[];
extern char szConnectNode[];
extern char szConnectSocket[];
extern char szListenSocket[];
extern BOOL i_should_sleep;

// For NON-MDI applications, uncomment line 1 below and comment
// line 2.  For MDI applications, uncomment line 2 below, comment
// line 1, and then define hwndMDIIPXChat as a global variable in
// INIT.C
#define hwndMDIIPXChat NULL        /* (1) Stub for NON-MDI applications. */
// extern HWND hwndMDIIPXChat;     /* (2) For MDI applications.          */

//-------------------------------------------------------------------------
// Message and command dispatch infrastructure.  The following type
// definitions and functions are used by the message and command dispatching
// mechanism and do not need to be changed.

    // Function pointer prototype for message handling functions.
typedef LRESULT (*PFNMSG)(HWND,UINT,WPARAM,LPARAM);

    // Function pointer prototype for command handling functions.
typedef LRESULT (*PFNCMD)(HWND,WORD,WORD,HWND);

    // Enumerated type used to determine which default window procedure
    // should be called by the message- and command-dispatching mechanism
    // if a message or command is not handled explicitly.
typedef enum
{
   edwpNone,            // Do not call any default procedure.
   edwpWindow,          // Call DefWindowProc.
   edwpDialog,          // Call DefDlgProc (This should be used only for
                        // custom dialogs - standard dialog use edwpNone).
   edwpMDIChild,        // Call DefMDIChildProc.
   edwpMDIFrame         // Call DefFrameProc.
} EDWP;                // Enumeration for Default Window Procedures

    // This structure maps messages to message handling functions.
typedef struct _MSD
{
    UINT   uMessage;
    PFNMSG pfnmsg;
} MSD;                 // MeSsage Dispatch structure

    // This structure contains all of the information that a window
    // procedure passes to DispMessage in order to define the message
    // dispatching behavior for the window.
typedef struct _MSDI
{
    int  cmsd;          // Number of message dispatch structs in rgmsd
    MSD *rgmsd;         // Table of message dispatch structures
    EDWP edwp;          // Type of default window handler needed.
} MSDI, FAR *LPMSDI;   // MeSsage Dipatch Information

    // This structure maps command IDs to command handling functions.
typedef struct _CMD
{
    WORD   wCommand;
    PFNCMD pfncmd;
} CMD;                 // CoMmand Dispatch structure

    // This structure contains all of the information that a command
    // message procedure passes to DispCommand in order to define the
    // command dispatching behavior for the window.
typedef struct _CMDI
{
    int  ccmd;          // Number of command dispatch structs in rgcmd
    CMD *rgcmd;         // Table of command dispatch structures
    EDWP edwp;          // Type of default window handler needed.
} CMDI, FAR *LPCMDI;   // CoMmand Dispatch Information

    // Message and command dispatching functions.  They look up messages
    // and commands in the dispatch tables and call the appropriate handler
    // function.
LRESULT DispMessage(LPMSDI, HWND, UINT, WPARAM, LPARAM);
LRESULT DispCommand(LPCMDI, HWND, WPARAM, LPARAM);

    // Message dispatch information for the main window
extern MSDI msdiMain;
    // Command dispatch information for the main window
extern CMDI cmdiMain;

//-------------------------------------------------------------------------
// Version string definitions--Leave these alone.

#define SZRCOMPANYNAME "CompanyName"
#define SZRDESCRIPTION "FileDescription"
#define SZRVERSION     "FileVersion"
#define SZRAPPNAME     "InternalName"
#define SZRCOPYRIGHT   "LegalCopyright"
#define SZRTRADEMARK   "LegalTrademarks"
#define SZRPRODNAME    "ProductName"
#define SZRPRODVER     "ProuctVersion"


INIT.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:   init.c
//
//  PURPOSE:   Performs application and instance specific initialization.
//
//  FUNCTIONS:
//    InitApplication() - Initializes window data and registers window.
//
//  COMMENTS:
//

#include <windows.h>            // required for all Windows applications
#include <wsipx.h>              // IPX Sockets defs
#include "globals.h"            // prototypes specific to this application

HINSTANCE hInst;                // current instance

char szAppName[9];              // The name of this application
char szTitle[40];               // The title bar text

char szConnectNetwork[] = "00000000";
char szConnectNode[] = "000000000000";
char szConnectSocket[] = "2FFF";
char szListenSocket[] = "2FFF";

BOOL i_should_sleep = TRUE;

//
//  FUNCTION: InitApplication(HINSTANCE, int)
//
//  PURPOSE: Initializes window data and registers window class.
//
//  PARAMETERS:
//    hInstance - The handle to the instance of this application that
//                is currently being executed.
//    nCmdShow  - Specifies how the main window is to be displayed.
//
//  RETURN VALUE:
//    TRUE  - Success
//    FALSE - Initialization failed
//
//  COMMENTS:
//
//    This function is called at application initialization time.  It
//    performs initialization tasks for the current application instance.
//    Unlike Win16, in Win32, each instance of an application must register
//    window classes.
//
//    In this function, we initialize a window class by filling out a data
//    structure of type WNDCLASS and calling the Windows RegisterClass()
//    function.  Then we create the main window and show it.
//
//

BOOL InitApplication(HINSTANCE hInstance, int nCmdShow)
{
    WNDCLASS  wc;
    HWND      hwnd; // Main window handle.

    // Load the application name and description strings.

    LoadString(hInstance, IDS_APPNAME, szAppName, sizeof(szAppName));
    LoadString(hInstance, IDS_DESCRIPTION, szTitle, sizeof(szTitle));

    // Save the instance handle in static variable, which will be used in
    // many subsequence calls from this application to Windows.

    hInst = hInstance; // Store instance handle in our global variable

    // Fill in window class structure with parameters that describe the
    // main window.

    wc.style         = CS_HREDRAW | CS_VREDRAW; // Class style(s).
    wc.lpfnWndProc   = (WNDPROC)WndProc;        // Window Procedure
    wc.cbClsExtra    = 0;                       // No per-class extra data.
    wc.cbWndExtra    = 0;                       // No per-window extra data.
    wc.hInstance     = hInstance;               // Owner of this class
    wc.hIcon         = LoadIcon (hInstance, szAppName); // Icon name from .RC
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW); // Cursor
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); // Default color
    wc.lpszMenuName  = szAppName;               // Menu name from .RC
    wc.lpszClassName = szAppName;               // Name to register as

    // Register the window class and return FALSE if unsuccesful.

    if (!RegisterClass(&wc))
    {
        return FALSE;
    }

    // Create a main window for this application instance.
    hwnd = CreateWindow(szAppName,           // See RegisterClass() call
                        szTitle,             // Text for window title bar
                        WS_OVERLAPPEDWINDOW, // Window style
                        CW_USEDEFAULT, 0,    // Use default positioning
                        CW_USEDEFAULT, 0,    // Use default size
                        NULL,                // Overlapped has no parent
                        NULL,                // Use the window class menu
                        hInstance,           // This instance owns this window
                        NULL                 // Don't need data in WM_CREATE
    );

    // If window could not be created, return "failure"
    if (!hwnd)
        return FALSE;

    // Make the window visible; update its client area; and return "success"
    ShowWindow(hwnd, nCmdShow);  // Show the window
    UpdateWindow(hwnd);          // Sends WM_PAINT message

    return TRUE;                 // We succeeded...
}


IPXCHAT.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:   ipxchat.c
//
//  PURPOSE:   Implement the windows procedure for the main application
//    windows.  Also implement the generic message and command dispatchers.
//
//  FUNCTIONS:
//    WndProc           - Processes messages for the main window.
//    MsgCreate         - Initializes Edit Controls for text input/output.
//    MsgSize           - Adjusts size of Edit Controls when window is resized.
//    MsgSetfocus       - Keeps window focus on edit control instead of parent.
//    MsgDataready      - Reads data from incoming IPC mechanism.
//    MsgRefreshdisplay - Refills Inbox edit control text contents
//    MsgDisconnected   - Cleans up connection killed by other side
//    MsgCommand        - Handle the WM_COMMAND messages for the main window.
//    MsgDestroy        - Handles the WM_DESTROY message by calling
//                        PostQuitMessage().
//    CmdOutbox         - Handles messages from Outbox edit control.
//    CmdDisconnected   - Disconnects current connection
//    CmdExit           - Handles the file exit command by calling destory
//                        window on the main window.
//
//  COMMENTS:
//

#include <windows.h>            // required for all Windows applications
#include <windowsx.h>
#include <wsipx.h>
#include "globals.h"            // prototypes specific to this application

// Main window message table definition.
MSD rgmsd[] =
{
    {WM_CREATE,         MsgCreate},
    {WM_SIZE,           MsgSize},
    {WM_SETFOCUS,       MsgSetfocus},
    {WM_COMMAND,        MsgCommand},
    {WM_DESTROY,        MsgDestroy},
    {MW_DATAREADY,      MsgDataready},
    {MW_DISCONNECTED,   MsgDisconnected},
    {MW_DISPLAYREFRESH, MsgRefreshdisplay}
};

MSDI msdiMain =
{
    sizeof(rgmsd) / sizeof(MSD),
    rgmsd,
    edwpWindow
};

// Main window command table definition.
CMD rgcmd[] =
{
    {IDM_CONNECT,    CmdConnect},
    {IDM_LISTEN,     CmdListen},
    {IDM_EXIT,       CmdExit},
    {IDM_ABOUT,      CmdAbout},
    {ID_OUTBOX,      CmdOutbox},
    {IDM_DISCONNECT, CmdDisconnect}
};

CMDI cmdiMain =
{
    sizeof(rgcmd) / sizeof(CMD),
    rgcmd,
    edwpWindow
};

//
//  FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE:  Processes messages for the main window.
//
//  PARAMETERS:
//    hwnd     - window handle
//    uMessage - message number
//    wparam   - additional information (dependant on message number)
//    lparam   - additional information (dependant on message number)
//
//  RETURN VALUE:
//    The return value depends on the message number.  If the message
//    is implemented in the message dispatch table, the return value is
//    the value returned by the message handling function.  Otherwise,
//    the return value is the value returned by the default window procedure.
//
//  COMMENTS:
//    Call the DispMessage() function with the main window's message dispatch
//    information (msdiMain) and the message specific information.
//

LRESULT CALLBACK WndProc(HWND   hwnd,
                         UINT   uMessage,
                         WPARAM wparam,
                         LPARAM lparam)
{
    return DispMessage(&msdiMain, hwnd, uMessage, wparam, lparam);
}

//
//  FUNCTION: MsgCreate(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Creates Inbox and Outbox Edit controls
//
//  PARAMETERS:
//
//    hwnd      - Window handle
//    uMessage  - Message number (Unused)
//    wparam    - Extra data     (Unused)
//    lparam    - Extra data     (Unused)
//
//  RETURN VALUE:
//
//    Always returns 0 - Message handled
//
//  COMMENTS:
//
//

LRESULT MsgCreate(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    // Create Edit control for typing to be sent to server
    hOutWnd = CreateWindow("EDIT",
                           NULL,
                           WS_BORDER | WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_LEFT |
                           ES_MULTILINE | ES_AUTOVSCROLL,
                           0,0,0,0,
                           hwnd,
                           (HMENU) ID_OUTBOX,
                           (HINSTANCE) GetWindowLong(hwnd, GWL_HINSTANCE),
                           0);
    // Create Edit control for typing to be received from server
    hInWnd = CreateWindow("EDIT",
                          NULL,
                          WS_BORDER | WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_LEFT |
                          ES_MULTILINE | ES_AUTOVSCROLL,
                          0,0,0,0,
                          hwnd,
                          (HMENU) ID_INBOX,
                          (HINSTANCE) GetWindowLong(hwnd, GWL_HINSTANCE),
                          0);

    return (TRUE);
}

//
//  FUNCTION: MsgSize(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Adjust Size of Inbox and Outbox Edit controls
//
//  PARAMETERS:
//
//    hwnd      - Window handle
//    uMessage  - Message number (Unused)
//    wparam    - Extra data     (Unused)
//    lparam    - Parent Window Size
//
//  RETURN VALUE:
//
//    Always returns 0 - Message handled
//
//  COMMENTS:
//
//

LRESULT MsgSize(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    // Size OutBox Edit Control
    MoveWindow(hOutWnd,
               1,1,                    // Upper Left Corner
               LOWORD(lparam) - 2,         // Width of Parent Window
               (HIWORD(lparam) / 2) - 2 ,     // Half the height of Parent
               TRUE);                  // repaint

    // Size Inbox Edit Control
    MoveWindow(hInWnd,
               1, (HIWORD(lparam) / 2) + 1,  // Half Way down right side
               LOWORD(lparam) - 2,         // Width of Parent Window
               (HIWORD(lparam) / 2) - 2,      // Half the height of Parent
               TRUE);                  // repaint
    return (TRUE);
}

//
//  FUNCTION: MsgSetfocus(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Keeps Window focus on edit control
//
//  PARAMETERS:
//
//    hwnd      - Window handle
//    uMessage  - Message number (Unused)
//    wparam    - Extra data     (Unused)
//    lparam    - Extra data     (Unused)
//
//  RETURN VALUE:
//
//    Always returns 0 - Message handled
//
//  COMMENTS:
//
//

LRESULT MsgSetfocus(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    SetFocus(hOutWnd);  // Don't let main window have focus
    return (TRUE);
}

//
//  FUNCTION: MsgDataready(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Read data and post message to display data
//
//  PARAMETERS:
//
//    hwnd      - Window handle
//    uMessage  - Message number (Unused)
//    wparam    - forwarded to ReceiveInBox
//    lparam    - forwarded to ReceiveInBox
//
//  RETURN VALUE:
//
//    Always returns 0 - Message handled
//
//  COMMENTS:
//
//

LRESULT MsgDataready(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    // Call protocol specific program to Read data and put in szRcvBuf
    if(ReceiveInBox(hwnd, wparam, lparam, szRcvBuf, sizeof(szRcvBuf)))
    {
        PostMessage(hwnd, MW_DISPLAYREFRESH, 0, 0);
    }
    return(TRUE);
}
//
//  FUNCTION: MsgRefreshdisplay(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Display socket data in inbox
//
//  PARAMETERS:
//
//    hwnd      - Window handle
//    uMessage  - Message number (Unused)
//    wparam    - Extra Data     (Unused)
//    lparam    - Extra Data     (Unused)
//
//  RETURN VALUE:
//
//    Always returns 0 - Message handled
//
//  COMMENTS:
//
//

LRESULT MsgRefreshdisplay(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    MSG peekmsg;

    // Don't bother displaying if there is already another update queued
    if(PeekMessage(&peekmsg, hwnd, MW_DATAREADY, MW_DISPLAYREFRESH, PM_NOREMOVE))
    {
       return(TRUE);
    }
    // Put data in szRcvBuf in InBox
    SendDlgItemMessage(hwnd,
                       ID_INBOX,
                       EM_SETSEL,
                       0,-1);
    SendDlgItemMessage(hwnd,
                       ID_INBOX,
                       EM_REPLACESEL,
                       0,
                       (LPARAM)szRcvBuf);
    return(TRUE);
}

//
//  FUNCTION: MsgDisconnected(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Cleanup connection dropped from other side
//
//  PARAMETERS:
//
//    hwnd      - Window handle
//    uMessage  - Message number (Unused)
//    wparam    - Extra data     (Unused)
//    lparam    - Extra data     (Unused)
//
//  RETURN VALUE:
//
//    Always returns 0 - Message handled
//
//  COMMENTS:
//
//

LRESULT MsgDisconnected(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    HMENU hmenu;

    // Let the user know
    MessageBox(hwnd,
               "Connection Disconnected",
               "Chat Dialog Stopped",
               MB_OK);
    // Protocol Specific Cleanup
    CleanUp();
    // Fix menus
    hmenu = GetMenu(hwnd);
    EnableMenuItem(hmenu, IDM_CONNECT, MF_ENABLED);
    EnableMenuItem(hmenu, IDM_LISTEN, MF_ENABLED);
    EnableMenuItem(hmenu, IDM_DISCONNECT, MF_GRAYED);
    SetWindowText(hwnd, szTitle);
    return(TRUE);
}

//
//  FUNCTION: MsgCommand(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Handle the WM_COMMAND messages for the main window.
//
//  PARAMETERS:
//    hwnd     - window handle
//    uMessage - WM_COMMAND (Unused)
//    GET_WM_COMMAND_ID(wparam, lparam)   - Command identifier
//    GET_WM_COMMAND_HWND(wparam, lparam) - Control handle
//
//  RETURN VALUE:
//    The return value depends on the message number.  If the message
//    is implemented in the message dispatch table, the return value is
//    the value returned by the message handling function.  Otherwise,
//    the return value is the value returned by the default window procedure.
//
//  COMMENTS:
//    Call the DispCommand() function with the main window's command dispatch
//    information (cmdiMain) and the command specific information.
//

LRESULT MsgCommand(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    return DispCommand(&cmdiMain, hwnd, wparam, lparam);
}

//
//  FUNCTION: MsgDestroy(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Calls PostQuitMessage().
//
//  PARAMETERS:
//
//    hwnd      - Window handle  (Unused)
//    uMessage  - Message number (Unused)
//    wparam    - Extra data     (Unused)
//    lparam    - Extra data     (Unused)
//
//  RETURN VALUE:
//
//    Always returns 0 - Message handled
//
//  COMMENTS:
//
//

LRESULT MsgDestroy(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    PostQuitMessage(0);
    return 0;
}

//
//  FUNCTION: CmdOutbox(HWND, WORD, WORD, HWND)
//
//  PURPOSE: Handle messages from Outbox--Send data if EN_CHANGE
//
//  PARAMETERS:
//    hwnd     - The window.
//    wCommand - ID_OUTBOX (unused)
//    wNotify  - Notification number (unused)
//    hwndCtrl - NULL (unused)
//
//  RETURN VALUE:
//    Always returns 0 - command handled.
//
//  COMMENTS:
//
//

LRESULT CmdOutbox(HWND hwnd, WORD wCommand, WORD wNotify, HWND hwndCtrl)
{
    int cSendLen;

    if (wNotify != EN_CHANGE)  // We only care if text has changed
    {
        return 0;
    }
    // Text has changed!  Put OutBox's text in szSndBuf
    cSendLen = GetDlgItemText(hwnd,
                              ID_OUTBOX,
                              szSndBuf,
                              sizeof(szSndBuf));

    // Do protocol specific send
    SendOutBox(szSndBuf, cSendLen);
    return 0;
}

//
//  FUNCTION: CmdDisconnect(HWND, WORD, WORD, HWND)
//
//  PURPOSE: Cut off session and fix menus appropriately
//
//  PARAMETERS:
//    hwnd     - The window.
//    wCommand - ID_OUTBOX (unused)
//    wNotify  - Notification number (unused)
//    hwndCtrl - NULL (unused)
//
//  RETURN VALUE:
//    Always returns 0 - command handled.
//
//  COMMENTS:
//
//

LRESULT CmdDisconnect(HWND hwnd, WORD wCommand, WORD wNotify, HWND hwndCtrl)
{
    HMENU hmenu;

    // Protocol Specific cleanup
    CleanUp();

    // Fix menus
    hmenu = GetMenu(hwnd);
    EnableMenuItem(hmenu, IDM_CONNECT, MF_ENABLED);
    EnableMenuItem(hmenu, IDM_LISTEN, MF_ENABLED);
    EnableMenuItem(hmenu, IDM_DISCONNECT, MF_GRAYED);
    SetWindowText(hwnd, szTitle);
    return 0;
}

//
//  FUNCTION: CmdExit(HWND, WORD, WORD, HWND)
//
//  PURPOSE: Exit the application.
//
//  PARAMETERS:
//    hwnd     - The window.
//    wCommand - IDM_EXIT            (unused)
//    wNotify  - Notification number (unused)
//    hwndCtrl - NULL                (unused)
//
//  RETURN VALUE:
//    Always returns 0 - command handled.
//
//  COMMENTS:
//
//

LRESULT CmdExit(HWND hwnd, WORD wCommand, WORD wNotify, HWND hwndCtrl)
{
    DestroyWindow(hwnd);
    return 0;
}


LISTEN.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:   listen.c
//
//  PURPOSE:   Displays the "Listen" dialog box
//
//  FUNCTIONS:
//    CmdListen          - Displays the "Listen" dialog box.
//    Listen             - Processes messages for "Listen" dialog box.
//    MsgListenInit      - Centers dialog and initializes edit controls.
//    MsgListenConnected - Handles Connected message when socket is connected.
//    MsgListenCommand   - Process WM_COMMAND message sent to the listen box.
//    CmdListenDone      - Free the listen box and related data.
//    CmdListenNow       - Sets up listen on specified socket
//
//  COMMENTS:
//
//

#include <windows.h>            // required for all Windows applications
#include <windowsx.h>
#include <wsipx.h>
#include <stdlib.h>
#include <stdio.h>
#include "globals.h"            // prototypes specific to this application

LRESULT CALLBACK Listen(HWND, UINT, WPARAM, LPARAM);
LRESULT MsgListenInit(HWND, UINT, WPARAM, LPARAM);
LRESULT MsgListenCommand(HWND, UINT, WPARAM, LPARAM);
LRESULT MsgListenConnected(HWND, UINT, WPARAM, LPARAM);
LRESULT CmdListenNow(HWND, WORD, WORD, HWND);
LRESULT CmdListenDone(HWND, WORD, WORD, HWND);

// Listen dialog message table definition.
MSD rgmsdListen[] =
{
    {WM_COMMAND,    MsgListenCommand},
    {WM_INITDIALOG, MsgListenInit},
    {LDM_CONNECTED, MsgListenConnected}
};

MSDI msdiListen =
{
    sizeof(rgmsdListen) / sizeof(MSD),
    rgmsdListen,
    edwpNone
};

// Listen dialog command table definition.
CMD rgcmdListen[] =
{
    {IDOK,     CmdListenNow},
    {IDCANCEL, CmdListenDone}
};

CMDI cmdiListen =
{
    sizeof(rgcmdListen) / sizeof(CMD),
    rgcmdListen,
    edwpNone
};

// Module specific "globals"  Used when a variable needs to be
// accessed in more than one handler function.

HFONT hfontDlg;

//
//  FUNCTION: CmdListen(HWND, WORD, WORD, HWND)
//
//  PURPOSE: Displays the "Listen" dialog box
//
//  PARAMETERS:
//    hwnd      - Window handle
//    wCommand  - IDM_LISTEN          (unused)
//    wNotify   - Notification number (unused)
//    hwndCtrl  - NULL                (unused)
//
//  RETURN VALUE:
//
//    Always returns 0 - Message handled
//
//  COMMENTS:
//    To process the IDM_LISTEN message, call DialogBox() to display the
//    Listen dialog box.

LRESULT CmdListen(HWND hwnd, WORD wCommand, WORD wNotify, HWND hwndCtrl)
{
    HMENU hmenu;

    SetWindowText(hwnd, "IPX Chat Server");     // Change title bar text

    // Start Dialog
    if(DialogBox(hInst, "ListenBox", hwnd, (DLGPROC)Listen))
    {

        // Dialog got a connection!  Set Message to indicate
        // when we have data to read, or if the connection is
        // closed on us.
        if (WSAAsyncSelect(sock,
                           hwnd,
                           MW_DATAREADY,
                           FD_READ | FD_CLOSE) == SOCKET_ERROR)
        {
            MessageBox(hwnd, "WSAAsyncSelect Failed!", NULL, MB_OK);
            CleanUp();
            return 0;
        }

        // Fix menus
        hmenu = GetMenu(hwnd);
        EnableMenuItem(hmenu, IDM_CONNECT, MF_GRAYED);
        EnableMenuItem(hmenu, IDM_LISTEN, MF_GRAYED);
        EnableMenuItem(hmenu, IDM_DISCONNECT, MF_ENABLED);
        return 0;
    }

    // Listen Failed
    SetWindowText(hwnd, szTitle);
    return 0;
}

//
//  FUNCTION: Listen(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE:  Processes messages for "Listen" dialog box.
//
//  PARAMETERS:
//    hdlg - window handle of the dialog box
//    wMessage - type of message
//    wparam - message-specific information
//    lparam - message-specific information
//
//  RETURN VALUE:
//    TRUE - message handled
//    FALSE - message not handled
//
//  COMMENTS:
//
//     Gets port information from user and then listens
//
//     Listen when user clicks on the OK button.  Kill Dialog when connection
//     established.
//

LRESULT CALLBACK Listen(HWND hdlg, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    return DispMessage(&msdiListen, hdlg, uMessage, wparam, lparam);
}

//
//  FUNCTION: MsgListenInit(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: To center dialog and limit size of edit controls and initialize
//
//  PARAMETERS:
//    hdlg - The window handing the message.
//    uMessage - The message number. (unused).
//    wparam - Message specific data (unused).
//    lparam - Message specific data (unused).
//
//  RETURN VALUE:
//    Always returns 0 - message handled.
//
//  COMMENTS:
//    Set size of edit controls for the following
//           Socket   4  chars (2 2-digit hex numbers)
//

LRESULT MsgListenInit(HWND hdlg, UINT uMessage, WPARAM wparam, LPARAM lparam)
{

    // Create a font to use
    hfontDlg = CreateFont(14, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0,
                          VARIABLE_PITCH | FF_SWISS, "");

    // Center the dialog over the application window
    CenterWindow (hdlg, GetWindow (hdlg, GW_OWNER));

    // Initialize Socket Addresses
    SetDlgItemText(hdlg, LD_SOCKET, szListenSocket);

    // Limit input to proper size strings
    SendDlgItemMessage(hdlg, LD_SOCKET, EM_LIMITTEXT, 4, 0);

    return (TRUE);
}

//
//  FUNCTION: MsgListenConnected(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: To handle connected message when socket is connected
//
//  PARAMETERS:
//    hdlg - The window handing the message.
//    uMessage - The message number. (unused).
//    wparam - Message specific data (unused).
//    lparam - Message specific data (unused).
//
//  RETURN VALUE:
//    Always returns 0 - message handled.
//
//  COMMENTS:
//    Performs accept() on incoming socket
//

LRESULT MsgListenConnected(HWND hdlg, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    char outtext[128];

    SetDlgItemText(hdlg,
                   LD_STATUS,
                   "Client Connected!");

    SetDlgItemText(hdlg,
                   LD_STATUS,
                   "Calling accept()");

    pRemAddr = (PSOCKADDR_IPX)&addr;
    addrlen = sizeof(addr);

    // Accept connection
    if ((sock = accept(SrvSock, (struct sockaddr *)pRemAddr, &addrlen)) == INVALID_SOCKET)
    {
        // accept() failed -- show status and clean up dialog
        sprintf(outtext, "Accept() failed, error %u",WSAGetLastError());
        SetDlgItemText(hdlg,
                       LD_STATUS,
                       outtext);
        closesocket(SrvSock);
        WSACleanup();
        EnableWindow(GetDlgItem(hdlg, IDOK), TRUE);
        EnableWindow(GetDlgItem(hdlg, LD_SOCKET), TRUE);
        SetFocus(GetDlgItem(hdlg, LD_SOCKET));
        return(TRUE);
    }

    // We're connected!
    GetAddrString(pRemAddr, outtext);
    lstrcat(outtext, " connected!");

    SetDlgItemText(hdlg,
                   LD_STATUS,
                   outtext);

    EndDialog(hdlg, TRUE);          // Exit the dialog
    DeleteObject (hfontDlg);        // Drop font
    return (TRUE);
}

//
//  FUNCTION: MsgListenCommand(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Process WM_COMMAND message sent to the Listen box.
//
//  PARAMETERS:
//    hwnd - The window handing the message.
//    uMessage - The message number. (unused).
//    wparam - Message specific data (unused).
//    lparam - Message specific data (unused).
//
//  RETURN VALUE:
//    Always returns 0 - message handled.
//
//  COMMENTS:
//    Uses this DispCommand function defined in wndproc.c combined
//    with the cmdiListen structure defined in this file to handle
//    the command messages for the Listen dialog box.
//

LRESULT MsgListenCommand(HWND   hwnd,
                        UINT   uMessage,
                        WPARAM wparam,
                        LPARAM lparam)
{
    return DispCommand(&cmdiListen, hwnd, wparam, lparam);
}

//
//  FUNCTION: CmdListenDone(HWND, WORD, HWND)
//
//  PURPOSE: Free the Listen box and related data.
//
//  PARAMETERS:
//    hdlg - The window handling the command.
//    wCommand - The command to be handled (unused).
//    wNotify   - Notification number      (unused)
//    hwndCtrl - NULL                      (unused).
//
//  RETURN VALUE:
//    Always returns TRUE.
//
//  COMMENTS:
//    Cleans up sockets then calls EndDialog to finish the dialog session.
//

LRESULT CmdListenDone(HWND hdlg, WORD wCommand, WORD wNotify, HWND hwndCtrl)
{

    closesocket(SrvSock);    // Free any aborted socket resources
    WSACleanup();
    DeleteObject (hfontDlg); // Drop the font
    EndDialog(hdlg, FALSE);  // Exit Dialog -- rtrn false since no connection
    return TRUE;
}

//
//  FUNCTION: CmdListenNow(HWND, WORD, WORD, HWND)
//
//  PURPOSE: Handles ID_OK message and listens on the specified socket
//
//  PARAMETERS:
//    hwnd - The window handling the command.
//    wCommand - The command to be handled (unused).
//    wNotify   - Notification number (unused)
//    hwndCtrl - NULL (unused).
//
//  RETURN VALUE:
//    Always returns TRUE.
//
//  COMMENTS:
//    Shows Listening address on status bar when listen is successful.
//

LRESULT CmdListenNow(HWND hdlg, WORD wCommand, WORD wNotify, HWND hwndCtrl)
{
    char szXferBuffer[5];
    WORD wVersionRequested;
    WSADATA wsaData;
    char outtext[80];

    // Get Socket Address
    GetDlgItemText(hdlg,
         LD_SOCKET,
         szXferBuffer,
         13);
    wVersionRequested = MAKEWORD(1, 1);

    SetDlgItemText(hdlg,
                  LD_STATUS,
                  "Calling WSAStartup");

    // Initializes winsock dll
    if(WSAStartup(wVersionRequested, &wsaData) == SOCKET_ERROR)
    {

        SetDlgItemText(hdlg,
                       LD_STATUS,
                       "WSAStartup failed");
        return (TRUE);
    }

    SetDlgItemText(hdlg,
          LD_STATUS,
          "WSAStartup Succeeded");

    SetDlgItemText(hdlg,
          LD_STATUS,
          "Calling socket()");

    // Allocate socket handle
    SrvSock = socket(AF_IPX,         // IPX Family
                     SOCK_SEQPACKET, // Gives message mode transfers
                     NSPROTO_SPX);   // SPX is connection oriented transport

    if(SrvSock == INVALID_SOCKET) {
        SetDlgItemText(hdlg,
                       LD_STATUS,
                       "ERROR on socket()");
        WSACleanup();
        return(TRUE);
    }

    SetDlgItemText(hdlg,
                   LD_STATUS,
                   "socket() Succeeded");

    // Set up socket address to bind to
    memset(&addr, 0, sizeof(addr));    // Clear address
    pSockAddr = (PSOCKADDR_IPX)&addr;  // Set pointer
    pSockAddr->sa_family = AF_IPX;     // IPX Family
    // Make sure socket number is in network order
    AtoH(szXferBuffer, (char *)&pSockAddr->sa_socket, 2);

    SetDlgItemText(hdlg,
                   LD_STATUS,
                   "Calling bind()");

    // Bind to socket address
    if(bind(SrvSock,
            (PSOCKADDR) pSockAddr,
            sizeof(SOCKADDR_IPX)) == SOCKET_ERROR)
    {
        SetDlgItemText(hdlg,
                       LD_STATUS,
                       "Error on bind()");
        closesocket(SrvSock);
        WSACleanup();
        return(TRUE);
    }

    SetDlgItemText(hdlg,
                   LD_STATUS,
                   "bind() Succeeded");

    SetDlgItemText(hdlg,
                   LD_STATUS,
                   "Calling listen()");

    // Set up listen queue - this app only supports one connection so
    // queue length is set to 1.
    if (listen(SrvSock, 1) == SOCKET_ERROR)
    {
        sprintf(outtext, "FAILURE: listen() returned %u", WSAGetLastError());
        SetDlgItemText(hdlg,
                       LD_STATUS,
                       outtext);
        closesocket(SrvSock);
        WSACleanup();
        return(TRUE);
    }

    SetDlgItemText(hdlg,
                   LD_STATUS,
                   "listen() succeeded");

    SetDlgItemText(hdlg,
                   LD_STATUS,
                   "Calling WSAAsyncSelect()");

    // Specify message to be posted when client connects
    if(WSAAsyncSelect(SrvSock,
                      hdlg,
                      LDM_CONNECTED,
                      FD_ACCEPT) == SOCKET_ERROR)
    {
        SetDlgItemText(hdlg,
                       LD_STATUS,
                       "WSAAsyncSelect() failed");
        closesocket(SrvSock);
        WSACleanup();
        return(TRUE);
    }

    SetDlgItemText(hdlg,
                   LD_STATUS,
                   "WSAAsyncSelect() succeeded");

    addrlen = sizeof(addr);

    // Get full network.number.socket address
    if(getsockname(SrvSock,&addr,&addrlen) == SOCKET_ERROR)
    {
        lstrcpy(outtext, "ERROR getsocketname()");
    }
    else
    {
        lstrcpy(outtext, "Listening on ");
        GetAddrString((PSOCKADDR_IPX)&addr, outtext + lstrlen(outtext));
    }

    SetDlgItemText(hdlg,
                   LD_STATUS,
                   outtext);

    SetFocus(GetDlgItem(hdlg, IDCANCEL));             // Give Cancel Button focus
    EnableWindow(GetDlgItem(hdlg, IDOK), FALSE);      // Grey OK button
    EnableWindow(GetDlgItem(hdlg, LD_SOCKET), FALSE); // Grey Socket Edit control
    return (TRUE);
}


MISC.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:   misc.c
//
//  PURPOSE:  Contains all helper functions "global" to the application.
//
//  FUNCTIONS:
//    CenterWindow - Center one window over another.
//    ReceiveInBox - Reads incoming socket data.
//    SendOutBox   - Writes outgoing socket data.
//    AtoH         - Converts ascii string to network order hex
//    BtoH         - Converts ascii byte to hex
//    CleanUp      - closes sockets and detaches winsock dll
//    GetAddrString - Puts IPX address into <network>.<node>.<socket> string format
//    HtoA          - Converts network order hex to ascii string
//    HtoB          - Converts hex byte to asci string
//
//  COMMENTS:
//
//

#include <windows.h>            // required for all Windows applications
#include <windowsx.h>
#include <wsipx.h>
#include "globals.h"            // prototypes specific to this application

//
//  FUNCTION: CenterWindow(HWND, HWND)
//
//  PURPOSE:  Center one window over another.
//
//  PARAMETERS:
//    hwndChild - The handle of the window to be centered.
//    hwndParent- The handle of the window to center on.
//
//  RETURN VALUE:
//
//    TRUE  - Success
//    FALSE - Failure
//
//  COMMENTS:
//
//    Dialog boxes take on the screen position that they were designed
//    at, which is not always appropriate. Centering the dialog over a
//    particular window usually results in a better position.
//

BOOL CenterWindow(HWND hwndChild, HWND hwndParent)
{
    RECT    rcChild, rcParent;
    int     cxChild, cyChild, cxParent, cyParent;
    int     cxScreen, cyScreen, xNew, yNew;
    HDC     hdc;

    // Get the Height and Width of the child window
    GetWindowRect(hwndChild, &rcChild);
    cxChild = rcChild.right - rcChild.left;
    cyChild = rcChild.bottom - rcChild.top;

    // Get the Height and Width of the parent window
    GetWindowRect(hwndParent, &rcParent);
    cxParent = rcParent.right - rcParent.left;
    cyParent = rcParent.bottom - rcParent.top;

    // Get the display limits
    hdc = GetDC(hwndChild);
    cxScreen = GetDeviceCaps(hdc, HORZRES);
    cyScreen = GetDeviceCaps(hdc, VERTRES);
    ReleaseDC(hwndChild, hdc);

    // Calculate new X position, then adjust for screen
    xNew = rcParent.left + ((cxParent - cxChild) / 2);
    if (xNew < 0)
    {
        xNew = 0;
    }
    else if ((xNew + cxChild) > cxScreen)
    {
        xNew = cxScreen - cxChild;
    }

    // Calculate new Y position, then adjust for screen
    yNew = rcParent.top  + ((cyParent - cyChild) / 2);
    if (yNew < 0)
    {
        yNew = 0;
    }
    else if ((yNew + cyChild) > cyScreen)
    {
        yNew = cyScreen - cyChild;
    }

    // Set it, and return
    return SetWindowPos(hwndChild,
                        NULL,
                        xNew, yNew,
                        0, 0,
                        SWP_NOSIZE | SWP_NOZORDER);
}

//
//  FUNCTION: ReceiveInBox(HWND, WPARAM, LPARAM, char *, int)
//
//  PURPOSE:  Reads incoming data from socket
//
//  PARAMETERS:
//    hWnd      - Handle to current window
//    uParam    - WPARAM (unused)
//    lParam    - LPARAM contains event (FD_READ or FD_CLOSE).
//    szRBuf    - Receive Buffer
//    cRBufLen  - size of Receive Buffer
//
//  RETURN VALUE:
//
//    TRUE  - Data Read
//    FALSE - If FD_CLOSE message
//
//  COMMENTS:
//
//    Called if socket has data OR if it is closed.  If closed post
//    WM_DISCONNECTED message.  Else read data and make sure it is
//    NULL terminated.
//

BOOL ReceiveInBox(HWND hWnd, WPARAM uParam, LPARAM lParam, char * szRBuf, int cRBufLen)
{
    char * pRBuf;     // temp buf pointer
    int cBytesRead;   // count of bytes actually read

    if (LOWORD(lParam) == FD_CLOSE)                   // Is this a FD_CLOSE event?
    {
        SendMessage(hWnd, MW_DISCONNECTED, 0, 0);     // Yes, post message
        return(FALSE);
    }

    pRBuf = szRBuf;   // Set temp pointer
    cRBufLen--;       // Save room for null terminator

    // read socket
    if((cBytesRead = recv(sock, pRBuf, cRBufLen, 0)) != SOCKET_ERROR)
        pRBuf += cBytesRead;    // Move temp pointer to end of buffer

    *pRBuf = 0;   // Null terminate - if recv() failed, then prBuf will
                  // point to first byte of the buffer

    return (TRUE);   // We've got a buffer to display
}

//
//  FUNCTION: SendOutBox(char *, int)
//
//  PURPOSE:  Reads incoming data from socket
//
//  PARAMETERS:
//    szSBuf    - Send Buffer
//    cSBufLen  - size of Send Buffer
//
//  COMMENTS:
//
//    Writes send buffer to socket -- repeats until all data is sent.
//

void SendOutBox(char * szSBuf, int cSBufLen)
{
    char * pSBuf;
    int cBytesSent;

    pSBuf = szSBuf; // Set temp pointer

    while((cBytesSent = send(sock,
                             pSBuf,
                             cSBufLen,
                             0)) != SOCKET_ERROR)
    {
        pSBuf += cBytesSent;
        cSBufLen -= cBytesSent;
        if(!cSBufLen) return;
    }
}

//
//  FUNCTION: AtoH(char *, char *, int)
//
//  PURPOSE:  Converts ascii string to network order hex
//
//  PARAMETERS:
//    src    - pointer to input ascii string
//    dest   - pointer to output hex
//    destlen - size of dest
//
//  COMMENTS:
//
//    2 ascii bytes make a hex byte so must put 1st ascii byte of pair
//    into upper nibble and 2nd ascii byte of pair into lower nibble.
//

void AtoH(char * src, char * dest, int destlen)
{
    char * srcptr;

    srcptr = src;

    while(destlen--)
    {
    *dest = BtoH(*srcptr++) << 4;    // Put 1st ascii byte in upper nibble.
    *dest++ += BtoH(*srcptr++);      // Add 2nd ascii byte to above.
    }
}

//
//  FUNCTION: BtoH(char *, char *, int)
//
//  PURPOSE:  Converts ascii byte to numeric
//
//  PARAMETERS:
//    ch - ascii byte to convert
//
//  RETURNS:
//    associated numeric value
//
//  COMMENTS:
//
//    Will convert any hex ascii digit to its numeric counterpart.
//    Puts in 0xff if not a valid hex digit.
//

unsigned char BtoH(char ch)
{
    if (ch >= '0' && ch <= '9') return (ch - '0');        // Handle numerals
    if (ch >= 'A' && ch <= 'F') return (ch - 'A' + 0xA);  // Handle capitol hex digits
    if (ch >= 'a' && ch <= 'f') return (ch - 'a' + 0xA);  // Handle small hex digits
    return(255);
}

//
//  FUNCTION: CleanUp(void)
//
//  PURPOSE:  Protocol specific cleanup function
//
//  COMMENTS:
//
//    Deletes our two possible sockets (they might not exist which
//    just means closesocket might return an error -- we don't care).
//

void CleanUp(void)
{
    closesocket(SrvSock);  // Close our server side socket
    closesocket(sock);     // Close our connection specific socket
    WSACleanup();          // Nix the DLL
}

//
//  FUNCTION: GetAddrString(PSOCKADDR_IPX, char *)
//
//  PURPOSE:  Converts IPX address to ascii string for displaying
//
//  PARAMETERS:
//    pSAddr - pointer to socket address struc
//    dest   - pointer to destination string
//
//  COMMENTS:
//
//    Address is in network order to use HtoA to convert to ascii.
//
//    Final format is
//      <8 char network address>.<12 char node address>.<4 char sock address>
//

void GetAddrString(PSOCKADDR_IPX pSAddr, char * dest)
{
    char abuf[15];                                // temp buffer
    char * currptr;                               // temp destination pointer
    int saddrlen = sizeof(struct sockaddr_ipx);   // sizeof address struc

    currptr = dest; // initialize destination pointer

    HtoA((char *)&pSAddr->sa_netnum, abuf, 4);    // convert network number
    lstrcpy(currptr, abuf);
    currptr += 8;
    lstrcat(currptr, ".");                        // don't forget seperator
    currptr++;

    HtoA((char *)&pSAddr->sa_nodenum, abuf, 6);   // convert node number
    lstrcat(currptr, abuf);
    currptr += 12;
    lstrcat(currptr, ".");                        // seperator
    currptr++;

    HtoA((char *)&pSAddr->sa_socket, abuf, 2);    // convert socket number
    lstrcat(currptr, abuf);

}

//
//  FUNCTION: HtoA(char *, char *, int)
//
//  PURPOSE:  Converts network ordered hex to ascii string
//
//  PARAMETERS:
//    src     - pointer to network ordered hex
//    dest    - pointer to ascii string
//    srclen  - size of hex number in bytes
//
//  COMMENTS:
//
//    1 byte hex = 2 bytes ascii so convert high order nibble with HtoB()
//    then convert low order nibble. dest buffer better be 2*srclen + 1.
//

void HtoA(char * src, char * dest, int srclen)
{
    char * destptr; // temp pointers
    UCHAR * srcptr;

    srcptr = (UCHAR *)src;
    destptr = dest;

    while(srclen--)
    {
    *destptr++ = HtoB((UCHAR)(*srcptr >> 4));      // Convert high order nibble
    *destptr++ = HtoB((UCHAR)(*srcptr++ & 0x0F));  // Convert low order nibble
    }
    *destptr = 0;  // Null terminator
}

//
//  FUNCTION: HtoB(UCHAR)
//
//  PURPOSE:  Converts hex byte to ascii byte
//
//  PARAMETERS:
//    ch - Hex byte
//
//  RETURNS:
//    ascii byte
//
//  COMMENTS:
//
//    We actually only convert a nibble since 1 byte hex = 2 bytes ascii.
//    So if ch > 0xf we just return 'X'.
//

char HtoB(UCHAR ch)
{
    if (ch <= 9) return ('0' + ch);             // handle decimal values
    if (ch <= 0xf) return ('A' + ch - 10);      // handle hexidecimal specific values
    return('X');                                // Someone screwed up
}

//---------------------------------------------------------------------------
//
// 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;
}


ABOUT.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:   about.c
//
//  PURPOSE:   Displays the "About" dialog box
//
//  FUNCTIONS:
//    CmdAbout        - Displays the "About" dialog box
//    About           - Processes messages for "About" dialog box.
//    MsgAboutInit    - To initialize the about box with version info
//                      from resources.
//    MsgAboutCommand - Process WM_COMMAND message sent to the about box.
//    CmdAboutDone    - Free the about box and related data.
//
//  COMMENTS:
//
//

#include <windows.h>            // required for all Windows applications
#include <windowsx.h>
#include <wsipx.h>              // IPX Sockets defs
#include "globals.h"            // prototypes specific to this application

LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM);
LRESULT MsgAboutInit(HWND, UINT, WPARAM, LPARAM);
LRESULT MsgAboutCommand(HWND, UINT, WPARAM, LPARAM);
LRESULT CmdAboutDone(HWND, WORD, WORD, HWND);

// About dialog message table definition.
MSD rgmsdAbout[] =
{
    {WM_COMMAND,    MsgAboutCommand},
    {WM_INITDIALOG, MsgAboutInit}
};

MSDI msdiAbout =
{
    sizeof(rgmsdAbout) / sizeof(MSD),
    rgmsdAbout,
    edwpNone
};

// About dialog command table definition.
CMD rgcmdAbout[] =
{
    {IDOK,     CmdAboutDone},
    {IDCANCEL, CmdAboutDone}
};

CMDI cmdiAbout =
{
    sizeof(rgcmdAbout) / sizeof(CMD),
    rgcmdAbout,
    edwpNone
};

// Module specific "globals"  Used when a variable needs to be
// accessed in more than on handler function.

HFONT hFontCopyright;

//
//  FUNCTION: CmdAbout(HWND, WORD, WORD, HWND)
//
//  PURPOSE: Displays the "About" dialog box
//
//  PARAMETERS:
//    hwnd      - Window handle
//    wCommand  - IDM_ABOUT (unused)
//    wNotify   - Notification number (unused)
//    hwndCtrl  - NULL (unused)
//
//  RETURN VALUE:
//
//    Always returns 0 - Message handled
//
//  COMMENTS:
//    To process the IDM_ABOUT message, call DialogBox() to display the
//    about dialog box.

LRESULT CmdAbout(HWND hwnd, WORD wCommand, WORD wNotify, HWND hwndCtrl)
{
    DialogBox(hInst, "AboutBox", hwnd, (DLGPROC)About);
    return 0;
}

//
//  FUNCTION: About(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE:  Processes messages for "About" dialog box.
//
//  PARAMETERS:
//    hdlg - window handle of the dialog box
//    wMessage - type of message
//    wparam - message-specific information
//    lparam - message-specific information
//
//  RETURN VALUE:
//    TRUE - message handled
//    FALSE - message not handled
//
//  COMMENTS:
//
//     Display version information from the version section of the
//     application resource.
//
//     Wait for user to click on "Ok" button, then close the dialog box.
//

LRESULT CALLBACK About(HWND hdlg, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    return DispMessage(&msdiAbout, hdlg, uMessage, wparam, lparam);
}

//
//  FUNCTION: MsgAboutInit(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: To initialize the about box with version info from resources.
//
//  PARAMETERS:
//    hwnd - The window handing the message.
//    uMessage - The message number. (unused).
//    wparam - Message specific data (unused).
//    lparam - Message specific data (unused).
//
//  RETURN VALUE:
//    Always returns 0 - message handled.
//
//  COMMENTS:
//    Uses the version apis to retrieve version information for
//    each of the static text boxes in the about box.
//

LRESULT MsgAboutInit(HWND hdlg, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
    #define POINTSIZE 8

    char  szFullPath[256];
    DWORD dwVerHnd;
    DWORD dwVerInfoSize;
    HDC   hDC;
    int   iLogPixelsY, iPointSize;

    // Center the dialog over the application window
    CenterWindow(hdlg, GetWindow(hdlg, GW_OWNER));

    // Set the copyright font to something smaller than default
    hDC = GetDC(hdlg);
    iLogPixelsY = GetDeviceCaps(hDC, LOGPIXELSY);
    ReleaseDC(hdlg, hDC);
    iPointSize = MulDiv(iLogPixelsY, POINTSIZE, 72);
    iPointSize *= -1;

    hFontCopyright = CreateFont(iPointSize,
                                0, 0, 0,
                                FW_DONTCARE,
                                0, 0, 0, 0,
                                0, 0, 0, 0,
                                "System");

    SendDlgItemMessage(hdlg,
                       IDD_VERLAST,
                       WM_SETFONT,
                       (WPARAM)hFontCopyright,
                       0L);

    // Get version information from the application
    GetModuleFileName(hInst, szFullPath, sizeof(szFullPath));
    dwVerInfoSize = GetFileVersionInfoSize(szFullPath, &dwVerHnd);
    if (dwVerInfoSize)
    {
        // If we were able to get the information, process it:
        HANDLE  hMem;
        LPVOID  lpvMem;
        char    szGetName[256];
        int     cchRoot;
        int     i;

        hMem = GlobalAlloc(GMEM_MOVEABLE, dwVerInfoSize);
        lpvMem = GlobalLock(hMem);
        GetFileVersionInfo(szFullPath, dwVerHnd, dwVerInfoSize, lpvMem);
        lstrcpy(szGetName, "\\StringFileInfo\\040904E4\\");
        cchRoot = lstrlen(szGetName);

        // Walk through the dialog items that we want to replace:
        for (i = IDD_VERFIRST; i <= IDD_VERLAST; i++)
        {
            BOOL  fRet;
            UINT  cchVer = 0;
            LPSTR lszVer = NULL;
            char  szResult[256];

            GetDlgItemText(hdlg, i, szResult, sizeof(szResult));
            lstrcpy(&szGetName[cchRoot], szResult);
            fRet = VerQueryValue(lpvMem, szGetName, &lszVer, &cchVer);

            if (fRet && cchVer && lszVer)
            {
                // Replace dialog item text with version info
                lstrcpy(szResult, lszVer);
                SetDlgItemText(hdlg, i, szResult);
            }
        }
        GlobalUnlock(hMem);
        GlobalFree(hMem);
    }
    return TRUE;
}

//
//  FUNCTION: MsgAboutCommand(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Process WM_COMMAND message sent to the about box.
//
//  PARAMETERS:
//    hwnd - The window handing the message.
//    uMessage - The message number. (unused).
//    wparam - Message specific data (unused).
//    lparam - Message specific data (unused).
//
//  RETURN VALUE:
//    Always returns 0 - message handled.
//
//  COMMENTS:
//    Uses this DipsCommand function defined in wndproc.c combined
//    with the cmdiAbout structure defined in this file to handle
//    the command messages for the about dialog box.
//

LRESULT MsgAboutCommand(HWND   hwnd,
                        UINT   uMessage,
                        WPARAM wparam,
                        LPARAM lparam)
{
    return DispCommand(&cmdiAbout, hwnd, wparam, lparam);
}

//
//  FUNCTION: CmdAboutDone(HWND, WORD, HWND)
//
//  PURPOSE: Free the about box and related data.
//
//  PARAMETERS:
//    hwnd - The window handling the command.
//    wCommand - The command to be handled (unused).
//    wNotify   - Notification number (unused)
//    hwndCtrl - NULL (unused).
//
//  RETURN VALUE:
//    Always returns TRUE.
//
//  COMMENTS:
//    Calls EndDialog to finish the dialog session.
//

LRESULT CmdAboutDone(HWND hdlg, WORD wCommand, WORD wNotify, HWND hwndCtrl)
{
    if (hFontCopyright)
       DeleteObject(hFontCopyright);

    EndDialog(hdlg, TRUE);          // Exit the dialog
    return TRUE;
}


WINMAIN.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:   winmain.c
//
//  PURPOSE:   Calls initialization functions and processes the message loop
//
//
//  PLATFORMS: Windows 95, Windows NT
//
//  FUNCTIONS:
//    WinMain() - calls initialization functions, processes message loop
//
//  COMMENTS:
//
//

#include <windows.h>            // required for all Windows applications
#include <wsipx.h>              // IPX Socket defs
#include "globals.h"            // prototypes specific to this application

//
//  FUNCTION: WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
//
//  PURPOSE: calls initialization function, processes message loop
//
//  PARAMETERS:
//
//    hInstance - The handle to the instance of this application that
//          is currently being executed.
//
//    hPrevInstance - This parameter is always NULL in Win32
//          applications.
//
//    lpCmdLine - A pointer to a null terminated string specifying the
//          command line of the application.
//
//    nCmdShow - Specifies how the main window is to be diplayed.
//
//  RETURN VALUE:
//    If the function terminates before entering the message loop,
//    return FALSE.
//    Otherwise, return the WPARAM value sent by the WM_QUIT message.
//
//
//  COMMENTS:
//
//    Windows recognizes this function by name as the initial entry point
//    for the program.  This function calls the 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 APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
    MSG msg;
    HANDLE hAccelTable;

    // Initialize application by setting up the main window.
    if (!InitApplication(hInstance, nCmdShow))
    {
        return FALSE;           // Exits if unable to initialize
    }

    hAccelTable = LoadAccelerators(hInstance, szAppName);

    // Acquire and dispatch messages until a WM_QUIT message is received.
    while (GetMessage(&msg, NULL, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);  // Translates virtual key codes
            DispatchMessage(&msg);   // Dispatches message to window
        }
    }
    return msg.wParam;  // Returns the value from PostQuitMessage
}


README.TXT

The WinSock2 service provider interface includes provisions for
"layering" service providers.  This sample code demonstrates how
that is done.

When the sample code is built, a DLL and an EXE result.  The DLL
is the actual service provider.  The EXE is a utility that a user
can use to install and remove the layered provider.


DASYNCW.CPP

/*++

     Copyright (c) 1996 Intel Corporation
     Copyright 1996 - 1998 Microsoft Corporation
     All Rights Reserved

     Permission is granted to use, copy and distribute this software and
     its documentation for any purpose and without fee, provided, that
     the above copyright notice and this statement appear in all copies.
     Intel makes no representations about the suitability of this
     software for any purpose.  This software is provided "AS IS."

     Intel specifically disclaims all warranties, express or implied,
     and all liability, including consequential and other indirect
     damages, for the use of this software, including liability for
     infringement of any proprietary rights, and including the
     warranties of merchantability and fitness for a particular purpose.
     Intel does not assume any responsibility for any errors which may
     appear in this software nor any responsibility to update it.

Module Name:

Abstract:

--*/

#include "precomp.h"

#define INVALID_ASYNC_WINDOW ((HWND)-1)
DWORD
AsyncThreadProc(
    DWORD  Context
    )
/*++
Routine Description:

    Thread procedure passed to CreatThread().

Arguments:

    Context - Context value passed to CreateThread().  The context value is the
              async thread object.

Return Value:

    The Return value of the worker thread

--*/

{
    PDASYNCWINDOW             Thread;
    HINSTANCE                 HModule;
    MSG                       msg;

    HModule = LoadLibraryA (gLibraryName);
    Thread = (PDASYNCWINDOW)Context;
    if (Thread->CreateAsyncWindow ()==NO_ERROR) {
while(GetMessage( &msg, NULL, 0, 0 )== TRUE) {
TranslateMessage( &msg );
DispatchMessage( &msg );
}
}

    DEBUGF( DBG_TRACE,
            ("Exiting async thread.\n"));
    FreeLibraryAndExitThread (HModule, 0);
    return 0;   // Keep compiler happy
}

LRESULT
CALLBACK
AsyncWndProc(
    HWND hwnd,
    UINT msg,
    WPARAM wParam,
    LPARAM lParam
    )
{
    PDASYNCWINDOW    Thread = (PDASYNCWINDOW)GetWindowLong (hwnd, 0);
    if (Thread) {
        BOOL             ReturnCode;

        ReturnCode = Thread->ProcessAsyncMessage (msg, (SOCKET)wParam, lParam);
        if (ReturnCode==TRUE)
            return 0;
        else if (ReturnCode == -1)
            delete Thread;
    }

    return DefWindowProc(hwnd, msg, wParam, lParam);
}

DASYNCWINDOW::DASYNCWINDOW()
/*++
Routine Description:

    Creates any internal state.

Arguments:

    None

Return Value:

    None

--*/

{
    m_async_thread = NULL;
    m_async_window = NULL;
m_exit_thread = FALSE;
}

DASYNCWINDOW::~DASYNCWINDOW()
/*++
Routine Description:

    destroys any internal state.

Arguments:

    None

Return Value:

    None

--*/
{

    DEBUGF( DBG_TRACE,
            ("Destroyed async thread object\n"));
}

INT
DASYNCWINDOW::Initialize(
    )
/*++
Routine Description:

    Initializes the DASYNCWINDOW object.

Arguments:

    NONE

Return Value:

    If no error occurs, Initialize() returns NO_ERROR.  Otherwise the value
    SOCKET_ERROR  is  returned,  and  a  specific  error  code  is available in
    lpErrno.

--*/
{
    INT ReturnCode;
    DWORD ThreadId;

    DEBUGF( DBG_TRACE,
            ("Initializing async thread \n"));

    //
    // Create the worker thread and wait for the new thred to finish its
    // initialization.
    //
    m_async_thread = CreateThread(
        NULL,
        0,
        (LPTHREAD_START_ROUTINE)::AsyncThreadProc,
        this,
        0,
        &ThreadId);
    if (m_async_thread){
while (m_async_window==NULL)
Sleep (0);
if (m_async_window!=INVALID_ASYNC_WINDOW)
ReturnCode = NO_ERROR;
else
ReturnCode = WSAENOBUFS;
    }
    else {
        ReturnCode = CreateAsyncWindow ();
    }

    return ReturnCode;

} //Initailize

VOID
DASYNCWINDOW::Destroy(
    )
/*++
Routine Description:

    Initiates destruction of the DASYNCWINDOW object.
    This can't be done syncronously in destructor because this
    will require waiting for thread object to exit which we wont
    to avoid.

Arguments:

    NONE

Return Value:

    NONE
--*/
{
        // Just destroy the window and it will quit the thread
        // if one exists
    if ((m_async_window!=NULL) && (m_async_window!=INVALID_ASYNC_WINDOW)) {
m_exit_thread = TRUE;
        DestroyWindow (m_async_window);
}
}

DWORD
DASYNCWINDOW::CreateAsyncWindow (
    )
{
    WNDCLASSA wndclass;

    //
    // Register the window class if necessary.
    //

    wndclass.style = CS_HREDRAW | CS_VREDRAW;
    wndclass.lpfnWndProc = (WNDPROC)AsyncWndProc;
    wndclass.cbClsExtra = 0;
    wndclass.cbWndExtra = sizeof(PDASYNCWINDOW);
    wndclass.hInstance = HDllInstance;
    wndclass.hIcon = LoadIcon( NULL, IDI_APPLICATION );
    wndclass.hCursor = LoadCursor( NULL, IDC_ARROW );
    wndclass.hbrBackground = (HBRUSH)GetStockObject( WHITE_BRUSH );
    wndclass.lpszMenuName = NULL;
    wndclass.lpszClassName = ASYNC_WINDOW_CLASS_NAME;

    if( RegisterClass( &wndclass ) == 0 ) {

        return WSAENOBUFS;

    }

    //
    // Create the window.
    //

    m_async_window = CreateWindowA(
                                ASYNC_WINDOW_CLASS_NAME,    // lpszClassName
                                "",                         // lpszWindowName
                                WS_OVERLAPPEDWINDOW,        // dwStyle
                                CW_USEDEFAULT,              // x
                                CW_USEDEFAULT,              // y
                                CW_USEDEFAULT,              // nWidth
                                CW_USEDEFAULT,              // nHeight
                                NULL,                       // hwndParent
                                NULL,                       // hmenu
                                HDllInstance,               // hinst
                                NULL                        // lpvParam
                                );

    if( m_async_window != NULL ) {

        //
        // Associate the async thread object with window.
        //
        SetWindowLong (m_async_window, 0, (DWORD)this);
        return NO_ERROR;

    }
    else {
m_async_window = INVALID_ASYNC_WINDOW;
        return WSAENOBUFS;
}

}

BOOL
DASYNCWINDOW::ProcessAsyncMessage (
    UINT    msg,
    SOCKET  s,
    LPARAM  param
    ) {
    PDSOCKET    Socket;

if (!m_exit_thread) {
switch( msg ) {
case WM_SELECT_MESSAGE:
Socket = DSOCKET::FindDSocketFromProviderSocket (s);
if (Socket)
Socket->SignalAsyncEvents (param);
return TRUE;

case WM_DESTROY :

if (m_async_thread) {
PostThreadMessage (NULL, WM_QUIT, 0, 0);
CloseHandle (m_async_thread);
m_async_thread = NULL;
DEBUGF (DBG_TRACE, ("Async thread received quit message.\n"));
}
return -1;
default:
return FALSE;
}
}
else {
if (m_async_thread) {
PostThreadMessage (NULL, WM_QUIT, 0, 0);
CloseHandle (m_async_thread);
m_async_thread = NULL;
DEBUGF (DBG_TRACE, ("Async thread exit flag set.\n"));
}
return -1;
}
}

INT
DASYNCWINDOW::RegisterSocket(
    PDSOCKET Socket
    )
/*++
Routine Description:

    Register the socket with provider to receive messages for async window
    maintained by this async thread object

Arguments:

    Socket - A pointer to a DSOCKET object for the socket that is registering.

Return Value:

    On success NO_ERROR else a valid winsock error code.

--*/
{
    INT        ReturnCode;
    LONG       SocketEventMask;
    SOCKET     ProviderSocket;
    PDPROVIDER Provider;

    // Get the info we need from the socket
    ProviderSocket    = Socket->GetProviderSocket();
    SocketEventMask = Socket->GetAsyncEventMask();
    Provider        = Socket->GetDProvider();

    //Add the socket to the wait array
    if (Provider->WSPAsyncSelect(
            ProviderSocket,
            m_async_window,
            WM_SELECT_MESSAGE,
            SocketEventMask,
            &ReturnCode)==0)
        return NO_ERROR;
    else
        return ReturnCode;
}


DASYNCW.H

/*++

     Copyright c 1996 Intel Corporation
     All Rights Reserved

     Permission is granted to use, copy and distribute this software and
     its documentation for any purpose and without fee, provided, that
     the above copyright notice and this statement appear in all copies.
     Intel makes no representations about the suitability of this
     software for any purpose.  This software is provided "AS IS."

     Intel specifically disclaims all warranties, express or implied,
     and all liability, including consequential and other indirect
     damages, for the use of this software, including liability for
     infringement of any proprietary rights, and including the
     warranties of merchantability and fitness for a particular purpose.
     Intel does not assume any responsibility for any errors which may
     appear in this software nor any responsibility to update it.

Module Name:

dasyncw.h

Abstract:

   This module contins the interface to the DASYNCWINDOW class for
   LSP.DLL.

--*/

#ifndef _DASYNCWINDOW_
#define _DASYNCWINDOW_

#include <winsock2.h>
#include "classfwd.h"

#define WM_SELECT_MESSAGE   (WM_USER+100)
#define ASYNC_WINDOW_CLASS_NAME "Layered Provider Async Window"

class DASYNCWINDOW
{
  public:

    DASYNCWINDOW();

    INT
    Initialize();

    VOID
    Destroy ();

    ~DASYNCWINDOW();

    DWORD
    CreateAsyncWindow ();

    BOOL
    ProcessAsyncMessage (
        UINT    msg,
        SOCKET  s,
        LPARAM  param
        );

    INT
    RegisterSocket(
        PDSOCKET Socket
        );

private:

    HWND    m_async_window;
    HANDLE  m_async_thread;
BOOLm_exit_thread;

};

#endif


DBGTRACE.H

/*++

Copyright (c) 1995 Intel Corp

File Name:

    dbgtrace.h

Abstract:

    This header describes the interface to the WinSock 2 debug/trace
    DLL.  Please see the design spec for more information.

--*/

#ifndef _DBGTRACE_H
#define _DBGTRACE_H

#include "warnoff.h"

#include <winsock2.h>

//
// This type defines a pointer to the Pre/PostApiNotify functions
//

typedef BOOL (WINAPIV * LPFNWSANOTIFY)(
    IN  INT    NotificationCode,
    OUT LPVOID ReturnCode,
    IN  LPSTR  LibraryName,
    ...);

//
// Function prototypes for Pre/PostApiNotify
//

BOOL WINAPIV
WSAPreApiNotify(
    IN  INT    NotificationCode,
    OUT LPVOID ReturnCode,
    IN  LPSTR  LibraryName,
    ...);

BOOL WINAPIV
WSAPostApiNotify(
    IN  INT    NotificationCode,
    OUT LPVOID ReturnCode,
    IN  LPSTR  LibraryName,
    ...);

//
// API function codes for Pre/PostApiNotify functions.  Note:  These must start
// at  1  or more and be fairly densely assigned.  Small gaps can be tolerated.
// Note  that  a  "MAX_DTCODE"  definition  should  be updated if new codes are
// added.
//

#define DTCODE_accept 1
#define DTCODE_bind 2
#define DTCODE_closesocket 3
#define DTCODE_connect 4
#define DTCODE_getpeername 5
#define DTCODE_getsockname 6
#define DTCODE_getsockopt 7
#define DTCODE_htonl 8
#define DTCODE_htons 9
#define DTCODE_ioctlsocket 10
#define DTCODE_listen 11
#define DTCODE_ntohl 12
#define DTCODE_ntohs 13
#define DTCODE_recv 14
#define DTCODE_recvfrom 15
#define DTCODE_select 16
#define DTCODE_send 17
#define DTCODE_sendto 18
#define DTCODE_setsockopt 19
#define DTCODE_shutdown 20
#define DTCODE_socket 21
#define DTCODE_WSAAccept 22
#define DTCODE_WSAAsyncSelect 23
#define DTCODE_WSACancelBlockingCall 24
#define DTCODE_WSACleanup 25
#define DTCODE_WSACloseEvent 26
#define DTCODE_WSAConnect 27
#define DTCODE_WSACreateEvent 28
#define DTCODE_WSADuplicateSocketA 29
#define DTCODE_WSAEnumNetworkEvents 30
#define DTCODE_WSAEnumProtocolsA 31
#define DTCODE_WSAEventSelect 32
#define DTCODE_WSAGetLastError 33
#define DTCODE_WSAGetOverlappedResult 34
#define DTCODE_WSAGetQOSByName 35
#define DTCODE_WSAHtonl 36
#define DTCODE_WSAHtons 37
#define DTCODE_WSAIoctl 38
#define DTCODE_WSAIsBlocking 39
#define DTCODE_WSAJoinLeaf 40
#define DTCODE_WSANtohl 41
#define DTCODE_WSANtohs 42
#define DTCODE_WSARecv 43
#define DTCODE_WSARecvDisconnect 44
#define DTCODE_WSARecvFrom 45
#define DTCODE_WSAResetEvent 46
#define DTCODE_WSASend 47
#define DTCODE_WSASendDisconnect 48
#define DTCODE_WSASendTo 49
#define DTCODE_WSASetBlockingHook 50
#define DTCODE_WSASetEvent 51
#define DTCODE_WSASetLastError 52
#define DTCODE_WSASocketA 53
#define DTCODE_WSAStartup 54
#define DTCODE_WSAUnhookBlockingHook 55
#define DTCODE_WSAWaitForMultipleEvents 56
#define DTCODE_gethostbyaddr 57
#define DTCODE_gethostbyname 58
#define DTCODE_gethostname 59
#define DTCODE_getprotobyname 60
#define DTCODE_getprotobynumber 61
#define DTCODE_getservbyname 62
#define DTCODE_getservbyport 63
#define DTCODE_inet_addr 64
#define DTCODE_inet_ntoa 65
#define DTCODE_WSAAsyncGetHostByAddr 66
#define DTCODE_WSAAsyncGetHostByName 67
#define DTCODE_WSAAsyncGetProtoByName 68
#define DTCODE_WSAAsyncGetProtoByNumber 69
#define DTCODE_WSAAsyncGetServByName 70
#define DTCODE_WSAAsyncGetServByPort 71
#define DTCODE_WSACancelAsyncRequest 72
#define DTCODE_WSPAccept 73
#define DTCODE_WSPAsyncSelect 74
#define DTCODE_WSPBind 75
#define DTCODE_WSPCancelBlockingCall 76
#define DTCODE_WSPCleanup 77
#define DTCODE_WSPCloseSocket 78
#define DTCODE_WSPConnect 79
#define DTCODE_WSPDuplicateSocket 80
#define DTCODE_WSPEnumNetworkEvents 81
#define DTCODE_WSPEventSelect 82
#define DTCODE_WSPGetOverlappedResult 83
#define DTCODE_WSPGetPeerName 84
// The  WSPGetProcTable  function  has  been removed, but the code numbers have
// been kept the same.
// #define DTCODE_WSPGetProcTable 85
#define DTCODE_WSPGetSockName 86
#define DTCODE_WSPGetSockOpt 87
#define DTCODE_WSPGetQOSByName 88
#define DTCODE_WSPIoctl 89
#define DTCODE_WSPJoinLeaf 90
#define DTCODE_WSPListen 91
#define DTCODE_WSPRecv 92
#define DTCODE_WSPRecvDisconnect 93
#define DTCODE_WSPRecvFrom 94
#define DTCODE_WSPSelect 95
#define DTCODE_WSPSend 96
#define DTCODE_WSPSendDisconnect 97
#define DTCODE_WSPSendTo 98
#define DTCODE_WSPSetSockOpt 99
#define DTCODE_WSPShutdown 100
#define DTCODE_WSPSocket 101
#define DTCODE_WSPStartup 102
#define DTCODE_WPUCloseEvent 103
#define DTCODE_WPUCloseSocketHandle 104
#define DTCODE_WPUCreateEvent 105
#define DTCODE_WPUCreateSocketHandle 106
#define DTCODE_WSCDeinstallProvider 107
#define DTCODE_WSCInstallProvider 108
#define DTCODE_WPUModifyIFSHandle 109
#define DTCODE_WPUQueryBlockingCallback 110
#define DTCODE_WPUQuerySocketHandleContext 111
#define DTCODE_WPUQueueApc 112
#define DTCODE_WPUResetEvent 113
#define DTCODE_WPUSetEvent 114
#define DTCODE_WSCEnumProtocols 115
#define DTCODE_WPUGetProviderPath 116
#define DTCODE_WPUPostMessage 117
#define DTCODE_WPUFDIsSet 118
#define DTCODE_WSADuplicateSocketW 119
#define DTCODE_WSAEnumProtocolsW 120
#define DTCODE_WSASocketW 121
#define DTCODE___WSAFDIsSet 122
#define DTCODE_WSAAddressToStringA 123
#define DTCODE_WSAAddressToStringW 124
#define DTCODE_WSAStringToAddressA 125
#define DTCODE_WSAStringToAddressW 126
#define DTCODE_WSALookupServiceBeginA 127
#define DTCODE_WSALookupServiceBeginW 128
#define DTCODE_WSALookupServiceNextA 129
#define DTCODE_WSALookupServiceNextW 130
#define DTCODE_WSALookupServiceEnd 131
#define DTCODE_WSAGetAddressByNameA 132
#define DTCODE_WSAGetAddressByNameW 133
#define DTCODE_WSAInstallServiceClassA 134
#define DTCODE_WSAInstallServiceClassW 135
#define DTCODE_WSASetServiceA 136
#define DTCODE_WSASetServiceW 137
#define DTCODE_WSARemoveServiceClass 138
#define DTCODE_WSAGetServiceClassInfoA 139
#define DTCODE_WSAGetServiceClassInfoW 140
#define DTCODE_WSAEnumNameSpaceProvidersA 141
#define DTCODE_WSAEnumNameSpaceProvidersW 142
#define DTCODE_WSAGetServiceClassNameByClassIdA 143
#define DTCODE_WSAGetServiceClassNameByClassIdW 144
#define DTCODE_WSPAddressToString 145
#define DTCODE_WSPStringToAddress 146
#define DTCODE_NSPLookupServiceBegin 147
#define DTCODE_NSPLookupServiceNext 148
#define DTCODE_NSPLookupServiceEnd 149
#define DTCODE_NSPGetAddressByName 150
#define DTCODE_NSPInstallServiceClass 151
#define DTCODE_NSPSetService 152
#define DTCODE_NSPRemoveServiceClass 153
#define DTCODE_NSPGetServiceClassInfo 154
#define DTCODE_NSPEnumNameSpaceProviders 155
#define DTCODE_NSPGetServiceClassNameByClassId 156

#define MAX_DTCODE DTCODE_NSPGetServiceClassNameByClassId

#endif


DBUFFMGR.CPP

/*++

     Copyright (c) 1996 Intel Corporation
     Copyright 1996 - 1998 Microsoft Corporation
     All Rights Reserved

     Permission is granted to use, copy and distribute this software and
     its documentation for any purpose and without fee, provided, that
     the above copyright notice and this statement appear in all copies.
     Intel makes no representations about the suitability of this
     software for any purpose.  This software is provided "AS IS."

     Intel specifically disclaims all warranties, express or implied,
     and all liability, including consequential and other indirect
     damages, for the use of this software, including liability for
     infringement of any proprietary rights, and including the
     warranties of merchantability and fitness for a particular purpose.
     Intel does not assume any responsibility for any errors which may
     appear in this software nor any responsibility to update it.

Module Name:

dbuffmgr.cpp

Abstract:

     This module defines and interface for a buffer manager for managing
     WSABUF's.  If a layered provider needs to modify application data The
     CopyBuffer() routine should be modifed to do so.

--*/

#include "precomp.h"

DBUFFERMANAGER::DBUFFERMANAGER(
    )
/*++

Routine Description:

    DBUFFERMANAGER  object  constructor.   Creates and returns a DBUFFERMANAGER
    object.  Note that  the  DBUFFERMANAGER object has not been fully
    initialized.  The "Initialize" member function must be the first member
    function called on the new DBUFFERMANAGER object.

Arguments:

    None

Return Value:

    None
--*/
{
    // Null function body.  A full implemantation would initialize member
    // varialbles to know values for use in Initailize().
}

INT
DBUFFERMANAGER::Initialize(
        )
/*++

Routine Description:

    The initialization routine for a buffer manager object.  This procedure
    completes the initialzation of the object.  This procedure preforms
    initialization operations that may fail and must be reported since there is
    no way to fail the constructor.

Arguments:

    None
Return Value:

    The  function returns NO_ERROR if successful.  Otherwise it
    returns an appropriate WinSock error code if the initialization
    cannot be completed.
--*/
{
    return(NO_ERROR);
}

DBUFFERMANAGER::~DBUFFERMANAGER()
/*++

Routine Description:

    DBUFFERMANAGER object destructor.  This procedure has the responsibility to
    perform any required shutdown operations for the DBUFFERMANAGER object
    before the object memory is deallocated.

Arguments:

    None

Return Value:

    None
--*/
{
    // Null function body
}

INT
DBUFFERMANAGER::AllocBuffer(
    IN  LPWSABUF   UserBuffer,
    IN  DWORD      UserBufferCount,
    OUT LPWSABUF*  InternalBuffer,
    OUT DWORD*     InternalBufferCount
    )
/*++

Routine Description:

    This routine allocates a set of WSABUF's to pass to the underlying service
    provider.

Arguments:

    UserBuffer - A pointer to the array user WSABUFs
    UserBufferCount - The number of user WSABUF structs.
    InternalBuffer - A pointer to a pointer to a WSABUF
    InternalBufferCount - The pointer to a DWORD to revceive the number of
                          WSABUFs pointed to by InternalBuffer.

Return Value:

    The  function returns NO_ERROR if successful.  Otherwise it
    returns an appropriate WinSock error code if the initialization
    cannot be completed.

--*/
{
    // ******
    // Note this procedure returns the user buffer(s) undistrubed.  This is the
    // wrong thing for providers that wish to modify the data stream.  The
    // proper buffer management policy is left to the provider developer.
    *InternalBuffer = UserBuffer;
    *InternalBufferCount = UserBufferCount;
    return(NO_ERROR);
}

VOID
DBUFFERMANAGER::FreeBuffer(
    IN LPWSABUF  InternalBuffer,
    IN DWORD     InternalBufferCount
    )
/*++

Routine Description:

    This routine frees a set of WSABUF's allocated by AllocBuffer();

Arguments:

    InternalBuffer - A pointer to WSABUF array
    InternalBufferCount - The number of WSABUFs pointed to by InternalBuffer.

Return Value:

    The  function returns NO_ERROR if successful.  Otherwise it
    returns an appropriate WinSock error code if the initialization
    cannot be completed.

--*/
{
}

INT
DBUFFERMANAGER::CopyBuffer(
    IN  LPWSABUF   SourceBuffer,
    IN  DWORD      SourceBufferCount,
IN  DWORD   SourceOffset,
    INDWORD   BytesToCopy,
    IN  LPWSABUF   DestinationBuffer,
    IN  DWORD      DestinationBufferCount,
IN  DWORD   DestionationOffset
    )
/*++

Routine Description:

    This routine copies one set of WSABUFs to another.

Arguments:

    SourceBuffer - A pointer to an array of WSABUFs.

    SourceBuferCount - The number of WSABUFs pointed to by SourceBuffer.

SourceOffset  - offset to start copying from in source
BytesToCopy  - maximum number of bytest to copy
    DestinationBuffer - A pointer to an array of WSABUFs.

    DestinationBufferCount - The number of WSABUFs pointed to by
                             DestinationBuffer .
DestinationOffset - offset to start writing to in destionation buffer

Return Value:

    The  function returns NO_ERROR if successful.  Otherwise it
    returns an appropriate WinSock error code if the initialization
    cannot be completed.

--*/
{
    return(NO_ERROR);
}


DBUFFMGR.H

/*++

     Copyright c 1996 Intel Corporation
     All Rights Reserved

     Permission is granted to use, copy and distribute this software and
     its documentation for any purpose and without fee, provided, that
     the above copyright notice and this statement appear in all copies.
     Intel makes no representations about the suitability of this
     software for any purpose.  This software is provided "AS IS."

     Intel specifically disclaims all warranties, express or implied,
     and all liability, including consequential and other indirect
     damages, for the use of this software, including liability for
     infringement of any proprietary rights, and including the
     warranties of merchantability and fitness for a particular purpose.
     Intel does not assume any responsibility for any errors which may
     appear in this software nor any responsibility to update it.

Module Name:

dbuffmgr.h

Abstract:

--*/

#ifndef _DBUFFERMANAGER_
#define _DBUFFERMANAGER_

#include <winsock2.h>

class DBUFFERMANAGER
{
  public:

    DBUFFERMANAGER();

    ~DBUFFERMANAGER();

    INT
    Initialize(
        );

    INT
    AllocBuffer(
        IN  LPWSABUF   UserBuffer,
        IN  DWORD      UserBufferCount,
        OUT LPWSABUF*  InternalBuffer,
        OUT DWORD*     InternalBufferCount
        );

    VOID
    FreeBuffer(
        IN LPWSABUF  InternalBuffer,
        IN DWORD     InternalBufferCount
        );

    INT
    CopyBuffer(
IN  LPWSABUF   SourceBuffer,
IN  DWORD      SourceBufferCount,
IN  DWORD   SourceOffset,
INDWORD   BytesToCopy,
IN  LPWSABUF   DestinationBuffer,
IN  DWORD      DestinationBufferCount,
IN  DWORD   DestionationOffset
        );

  private:

};   // class DBUFFERMANAGER

#endif // _DBUFFERMANAGER_


DCATALOG.CPP

/*++

     Copyright (c) 1996 Intel Corporation
     Copyright 1996 - 1998 Microsoft Corporation
     All Rights Reserved

     Permission is granted to use, copy and distribute this software and
     its documentation for any purpose and without fee, provided, that
     the above copyright notice and this statement appear in all copies.
     Intel makes no representations about the suitability of this
     software for any purpose.  This software is provided "AS IS."

     Intel specifically disclaims all warranties, express or implied,
     and all liability, including consequential and other indirect
     damages, for the use of this software, including liability for
     infringement of any proprietary rights, and including the
     warranties of merchantability and fitness for a particular purpose.
     Intel does not assume any responsibility for any errors which may
     appear in this software nor any responsibility to update it.

Module Name:

    dcatalog.cpp

Abstract:

    This module contains the implementation of the dcatalog class. This class
    maintains a catalog of installed WinSock2 service providers.

--*/

#include "precomp.h"
#include "install.h"

DCATALOG::DCATALOG()
/*++

Routine Description:

    Constructor for the DCATALOG object. Set member variables to known
    state. Initialization of the object is completed in Initialize().

Arguments:

    NONE.

Return Value:

    NONE.

--*/
{

    m_num_items = 0;
    m_local_item = NULL;

    // initialize the critical section object
    InitializeCriticalSection( &m_catalog_lock );
    InitializeListHead( &m_protocol_list );
}

INT
DCATALOG::Initialize(
    )
/*++

Routine Description:

    Initialization routine for the DCATALOG object. Completes the
    initialization of the DCATALOG object.  This MUST be the first member
    fuction called after a DCATALOG object is created.

Arguments:

    NONE.

Return Value:

    NO_ERROR if the fuction succeeds else a winsock2 error code.

--*/
{
    LPWSAPROTOCOL_INFOW   ProtocolInfoBuff = NULL;
    DWORD                ProtocolInfoBuffSize = 0;
    PPROTO_CATALOG_ITEM  CatalogItem;
    INT                  ReturnCode;
    INT                  ErrorCode;
    INT                  EnumResult;
    INT                  Index;

    // Call WSCEnumProtocols with a zero length buffer so we know what size to
    // send in to get all the installed PROTOCOL_INFO structs.
    WSCEnumProtocols(
        NULL,                     // lpiProtocols
        ProtocolInfoBuff,         // lpProtocolBuffer
        & ProtocolInfoBuffSize,   // lpdwBufferLength
        & ErrorCode);             // lpErrno

    ReturnCode = WSA_NOT_ENOUGH_MEMORY;
    ProtocolInfoBuff = (LPWSAPROTOCOL_INFOW)new char[ProtocolInfoBuffSize];
    if (ProtocolInfoBuff){
        EnumResult = WSCEnumProtocols(
            NULL,                     // lpiProtocols
            ProtocolInfoBuff,         // lpProtocolBuffer
            & ProtocolInfoBuffSize,   // lpdwBufferLength
            & ErrorCode);

        ReturnCode = WSASYSNOTREADY;
        if (EnumResult != SOCKET_ERROR){
            for (Index=0; Index < EnumResult ; Index++){

                //Create a new catalog item for the PROTOCOL_INFO struct.
                CatalogItem = new PROTO_CATALOG_ITEM;
                if (CatalogItem){

                    ReturnCode = CatalogItem->Initialize(
                        &ProtocolInfoBuff[Index]);
                    if (NO_ERROR == ReturnCode){

                        //Add the new catalog item to the catalog
                        AcquireCatalogLock();
                        AppendCatalogItem(
                            CatalogItem);
                        if (memcmp (&CatalogItem->GetProtocolInfo()->ProviderId,
                                &LayeredProviderGuid,
sizeof (GUID))==0)
                            m_local_item = CatalogItem;
                        ReleaseCatalogLock();
                    } //if
                    else{
                        break;
                    } //else
                } //if
            } //for

            if ((NO_ERROR==ReturnCode)
                    && (m_local_item==NULL))
                ReturnCode = WSASYSNOTREADY;
        } //if
        delete(ProtocolInfoBuff);
    } //if
    return(ReturnCode);
}

DCATALOG::~DCATALOG()
/*++

Routine Description:

    This  function  destroys the catalog object.  It takes care of removing and
    destroying  all  of  the  catalog  entries  in  the catalog.  This includes
    destroying  all  of the DPROVIDER objects referenced by the catalog.  It is
    the  caller's responsibility to make sure that the DPROVIDER objects are no
    longer referenced.

Arguments:

    None

Return Value:

    None

Implementation Notes:

    for each catalog entry
        remove the entry
        get its DPROVIDER reference
        if reference is non-null
            Set providers for all entries with matching IDs null
            destroy the DPROVIDER
        endif
        destroy the entry
    end for
    deallocate the list head
    close the catalog registry mutex
--*/
{
    PLIST_ENTRY  this_linkage;
    PPROTO_CATALOG_ITEM  this_item;
    PDPROVIDER  this_provider;

    DEBUGF(
        DBG_TRACE,
        ("Catalog destructor\n"));

    AcquireCatalogLock();

    while ((this_linkage = m_protocol_list.Flink) != & m_protocol_list) {
        this_item = CONTAINING_RECORD(
            this_linkage,        // address
            PROTO_CATALOG_ITEM,  // type
            m_CatalogLinkage     // field
            );
        RemoveCatalogItem(
            this_item  // CatalogItem
            );
        this_provider = this_item->GetProvider();
        if (this_provider)
            delete this_provider;
        delete this_item;
    }  // while (get entry linkage)

    assert( m_num_items == 0 );

    ReleaseCatalogLock();
    DeleteCriticalSection( &m_catalog_lock );

}  // ~DCATALOG

VOID
DCATALOG::EnumerateCatalogItems(
    IN CATALOGITERATION  Iteration,
    IN DWORD             PassBack
    )
/*++

Routine Description:

    This  procedure enumerates all of the DPROTO_CATALOG_ITEM structures in the
    catalog  by  calling  the indicated iteration procedure once for each item.
    The called procedure can stop the iteration early by returning FALSE.

    Note  that  the DPROVIDER associated with an enumerated DPROTO_CATALOG_ITEM
    may  be  NULL.   To retrieve DPROTO_CATALOG_ITEM structure that has had its
    DPROVIDER      loaded      and      initialized,      you      can      use
    GetCatalogItemFromCatalogEntryId.

Arguments:

    Iteration - Supplies   a  reference  to  the  catalog  iteration  procedure
                supplied by the client.

    PassBack  - Supplies  a  value uninterpreted by this procedure.  This value
                is  passed  unmodified to the catalog iteration procedure.  The
                client can use this value to carry context between the original
                call site and the iteration procedure.

Return Value:

    None
--*/
{
    PLIST_ENTRY         ListMember;
    PPROTO_CATALOG_ITEM CatalogEntry;
    BOOL                enumerate_more;

    assert(Iteration != NULL);

    enumerate_more = TRUE;

    AcquireCatalogLock();

    ListMember = m_protocol_list.Flink;

    while (enumerate_more && (ListMember != & m_protocol_list)) {
        CatalogEntry = CONTAINING_RECORD(
            ListMember,
            PROTO_CATALOG_ITEM,
            m_CatalogLinkage);
        ListMember = ListMember->Flink;
        enumerate_more = (* Iteration) (
            PassBack,     // PassBack
            CatalogEntry  // CatalogEntry
            );
    } //while

    ReleaseCatalogLock();

}  // EnumerateCatalogItems

INT
DCATALOG::FindNextProviderInChain(
        IN  LPWSAPROTOCOL_INFOW lpProtocolInfo,
        OUT PDPROVIDER          *NextProvider,
OUT PPROTO_CATALOG_ITEM*BaseProviderCatalogEntry
    )
/*++

Routine Description:

    This procedure finds and loads the provider below this
    provider in the protocol chain.

Arguments:
    lpLocalProtocolInfo - A pointer to the WSAPROTOCOL_INFO struct for the
                          current protocol chain.

    NextProvider        - A pointer to DPROVIDER object pointer for the
                          next provider in chain
BaseProviderCatalogEntry - If next provider is a base provider, this
  pointer will contain the next provider catalog
  entry
Return Value:

    NO_ERROR if the next provider is located else a valid winsock2 error
    code.
--*/
{

    PLIST_ENTRY         ListMember;
    PPROTO_CATALOG_ITEM CatalogEntry;
    PPROTO_CATALOG_ITEM NextProviderCatalogEntry;
DWORDLocalCatalogEntryId;
    DWORD               NextProviderCatalogEntryId;
    INT                 Index;
    INT                 ReturnCode =WSASYSNOTREADY;

assert (NextProvider!=NULL);

if (m_local_item==NULL)
return WSASYSNOTREADY;

LocalCatalogEntryId = m_local_item->GetProtocolInfo()->dwCatalogEntryId;

    AcquireCatalogLock();

// First try to see if we have processed this chain already and
// thus loaded the provider that follows us in it
    ListMember = m_protocol_list.Flink;

    while (ListMember != & m_protocol_list) {

        CatalogEntry = CONTAINING_RECORD(
            ListMember,
            PROTO_CATALOG_ITEM,
            m_CatalogLinkage);
        ListMember = ListMember->Flink;
        if (CatalogEntry->GetProtocolInfo()->dwCatalogEntryId ==
lpProtocolInfo->dwCatalogEntryId) {
*NextProvider = CatalogEntry->GetProvider ();
if (*NextProvider!=NULL) {
 ReturnCode = NO_ERROR;
}
else {
//
// Get the next providers CatalogEntryId from the protocol chain
//
for (Index=0;
 Index < lpProtocolInfo->ProtocolChain.ChainLen;
 Index++){
if ((LocalCatalogEntryId==
lpProtocolInfo->ProtocolChain.ChainEntries[Index])
&& (lpProtocolInfo->ProtocolChain.ChainLen>Index+1)) {
NextProviderCatalogEntryId =
lpProtocolInfo->ProtocolChain.ChainEntries[Index+1];
break;
} //if
} // for
//
// If we found ourselves before reaching the end of the chain,
// go load the next guy in the chain
//
if (Index<lpProtocolInfo->ProtocolChain.ChainLen) {
ReturnCode = GetCatalogItemFromCatalogEntryId (
NextProviderCatalogEntryId,
&NextProviderCatalogEntry);
if (NO_ERROR==ReturnCode) {
// Pass the chain protocol info if
// the provider we are loading is not
// the last in the chain (base provider)
if (Index==lpProtocolInfo->ProtocolChain.ChainLen-1)
ReturnCode = LoadProvider (
NextProviderCatalogEntry,
NextProviderCatalogEntry->GetProtocolInfo(),
NextProvider);
else
ReturnCode = LoadProvider (
NextProviderCatalogEntry,
lpProtocolInfo,
NextProvider);

// Cache provider if we succeded
if (NO_ERROR==ReturnCode) {
CatalogEntry->SetProvider (*NextProvider);
CatalogEntry->SetProviderCatalogEntry (NextProviderCatalogEntry);
}
} //if
}
}
if ((NO_ERROR==ReturnCode)
&& (CatalogEntry->GetProviderCatalogEntry()->GetProtocolInfo()->ProtocolChain.ChainLen==BASE_PROTOCOL))
*BaseProviderCatalogEntry = CatalogEntry->GetProviderCatalogEntry();

break;
}
}

    ReleaseCatalogLock();
    return(ReturnCode);
}

INT
DCATALOG::GetCatalogItemFromCatalogEntryId(
    IN  DWORD                     CatalogEntryId,
    OUT PPROTO_CATALOG_ITEM FAR * CatalogItem
    )
/*++

Routine Description:

    This  procedure  retrieves  a  reference  to a catalog item given a catalog
    entry ID to search for.

Arguments:

    CatalogEntryId  - Supplies The ID of a catalog entry to be searched for.

    CatalogItem     - Returns a reference to the catalog item with the matching
                      catalog entry ID if it is found, otherwise returns NULL.

Return Value:

  The  function  returns  NO_ERROR  if  successful, otherwise it returns an
  appropriate WinSock error code.
--*/
{
    PLIST_ENTRY         ListMember;
    INT                 ReturnCode=WSASYSNOTREADY;
    PPROTO_CATALOG_ITEM CatalogEntry;

    assert(CatalogItem != NULL);

    // Prepare for early error return
    * CatalogItem = NULL;

    AcquireCatalogLock();

    ListMember = m_protocol_list.Flink;

    while (ListMember != & m_protocol_list) {

        CatalogEntry = CONTAINING_RECORD(
            ListMember,
            PROTO_CATALOG_ITEM,
            m_CatalogLinkage);
        ListMember = ListMember->Flink;
        if (CatalogEntry->GetProtocolInfo()->dwCatalogEntryId ==
CatalogEntryId) {
            * CatalogItem = CatalogEntry;
            ReturnCode = NO_ERROR;
break;
        } //if
    } //while

    ReleaseCatalogLock();
    return(ReturnCode);
}  // GetCatalogItemFromCatalogEntryId

INT
DCATALOG::LoadProvider(
    IN PPROTO_CATALOG_ITEM CatalogEntry,
    IN LPWSAPROTOCOL_INFOW lpProtocolInfo,
    OUT PDPROVIDER         *Provider
    )
/*++

Routine Description:

    Load   the   provider  described  by  CatalogEntry.

Arguments:

    CatalogEntry - Supplies  a reference to a protocol catalog entry describing
                   the provider to load.

    lpProtocolInfo - Protocol info structure to pass to provider when
loading it

    Provider     - Returns a reference to the newly loaded provider object.

Return Value:

    The  function  returns NO_ERROR if successful, otherwise it returns an
    appropriate WinSock error code.
--*/
{
    INT ReturnCode = WSA_NOT_ENOUGH_MEMORY;
    PDPROVIDER LocalProvider;

    assert(CatalogEntry != NULL);
    assert(Provider != NULL);

    *Provider = NULL;

    LocalProvider = new(DPROVIDER);
    if (LocalProvider) {

        ReturnCode = LocalProvider->Initialize(
            CatalogEntry->GetLibraryPath(),
            lpProtocolInfo
            );
        if (NO_ERROR == ReturnCode) {
            *Provider = LocalProvider;
        } //if
        else {
            delete(LocalProvider);
        } //else
    } //if
    return(ReturnCode);
}  // LoadProvider

VOID
DCATALOG::AppendCatalogItem(
    IN  PPROTO_CATALOG_ITEM  CatalogItem
    )
/*++

Routine Description:

    This procedure appends a catalog item to the end of the (in-memory) catalog
    object.   It becomes the last item in the catalog.

Arguments:

    CatalogItem - Supplies a reference to the catalog item to be added.

Return Value:

    None
--*/
{
    assert(CatalogItem != NULL);

    InsertTailList(
        & m_protocol_list,               // ListHead
        & CatalogItem->m_CatalogLinkage  // Entry
       );
    m_num_items++;
}  // AppendCatalogItem

VOID
DCATALOG::RemoveCatalogItem(
    IN  PPROTO_CATALOG_ITEM  CatalogItem
    )
/*++

Routine Description:

    This  procedure removes a catalog item from the (in-memory) catalog object.
    The catalog information in the registry is NOT updated.

Arguments:

    CatalogItem - Supplies a reference to the catalog item to be removed.

Return Value:

    None
--*/
{
    assert(CatalogItem != NULL);

    RemoveEntryList(
        & CatalogItem->m_CatalogLinkage  // Entry
        );
    assert(m_num_items > 0);
    m_num_items--;
}  // RemoveCatalogItem


DCATALOG.H

/*++

     Copyright c 1996 Intel Corporation
     All Rights Reserved

     Permission is granted to use, copy and distribute this software and
     its documentation for any purpose and without fee, provided, that
     the above copyright notice and this statement appear in all copies.
     Intel makes no representations about the suitability of this
     software for any purpose.  This software is provided "AS IS."

     Intel specifically disclaims all warranties, express or implied,
     and all liability, including consequential and other indirect
     damages, for the use of this software, including liability for
     infringement of any proprietary rights, and including the
     warranties of merchantability and fitness for a particular purpose.
     Intel does not assume any responsibility for any errors which may
     appear in this software nor any responsibility to update it.

Module Name:

    dcatalog.h

Abstract:

    This  module  contains  the  interface  to  the  catalog  of  protocol_info
    structures and their associated providers.

--*/

#ifndef _DCATALOG_
#define _DCATALOG_

#include <winsock2.h>
#include "classfwd.h"
#include "llist.h"

typedef
BOOL
(* CATALOGITERATION) (
    IN DWORD                PassBack,
    IN PPROTO_CATALOG_ITEM  CatalogEntry
    );
/*++

Routine Description:

    CATALOGITERATION  is  a place-holder for a function supplied by the client.
    The  function  is  called once for each PROTO_CATALOG_ITEM structure in the
    catalog while enumerating the catalog.  The client can stop the enumeration
    early by returning FALSE from the function.

    Note  that  the DPROVIDER associated with an enumerated DPROTO_CATALOG_ITEM
    may  be  NULL.   To retrieve DPROTO_CATALOG_ITEM structure that has had its
    DPROVIDER      loaded      and      initialized,      you      can      use
    GetCatalogItemFromCatalogEntryId.

Arguments:

    PassBack     - Supplies  to  the  client an uninterpreted, unmodified value
                   that  was  specified  by the client in the original function
                   that  requested  the  enumeration.   The client can use this
                   value  to  carry context between the requesting site and the
                   enumeration function.

    CatalogEntry - Supplies  to  the client a reference to a PROTO_CATALOG_ITEM
                   structure with values for this item of the enumeration.

Return Value:

    TRUE  - The  enumeration  should continue with more iterations if there are
            more structures to enumerate.

    FALSE - The enumeration should stop with this as the last iteration even if
            there are more structures to enumerate.

--*/

class DCATALOG
{
public:

    DCATALOG();

    INT
    Initialize();

    ~DCATALOG();

    VOID
    EnumerateCatalogItems(
        IN CATALOGITERATION  Iteration,
        IN DWORD             PassBack
        );

    INT
    FindNextProviderInChain(
        IN  LPWSAPROTOCOL_INFOW lpLocalProtocolInfo,
        OUT PDPROVIDER FAR *    NextProvider,
OUT PPROTO_CATALOG_ITEM*BaseProviderCatalogEntry
        );

INT
GetCatalogItemFromCatalogEntryId(
IN  DWORD                     CatalogEntryId,
OUT PPROTO_CATALOG_ITEM FAR * CatalogItem
);

PPROTO_CATALOG_ITEM
    GetLocalProvider ();

    VOID
    AcquireCatalogLock(
        VOID
        );

    VOID
    ReleaseCatalogLock(
        VOID
        );

    VOID
    AppendCatalogItem(
        IN  PPROTO_CATALOG_ITEM  CatalogItem
        );

    VOID
    RemoveCatalogItem(
        IN  PPROTO_CATALOG_ITEM  CatalogItem
        );

private:

    INT
    LoadProvider(
        IN PPROTO_CATALOG_ITEM CatalogEntry,
        IN LPWSAPROTOCOL_INFOW lpChainedProtocolInfo,
        OUT PDPROVIDER FAR* Provider
        );

    LIST_ENTRY  m_protocol_list;
    // The head of the list of protocol catalog items

    CRITICAL_SECTION m_catalog_lock;
    // A critical section object protecting this class.

    DWORD m_num_items;
    // Number of items in this catalog.

    PPROTO_CATALOG_ITEM     m_local_item;
    // Pointer to our own protocol catalog item

};  // class dcatalog

inline
VOID
DCATALOG::AcquireCatalogLock(
    )
/*++

Routine Description:

    Acquires the critical section used to protect the list of catalog items.

Arguments:

    None

Return Value:

    None
--*/
{
    EnterCriticalSection( &m_catalog_lock );
}

inline
VOID
DCATALOG::ReleaseCatalogLock(
    VOID
    )
/*++

Routine Description:

    Releases the critical section used to protect the list of catalog items.

Arguments:

Return Value:

    None
--*/
{
    LeaveCriticalSection( &m_catalog_lock );
}

inline
PPROTO_CATALOG_ITEM
DCATALOG::GetLocalProvider(
    VOID
    )
/*++

Routine Description:

    Returns pointer to catalog item of our own provider

Arguments:

    None

Return Value:

    Pointer to local catalog item object
--*/
{
    return m_local_item;
}

#endif // _DCATALOG


DCATITEM.CPP

/*++

     Copyright (c) 1996 Intel Corporation
     Copyright 1996 - 1998 Microsoft Corporation
     All Rights Reserved

     Permission is granted to use, copy and distribute this software and
     its documentation for any purpose and without fee, provided, that
     the above copyright notice and this statement appear in all copies.
     Intel makes no representations about the suitability of this
     software for any purpose.  This software is provided "AS IS."

     Intel specifically disclaims all warranties, express or implied,
     and all liability, including consequential and other indirect
     damages, for the use of this software, including liability for
     infringement of any proprietary rights, and including the
     warranties of merchantability and fitness for a particular purpose.
     Intel does not assume any responsibility for any errors which may
     appear in this software nor any responsibility to update it.

Module Name:

    dcatitem.cpp

Abstract:

    This  file  contains  the  class  implementation for the PROTO_CATALOG_ITEM
    class.   This  class  defines  the  interface  to  the  entries that can be
    retrieved from the protocol catalog.

--*/

#include "precomp.h"

PROTO_CATALOG_ITEM::PROTO_CATALOG_ITEM()
/*++

Routine Description:

    This  procedure  constructs  an empty PROTO_CATALOG_ITEM object.  The first
    method  invoked  after  this  constructor must be Initialize().

Arguments:

    None

Return Value:

    Implicitly  returns  a pointer to a newly created PROTO_CATALOG_ITEM object
    or NULL if there is a memory allocation failure.
--*/
{
    DEBUGF(
        DBG_TRACE,
        ("PROTO_CATALOG_ITEM constructor\n"));

    // Simply initialize embedded pointer values for safety.
    m_LibraryPath[0] = '\0';
    m_Provider = NULL;
m_ProviderCatalogEntry = NULL;
}  // PROTO_CATALOG_ITEM

INT
PROTO_CATALOG_ITEM::Initialize(
    IN  LPWSAPROTOCOL_INFOW  ProtoInfo
    )
/*++

Routine Description:

    This  procedure initializes the protocol info part of the catalog item from
    the  values  supplied.  Values are copied from the structures passed by the
    caller,  so  the  caller  is  free  to  deallocate the passed structures on
    return.

Arguments:

    ProtoInfo    - Supplies  a reference to the fully initialized protocol info
                   structure describing this protocol.

return Value:

    The  function  returns ERROR_SUCCESS if successful, otherwise it returns an
    appropriate WinSock error code.

--*/
{
    INT ReturnValue = ERROR_SUCCESS;
    INT BufferSize  = sizeof(m_LibraryPath);

    // Copy ProtoInfo
    m_ProtoInfo = *ProtoInfo;

    // Get the LibraryPath
    gUpCallTable.lpWPUGetProviderPath(
        &ProtoInfo->ProviderId,
        (WCHAR*)&m_LibraryPath,
        &BufferSize,
        &ReturnValue);

    if (ERROR_SUCCESS != ReturnValue){
        m_LibraryPath[0] = '\0';
    } //if

    return (ReturnValue);

}  // Initialize

PROTO_CATALOG_ITEM::~PROTO_CATALOG_ITEM()
/*++

Routine Description:

    This  procedure  destroys  a  protocol catalog item, deallocating memory it
    owns.   It  is the caller's responsibility to remove the item from the list
    it  occupies  before  calling  this  procedure.   It  is  also the caller's
    responsibility  to unload and/or destroy any dprovider associated with this
    catalog item if appropriate.

Arguments:

    None

Return Value:

    None
--*/
{
    m_LibraryPath[0] = '\0';
}  // ~PROTO_CATALOG_ITEM

LPWSAPROTOCOL_INFOW
PROTO_CATALOG_ITEM::GetProtocolInfo()
/*++

Routine Description:

    This  procedure  retrieves a reference to the protocol info associated with
    the  catalog  item.   Note  that  the  reference is to storage owned by the
    catalog item.  It is the caller's responsibility to make sure the reference
    is no longer used after the catalog item is destroyed.

Arguments:

    None

Return Value:

    Returns a pointer to the associated protocol info.
--*/
{
    assert(m_LibraryPath[0] != '\0');
    return(& m_ProtoInfo);
}  // GetProtocolInfo

PWCHAR
PROTO_CATALOG_ITEM::GetLibraryPath()
/*++

Routine Description:

    This    procedure   retrieves   a   reference   to   the   zero-terminated,
    fully-qualified  path  of  the library that is the service provider for the
    protocol  associated  with the catalog item.  Note that the reference is to
    storage  owned  by  the catalog item.  It is the caller's responsibility to
    make  sure  the  reference  is  no  longer  used  after the catalog item is
    destroyed.

Arguments:

    None

Return Value:

    Returns a pointer to the library path string.
--*/
{
    assert(m_LibraryPath[0] != '\0');
    return(m_LibraryPath);
}  // GetLibraryPath

VOID
PROTO_CATALOG_ITEM::SetProviderCatalogEntry(
    IN  PPROTO_CATALOG_ITEM  CatalogEntry
    )
/*++

Routine Description:

    This procedure sets the PPROTO_CATALOG_ENTRY

Arguments:

    Provider - Supplies the new catalog entry reference.

Return Value:

    None
--*/
{
    if (m_ProviderCatalogEntry != NULL) {
        DEBUGF(
            DBG_WARN,
            ("Suspicious - overwriting a catalog entry reference\n"));
    }
    m_ProviderCatalogEntry = CatalogEntry;
}  // SetProviderCatalogEntry

VOID
PROTO_CATALOG_ITEM::SetProvider(
    IN  PDPROVIDER  Provider
    )
/*++

Routine Description:

    This procedure sets the DPROVIDER associated with a catalog entry.

Arguments:

    Provider - Supplies the new DPROVIDER reference.

Return Value:

    None
--*/
{
    if (m_Provider != NULL) {
        DEBUGF(
            DBG_WARN,
            ("Suspicious - overwriting a dprovider reference\n"));
    }
    m_Provider = Provider;
}  // SetProvider

PDPROVIDER
PROTO_CATALOG_ITEM::GetProvider()
/*++

Routine Description:

    This  procedure  retrieves  a  reference to the DPROVIDER associated with a
    catalog  entry.  Note that the reference may be NULL if no provider has yet
    been loaded for this protocol.

Arguments:

    None

Return Value:

    Returns  the  current provider reference, or NULL if there is no associated
    provider.
--*/
{
    return(m_Provider);
}  // GetProvider

PPROTO_CATALOG_ITEM
PROTO_CATALOG_ITEM::GetProviderCatalogEntry()
/*++

Routine Description:

    This  procedure  retrieves  a  reference to the protocol catalog entry associated with a
    catalog  entry.  Note that the reference may be NULL if no provider has yet
    been loaded for this protocol.

Arguments:

    None

Return Value:

    Returns  the  current provider catalog entry reference, or NULL if there is no associated
    provider.
--*/
{
    return(m_ProviderCatalogEntry);
}


DCATITEM.H

/*++

     Copyright c 1996 Intel Corporation
     All Rights Reserved

     Permission is granted to use, copy and distribute this software and
     its documentation for any purpose and without fee, provided, that
     the above copyright notice and this statement appear in all copies.
     Intel makes no representations about the suitability of this
     software for any purpose.  This software is provided "AS IS."

     Intel specifically disclaims all warranties, express or implied,
     and all liability, including consequential and other indirect
     damages, for the use of this software, including liability for
     infringement of any proprietary rights, and including the
     warranties of merchantability and fitness for a particular purpose.
     Intel does not assume any responsibility for any errors which may
     appear in this software nor any responsibility to update it.

Module Name:

    dcatitem.h

Abstract:

    This  file  contains the class definition for the PROTO_CATALOG_ITEM class.
    This  class  defines the interface to the entries that can be installed and
    retrieved in the protocol catalog.

--*/

#ifndef _DCATITEM_
#define _DCATITEM_

#include <winsock2.h>
#include "llist.h"
#include "classfwd.h"

class PROTO_CATALOG_ITEM {
public:

    PROTO_CATALOG_ITEM();

    INT
    Initialize(
        LPWSAPROTOCOL_INFOW  ProtoInfo
        );

    ~PROTO_CATALOG_ITEM();

    LPWSAPROTOCOL_INFOW
    GetProtocolInfo();

    PWCHAR
    GetLibraryPath();

    VOID
    SetProvider(
        IN  PDPROVIDER  Provider
        );

    VOID
    SetProviderCatalogEntry(
        IN  PPROTO_CATALOG_ITEM  CatalogEntry
        );

    PDPROVIDER
    GetProvider();

    PPROTO_CATALOG_ITEM
    GetProviderCatalogEntry();

    LIST_ENTRY     m_CatalogLinkage;
    // Used  to  link  items  in  catalog.   Note  that  this particular member
    // variable  is in the public section to make it available for manipulation
    // by the catalog object.

private:

    WCHAR m_LibraryPath[MAX_PATH];
    // Fully qualified path to the provider's DLL image.

    WSAPROTOCOL_INFOW  m_ProtoInfo;
    // The cataloged WSAPROTOCOL_INFOA structure.  This is typically used for
    // comparison  when  selecting  a  provider by address family, socket
    // type, etc.

    PDPROVIDER  m_Provider;
    // Pointer to the dprovider object attached to this catalog entry.

PPROTO_CATALOG_ITEM m_ProviderCatalogEntry;
// Pointer to the attached provider catalog item

};  // class PROTO_CATALOG_ITEM

#endif // _DCATITEM_


DLLMAIN.CPP

/*++

     Copyright (c) 1996 Intel Corporation
     Copyright 1996 - 1998 Microsoft Corporation
     All Rights Reserved

     Permission is granted to use, copy and distribute this software and
     its documentation for any purpose and without fee, provided, that
     the above copyright notice and this statement appear in all copies.
     Intel makes no representations about the suitability of this
     software for any purpose.  This software is provided "AS IS."

     Intel specifically disclaims all warranties, express or implied,
     and all liability, including consequential and other indirect
     damages, for the use of this software, including liability for
     infringement of any proprietary rights, and including the
     warranties of merchantability and fitness for a particular purpose.
     Intel does not assume any responsibility for any errors which may
     appear in this software nor any responsibility to update it.

Module Name:

    dllmain.cpp

Abstract:
    This module contains the DllMain entry point for lsp.dll to
    control the global init and shutdown of the DLL.

--*/

#pragma warning(disable: 4001)      /* Single-line comment */

#include "precomp.h"

HINSTANCE   HDllInstance;
DWORD       TlsIndex=0xFFFFFFFF;

BOOL WINAPI DllMain(
    IN HINSTANCE hinstDll,
    IN DWORD fdwReason,
    LPVOID lpvReserved
    )
{

   switch (fdwReason) {

   case DLL_PROCESS_ATTACH:
      // DLL is attaching to the address
      // space of the current process.
      DTHookInitialize();
      InitializeCriticalSection(&gInitCriticalSection);
      HDllInstance = hinstDll;
  TlsIndex = TlsAlloc();
      break;

   case DLL_THREAD_ATTACH:
      // A new thread is being created in the current process.
      break;

   case DLL_THREAD_DETACH:
      // A thread is exiting cleanly.
      break;

   case DLL_PROCESS_DETACH:
      // The calling process is detaching
      // the DLL from its address space.
      //
      // Note that lpvReserved will be NULL if the detach is due to
      // a FreeLibrary() call, and non-NULL if the detach is due to
      // process cleanup.
      //

  if( lpvReserved == NULL ) {
  if (TlsIndex!=0xFFFFFFFF) {
  TlsFree (TlsIndex);
  TlsIndex = 0xFFFFFFFF;
  }
      }

  DeleteCriticalSection(&gInitCriticalSection);
      DTHookShutdown();
      break;
   }

   return(TRUE);
}


DOVERLAP.CPP

/*++

     Copyright (c) 1996 Intel Corporation
     Copyright 1996 - 1998 Microsoft Corporation
     All Rights Reserved

     Permission is granted to use, copy and distribute this software and
     its documentation for any purpose and without fee, provided, that
     the above copyright notice and this statement appear in all copies.
     Intel makes no representations about the suitability of this
     software for any purpose.  This software is provided "AS IS."

     Intel specifically disclaims all warranties, express or implied,
     and all liability, including consequential and other indirect
     damages, for the use of this software, including liability for
     infringement of any proprietary rights, and including the
     warranties of merchantability and fitness for a particular purpose.
     Intel does not assume any responsibility for any errors which may
     appear in this software nor any responsibility to update it.

Module Name:

    doverlap.cpp

Abstract:

    This module defines the layered class dprovider along with its methods.

--*/

#include "precomp.h"

DOVERLAPPEDSTRUCTMGR::DOVERLAPPEDSTRUCTMGR()
/*++

Routine Description:

    DOVERLAPPEDSTRUCTMGR  object  constructor.   Creates and returns a
    DOVERLAPPEDSTRUCTMGR object.  Note that  the  DOVERLAPPEDSTRUCTMGR object
    has not been fully initialized.  The "Initialize" member function must be
    the first member function called on the new DOVERLAPPEDSTRUCTMGR object.

Arguments:

    None

Return Value:

    None
--*/
{
    // Setup the free list
    m_overlapped_free_list.Next = NULL;
    m_overlapped_struct_block = NULL;
    InitializeCriticalSection(&m_overlapped_free_list_lock);
}

INT
DOVERLAPPEDSTRUCTMGR::Initialize(
        )
/*++

Routine Description:

    The initialization routine for a DOVERLAPPEDSTRUCTMGR  object.  This
    procedure completes the initialzation of the object.  This procedure
    preforms initialization operations that may fail and must be reported since
    there is no way to fail the constructor.

Arguments:

    None
Return Value:

    The  function returns NO_ERROR if successful.  Otherwise it
    returns an appropriate WinSock error code if the initialization
    cannot be completed.
--*/
{
    INT   ReturnCode;
    ULONG BlockSize;
    PBYTE CurrentBlock;
    ULONG BytesAvailable;
    ULONG StructSize;

    ReturnCode = WSAENOBUFS;

    //
    // Initialize the pool of internal overlapped structs.
    //

    // Get memory for our overlapped structs
    BlockSize = sizeof(INTERNALOVERLAPPEDSTRUCT) *
        OUTSTANDINGOVERLAPPEDSTRUCTS;
    m_overlapped_struct_block = (PBYTE) new BYTE[BlockSize];

    if (m_overlapped_struct_block){

        // Dice up the memory block into internal overlapped structs and add
        // them to the free list.
        EnterCriticalSection(&m_overlapped_free_list_lock);

        StructSize = sizeof(INTERNALOVERLAPPEDSTRUCT);

        BytesAvailable = BlockSize;
        CurrentBlock   = m_overlapped_struct_block;

        while (BytesAvailable > StructSize){
            PushEntryList(&m_overlapped_free_list,
                          (PSINGLE_LIST_ENTRY) CurrentBlock);

            BytesAvailable -= StructSize;
            CurrentBlock += StructSize;
        } //while
        ReturnCode = NO_ERROR;
        LeaveCriticalSection(&m_overlapped_free_list_lock);
    } //if

    return(ReturnCode);
}

DOVERLAPPEDSTRUCTMGR::~DOVERLAPPEDSTRUCTMGR()
/*++

Routine Description:

    DOVERLAPPEDSTRUCTMGR object destructor.  This procedure has the
    responsibility to perform any required shutdown operations for the
    DOVERLAPPEDSTRUCTMGR object before the object memory is deallocated.

Arguments:

    None

Return Value:

    None
--*/
{
    // Clean up the overlapped struct pool
    EnterCriticalSection(&m_overlapped_free_list_lock);
    delete(m_overlapped_struct_block);
    m_overlapped_free_list.Next = NULL;
    LeaveCriticalSection(&m_overlapped_free_list_lock);
    DeleteCriticalSection(&m_overlapped_free_list_lock);
}

PINTERNALOVERLAPPEDSTRUCT
DOVERLAPPEDSTRUCTMGR::AllocateOverlappedStruct(
    )
/*++

Routine Description:

    Allocates an interanl overlapped structure from the pool of available
    structures.
Arguments:

    None

Return Value:

    A pointer the a internal overlapped struct on success else NULL.

--*/
{
    PINTERNALOVERLAPPEDSTRUCT ReturnValue;

    ReturnValue = PopOverlappedStruct();

    if (ReturnValue){
        ReturnValue->iolSignature = STRUCTSIGNATURE;
    } //if

    return(ReturnValue);
}

VOID
DOVERLAPPEDSTRUCTMGR::FreeOverlappedStruct(
    LPWSAOVERLAPPED   OverlappedStruct
    )
/*++

Routine Description:

    Frees an interanl overlapped structure to the pool of available
    structures.
Arguments:

    OverlappedStruct - A pointer to the InternalOverlappedStruct member of an
    interanl overlapped struct.

Return Value:

    NONE

--*/
{
    PushOverlappedStruct(OverlappedStruct);
}

PINTERNALOVERLAPPEDSTRUCT
DOVERLAPPEDSTRUCTMGR::PopOverlappedStruct()
/*++

Routine Description:

    Pops a internal overlapped structure off of the free list and initializes
    the structure.

Arguments:

    NONE
Return Value:

    A pointer to a internal overlapped structure on success else NULL

--*/
{
    PINTERNALOVERLAPPEDSTRUCT ReturnValue;

    EnterCriticalSection(&m_overlapped_free_list_lock);

    ReturnValue =
        (PINTERNALOVERLAPPEDSTRUCT)PopEntryList(&m_overlapped_free_list);

    LeaveCriticalSection(&m_overlapped_free_list_lock);

    if (ReturnValue){

        // Init the structure
        ZeroMemory(
            ReturnValue,
            sizeof(INTERNALOVERLAPPEDSTRUCT));
        ReturnValue->iolSignature = STRUCTSIGNATURE;
    } //if

    return(ReturnValue);
}

VOID
DOVERLAPPEDSTRUCTMGR::PushOverlappedStruct(
    LPWSAOVERLAPPED OverlappedStruct
    )
/*++

Routine Description:

    Pushes an internal overlapped structure onto the free list.

Arguments:

    A pointer to the InteranlOverlappedStruct member of an internal overlapped
    struct.
Return Value:

    NONE
--*/
{
    PINTERNALOVERLAPPEDSTRUCT InternalOverlappedStruct;

    InternalOverlappedStruct = CONTAINING_RECORD (
                            OverlappedStruct,
                            INTERNALOVERLAPPEDSTRUCT,
                            iolInternalOverlappedStruct);

    assert(STRUCTSIGNATURE == InternalOverlappedStruct->iolSignature);

    EnterCriticalSection(&m_overlapped_free_list_lock);

    PushEntryList(&m_overlapped_free_list,
                  (PSINGLE_LIST_ENTRY)InternalOverlappedStruct);

    LeaveCriticalSection(&m_overlapped_free_list_lock);

}

PINTERNALOVERLAPPEDSTRUCT
DOVERLAPPEDSTRUCTMGR::GetInternalOverlappedStructure(
    IN  LPWSAOVERLAPPED                     pCompletedOverlappedStruct
    )
{
    PINTERNALOVERLAPPEDSTRUCT pOverlappedStruct;

    pOverlappedStruct = CONTAINING_RECORD (
                            pCompletedOverlappedStruct,
                            INTERNALOVERLAPPEDSTRUCT,
                            iolInternalOverlappedStruct);

    if (STRUCTSIGNATURE == pOverlappedStruct->iolSignature)
        return pOverlappedStruct;
else
return(NULL);
}


DOVERLAP.H

/*++

     Copyright c 1996 Intel Corporation
     All Rights Reserved

     Permission is granted to use, copy and distribute this software and
     its documentation for any purpose and without fee, provided, that
     the above copyright notice and this statement appear in all copies.
     Intel makes no representations about the suitability of this
     software for any purpose.  This software is provided "AS IS."

     Intel specifically disclaims all warranties, express or implied,
     and all liability, including consequential and other indirect
     damages, for the use of this software, including liability for
     infringement of any proprietary rights, and including the
     warranties of merchantability and fitness for a particular purpose.
     Intel does not assume any responsibility for any errors which may
     appear in this software nor any responsibility to update it.

Module Name:

doverlap.h

Abstract:

    This module defines the DOVERLAPPEDSTRUCTMGR class.  This class
maintains a pool of internal overlapped structures.  These structures
are used to store the information the layered service provider will
need to satisfy users overlapped I/O requests.

--*/

#ifndef _DOVERLAPPEDSTRUCTMGR_
#define _DOVERLAPPEDSTRUCTMGR_

#include <winsock2.h>
#include "llist.h"
#include "classfwd.h"
#include "stddef.h"

#define  OUTSTANDINGOVERLAPPEDSTRUCTS 1000
#define  STRUCTSIGNATURE 0xbeadface
// Maximum number of buffers inside the structure (if we have to handle
// more than that, we'll allocate from heap)
#define  MAX_FAST_BUFS 8

// The internal overlapped struct
typedef struct _INTERNALOVERLAPPEDSTRUCT
{
    DWORD                              iolSignature;
    DWORD                              iolOperationType;
SOCKET   iolSocket;
    PDPROVIDER                         iolProvider;
union {
SOCKET                             iolProviderSocket;
SOCKET   iolListenSocket;
};
    WSATHREADID                        iolUserThreadId;
    LPWSAOVERLAPPED                    iolUserOverlappedStruct;
LPWSAOVERLAPPED_COMPLETION_ROUTINE iolUserCompletionRoutine;
union {
TRANSMIT_FILE_BUFFERS   iolTransmitBuffers;
struct {
union {
WSABUF   UserBuffers[MAX_FAST_BUFS];
#define iolUserBuffers iolRest.UserBuffers
LPWSABUF   pUserBuffers;
#define iolpUserBuffers iolRest.pUserBuffers
LPVOID   InputBuffer;
#define iolInputBuffer iolRest.InputBuffer
};
union {
DWORD                              UserBufferCount;
#define iolUserBufferCount iolRest.UserBufferCount
DWORD   InputBufferLength;
#define iolInputBufferLength iolRest.InputBufferLength
};
union {
WSABUF                             InternalBuffers[MAX_FAST_BUFS];
#define iolInternalBuffers iolRest.InternalBuffers
LPWSABUF                           pInternalBuffers;
#define iolpInternalBuffers iolRest.pInternalBuffers
LPVOID   OutputBuffer;
#define iolOutputBuffer iolRest.OutputBuffer
};
union {
DWORD                              InternalBufferCount;
#define iolInternalBufferCount iolRest.InternalBufferCount
DWORD   OutputBufferLength;
#define iolOutputBufferLength iolRest.OutputBufferLength
};
}iolRest;
};
union {
struct sockaddr FAR *              iolSockAddr;
DWORD   iolLocalAddressLength;
DWORD   iolBytesToWrite;
};
union {
    LPINT                              iolSockAddrLenPtr;
INT                                iolSockAddrLen;
DWORD   iolRemoteAddressLength;
DWORD   iolBytesPerSend;
};
union {
DWORD                              iolFlags;
DWORD   iolIoControlCode;
SOCKET   iolAcceptSocket;
HANDLE   iolFileHandle;
};
DWORD   iolReserved;
    LIST_ENTRY                         iolListLinkage;
    WSAOVERLAPPED                      iolInternalOverlappedStruct;
} INTERNALOVERLAPPEDSTRUCT, *PINTERNALOVERLAPPEDSTRUCT;

class DOVERLAPPEDSTRUCTMGR
{
  public:

    DOVERLAPPEDSTRUCTMGR();

    INT
    Initialize(
        );

    ~DOVERLAPPEDSTRUCTMGR();

    PINTERNALOVERLAPPEDSTRUCT
    AllocateOverlappedStruct(
        );

    VOID
    FreeOverlappedStruct(
        LPWSAOVERLAPPED   pOverlappedStruct
        );

    PINTERNALOVERLAPPEDSTRUCT
GetInternalOverlappedStructure (
LPWSAOVERLAPPED  pOverlappedStruct
);

  private:

    PINTERNALOVERLAPPEDSTRUCT
    PopOverlappedStruct();

    VOID
    PushOverlappedStruct(
        LPWSAOVERLAPPED OverlappedStruct
        );

    SINGLE_LIST_ENTRY m_overlapped_free_list;
    // A list of available internal overlapped structs.

    CRITICAL_SECTION  m_overlapped_free_list_lock;
    // Syncronization object for updateing m_overlapped_free_list.

    PBYTE             m_overlapped_struct_block;
    // Pointer to the memory block containing the internal overlapped
    // structures.

};   // class DOVERLAPPEDSTRUCTMGR

#endif // _DOVERLAPPEDSTRUCTMGR_


DPROVIDE.CPP

/*++

     Copyright (c) 1996 Intel Corporation
     Copyright 1996 - 1998 Microsoft Corporation
     All Rights Reserved

     Permission is granted to use, copy and distribute this software and
     its documentation for any purpose and without fee, provided, that
     the above copyright notice and this statement appear in all copies.
     Intel makes no representations about the suitability of this
     software for any purpose.  This software is provided "AS IS."

     Intel specifically disclaims all warranties, express or implied,
     and all liability, including consequential and other indirect
     damages, for the use of this software, including liability for
     infringement of any proprietary rights, and including the
     warranties of merchantability and fitness for a particular purpose.
     Intel does not assume any responsibility for any errors which may
     appear in this software nor any responsibility to update it.

Module Name:

    dprovide.cpp

Abstract:

    This module defines the class dprovider along with its methods.

--*/

#include "precomp.h"
#include <stdlib.h>

GUID TransmitFileGuid = WSAID_TRANSMITFILE;
GUID AcceptExGuid = WSAID_ACCEPTEX;
GUID GetAcceptExSockAddrsGuild = WSAID_GETACCEPTEXSOCKADDRS;

DPROVIDER::DPROVIDER()
/*++
Routine Description:

    Creates any internal state.

Arguments:

    None

Return Value:

    None

--*/

{
    m_proctable = NULL;
    m_library_handle = NULL;
    m_lib_name = NULL;
m_extensions_initialized;
}

DPROVIDER::~DPROVIDER()
/*++
Routine Description:

    destroys any internal state.

Arguments:

    None

Return Value:

    None

--*/
{
    int ErrorCode;

    if (m_library_handle)
    {
        if (m_proctable->lpWSPCleanup)
        {
            DEBUGF( DBG_TRACE,
                    ("\nCalling WSPCleanup for provider %X", this));
            //Call the servce provider cleanup routine
            WSPCleanup(&ErrorCode);
        } //if

        // Free the service provider DLL
        FreeLibrary(m_library_handle);
    } //if

    if(m_proctable){
        delete m_proctable;
    }
    delete m_lib_name;
    DEBUGF( DBG_TRACE,
            ("\nDestroying provider %X", this));
}

INT
DPROVIDER::Initialize(
    IN PWCHAR lpszLibFile,
    IN LPWSAPROTOCOL_INFOW lpProtocolInfo
    )
/*++
Routine Description:

    Initializes the DPROVIDER object.

Arguments:

    lpszLibFile - A  Null  terminating  string  that  points to the .DLL of the
                  service to load.

    lpProtocolInfo - A pointer to a WSAPROTOCOL_INFO struct to hand to the
                     provider startup routine.

Return Value:

    If no error occurs, Initialize() returns ERROR_SUCEES.  Otherwise the value
    SOCKET_ERROR  is  returned,  and  a  specific  error  code  is available in
    lpErrno.

--*/
{
    LPWSPSTARTUP        WSPStartupFunc          = NULL;
    WORD                wVersionRequested       = MAKEWORD(2,2);
    INT                 error_code              = 0;
    WSPDATA             WSPData;
    CHAR                LibraryPath[MAX_PATH];
    CHAR                lpszLibFileA[MAX_PATH];

    DEBUGF( DBG_TRACE,
            ("\nInitializing provider %X", this));

    m_proctable = new WSPPROC_TABLE;
    if(!m_proctable){
        DEBUGF(
            DBG_ERR,
            ("\nFailed to allocate WSPPROC_TABLE for provider object"));
        return WSA_NOT_ENOUGH_MEMORY;
    }

    // Zero out contents of m_proctable
    ZeroMemory(
        (PVOID) m_proctable,      // Destination
        sizeof(LPWSPPROC_TABLE)); // Length

    //
    // Expand the library name to pickup environment/registry variables
    //
    wcstombs (lpszLibFileA, lpszLibFile, MAX_PATH);
    if (!( ExpandEnvironmentStrings(lpszLibFileA,
                                    LibraryPath,
                                    MAX_PATH))){
        DEBUGF(
            DBG_ERR,
            ("\nExpansion of environment variables failed"));
        return WSASYSCALLFAILURE;
    } //if

m_lib_name = new char[strlen(LibraryPath)+1];

strcpy(m_lib_name,LibraryPath);

    //
    // First load the DLL for the service provider. Then get two functions that
    // init the service provider structures.
    //
    m_library_handle = LoadLibrary(LibraryPath);
    if(!m_library_handle){
        DEBUGF(
            DBG_ERR,
            ("\nFailed to load DLL %s",LibraryPath));
        return  WSAEPROVIDERFAILEDINIT;
    }

    WSPStartupFunc = (LPWSPSTARTUP)GetProcAddress(
        m_library_handle,
        "WSPStartup"
        );

    if(!(WSPStartupFunc)){

        DEBUGF( DBG_ERR,("\nCould get startup entry point for %s",
                         lpszLibFile));
        return  WSAEPROVIDERFAILEDINIT;
    }

#if !defined(DEBUG_TRACING)
    error_code = (*WSPStartupFunc)(
        wVersionRequested,
        & WSPData,
        lpProtocolInfo,
        gUpCallTable,
        m_proctable);
#else
    { // declaration block
        LPWSPDATA  pWSPData = & WSPData;
        BOOL       bypassing_call;

        bypassing_call = PREAPINOTIFY((
            DTCODE_WSPStartup,
            & error_code,
            LibraryPath,
            & wVersionRequested,
            & pWSPData,
            & lpProtocolInfo,
            gUpCallTable,
            m_proctable));
        if (! bypassing_call) {
            error_code = (*WSPStartupFunc)(
                wVersionRequested,
                & WSPData,
                lpProtocolInfo,
                gUpCallTable,
                m_proctable);
            POSTAPINOTIFY((
                DTCODE_WSPStartup,
                & error_code,
                LibraryPath,
                & wVersionRequested,
                & pWSPData,
                & lpProtocolInfo,
                gUpCallTable.lpWPUCloseEvent,
                m_proctable->lpWSPAccept));
        } // if ! bypassing_call
    } // declaration block
#endif // !defined(DEBUG_TRACING)

    if(ERROR_SUCCESS != error_code){
        DEBUGF(DBG_ERR, ("\nWSPStartup for %s Failed",lpszLibFile));
        return error_code;
    }

    //
    // Make sure that all of the procedures at least have a non null pointer.
    //
    if( !m_proctable->lpWSPAccept              ||
        !m_proctable->lpWSPAddressToString     ||
        !m_proctable->lpWSPAsyncSelect         ||
        !m_proctable->lpWSPBind                ||
        !m_proctable->lpWSPCancelBlockingCall  ||
        !m_proctable->lpWSPCleanup             ||
        !m_proctable->lpWSPCloseSocket         ||
        !m_proctable->lpWSPConnect             ||
        !m_proctable->lpWSPDuplicateSocket     ||
        !m_proctable->lpWSPEnumNetworkEvents   ||
        !m_proctable->lpWSPEventSelect         ||
        !m_proctable->lpWSPGetOverlappedResult ||
        !m_proctable->lpWSPGetPeerName         ||
        !m_proctable->lpWSPGetSockName         ||
        !m_proctable->lpWSPGetSockOpt          ||
        !m_proctable->lpWSPGetQOSByName        ||
        !m_proctable->lpWSPIoctl               ||
        !m_proctable->lpWSPJoinLeaf            ||
        !m_proctable->lpWSPListen              ||
        !m_proctable->lpWSPRecv                ||
        !m_proctable->lpWSPRecvDisconnect      ||
        !m_proctable->lpWSPRecvFrom            ||
        !m_proctable->lpWSPSelect              ||
        !m_proctable->lpWSPSend                ||
        !m_proctable->lpWSPSendDisconnect      ||
        !m_proctable->lpWSPSendTo              ||
        !m_proctable->lpWSPSetSockOpt          ||
        !m_proctable->lpWSPShutdown            ||
        !m_proctable->lpWSPSocket              ||
        !m_proctable->lpWSPStringToAddress ){

        DEBUGF(DBG_ERR,
               ("\nService provider %s returned an invalid procedure table",
                lpszLibFile));
        return WSAEINVALIDPROCTABLE;
    }

    //
    // Confirm that the WinSock service provider supports 2.0. If it supports a
    // version greater then 2.0 it will still return 2.0 since this is the
    // version  we requested.
    //
    if(WSPData.wVersion != MAKEWORD(2,2)){
        if(m_proctable->lpWSPCleanup) {
            if(m_proctable->lpWSPCleanup(&error_code)){
                DEBUGF( DBG_ERR,
                        ("\nService Provider %s does not support version 2.2",
                         lpszLibFile));
                return WSAVERNOTSUPPORTED;
            }
        }
        return WSAEINVALIDPROVIDER;
    }

    return ERROR_SUCCESS;
} //Initailize

INT
DPROVIDER::InterceptExtensions (
IN LPVOIDlpvInBuffer,
OUT LPVOIDlpvOutBuffer,
OUT LPINTlpErrno
)
{
INTReturnValue=NO_ERROR;

if (memcmp (lpvInBuffer,
&TransmitFileGuid,
sizeof (GUID))==0) {
m_transmitfile = *((LPFN_TRANSMITFILE *)lpvOutBuffer);
*((LPFN_TRANSMITFILE *)lpvOutBuffer) = WSPTransmitFile;
}
else if (memcmp (lpvInBuffer,
&AcceptExGuid,
sizeof (GUID))==0) {
m_acceptex = *((LPFN_ACCEPTEX *)lpvOutBuffer);
*((LPFN_ACCEPTEX *)lpvOutBuffer) = WSPAcceptEx;
}
else if (memcmp (lpvInBuffer,
&GetAcceptExSockAddrsGuild,
sizeof (GUID))==0)
// No translation required because no socket handle is
// involved in this call
;
else {
ReturnValue = SOCKET_ERROR;
*lpErrno = WSAEOPNOTSUPP;
}

return ReturnValue;
}


DPROVIDE.H

/*++

     Copyright c 1996 Intel Corporation
     All Rights Reserved

     Permission is granted to use, copy and distribute this software and
     its documentation for any purpose and without fee, provided, that
     the above copyright notice and this statement appear in all copies.
     Intel makes no representations about the suitability of this
     software for any purpose.  This software is provided "AS IS."

     Intel specifically disclaims all warranties, express or implied,
     and all liability, including consequential and other indirect
     damages, for the use of this software, including liability for
     infringement of any proprietary rights, and including the
     warranties of merchantability and fitness for a particular purpose.
     Intel does not assume any responsibility for any errors which may
     appear in this software nor any responsibility to update it.

Module Name:

        dprovide.h

Abstract:

        This module defines the layered service provider class dprovider along
        with its methods.

--*/
#ifndef _DPROVIDER_
#define _DPROVIDER_

#include <winsock2.h>
#include <ws2spi.h>
#include <mswsock.h>
#include "llist.h"
#include "dthook.h"

BOOL
PASCAL FAR
WSPTransmitFile (
IN SOCKET hSocket,
IN HANDLE hFile,
IN DWORD nNumberOfBytesToWrite,
IN DWORD nNumberOfBytesPerSend,
IN LPOVERLAPPED lpOverlapped,
IN LPTRANSMIT_FILE_BUFFERS lpTransmitBuffers,
IN DWORD dwReserved
);

BOOL
PASCAL FAR
WSPAcceptEx (
IN SOCKET sListenSocket,
IN SOCKET sAcceptSocket,
IN PVOID lpOutputBuffer,
IN DWORD dwReceiveDataLength,
IN DWORD dwLocalAddressLength,
IN DWORD dwRemoteAddressLength,
OUT LPDWORD lpdwBytesReceived,
IN LPOVERLAPPED lpOverlapped
);

class DPROVIDER {

  public:

    DPROVIDER();

    ~DPROVIDER();

    INT
    Initialize(
        IN PWCHAR lpszLibFile,
        IN LPWSAPROTOCOL_INFOW lpProtocolInfo);

    SOCKET
    WSPAccept(
        IN SOCKET s,
        OUT struct sockaddr FAR *addr,
        OUT INT FAR *addrlen,
        IN LPCONDITIONPROC lpfnCondition,
        IN DWORD dwCallbackData,
        OUT INT FAR *lpErrno);

    INT
    WSPAddressToString(
        IN     LPSOCKADDR lpsaAddress,
        IN     DWORD dwAddressLength,
        IN     LPWSAPROTOCOL_INFOW lpProtocolInfo,
        OUT    LPWSTR lpszAddressString,
        IN OUT LPDWORD lpdwAddressStringLength,
        OUT    LPINT lpErrno );

    INT
    WSPAsyncSelect(
        IN SOCKET s,
        IN HWND hWnd,
        IN unsigned int wMsg,
        IN long lEvent,
        OUT INT FAR *lpErrno);

    INT
    WSPBind(
        IN SOCKET s,
        IN const struct sockaddr FAR *name,
        IN INT namelen,
        OUT INT FAR *lpErrno);

    INT
    WSPCancelBlockingCall(
        OUT INT FAR *lpErrno);

    INT
    WSPCleanup(
        OUT INT FAR *lpErrno);

    INT
    WSPCloseSocket(
        IN SOCKET s,
        OUT INT FAR *lpErrno);

    INT
    WSPConnect(
        IN SOCKET s,
        IN const struct sockaddr FAR *name,
        IN INT namelen,
        IN LPWSABUF lpCallerData,
        IN LPWSABUF lpCalleeData,
        IN LPQOS lpSQOS,
        IN LPQOS lpGQOS,
        OUT INT FAR *lpErrno);

    INT
    WSPDuplicateSocket(
        IN SOCKET s,
        IN DWORD dwProcessID,
        IN LPWSAPROTOCOL_INFOW lpProtocolInfo,
        OUT INT FAR *lpErrno);

    INT
    WSPEnumNetworkEvents(
        IN SOCKET s,
        OUT WSAEVENT hEventObject,
        OUT LPWSANETWORKEVENTS lpNetworkEvents,
        OUT INT FAR *lpErrno);

    INT
    WSPEventSelect(
        IN SOCKET s,
        IN OUT WSAEVENT hEventObject,
        IN long lNetworkEvents,
        OUT INT FAR *lpErrno);

    INT
    WSPGetOverlappedResult(
        IN SOCKET s,
        IN LPWSAOVERLAPPED lpOverlapped,
        IN LPDWORD lpcbTransfer,
        IN BOOL fWait,
        OUT LPDWORD lpdwFlags,
        OUT INT FAR *lpErrno);

    INT
    WSPGetPeerName(
        IN SOCKET s,
        OUT struct sockaddr FAR *name,
        OUT INT FAR *namelen,
        OUT INT FAR *lpErrno);

    INT
    WSPGetQOSByName(
        IN SOCKET s,
        IN LPWSABUF lpQOSName,
        IN LPQOS lpQOS,
        OUT INT FAR *lpErrno);

    INT
    WSPGetSockName(
        IN SOCKET s,
        OUT struct sockaddr FAR *name,
        OUT INT FAR *namelen,
        OUT INT FAR *lpErrno);

    INT
    WSPGetSockOpt(
        IN SOCKET s,
        IN INT level,
        IN INT optname,
        OUT char FAR *optval,
        OUT INT FAR *optlen,
        OUT INT FAR *lpErrno);

    INT
    WSPIoctl(
        IN SOCKET s,
        IN DWORD dwIoControlCode,
        IN LPVOID lpvInBuffer,
        IN DWORD cbInBuffer,
        IN LPVOID lpvOutBuffer,
        IN DWORD cbOutBuffer,
        IN LPDWORD lpcbBytesReturned,
        IN LPWSAOVERLAPPED lpOverlapped,
        IN LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine,
        IN LPWSATHREADID lpThreadId,
        OUT INT FAR *lpErrno);

    INT
    WSPJoinLeaf(
        IN SOCKET s,
        IN const struct sockaddr FAR *name,
        IN INT namelen,
        IN LPWSABUF lpCallerData,
        IN LPWSABUF lpCalleeData,
        IN LPQOS lpSQOS,
        IN LPQOS lpGQOS,
        IN DWORD dwFlags,
        OUT INT FAR *lpErrno);

    INT
    WSPListen(
        IN SOCKET s,
        IN INT backlog,
        OUT INT FAR *lpErrno);

    INT
    WSPRecv(
        IN SOCKET s,
        IN LPWSABUF lpBuffers,
        IN DWORD dwBufferCount,
        IN LPDWORD lpNumberOfBytesRecvd,
        IN OUT LPDWORD lpFlags,
        IN LPWSAOVERLAPPED lpOverlapped,
        IN LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine,
        IN LPWSATHREADID lpThreadId,
        OUT INT FAR *lpErrno);

    INT
    WSPRecvDisconnect(
        IN SOCKET s,
        IN LPWSABUF lpInboundDisconnectData,
        OUT INT FAR *lpErrno);

    INT
    WSPRecvFrom(
        IN SOCKET s,
        IN LPWSABUF lpBuffers,
        IN DWORD dwBufferCount,
        IN LPDWORD lpNumberOfBytesRecvd,
        IN OUT LPDWORD lpFlags,
        OUT  struct sockaddr FAR *  lpFrom,
        IN LPINT lpFromlen,
        IN LPWSAOVERLAPPED lpOverlapped,
        IN LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine,
        IN LPWSATHREADID lpThreadId,
        OUT INT FAR *lpErrno);

    INT
    WSPSelect(
        IN INT nfds,
        IN OUT fd_set FAR *readfds,
        IN OUT fd_set FAR *writefds,
        IN OUT fd_set FAR *exceptfds,
        IN const struct timeval FAR *timeout,
        OUT INT FAR *lpErrno);

    INT
    WSPSend(
        IN SOCKET s,
        IN LPWSABUF lpBuffers,
        IN DWORD dwBufferCount,
        OUT LPDWORD lpNumberOfBytesSent,
        IN DWORD dwFlags,
        IN LPWSAOVERLAPPED lpOverlapped,
        IN LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine,
        IN LPWSATHREADID lpThreadId,
        OUT INT FAR *lpErrno);

    INT
    WSPSendDisconnect(
        IN SOCKET s,
        IN LPWSABUF lpOutboundDisconnectData,
        OUT INT FAR *lpErrno);

    INT
    WSPSendTo(
        IN SOCKET s,
        IN LPWSABUF lpBuffers,
        IN DWORD dbBufferCount,
        IN LPDWORD lpNumberOfBytesSent,
        IN DWORD dwFlags,
        IN const struct sockaddr FAR * lpTo,
        IN INT iTolen,
        IN LPWSAOVERLAPPED lpOverlapped,
        IN LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine,
        IN LPWSATHREADID lpThreadId,
        OUT INT FAR *lpErrno);

    INT
    WSPSetSockOpt(
        IN SOCKET s,
        IN INT level,
        IN INT optname,
        IN const char FAR *optval,
        IN INT optlen,
        OUT INT FAR *lpErrno);

    INT
    WSPShutdown(
        IN SOCKET s,
        IN INT how,
        OUT INT FAR *lpErrno);

    INT
    WSPSocket(
        IN int af,
        IN int type,
        IN int protocol,
        IN LPWSAPROTOCOL_INFOW lpProtocolInfo,
        IN GROUP g,
        IN DWORD dwFlags,
        OUT INT FAR *lpErrno);

    INT
    WSPStringToAddress(
        IN     LPWSTR AddressString,
        IN     INT AddressFamily,
        IN     LPWSAPROTOCOL_INFOW lpProtocolInfo,
        OUT    LPSOCKADDR lpAddress,
        IN OUT LPINT lpAddressLength,
        IN OUT LPINT lpErrno );

INT
TransmitFile (
IN SOCKET hSocket,
IN HANDLE hFile,
IN DWORD nNumberOfBytesToWrite,
IN DWORD nNumberOfBytesPerSend,
IN LPOVERLAPPED lpOverlapped,
IN LPTRANSMIT_FILE_BUFFERS lpTransmitBuffers,
IN DWORD dwReserved,
OUT LPINT lpErrno
);

INT
AcceptEx (
IN SOCKET sListenSocket,
IN SOCKET sAcceptSocket,
IN PVOID lpOutputBuffer,
IN DWORD dwReceiveDataLength,
IN DWORD dwLocalAddressLength,
IN DWORD dwRemoteAddressLength,
OUT LPDWORD lpdwBytesReceived,
IN LPOVERLAPPED lpOverlapped,
OUT LPINT lpErrno
);

INT
InterceptExtensions (
IN LPVOIDlpvInBuffer,
OUT LPVOIDlpvOutBuffer,
OUT LPINTlpErrno
);

  private:
// Variables
    LPWSPPROC_TABLE  m_proctable;
BOOL  m_extensions_initialized;
LPFN_TRANSMITFILE m_transmitfile;
LPFN_ACCEPTEX  m_acceptex;
    HINSTANCE         m_library_handle;
    LPSTR            m_lib_name;
};

inline SOCKET
DPROVIDER::WSPAccept(
    IN SOCKET s,
    OUT struct sockaddr FAR *addr,
    OUT INT FAR *addrlen,
    IN LPCONDITIONPROC lpfnCondition,
    IN DWORD dwCallbackData,
    OUT INT FAR *lpErrno
    )
/*++
Routine Description:

    Conditionally  accept a connection based on the return value of a condition
    function, and optionally create and/or join a socket group.

Arguments:

    s              - A  descriptor  identiying  a socket which is listening for
                     connections after a WSPListen().

    addr           - An optional pointer to a buffer which receives the address
                     of   the  connecting  entity,  as  known  to  the  service
                     provider.   The  exact  format  of  the  addr arguement is
                     determined  by  the  address  family  established when the
                     socket was created.

    addrlen        - An  optional  pointer  to  an  integer  which contains the
                     length of the address addr.

    lpfnCondition  - The  procedure  instance address of an optional, WinSock 2
                     client  supplied  condition  function  which  will make an
                     accept/reject  decision  based  on  the caller information
                     passed  in  as  parameters,  and optionally creaetd and/or
                     join  a  socket group by assigning an appropriate value to
                     the result parameter of this function.

    dwCallbackData - Callback data to be passed back to the WinSock 2 client as
                     a  condition  function  parameter.   This parameter is not
                     interpreted by the service provider.

    lpErrno        - A pointer to the error code.

Return Value:

    If  no  error occurs, WSPAccept() returns a value of type SOCKET which is a
    descriptor  for  the accepted socket.  Otherwise, a value of INVALID_SOCKET
    is returned, and a specific error code is available in lpErrno.

--*/
{
    SOCKET ReturnValue;

    if (PREAPINOTIFY(( DTCODE_WSPAccept,
                       &ReturnValue,
                       m_lib_name,
                       &s,
                       &addr,
                       &addrlen,
                       &lpfnCondition,
                       &dwCallbackData,
                       &lpErrno)) ) {
        return(ReturnValue);
    }

    ReturnValue = m_proctable->lpWSPAccept(
        s,
        addr,
        addrlen,
        lpfnCondition,
        dwCallbackData,
        lpErrno);

    POSTAPINOTIFY(( DTCODE_WSPAccept,
                    &ReturnValue,
                    m_lib_name,
                    &s,
                    &addr,
                    &addrlen,
                    &lpfnCondition,
                    &dwCallbackData,
                    &lpErrno));

    return(ReturnValue);

}

inline INT
DPROVIDER::WSPAddressToString(
    IN     LPSOCKADDR lpsaAddress,
    IN     DWORD dwAddressLength,
    IN     LPWSAPROTOCOL_INFOW lpProtocolInfo,
    OUT    LPWSTR lpszAddressString,
    IN OUT LPDWORD lpdwAddressStringLength,
    OUT    LPINT lpErrno )
/*++

Routine Description:

    WSPAddressToString() converts a SOCKADDR structure into a human-readable
    string representation of the address.  This is intended to be used mainly
    for display purposes. If the caller wishes the translation to be done by a
    particular provider, it should supply the corresponding WSAPROTOCOL_INFO
    struct in the lpProtocolInfo parameter.

Arguments:

    lpsaAddress - points to a SOCKADDR structure to translate into a string.

    dwAddressLength - the length of the Address SOCKADDR.

    lpProtocolInfo - (optional) the WSAPROTOCOL_INFO struct for a particular
                     provider.

    lpszAddressString - a buffer which receives the human-readable address
                        string.

    lpdwAddressStringLength - on input, the length of the AddressString buffer.
                              On output, returns the length of  the string
                              actually copied into the buffer.

Return Value:

    The return value is 0 if the operation was successful.  Otherwise the value
    SOCKET_ERROR is returned
--*/
{
     INT ReturnValue;

//      if (PREAPINOTIFY(( DTCODE_WSPAddressToString,
//                         &ReturnValue,
//                         m_lib_name,
//                         &lpsaAddress,
//                         &dwAddressLength,
//                         &lpProtocolInfo,
//                         &lpszAddressString,
//                         &lpdwAddressStringLength,
//                         &lpErrno)) ) {
//          return(ReturnValue);
//      }

    ReturnValue = m_proctable->lpWSPAddressToString(
        lpsaAddress,
        dwAddressLength,
        lpProtocolInfo,
        lpszAddressString,
        lpdwAddressStringLength,
        lpErrno);

//      POSTAPINOTIFY(DTCODE_WSPAddressToString,
//                    &ReturnValue,
//                    m_lib_name,
//                    &lpsaAddress,
//                    &dwAddressLength,
//                    &lpProtocolInfo,
//                    &lpszAddressString,
//                    &lpdwAddressStringLength,
//                    &lpErrno)));

    return(ReturnValue);
}

inline INT
DPROVIDER::WSPAsyncSelect(
    IN SOCKET s,
    IN HWND hWnd,
    IN unsigned int wMsg,
    IN long lEvent,
    OUT INT FAR *lpErrno
    )
/*++
Routine Description:

    Request  Windows  message-based  event notification of network events for a
    socket.

Arguments:

    s       - A  descriptor identiying a socket for which event notification is
              required.

    hWnd    - A  handle  identifying  the window which should receive a message
              when a network event occurs.

    wMsg    - The message to be sent when a network event occurs.

    lEvent  - bitmask  which specifies a combination of network events in which
              the WinSock client is interested.

    lpErrno - A pointer to the error code.

Return Value:

    The  return  value  is 0 if the WinSock client's declaration of interest in
    the  netowrk event set was successful.  Otherwise the value SOCKET_ERROR is
    returned, and a specific error code is available in lpErrno.

--*/
{
    INT ReturnValue;

    if (PREAPINOTIFY(( DTCODE_WSPAsyncSelect,
                       &ReturnValue,
                       m_lib_name,
                       &s,
                       &hWnd,
                       &wMsg,
                       &lEvent,
                       &lpErrno)) ) {
        return(ReturnValue);
    }

    ReturnValue = m_proctable->lpWSPAsyncSelect(
        s,
        hWnd,
        wMsg,
        lEvent,
        lpErrno);

    POSTAPINOTIFY(( DTCODE_WSPAsyncSelect,
                    &ReturnValue,
                    m_lib_name,
                    &s,
                    &hWnd,
                    &wMsg,
                    &lEvent,
                    &lpErrno));

    return(ReturnValue);

}

inline INT
DPROVIDER::WSPBind(
    IN SOCKET s,
    IN const struct sockaddr FAR *name,
    IN INT namelen,
    OUT INT FAR *lpErrno
    )
/*++
Routine Description:

    Associate a local address (i.e. name) with a socket.

Arguments:

    s       - A descriptor identifying an unbound socket.

    name    - The  address  to assign to the socket.  The sockaddr structure is
              defined as follows:

              struct sockaddr {
                  u_short sa_family;
                  char    sa_data[14];
              };

              Except  for  the sa_family field,
sockaddr contents are epxressed
              in network byte order.

    namelen - The length of the name.

    lpErrno - A pointer to the error code.

Return Value:

    If   no   erro   occurs,  WSPBind()  returns  0.   Otherwise, it  returns
    SOCKET_ERROR, and a specific error code is available in lpErrno.

--*/
{
    INT ReturnValue;
    if (PREAPINOTIFY(( DTCODE_WSPBind,
                       &ReturnValue,
                       m_lib_name,
                       &s,
                       &name,
                       &namelen,
                       &lpErrno)) ) {

        return(ReturnValue);
    }

    ReturnValue = m_proctable->lpWSPBind(
        s,
        name,
        namelen,
        lpErrno);

    POSTAPINOTIFY(( DTCODE_WSPBind,
                    &ReturnValue,
                    m_lib_name,
                    &s,
                    &name,
                    &namelen,
                    &lpErrno));

    return(ReturnValue);

}

inline INT
DPROVIDER::WSPCancelBlockingCall(OUT INT FAR *lpErrno)
/*++
Routine Description:

    Cancel a blocking call which is currently in progress.

Arguments:

    lpErrno - A pointer to the error code.

Return Value:

    The  value  returned  by  WSPCancelBlockingCall() is 0 if the operation was
    successfully canceled.  Otherwise the value SOCKET_ERROR is returned,
and a
    specific error code is available in lpErrno.

--*/
{
    INT ReturnValue;

    if (PREAPINOTIFY(( DTCODE_WSPCancelBlockingCall,
                       &ReturnValue,
                       m_lib_name,
                       &lpErrno)) ) {
        return(ReturnValue);
    }

    ReturnValue = m_proctable->lpWSPCancelBlockingCall(
        lpErrno);

    POSTAPINOTIFY(( DTCODE_WSPCancelBlockingCall,
                    &ReturnValue,
                    m_lib_name,
                    &lpErrno));

    return(ReturnValue);

}

inline INT
DPROVIDER::WSPCleanup(
    OUT INT FAR *lpErrno
    )
/*++
Routine Description:

    Terminate use of the WinSock service provider.

Arguments:

    lpErrno - A pointer to the error code.

Return Value:

    The  return  value  is  0 if the operation has been successfully initiated.
    Otherwise  the  value SOCKET_ERROR is returned,
and a specific error number
    is available in lpErrno.

--*/

{
    INT ReturnValue;

    if (PREAPINOTIFY(( DTCODE_WSPCleanup,
                       &ReturnValue,
                       m_lib_name,
                       &lpErrno)) ) {
        return(ReturnValue);
    }

    ReturnValue = m_proctable->lpWSPCleanup(
        lpErrno);

    POSTAPINOTIFY(( DTCODE_WSPCleanup,
                    &ReturnValue,
                    m_lib_name,
                    &lpErrno));

    return(ReturnValue);

}

inline INT
DPROVIDER::WSPCloseSocket(
    IN SOCKET s,
    OUT INT FAR *lpErrno
    )
/*++
Routine Description:

    Close a socket.

Arguments:

    s       - A descriptor identifying a socket.

    lpErrno - A pointer to the error code.

Return Value:

    If  no  erro  occurs, WSPCloseSocket()  returns  0.  Otherwise, a value of
    SOCKET_ERROR  is  returned,  and  a  specific  error  code  is available in
    lpErrno.

--*/
{
    INT ReturnValue;

    if (PREAPINOTIFY(( DTCODE_WSPCloseSocket,
                       &ReturnValue,
                       m_lib_name,
                       &s,
                       &lpErrno)) ) {
        return(ReturnValue);
    }

    ReturnValue = m_proctable->lpWSPCloseSocket(
        s,
        lpErrno);

    POSTAPINOTIFY(( DTCODE_WSPCloseSocket,
                    &ReturnValue,
                    m_lib_name,
                    &s,
                    &lpErrno));

    return(ReturnValue);

}

inline INT
DPROVIDER::WSPConnect(
    IN SOCKET s,
    IN const struct sockaddr FAR *name,
    IN INT namelen,
    IN LPWSABUF lpCallerData,
    IN LPWSABUF lpCalleeData,
    IN LPQOS lpSQOS,
    IN LPQOS lpGQOS,
    OUT INT FAR *lpErrno
    )
/*++
Routine Description:

    Establish a connection to a peer,
exchange connect data,
and specify needed
    quality of service based on the supplied flow spec.

Arguments:

    s            - A descriptor identifying an unconnected socket.

    name         - The name of the peer to which the socket is to be connected.

    namelen      - The length of the name.

    lpCallerData - A  pointer to the user data that is to be transferred to the
                   peer during connection established.

    lpCalleeData - A pointer to a buffer into which may be copied any user data
                   received from the peer during connection establishment.

    lpSQOS       - A  pointer  to  the  flow  specs  for socket s, one for each
                   direction.

    lpGQOS       - A  pointer  to  the  flow  specs  for  the  socket group (if
                   applicable).

    lpErrno      - A pointer to the error code.

Return Value:

    If  no  error  occurs, WSPConnect()  returns ERROR_SUCCESS.  Otherwise, it
    returns SOCKET_ERROR, and a specific erro rcode is available in lpErrno.

--*/
{
    INT ReturnValue;

    if (PREAPINOTIFY(( DTCODE_WSPConnect,
                       &ReturnValue,
                       m_lib_name,
                       &s,
                       &name,
                       &namelen,
                       &lpCallerData,
                       &lpCalleeData,
                       &lpSQOS,
                       &lpGQOS,
                       &lpErrno)) ) {
        return(ReturnValue);
    }

    ReturnValue = m_proctable->lpWSPConnect(
        s,
        name,
        namelen,
        lpCallerData,
        lpCalleeData,
        lpSQOS,
        lpGQOS,
        lpErrno);

    POSTAPINOTIFY(( DTCODE_WSPConnect,
                    &ReturnValue,
                    m_lib_name,
                    &s,
                    &name,
                    &namelen,
                    &lpCallerData,
                    &lpCalleeData,
                    &lpSQOS,
                    &lpGQOS,
                    &lpErrno));

    return(ReturnValue);

}

inline INT
DPROVIDER::WSPDuplicateSocket(
    IN SOCKET s,
    IN DWORD dwProcessID,
    IN LPWSAPROTOCOL_INFOW lpProtocolInfo,
    OUT INT FAR *lpErrno
    )
/*++
Routine Description:

    descriptor for a shared socket.

Arguments:

    s              - Specifies the local socket descriptor.

    dwProcessID    - Specifies  the  ID  of  the  target  process for which the
                     shared socket will be used.

    lpProtocolInfo - A  pointer  to  a  buffer  allocated by the client that is
                     large enough to contain a WSAPROTOCOL_INFOA struct.  The
                     service  provider copies the protocol info struct contents
                     to this buffer.

    lpErrno        - A pointer to the error code

Return Value:

    If  no  error  occurs, WPSDuplicateSocket()  returns zero.  Otherwise, the
    value of SOCKET_ERROR is returned, and a specific error number is available
    in lpErrno.

--*/
{
    INT ReturnValue;

    if (PREAPINOTIFY(( DTCODE_WSPDuplicateSocket,
                       &ReturnValue,
                       m_lib_name,
                       &s,
                       &dwProcessID,
                       &lpProtocolInfo,
                       &lpErrno)) ) {
        return(ReturnValue);
    }

    ReturnValue = m_proctable->lpWSPDuplicateSocket(
        s,
        dwProcessID,
        lpProtocolInfo,
        lpErrno);

    POSTAPINOTIFY(( DTCODE_WSPDuplicateSocket,
                    &ReturnValue,
                    m_lib_name,
                    &s,
                    &dwProcessID,
                    &lpProtocolInfo,
                    &lpErrno));

    return(ReturnValue);

}

inline INT
DPROVIDER::WSPEnumNetworkEvents(
    IN SOCKET s,
    OUT WSAEVENT hEventObject,
    OUT LPWSANETWORKEVENTS lpNetworkEvents,
    OUT INT FAR *lpErrno
    )
/*++
Routine Description:

    Report occurrences of network events for the indicated socket.

Arguments:

    s               - A descriptor identifying the socket.

    hEventObject    - An optional handle identifying an associated event object
                      to be reset.

    lpNetworkEvents - A  pointer  to  a WSANETWORKEVENTS struct which is filled
                      with   a  record  of  occurred  network  events  and  any
                      associated error codes.

    lpErrno         - A pointer to the error code.

Return Value:

    The  return  value  is  ERROR_SUCCESS  if  the  operation  was  successful.
    Otherwise  the  value SOCKET_ERROR is returned, and a specific error number
    is available in lpErrno.

--*/
{
    INT ReturnValue;

    if (PREAPINOTIFY(( DTCODE_WSPEnumNetworkEvents,
                       &ReturnValue,
                       m_lib_name,
                       &s,
                       &hEventObject,
                       &lpNetworkEvents,
                       &lpErrno)) ) {
        return(ReturnValue);
    }

    ReturnValue = m_proctable->lpWSPEnumNetworkEvents(
        s,
        hEventObject,
        lpNetworkEvents,
        lpErrno);

    POSTAPINOTIFY(( DTCODE_WSPEnumNetworkEvents,
                    &ReturnValue,
                    m_lib_name,
                    &s,
                    &hEventObject,
                    &lpNetworkEvents,
                    &lpErrno));

    return(ReturnValue);

}

inline INT
DPROVIDER::WSPEventSelect(
    IN SOCKET s,
    IN OUT WSAEVENT hEventObject,
    IN long lNetworkEvents,
    OUT INT FAR *lpErrno
    )
/*++
Routine Description:

    Specify  an  event object to be associated with the supplied set of network
    events.

Arguments:

    s              - A descriptor identifying the socket.

    hEventObject   - A  handle  identifying  the  event object to be associated
                     with the supplied set of network events.

    lNetworkEvents - A  bitmask  which  specifies  the  combination  of network
                     events in which the WinSock client has interest.

    lpErrno        - A pointer to the error code.

Return Value:

    The return value is 0 if the WinSock client's specification of the network
    events and the associated event object was successful. Otherwise the value
    SOCKET_ERROR is returned, and a specific error number is available in
    lpErrno

--*/
{
    INT ReturnValue;

    if (PREAPINOTIFY(( DTCODE_WSPEventSelect,
                       &ReturnValue,
                       m_lib_name,
                       &s,
                       &hEventObject,
                       &lNetworkEvents,
                       &lpErrno)) ) {
        return(ReturnValue);
    }

    ReturnValue = m_proctable->lpWSPEventSelect(
        s,
        hEventObject,
        lNetworkEvents,
        lpErrno);

    POSTAPINOTIFY(( DTCODE_WSPEventSelect,
                    &ReturnValue,
                    m_lib_name,

&s,
                    &hEventObject,
                    &lNetworkEvents,
                    &lpErrno));

    return(ReturnValue);

}

inline INT
DPROVIDER::WSPGetOverlappedResult(
    IN SOCKET s,
    IN LPWSAOVERLAPPED lpOverlapped,
    IN LPDWORD lpcbTransfer,
    IN BOOL fWait,
    OUT LPDWORD lpdwFlags,
    OUT INT FAR *lpErrno
    )
/*++
Routine Description:

    Returns the results of an overlapped operation on the specified socket.

Arguments:

    s            - Identifies  the  socket.   This  is the same socket that was
                   specified  when  the  overlapped  operation was started by a
                   call to WSPRecv(), WSPRecvFrom(), WSPSend(), WSPSendTo(), or
                   WSPIoctl().

    lpOverlapped - Points to a WSAOVERLAPPED structure that was specified
                   when the overlapped operation was started.

    lpcbTransfer - Points to a 32-bit variable that receives the number of
                   bytes that were actually transferred by a send or receive
                   operation, or by WSPIoctl().

    fWait        - Specifies  whether  the function should wait for the pending
                   overlapped  operation  to  complete.   If TRUE, the function
                   does  not return until the operation has been completed.  If
                   FALSE  and  the  operation  is  still  pending, the function
                   returns FALSE and lperrno is WSA_IO_INCOMPLETE.

    lpdwFlags    - Points  to  a  32-bit variable that will receive one or more
                   flags   that  supplement  the  completion  status.   If  the
                   overlapped   operation   was   initiated  via  WSPRecv()  or
                   WSPRecvFrom(), this parameter will contain the results value
                   for lpFlags parameter.

    lpErrno      - A pointer to the error code.

Return Value:

    If WSPGetOverlappedResult() succeeds,the return value is TRUE.  This means
    that the overlapped operation has completed successfully and that the value
    pointed  to  by lpcbTransfer has been updated.  If WSPGetOverlappedResult()
    returns  FALSE,  this  means  that  either the overlapped operation has not
    completed  or  the  overlapped operation completed but with errors, or that
    completion  status  could  not  be  determined due to errors in one or more
    parameters  to  WSPGetOverlappedResult().  On failure, the value pointed to
    by  lpcbTransfer  will  not be updated.  lpErrno indicates the cause of the
    failure (either of WSPGetOverlappedResult() or of the associated overlapped
    operation).

--*/
{
    INT ReturnValue;

    if (PREAPINOTIFY(( DTCODE_WSPGetOverlappedResult,
                       &ReturnValue,
                       m_lib_name,
                       &s,
                       &lpOverlapped,
                       &lpcbTransfer,
                       &fWait,
                       &lpdwFlags,
                       &lpErrno)) ) {
        return(ReturnValue);
    }

    ReturnValue = m_proctable->lpWSPGetOverlappedResult(
        s,
        lpOverlapped,
        lpcbTransfer,
        fWait,
        lpdwFlags,
        lpErrno);

    POSTAPINOTIFY(( DTCODE_WSPGetOverlappedResult,
                    &ReturnValue,
                    m_lib_name,
                    &s,
                    &lpOverlapped,
                    &lpcbTransfer,
                    &fWait,
                    &lpdwFlags,
                    &lpErrno));

    return(ReturnValue);
}

inline INT
DPROVIDER::WSPGetPeerName(
    IN SOCKET s,
    OUT struct sockaddr FAR *name,
    OUT INT FAR *namelen,
    OUT INT FAR *lpErrno
    )
/*++
Routine Description:

    Get the address of the peer to which a socket is connected.

Arguments:

    s       - A descriptor identifying a connected socket.

    name    - A  pointer  to  the structure which is to receive the name of the
              peer.

    namelen - A  pointer  to  an integer which, on input, indicates the size of
              the  structure  pointed  to  by name, and on output indicates the
              size of the returned name.

    lpErrno - A pointer to the error code.

Return Value:

    If  no  error occurs, WSPGetPeerName() returns ERROR_SUCCESS.  Otherwise, a
    value  of  SOCKET_ERROR is returned, and a specific error code is available
    in lpErrno

--*/
{
    INT ReturnValue;

    if (PREAPINOTIFY(( DTCODE_WSPGetPeerName,
                       &ReturnValue,
                       m_lib_name,
                       &s,
                       &name,
                       &namelen,
                       &lpErrno)) ) {
        return(ReturnValue);
    }

    ReturnValue = m_proctable->lpWSPGetPeerName(
        s,
        name,
        namelen,
        lpErrno);

    POSTAPINOTIFY(( DTCODE_WSPGetPeerName,
                    &ReturnValue,
                    m_lib_name,
                    &s,
                    &name,
                    &namelen,
                    &lpErrno));

    return(ReturnValue);
}

inline INT
DPROVIDER::WSPGetQOSByName(
    IN SOCKET s,
    IN LPWSABUF lpQOSName,
    IN LPQOS lpQOS,
    OUT INT FAR *lpErrno
    )
/*++
Routine Description:

    Initializes a QOS structure based on a named template.

Arguments:

    s         - A descriptor identifying a socket.

    lpQOSName - Specifies the QOS template name.

    lpQOS     - A pointer to the QOS structure to be filled.

    lpErrno   - A pointer to the error code.

Return Value:

    If the function succeeds, the return value is TRUE.  If the function fails,
    the  return  value  is  FALSE, and  a  specific error code is available in
    lpErrno.

--*/
{
    INT ReturnValue;

    if (PREAPINOTIFY(( DTCODE_WSPGetQOSByName,
                       &ReturnValue,
                       m_lib_name,
                       &s,
                       &lpQOSName,
                       &lpQOS,
                       &lpErrno)) ) {
        return(ReturnValue);
    }

    ReturnValue = m_proctable->lpWSPGetQOSByName(
        s,
        lpQOSName,
        lpQOS,
        lpErrno);

    POSTAPINOTIFY(( DTCODE_WSPGetQOSByName,
                    &ReturnValue,
                    m_lib_name,
                    &s,
                    &lpQOSName,
                    &lpQOS,
                    &lpErrno));

    return(ReturnValue);
}

inline INT
DPROVIDER::WSPGetSockName(
    IN SOCKET s,
    OUT struct sockaddr FAR *name,
    OUT INT FAR *namelen,
    OUT INT FAR *lpErrno
    )
/*++
Routine Description:

    Get the local name for a socket.

Arguments:

    s       - A descriptor identifying a bound socket.

    name    - A pointer to a structure used to supply the address (name) of the
              socket.

    namelen - A  pointer  to  an integer which, on input, indicates the size of
              the  structure  pointed  to  by name, and on output indicates the
              size of the returned name

    lpErrno - A Pointer to the error code.

Return Value:

    If  no  error occurs, WSPGetSockName() returns ERROR_SUCCESS.  Otherwise, a
    value  of  SOCKET_ERROR is returned, and a specific error code is available
    in lpErrno.

--*/
{
    INT ReturnValue;

    if (PREAPINOTIFY(( DTCODE_WSPGetSockName,
                       &ReturnValue,
                       m_lib_name,
                       &s,
                       &name,
                       &namelen,
                       &lpErrno)) ) {
        return(ReturnValue);
    }

    ReturnValue = m_proctable->lpWSPGetSockName(
        s,
        name,
        namelen,
        lpErrno);

    POSTAPINOTIFY(( DTCODE_WSPGetSockName,
                    &ReturnValue,
                    m_lib_name,
                    &s,
                    &name,
                    &namelen,
                    &lpErrno));

    return(ReturnValue);
}

inline INT
DPROVIDER::WSPGetSockOpt(
    IN SOCKET s,
    IN INT level,
    IN INT optname,
    OUT char FAR *optval,
    OUT INT FAR *optlen,
    OUT INT FAR *lpErrno
    )
/*++
Routine Description:

    Retrieve a socket option.

Arguments:

    s       - A descriptor identifying a socket.

    level   - The  level  at  which the option is defined; the supported levels
              include SOL_SOCKET (See annex for more protocol-specific levels.)

    optname - The socket option for which the value is to be retrieved.

    optval  - A  pointer  to  the  buffer  in which the value for the requested
              option is to be returned.

    optlen  - A pointer to the size of the optval buffer.

    lpErrno - A pointer to the error code.

Return Value:

    If  no  error  occurs,  WSPGetSockOpt()  returns  0.  Otherwise, a value of
    SOCKET_ERROR  is  returned,  and  a  specific  error  code  is available in
    lpErrno.

--*/
{
    INT ReturnValue;

    if (PREAPINOTIFY(( DTCODE_WSPGetSockOpt,
                       &ReturnValue,
                       m_lib_name,
                       &s,
                       &level,
                       &optname,
                       &optval,
                       &optlen,
                       &lpErrno)) ) {
        return(ReturnValue);
    }

    ReturnValue = m_proctable->lpWSPGetSockOpt(
        s,
        level,
        optname,
        optval,
        optlen,
        lpErrno);

    POSTAPINOTIFY(( DTCODE_WSPGetSockOpt,
                    &ReturnValue,
                    m_lib_name,
                    &s,
                    &level,
                    &optname,
                    &optval,
                    &optlen,
                    &lpErrno));

    return(ReturnValue);
}

inline INT
DPROVIDER::WSPIoctl(
    IN SOCKET s,
    IN DWORD dwIoControlCode,
    IN LPVOID lpvInBuffer,
    IN DWORD cbInBuffer,
    IN LPVOID lpvOutBuffer,
    IN DWORD cbOutBuffer,
    IN LPDWORD lpcbBytesReturned,
    IN LPWSAOVERLAPPED lpOverlapped,
    IN LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine,
    IN LPWSATHREADID lpThreadId,
    OUT INT FAR *lpErrno
    )
/*++
Routine Description:

    Control the mode of a socket.

Arguments:

    s                   - Handle to a socket

    dwIoControlCode     - Control code of operation to perform

    lpvInBuffer         - Address of input buffer

    cbInBuffer          - Size of input buffer

    lpvOutBuffer        - Address of output buffer

    cbOutBuffer         - Size of output buffer

    lpcbBytesReturned   - A pointer to the size of output buffer's contents.

    lpOverlapped        - Address of WSAOVERLAPPED structure

    lpCompletionRoutine - A  pointer  to the completion routine called when the
                          operation has been completed.

    lpThreadId          - A  pointer to a thread ID structure to be used by the
                          provider

    lpErrno             - A pointer to the error code.

Return Value:

    If  no error occurs and the operation has completed immediately, WSPIoctl()
    returns  0.   Note  that in this case the completion routine, if specified,
    will  have  already  been  queued.   Otherwise, a value of SOCKET_ERROR is
    returned, and  a  specific  error code is available in lpErrno.  The error
    code  WSA_IO_PENDING  indicates  that  an  overlapped  operation  has  been
    successfully  initiated  and  that  conpletion will be indicated at a later
    time.   Any  other  error  code  indicates that no overlapped operation was
    initiated and no completion indication will occur.

--*/
{
    INT ReturnValue;

    if (PREAPINOTIFY(( DTCODE_WSPIoctl,
                       &ReturnValue,
                       m_lib_name,
                       &s,
                       &dwIoControlCode,
                       &lpvInBuffer,
                       &cbInBuffer,
                       &lpvOutBuffer,
                       &cbOutBuffer,
                       &lpcbBytesReturned,
                       &lpOverlapped,
                       &lpCompletionRoutine,
                       &lpThreadId,
                       &lpErrno)) ) {
        return(ReturnValue);
    }

    ReturnValue = m_proctable->lpWSPIoctl(
        s,
        dwIoControlCode,
        lpvInBuffer,
        cbInBuffer,
        lpvOutBuffer,
        cbOutBuffer,
        lpcbBytesReturned,
        lpOverlapped,
        lpCompletionRoutine,
        lpThreadId,
        lpErrno);

    POSTAPINOTIFY(( DTCODE_WSPIoctl,
                    &ReturnValue,
                    m_lib_name,
                    &s,
                    &dwIoControlCode,
                    &lpvInBuffer,
                    &cbInBuffer,
                    &lpvOutBuffer,
                    &cbOutBuffer,
                    &lpcbBytesReturned,
                    &lpOverlapped,
                    &lpCompletionRoutine,
                    &lpThreadId,
                    &lpErrno));

    return(ReturnValue);
}

inline INT
DPROVIDER::WSPJoinLeaf(
    IN SOCKET s,
    IN const struct sockaddr FAR *name,
    IN INT namelen,
    IN LPWSABUF lpCallerData,
    IN LPWSABUF lpCalleeData,
    IN LPQOS lpSQOS,
    IN LPQOS lpGQOS,
    IN DWORD dwFlags,
    OUT INT FAR *lpErrno
    )
/*++
Routine Description:

    Join  a  leaf  node  into  a multipoint session, exchange connect data, and
    specify needed quality of service based on the supplied flow specs.

Arguments:

    s            - A descriptor identifying a multipoint socket.

    name         - The name of the peer to which the socket is to be joined.

    namelen      - The length of the name.

    lpCallerData - A  pointer to the user data that is to be transferred to the
                   peer during multipoint session establishment.

    lpCalleeData - A  pointer  to  the user data that is to be transferred back
                   from the peer during multipoint session establishment.

    lpSQOS       - A  pointer  to  the  flow  specs  for socket s, one for each
                   direction.

    lpGQOS       - A  pointer  to  the  flow  specs  for  the  socket group (if
                   applicable).

    dwFlags      - Flags  to  indicate  that  the socket is acting as a sender,
                   receiver, or both.

    lpErrno      - A pointer to the error code.

Return Value:

    If no error occurs,
WSPJoinLeaf() returns a value of type SOCKET which is a
    descriptor  for the newly created multipoint socket.  Otherwise,a value of
    INVALID_SOCKET  is  returned, and  a  specific  error code is available in
    lpErrno.

--*/
{
    INT ReturnValue;

    if (PREAPINOTIFY(( DTCODE_WSPJoinLeaf,
                       &ReturnValue,
                       m_lib_name,
                       &s,
                       &name,
                       &namelen,
                       &lpCallerData,
                       &lpCalleeData,
                       &lpSQOS,
                       &lpGQOS,
                       &dwFlags,
                       &lpErrno)) ) {
        return(ReturnValue);
    }

    ReturnValue = m_proctable->lpWSPJoinLeaf(
        s,
        name,
        namelen,
        lpCallerData,
        lpCalleeData,
        lpSQOS,
        lpGQOS,
        dwFlags,
        lpErrno);

    POSTAPINOTIFY(( DTCODE_WSPJoinLeaf,
                    &ReturnValue,
                    m_lib_name,
                    &s,
                    &name,
                    &namelen,
                    &lpCallerData,
                    &lpCalleeData,
                    &lpSQOS,
                    &lpGQOS,
                    &dwFlags,
                    &lpErrno));

    return(ReturnValue);

}

inline INT
DPROVIDER::WSPListen(
    IN SOCKET s,
    IN INT backlog,
    OUT INT FAR *lpErrno
    )
/*++
Routine Description:

    Establish a socket to listen for incoming connections.

Arguments:

    s       - A descriptor identifying a bound,
unconnected socket.

    backlog - The  maximum length to which the queue of pending connections may
              grow.   If  this  value  is  SOMAXCONN,
then the service provider
              should set the backlog to a maximum "reasonable" value.

    lpErrno - A pointer to the error code.

Return Value:

    If  no  error  occurs, WSPListen()  returns  0.   Otherwise, a  value  of
    SOCKET_ERROR  is  returned, and  a  specific  error  code  is available in
    lpErrno.

--*/
{
    INT ReturnValue;

    if (PREAPINOTIFY(( DTCODE_WSPListen,
                       &ReturnValue,
                       m_lib_name,
                       &s,
                       &backlog,
                       &lpErrno)) ) {
        return(ReturnValue);
    }

    ReturnValue = m_proctable->lpWSPListen(
        s,
        backlog,
        lpErrno);

    POSTAPINOTIFY(( DTCODE_WSPListen,
                    &ReturnValue,
                    m_lib_name,
                    &s,
                    &backlog,
                    &lpErrno));

    return(ReturnValue);

}

inline INT
DPROVIDER::WSPRecv(
    IN SOCKET s,
    IN LPWSABUF lpBuffers,
    IN DWORD dwBufferCount,
    IN LPDWORD lpNumberOfBytesRecvd,
    IN OUT LPDWORD lpFlags,
    IN LPWSAOVERLAPPED lpOverlapped,
    IN LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine,
    IN LPWSATHREADID lpThreadId,
    OUT INT FAR *lpErrno
    )
/*++
Routine Description:

    Receive data on a socket.

Arguments:

    s                    - A descriptor identifying a connected socket.

    lpBuffers            - A  pointer  to  an array of WSABUF structures.  Each
                           WSABUF  structure contains a pointer to a buffer and
                           the length of the buffer.

    dwBufferCount        - The  number  of  WSABUF  structures in the lpBuffers
                           array.

    lpNumberOfBytesRecvd - A  pointer  to  the number of bytes received by this
                           call.

    lpFlags              - A pointer to flags.

    lpOverlapped         - A pointer to a WSAOVERLAPPED structure.

    lpCompletionRoutine  - A  pointer to the completion routine called when the
                           receive operation has been completed.

    lpThreadId           - A pointer to a thread ID structure to be used by the
                           provider in a subsequent call to WPUQueueApc().

    lpErrno              - A pointer to the error code.

Return Value:

    If  no  error  occurs  and the receive operation has completed immediately,
    WSPRecv() returns the number of bytes received.  If the connection has been
    closed, it  returns  0.  Note that in this case the completion routine, if
    specified,  will   have  already  been  queued.   Otherwise, a  value  of
    SOCKET_ERROR  is  returned, and  a  specific  error  code  is available in
    lpErrno.   The  error  code WSA_IO_PENDING indicates that the overlapped an
    operation  has  been  successfully  initiated  and  that completion will be
    indicated  at  a  later  time.   Any  other  error  code  indicates that no
    overlapped  operations  was  initiated  and  no  completion indication will
    occur.
--*/
{
    INT ReturnValue;

    if (PREAPINOTIFY(( DTCODE_WSPRecv,
                       &ReturnValue,
                       m_lib_name,
                       &s,
                       &lpBuffers,
                       &dwBufferCount,
                       &lpNumberOfBytesRecvd,
                       &lpFlags,
                       &lpOverlapped,
                       &lpCompletionRoutine,
                       &lpThreadId,
                       &lpErrno)) ) {
        return(ReturnValue);
    }

    ReturnValue = m_proctable->lpWSPRecv(
        s,
        lpBuffers,
        dwBufferCount,
        lpNumberOfBytesRecvd,
        lpFlags,
        lpOverlapped,
        lpCompletionRoutine,
        lpThreadId,
        lpErrno);

    POSTAPINOTIFY(( DTCODE_WSPRecv,
                    &ReturnValue,
                    m_lib_name,
                    &s,
                    &lpBuffers,
                    &dwBufferCount,
                    &lpNumberOfBytesRecvd,
                    &lpFlags,
                    &lpOverlapped,
                    &lpCompletionRoutine,
                    &lpThreadId,
                    &lpErrno));

    return(ReturnValue);
}

inline INT
DPROVIDER::WSPRecvDisconnect(
    IN SOCKET s,
    IN LPWSABUF lpInboundDisconnectData,
    OUT INT FAR *lpErrno
    )
/*++
Routine Description:

    Terminate  reception  on  a socket, and retrieve the disconnect data if the
    socket is connection-oriented.

Arguments:

    s                       - A descriptor identifying a socket.

    lpInboundDisconnectData - A  pointer to a buffer into which disconnect data
                              is to be copied.

    lpErrno                 - A pointer to the error code.

Return Value:

    If  no error occurs, WSPRecvDisconnect() returns ERROR_SUCCESS.  Otherwise,
    a value of SOCKET_ERROR is returned, and a specific error code is available
    in lpErrno.

--*/
{
    INT ReturnValue;

    if (PREAPINOTIFY(( DTCODE_WSPRecvDisconnect,
                       &ReturnValue,
                       m_lib_name,
                       &s,
                       &lpInboundDisconnectData,
                       &lpErrno)) ) {
        return(ReturnValue);
    }

    ReturnValue = m_proctable->lpWSPRecvDisconnect(
        s,
        lpInboundDisconnectData,
        lpErrno);

    POSTAPINOTIFY(( DTCODE_WSPRecvDisconnect,
                    &ReturnValue,
                    m_lib_name,
                    &s,
                    &lpInboundDisconnectData,
                    &lpErrno));

    return(ReturnValue);

}

inline INT
DPROVIDER::WSPRecvFrom(
    IN  SOCKET s,
    IN  LPWSABUF lpBuffers,
    IN  DWORD dwBufferCount,
    IN  LPDWORD lpNumberOfBytesRecvd,
    IN  OUT LPDWORD lpFlags,
    OUT struct sockaddr FAR *  lpFrom,
    IN  LPINT lpFromlen,
    IN  LPWSAOVERLAPPED lpOverlapped,
    IN  LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine,
    IN LPWSATHREADID lpThreadId,
    OUT INT FAR *lpErrno
    )
/*++
Routine Description:

    Receive a datagram and store the source address.

Arguments:

    s                    - A descriptor identifying a socket.

    lpBuffers            - A  pointer  to  an array of WSABUF structures.  Each
                           WSABUF  structure contains a pointer to a buffer and
                           the length of the buffer.

    dwBufferCount        - The  number  of  WSABUF  structures in the lpBuffers
                           array.

    lpNumberOfBytesRecvd - A  pointer  to  the number of bytes received by this
                           call.

    lpFlags              - A pointer to flags.

    lpFrom               - An  optional pointer to a buffer which will hold the
                           source address upon the completion of the overlapped
                           operation.

    lpFromlen            - A  pointer  to the size of the from buffer, required
                           only if lpFrom is specified.

    lpOverlapped         - A pointer to a WSAOVERLAPPED structure.

    CompletionRoutine    - A  pointer to the completion routine called when the
                           receive operation has been completed.

    lpThreadId           - A pointer to a thread ID structure to be used by the
                           provider in a subsequent call to WPUQueueApc().

    lpErrno              - A pointer to the error code.

Return Value:

    If  no  error  occurs  and the receive operation has completed immediately,
    WSPRecvFrom()  returns the number of bytes received.  If the connection has
    been  closed, it returns 0.  Note that in this case the completion routine,
    if  specified  will  have  already  been  queued.   Otherwise,  a  value of
    SOCKET_ERROR  is  returned, and  a  specific  error  code  is available in
    lpErrno.   The  error  code  WSA_IO_PENDING  indicates  that the overlapped
    operation  has  been  successfully  initiated  and  that completion will be
    indicated  at  a  later  time.   Any  other  error  code  indicates that no
    overlapped  operations  was  initiated  and  no  completion indication will
    occur.

--*/
{
    INT ReturnValue;

    if (PREAPINOTIFY(( DTCODE_WSPRecvFrom,
                       &ReturnValue,
                       m_lib_name,
                       &s,
                       &lpBuffers,
                       &dwBufferCount,
                       &lpNumberOfBytesRecvd,
                       &lpFlags,
                       &lpFrom,
                       &lpFromlen,
                       &lpOverlapped,
                       &lpCompletionRoutine,
                       &lpThreadId,
                       &lpErrno)) ) {
        return(ReturnValue);
    }

    ReturnValue = m_proctable->lpWSPRecvFrom(
        s,
        lpBuffers,
        dwBufferCount,
        lpNumberOfBytesRecvd,
        lpFlags,
        lpFrom,
        lpFromlen,
        lpOverlapped,
        lpCompletionRoutine,
        lpThreadId,
        lpErrno);

    POSTAPINOTIFY(( DTCODE_WSPRecvFrom,
                    &ReturnValue,
                    m_lib_name,
                    &s,
                    &lpBuffers,
                    &dwBufferCount,
                    &lpNumberOfBytesRecvd,
                    &lpFlags,
                    &lpFrom,
                    &lpFromlen,
                    &lpOverlapped,
                    &lpCompletionRoutine,
                    &lpThreadId,
                    &lpErrno));

    return(ReturnValue);
}

inline INT
DPROVIDER::WSPSelect(
    IN INT nfds,
    IN OUT fd_set FAR *readfds,
    IN OUT fd_set FAR *writefds,
    IN OUT fd_set FAR *exceptfds,
    IN const struct timeval FAR *timeout,
    OUT INT FAR *lpErrno
    )
/*++
Routine Description:

    Determine the status of one or more sockets.

Arguments:

    nfds      - This  argument  is  ignored  and  included only for the sake of
                compatibility.

    readfds   - An  optional  pointer  to  a  set  of sockets to be checked for
                readability.

    writefds  - An  optional  pointer  to  a  set  of sockets to be checked for
                writability

    exceptfds - An  optional  pointer  to  a  set  of sockets to be checked for
                errors.

    timeout   - The  maximum  time  for  WSPSelect()  to  wait, or  NULL for a
                blocking operation.

    lpErrno   - A pointer to the error code.

Return Value:

    WSPSelect()  returns  the  total  number of descriptors which are ready and
    contained  in  the  fd_set  structures, 0  if  the  time limit expired, or
    SOCKET_ERROR  if an error occurred.  If the return value is SOCKET_ERROR, a
    specific error code is available in lpErrno.

--*/
{
    INT ReturnValue;

    if (PREAPINOTIFY(( DTCODE_WSPSelect,
                       &ReturnValue,
                       m_lib_name,
                       &nfds,
                       &readfds,
                       &writefds,
                       &exceptfds,
                       &timeout,
                       &lpErrno)) ) {
        return(ReturnValue);
    }

    ReturnValue = m_proctable->lpWSPSelect(
        nfds,
        readfds,
        writefds,
        exceptfds,
        timeout,
        lpErrno);

    POSTAPINOTIFY(( DTCODE_WSPSelect,
                    &ReturnValue,
                    m_lib_name,
                    &nfds,
                    &readfds,
                    &writefds,
                    &exceptfds,
                    &timeout,
                    &lpErrno));

    return(ReturnValue);
}

inline INT
DPROVIDER::WSPSend(
    IN SOCKET s,
    IN LPWSABUF lpBuffers,
    IN DWORD dwBufferCount,
    IN LPDWORD lpNumberOfBytesSent,
    IN DWORD dwFlags,
    IN LPWSAOVERLAPPED lpOverlapped,
    IN LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine,
    IN LPWSATHREADID lpThreadId,
    OUT INT FAR *lpErrno
    )
/*++

Routine Description:

    Send data on a connected socket.

Arguments:

    s                   - A descriptor identifying a connected socket.

    lpBuffers           - A  pointer  to  an  array of WSABUF structures.  Each
                          WSABUF  structure  contains a pointer to a buffer and
                          the length of the buffer.

    dwBufferCount       - The  number  of  WSABUF  structures  in the lpBuffers
                          array.

    lpNumberOfBytesSent - A pointer to the number of bytes sent by this call.

    dwFlags             - Flags.

    lpOverlapped        - A pointer to a WSAOVERLAPPED structure.

    lpCompletionRoutine - A  pointer  to the completion routine called when the
                          send operation has been completed.

    lpThreadId          - A  pointer to a thread ID structure to be used by the
                          provider in a subsequent call to WPUQueueApc().

    lpErrno             - A pointer to the error code.

Return Value:

    If  no  error  occurs  and  the  send  operation has completed immediately,
    WSPSend() returns the number of bytes received.  If the connection has been
    closed,  it  returns  0.  Note that in this case the completion routine, if
    specified, will   have  already  been  queued.   Otherwise, a  value  of
    SOCKET_ERROR  is  returned, and  a  specific  error  code  is available in
    lpErrno.   The  error  code  WSA_IO_PENDING  indicates  that the overlapped
    operation  has  been  successfully  initiated  and  that completion will be
    indicated  at  a  later  time.   Any  other  error  code  indicates that no
    overlapped operation was initiated and no completion indication will occur.

--*/
{
    INT ReturnValue;

    if (PREAPINOTIFY(( DTCODE_WSPSend,
                       &ReturnValue,
                       m_lib_name,
                       &s,
                       &lpBuffers,
                       &dwBufferCount,
                       &lpNumberOfBytesSent,
                       &dwFlags,
                       &lpOverlapped,
                       &lpCompletionRoutine,
                       &lpThreadId,
                       &lpErrno)) ) {
        return(ReturnValue);
    }

    ReturnValue = m_proctable->lpWSPSend(
        s,
        lpBuffers,
        dwBufferCount,
        lpNumberOfBytesSent,
        dwFlags,
        lpOverlapped,
        lpCompletionRoutine,
        lpThreadId,
        lpErrno);

    POSTAPINOTIFY(( DTCODE_WSPSend,
                    &ReturnValue,
                    m_lib_name,
                    &s,
                    &lpBuffers,
                    &dwBufferCount,
                    &lpNumberOfBytesSent,
                    &dwFlags,
                    &lpOverlapped,
                    &lpCompletionRoutine,
                    &lpThreadId,
                    &lpErrno));

    return(ReturnValue);
}

inline INT
DPROVIDER::WSPSendDisconnect(
    IN SOCKET s,
    IN LPWSABUF lpOutboundDisconnectData,
    OUT INT FAR *lpErrno
    )
/*++
Routine Description:

    Initiate  termination  of the connection for the socket and send disconnect
    data.

Arguments:

    s                        - A descriptor identifying a socket.

    lpOutboundDisconnectData - A pointer to the outgoing disconnect data.

    lpErrno                  - A pointer to the error code.

Return Value:

    If  no  error occurs, WSPSendDisconnect() returns 0.  Otherwise, a value of
    SOCKET_ERROR  is  returned, and  a  specific  error  code  is available in
    lpErrno.

--*/
{
    INT ReturnValue;

    if (PREAPINOTIFY(( DTCODE_WSPSendDisconnect,
                       &ReturnValue,
                       m_lib_name,
                       &s,
                       &lpOutboundDisconnectData,
                       &lpErrno)) ) {
        return(ReturnValue);
    }

    ReturnValue = m_proctable->lpWSPSendDisconnect(
        s,
        lpOutboundDisconnectData,
        lpErrno);

    POSTAPINOTIFY(( DTCODE_WSPSendDisconnect,
                    &ReturnValue,
                    m_lib_name,
                    &s,
                    &lpOutboundDisconnectData,
                    &lpErrno));

    return(ReturnValue);

}

inline INT
DPROVIDER::WSPSendTo(
    IN SOCKET s,
    IN LPWSABUF lpBuffers,
    IN DWORD dbBufferCount,
    IN LPDWORD lpNumberOfBytesSent,
    IN DWORD dwFlags,
    IN const struct sockaddr FAR *  lpTo,
    IN INT iTolen,
    IN LPWSAOVERLAPPED lpOverlapped,
    IN LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine,
    IN LPWSATHREADID lpThreadId,
    OUT INT FAR *lpErrno
    )
/*++
Routine Description:

    Send data to a specific destination using overlapped I/O.

Arguments:

    s                   - A descriptor identifying a socket.

    lpBuffers           - A  pointer  to  an  array of WSABUF structures.  Each
                          WSABUF  structure  contains a pointer to a buffer and
                          the length of the buffer.

    dwBufferCount       - The  number  of  WSABUF  structures  in the lpBuffers
                          array.

    lpNumberOfBytesSent - A pointer to the number of bytes sent by this call.

    dwFlags             - Flags.

    lpTo                - An  optional  pointer  to  the  address of the target
                          socket.

    iTolen              - The size of the address in lpTo.

    lpOverlapped        - A pointer to a WSAOVERLAPPED structure.

    lpCompletionRoutine - A  pointer  to the completion routine called when the
                          send operation has been completed.

    lpThreadId          - A  pointer to a thread ID structure to be used by the
                          provider in a subsequent call to WPUQueueApc().

    lpErrno             - A pointer to the error code.

Return Value:

    If  no  error  occurs  and the receive operation has completed immediately,
    WSPSendTo()  returns  the  number of bytes received.  If the connection has
    been  closed,it returns 0.  Note that in this case the completion routine,
    if  specified, will  have  already  been  queued.   Otherwise, a value of
    SOCKET_ERROR  is  returned, and  a  specific  error  code  is available in
    lpErrno.   The  error  code  WSA_IO_PENDING  indicates  that the overlapped
    operation  has  been  successfully  initiated  and  that completion will be
    indicated  at  a  later  time.   Any  other  error  code  indicates that no
    overlapped operation was initiated and no completion indication will occur.

--*/

{
    INT ReturnValue;

    if (PREAPINOTIFY(( DTCODE_WSPSendTo,
                       &ReturnValue,
                       m_lib_name,
                       &s,
                       &lpBuffers,
                       &lpNumberOfBytesSent,
                       &dwFlags,
                       &lpTo,
                       &iTolen,
                       &lpOverlapped,
                       &lpCompletionRoutine,
                       &lpThreadId,
                       &lpErrno)) ) {
        return(ReturnValue);
    }

    ReturnValue = m_proctable->lpWSPSendTo(
        s,
        lpBuffers,
        dbBufferCount,
        lpNumberOfBytesSent,
        dwFlags,
        lpTo,
        iTolen,
        lpOverlapped,
        lpCompletionRoutine,
        lpThreadId,
        lpErrno);

    POSTAPINOTIFY(( DTCODE_WSPSendTo,
                    &ReturnValue,
                    m_lib_name,
                    &s,
                    &lpBuffers,
                    &lpNumberOfBytesSent,
                    &dwFlags,
                    &lpTo,
                    &iTolen,
                    &lpOverlapped,
                    &lpCompletionRoutine,
                    &lpThreadId,
                    &lpErrno));

    return(ReturnValue);
}

inline INT
DPROVIDER::WSPSetSockOpt(
    IN SOCKET s,
    IN INT level,
    IN INT optname,
    IN const char FAR *optval,
    IN INT optlen,
    OUT INT FAR *lpErrno
    )
/*++
Routine Description:

    Set a socket option.

Arguments:

    s       - A descriptor identifying a socket.

    level   - The  level  at  which the option is defined; the supported levels
              include   SOL_SOCKET.   (See  annex  for  more  protocol-specific
              levels.)

    optname - The socket option for which the value is to be set.

    optval  - A  pointer  to  the  buffer  in which the value for the requested
              option is supplied.

    optlen  - The size of the optval buffer.

    lpErrno - A pointer to the error code.

Return Value:

    If  no  error  occurs, WSPSetSockOpt()  returns  0.  Otherwise, a value of
    SOCKET_ERROR  is  returned, and  a  specific  error  code  is available in
    lpErrno.

--*/
{
    INT ReturnValue;

    if (PREAPINOTIFY(( DTCODE_WSPSetSockOpt,
                       &ReturnValue,
                       m_lib_name,
                       &s,
                       &level,
                       &optname,
                       &optval,
                       &optlen,
                       &lpErrno)) ) {
        return(ReturnValue);
    }

    ReturnValue = m_proctable->lpWSPSetSockOpt(
        s,
        level,
        optname,
        optval,
        optlen,
        lpErrno);

    POSTAPINOTIFY(( DTCODE_WSPSetSockOpt,
                    &ReturnValue,
                    m_lib_name,
                    &s,
                    &level,
                    &optname,
                    &optval,
                    &optlen,
                    &lpErrno));

    return(ReturnValue);

}

inline INT
DPROVIDER::WSPShutdown(
    IN SOCKET s,
    IN INT how,
    OUT INT FAR *lpErrno
    )
/*++
Routine Description:

    Disable sends and/or receives on a socket.

Arguments:

    s       - A descriptor identifying a socket.

    how     - A  flag  that describes what types of operation will no longer be
              allowed.

    lpErrno - A pointer to the error code.

Return Value:

    If  no  error  occurs, WSPShutdown()  returns  0.   Otherwise, a value of
    SOCKET_ERROR  is  returned, and  a  specific  error  code  is available in
    lpErrno.

--*/
{
    INT ReturnValue;

    if (PREAPINOTIFY(( DTCODE_WSPShutdown,
                       &ReturnValue,
                       m_lib_name,
                       &s,
                       &how,
                       &lpErrno)) ) {
        return(ReturnValue);
    }

    ReturnValue = m_proctable->lpWSPShutdown(
        s,
        how,
        lpErrno);

    POSTAPINOTIFY(( DTCODE_WSPShutdown,
                    &ReturnValue,
                    m_lib_name,
                    &s,
                    &how,
                    &lpErrno));

    return(ReturnValue);
}

inline INT
DPROVIDER::WSPSocket(
    IN int af,
    IN int type,
    IN int protocol,
    IN LPWSAPROTOCOL_INFOW lpProtocolInfo,
    IN GROUP g,
    IN DWORD dwFlags,
    OUT INT FAR *lpErrno
    )
/*++
Routine Description:

    Initialize  internal  data  and  prepare sockets for usage.  Must be called
    before any other socket routine.

Arguments:

    lpProtocolInfo - Supplies  a pointer to a WSAPROTOCOL_INFOA struct that
                     defines  the characteristics of the socket to be created.

    g              - Supplies  the identifier of the socket group which the new
                     socket is to join.

    dwFlags        - Supplies the socket attribute specification.

    lpErrno        - Returns the error code

Return Value:

    WSPSocket() returns zero if successful.  Otherwise it returns an error code
    as outlined in the SPI.

--*/
{
    INT ReturnValue;

    // Debug/Trace stuff
    if (PREAPINOTIFY(( DTCODE_WSPSocket,
                       &ReturnValue,
                       m_lib_name,
                       &af,
                       &type,
                       &protocol,
                       &lpProtocolInfo,
                       &g,
                       &dwFlags,
                       &lpErrno)) ) {
        return(ReturnValue);
    }
    assert(m_proctable->lpWSPSocket);

    // Actual code...
    ReturnValue = m_proctable->lpWSPSocket(
        af,
        type,
        protocol,
        lpProtocolInfo,
        g,
        dwFlags,
        lpErrno);

    // Debug/Trace stuff
    POSTAPINOTIFY(( DTCODE_WSPSocket,
                    &ReturnValue,
                    m_lib_name,
                    &af,
                    &type,
                    &protocol,
                    &lpProtocolInfo,
                    &g,
                    &dwFlags,
                    &lpErrno));

    return(ReturnValue);
}

inline INT
DPROVIDER::WSPStringToAddress(
    IN     LPWSTR AddressString,
    IN     INT AddressFamily,
    IN     LPWSAPROTOCOL_INFOW lpProtocolInfo,
    OUT    LPSOCKADDR lpAddress,
    IN OUT LPINT lpAddressLength,
    IN OUT LPINT lpErrno )
/*++

Routine Description:

    WSPStringToAddress() converts a human-readable string to a socket address
    structure (SOCKADDR) suitable for pass to Windows Sockets routines which
    take such a structure.  If the caller wishes the translation to be done by
    a particular provider, it should supply the corresponding WSAPROTOCOL_INFO
    struct in the lpProtocolInfo parameter.

Arguments:

    AddressString - points to the zero-terminated human-readable string to
                    convert.

    AddressFamily - the address family to which the string belongs.

    lpProtocolInfo - (optional) the WSAPROTOCOL_INFO struct for a particular
                     provider.

    Address - a buffer which is filled with a single SOCKADDR structure.

    lpAddressLength - The length of the Address buffer.  Returns the size of
                      the resultant SOCKADDR structure.

Return Value:

    The return value is 0 if the operation was successful.  Otherwise the value
    SOCKET_ERROR is returned.

--*/
{
     INT ReturnValue;

    if (PREAPINOTIFY(( DTCODE_WSPStringToAddress,
                       &ReturnValue,
                       m_lib_name,
                       &AddressString,
                       &AddressFamily,
                       &lpProtocolInfo,
                       &lpAddress,
                       &lpAddressLength,
                       &lpErrno)) ) {
        return(ReturnValue);
    }

    ReturnValue = m_proctable->lpWSPStringToAddress(
        AddressString,
        AddressFamily,
        lpProtocolInfo,
        lpAddress,
        lpAddressLength,
        lpErrno);

    POSTAPINOTIFY(( DTCODE_WSPStringToAddress,
                    &ReturnValue,
                    m_lib_name,
                    &AddressString,
                    &AddressFamily,
                    &lpProtocolInfo,
                    &lpAddress,
                    &lpAddressLength,
                    &lpErrno));

    return(ReturnValue);
}

inline INT
DPROVIDER::TransmitFile (
IN SOCKET hSocket,
IN HANDLE hFile,
IN DWORD nNumberOfBytesToWrite,
IN DWORD nNumberOfBytesPerSend,
IN LPOVERLAPPED lpOverlapped,
IN LPTRANSMIT_FILE_BUFFERS lpTransmitBuffers,
IN DWORD dwReserved,
OUT LPINT lpErrno
)
{
if (m_transmitfile) {
if (m_transmitfile(
hSocket,
hFile,
nNumberOfBytesToWrite,
nNumberOfBytesPerSend,
lpOverlapped,
lpTransmitBuffers,
dwReserved))
return NO_ERROR;
else {
*lpErrno = GetLastError ();
return SOCKET_ERROR;
}
}
else {
*lpErrno = WSAEOPNOTSUPP;
return SOCKET_ERROR;
}
}

inline INT
DPROVIDER::AcceptEx (
IN SOCKET sListenSocket,
IN SOCKET sAcceptSocket,
IN PVOID lpOutputBuffer,
IN DWORD dwReceiveDataLength,
IN DWORD dwLocalAddressLength,
IN DWORD dwRemoteAddressLength,
OUT LPDWORD lpdwBytesReceived,
IN LPOVERLAPPED lpOverlapped,
OUT LPINTlpErrno
)
{
if (m_acceptex) {
if (m_acceptex (
sListenSocket,
sAcceptSocket,
lpOutputBuffer,
dwReceiveDataLength,
dwLocalAddressLength,
dwRemoteAddressLength,
lpdwBytesReceived,
lpOverlapped))
return NO_ERROR;
else {
*lpErrno = GetLastError ();
return SOCKET_ERROR;
}
}
else  {
*lpErrno = WSAEOPNOTSUPP;
return SOCKET_ERROR;
}
}

#endif


DSOCKET.CPP

/*++

     Copyright (c) 1996 Intel Corporation
     Copyright 1996 - 1998 Microsoft Corporation
     All Rights Reserved

     Permission is granted to use, copy and distribute this software and
     its documentation for any purpose and without fee, provided, that
     the above copyright notice and this statement appear in all copies.
     Intel makes no representations about the suitability of this
     software for any purpose.  This software is provided "AS IS."

     Intel specifically disclaims all warranties, express or implied,
     and all liability, including consequential and other indirect
     damages, for the use of this software, including liability for
     infringement of any proprietary rights, and including the
     warranties of merchantability and fitness for a particular purpose.
     Intel does not assume any responsibility for any errors which may
     appear in this software nor any responsibility to update it.

Module Name:

dsocket.cpp

Abstract:

This module contains the implemetation of the dsocket object used
by lsp.dll

--*/

#include "precomp.h"

LIST_ENTRY  DSOCKET::m_socket_list;
CRITICAL_SECTION DSOCKET::m_socket_list_lock;

INT
DSOCKET::DSocketClassInitialize (
    )
{
    InitializeCriticalSection (&m_socket_list_lock);
    InitializeListHead (&m_socket_list);
    return 0;
}

INT
DSOCKET::DSocketClassCleanup(
    )
{
    PLIST_ENTRY  ListMember;
    PDSOCKET     Socket;
    PDPROVIDER   Provider;
    SOCKET       ProviderSocket;
    INT          Errno;

    //Kill all the open sockets
    ListMember = m_socket_list.Flink;

    while (ListMember != &m_socket_list){
        Socket = CONTAINING_RECORD(
            ListMember,
            DSOCKET,
            m_list_linkage);
        ListMember = ListMember->Flink;

        Socket->Remove ();
        Provider = Socket->GetDProvider();
        ProviderSocket = Socket->GetProviderSocket();

        Provider->WSPCloseSocket(
            ProviderSocket,
            &Errno);

        gUpCallTable.lpWPUCloseSocketHandle(
            Socket->GetSocketHandle(),
            &Errno);

        delete(Socket);
    } //while

    DeleteCriticalSection (&m_socket_list_lock);
    return 0;
}

PDSOCKET
DSOCKET::FindDSocketFromProviderSocket (
    SOCKET  s
    ) {
    PDSOCKET    Socket = NULL;
    PLIST_ENTRY ListMember;

    EnterCriticalSection (&m_socket_list_lock);
    ListMember = m_socket_list.Flink;

    while (ListMember != &m_socket_list){
        PDSOCKET    Socket2 = CONTAINING_RECORD(
            ListMember,
            DSOCKET,
            m_list_linkage);
        ListMember = ListMember->Flink;
        if (Socket2->GetProviderSocket()==s) {
            Socket = Socket2;
            break;
        }
    }
    LeaveCriticalSection (&m_socket_list_lock);

    return Socket;

}

DSOCKET::DSOCKET(
    )
/*++

Routine Description:

    DSOCKET  object  constructor.   Creates and returns a DSOCKET object.  Note
    that  the  DSOCKET object has not been fully initialized.  The "Initialize"
    member function must be the first member function called on the new DSOCKET
    object.

Arguments:

    None

Return Value:

    None
--*/
{
    // Set our data members to known values
    m_provider          = NULL;
    m_socket_handle     = INVALID_SOCKET;
    m_catalog_entry_id  = NULL;
    m_provider_socket   = INVALID_SOCKET;
    m_completion_context = INVALID_SOCKET;
    m_async_events      = NULL;
    m_async_window      = NULL;
m_async_message     = NULL;
m_closing= FALSE;
}

INT
DSOCKET::Initialize(
        IN PDPROVIDER Provider,
        IN SOCKET     ProviderSocket,
        IN DWORD      CatalogEntryId,
        IN SOCKET     SocketHandle
        )
/*++

Routine Description:

    Completes  the  initialization  of  the  DSOCKET object.  This must be the
    first  member  function  called  for  the  DSOCKET object.  This procedure
    should be called only once for the object.

Arguments:

    Provider - Supplies  a  reference  to  the DPROVIDER object associated with
               this DSOCKET object.

    ProviderSocket - The socket handle returned from the lower level provider.

    CatalogEntryId - The CatalogEntryId for the provider referenced by
                     m_provider.

    SocketHandle        - The socket handle returned from WPUCreateSocketHandle().

Return Value:

    The  function returns ERROR_SUCCESS if successful.  Otherwise it
    returns an appropriate WinSock error code if the initialization
    cannot be completed.
--*/
{
    // Store the provider and process object.
    m_provider = Provider;
    m_provider_socket = ProviderSocket;
    m_catalog_entry_id = CatalogEntryId;
    m_socket_handle = SocketHandle;

    // Add this socket to the list of sockets.
    EnterCriticalSection(&m_socket_list_lock);
    InsertHeadList(
        &m_socket_list,
        &m_list_linkage);
    LeaveCriticalSection(&m_socket_list_lock);
    DEBUGF( DBG_TRACE,
            ("Initializing socket %X\n",this));
    return(ERROR_SUCCESS);
}

VOID
DSOCKET::Remove (
    )
{
    EnterCriticalSection(&m_socket_list_lock);
    RemoveEntryList(&m_list_linkage);
    LeaveCriticalSection(&m_socket_list_lock);
}

DSOCKET::~DSOCKET()
/*++

Routine Description:

    DSOCKET  object  destructor.   This  procedure  has  the  responsibility to
    perform  any required shutdown operations for the DSOCKET object before the
    object  memory  is  deallocated.

Arguments:

    None

Return Value:

    None
--*/
{

    DEBUGF( DBG_TRACE,
            ("Destroying socket %X\n",this));
}

VOID
DSOCKET::RegisterAsyncOperation(
    HWND     Window,
    UINT     Message,
    LONG     Events
    )
/*++

Routine Description:

    Registers interest in net work events.

Arguments:

    Window  - The handle to the window that will receive notification of
              network events.

    Message - The message to send for net event notification.

    Events  - The events to be registered.

Return Value:

    NO_ERROR on success else a valid winsock errorcode.

--*/
{

    m_async_window  = Window;
    m_async_message = Message;
    m_async_events  = Events;
}

VOID
DSOCKET::SignalAsyncEvents(
    LPARAM    lParam
    )
/*++

Routine Description:

     The notification function called by the worker thread to signal network
     events.

Arguments:

    None

Return Value:

    None
--*/
{

    gUpCallTable.lpWPUPostMessage (m_async_window,
                                    m_async_message,
                                    (WPARAM)m_socket_handle,
                                    lParam);

}


DSOCKET.H

/*++

     Copyright c 1996 Intel Corporation
     All Rights Reserved

     Permission is granted to use, copy and distribute this software and
     its documentation for any purpose and without fee, provided, that
     the above copyright notice and this statement appear in all copies.
     Intel makes no representations about the suitability of this
     software for any purpose.  This software is provided "AS IS."

     Intel specifically disclaims all warranties, express or implied,
     and all liability, including consequential and other indirect
     damages, for the use of this software, including liability for
     infringement of any proprietary rights, and including the
     warranties of merchantability and fitness for a particular purpose.
     Intel does not assume any responsibility for any errors which may
     appear in this software nor any responsibility to update it.

Module Name:

dsocket.h

Abstract:

    This  header  defines the "DSOCKET" class.  The DSOCKET class defines state
    variables  and  operations for DSOCKET objects within the LSP.DLL.  A
    DSOCKET  object  represents  all  of the information that the LSP.DLL knows
    about a socket.
--*/

#ifndef _DSOCKET_
#define _DSOCKET_

#include <winsock2.h>
#include "llist.h"
#include "classfwd.h"

class DSOCKET
{
  public:

    static
    INT
    DSocketClassInitialize();

    static
    INT
    DSocketClassCleanup();

    static
    PDSOCKET
    FindDSocketFromProviderSocket (
        SOCKET s
        );

    DSOCKET();

    INT
    Initialize(
        IN PDPROVIDER Provider,
        IN SOCKET     ProviderSocket,
        IN DWORD      CatalogEntryId,
        IN SOCKET     Socket
        );
    VOID
    Remove ();

    ~DSOCKET();

    SOCKET
    GetProviderSocket();

    PDPROVIDER
    GetDProvider();

    DWORD
    GetCatalogEntryId();

    SOCKET
    GetSocketHandle();

    VOID
    RegisterAsyncOperation(
        HWND     Window,
        UINT     Message,
        LONG     Events
        );

    VOID
    SignalAsyncEvents(
        LPARAM    lParam
        );

    LONG
    GetAsyncEventMask();

    DWORD
    GetCompletionContext ();

    VOID
    SetCompletionContext (
        DWORD   context
        );

  private:

    PDPROVIDER  m_provider;
    // Reference  to  the  DPROVIDER object representing the service provider
    // that controls this socket.

    SOCKET  m_provider_socket;
// Socket handle exposed by the provider

    DWORD   m_catalog_entry_id;
    // The catalog entry id of the provider that this socket is attached to.

    SOCKET  m_socket_handle;
    // The socket handle returned from WPUCreateSocketHandle.

    DWORD   m_completion_context;
    // Completion key if provider socket is associated with
    // completion port

    LONG    m_async_events;
    // The event mask for the events the client has registered interest in.

    HWND    m_async_window;
    // The handle of the window to receive net event messages.

    UINT    m_async_message;
    // The message to send to the client to signal net envents.

BOOLm_closing;
// Indicates that socket is being closed

LIST_ENTRY  m_list_linkage;
    // Provides the linkage space for a list of DSOCKET objects

    // Note that no LIST_ENTRY is required to correspond to the DPROVIDER
    // object associated  with  this  DSOCKET object since the DPROVIDER object
    // does not  maintain a list of sockets it controls.

    static LIST_ENTRY m_socket_list;
    // Global list of all sockets

    static CRITICAL_SECTION  m_socket_list_lock;
    // Critical section that protects socket list

};   // class DSOCKET

inline SOCKET
DSOCKET::GetSocketHandle()
/*++

Routine Description:

    Retrieves  the  external socket-handle value corresponding to this internal
    DSOCKET object.

Arguments:

    None

Return Value:

    The corresponding external socket-handle value.
--*/
{
    return(m_socket_handle);
}

inline PDPROVIDER
DSOCKET::GetDProvider()
/*++

Routine Description:

    Retrieves  a reference to the DPROVIDER object associated with this DSOCKET
    object.

Arguments:

    None

Return Value:

    The reference to the DPROVIDER object associated with this DSOCKET object.
--*/
{
    return(m_provider);
}

inline DWORD
DSOCKET::GetCatalogEntryId()
/*++

Routine Description:

    Retrieves  a reference to the DPROVIDER object associated with this DSOCKET
    object.

Arguments:

    None

Return Value:

    The reference to the DPROVIDER object associated with this DSOCKET object.
--*/
{
    return(m_catalog_entry_id);
}

inline SOCKET
DSOCKET::GetProviderSocket()
/*++

Routine Description:

    Retrieves the handle for the provider socket

Arguments:

    None

Return Value:

    The provider socket associated with this socket
--*/
{
    return(m_provider_socket);
}

inline
LONG
DSOCKET::GetAsyncEventMask()
/*++

Routine Description:

    Returns the event mask for this socket

Arguments:

    None

Return Value:

    The event mask for this socket.
--*/
{
    return(m_async_events);
}

inline DWORD
DSOCKET::GetCompletionContext()
/*++

Routine Description:

    Retrieves provider's socket completion context,

Arguments:

    None

Return Value:

    Provider's socket completion context

--*/
{
    return(m_completion_context);
}

inline VOID
DSOCKET::SetCompletionContext(
    DWORD   Context
    )
/*++

Routine Description:

    Sets provider's socket completion context,

Arguments:

    Context     completion context

Return Value:

    None

--*/
{
    m_completion_context = Context;
}

#endif // _DSOCKET_


DT_DLL.H

/*++

Copyright (c) 1995 Intel Corp

File Name:

    dt_dll.h

Abstract:

    This header describes the interface to the WinSock 2 debug/trace
    DLL.  Please see the design spec for more information.

Author:

    Michael A. Grafton

--*/

#ifndef _DT_DLL_H
#define _DT_DLL_H

#include "warnoff.h"
#include <windows.h>

//
// This type defines a pointer to the Pre/PostApiNotify functions
//

typedef BOOL (WINAPIV * LPFNWSANOTIFY)(
    IN  INT    NotificationCode,
    OUT LPVOID ReturnCode,
    IN  LPSTR  LibraryName,
    ...);

//
// Function prototypes for Pre/PostApiNotify
//

BOOL WINAPIV
WSAPreApiNotify(
    IN  INT    NotificationCode,
    OUT LPVOID ReturnCode,
    IN  LPSTR  LibraryName,
    ...);

BOOL WINAPIV
WSAPostApiNotify(
    IN  INT    NotificationCode,
    OUT LPVOID ReturnCode,
    IN  LPSTR  LibraryName,
    ...);

//
// API function codes for Pre/PostApiNotify functions.  Note:  These must start
// at  1  or more and be fairly densely assigned.  Small gaps can be tolerated.
// Note  that  a  "MAX_DTCODE"  definition  should  be updated if new codes are
// added.
//

#define DTCODE_accept 1
#define DTCODE_bind 2
#define DTCODE_closesocket 3
#define DTCODE_connect 4
#define DTCODE_getpeername 5
#define DTCODE_getsockname 6
#define DTCODE_getsockopt 7
#define DTCODE_htonl 8
#define DTCODE_htons 9
#define DTCODE_ioctlsocket 10
#define DTCODE_listen 11
#define DTCODE_ntohl 12
#define DTCODE_ntohs 13
#define DTCODE_recv 14
#define DTCODE_recvfrom 15
#define DTCODE_select 16
#define DTCODE_send 17
#define DTCODE_sendto 18
#define DTCODE_setsockopt 19
#define DTCODE_shutdown 20
#define DTCODE_socket 21
#define DTCODE_WSAAccept 22
#define DTCODE_WSAAsyncSelect 23
#define DTCODE_WSACancelBlockingCall 24
#define DTCODE_WSACleanup 25
#define DTCODE_WSACloseEvent 26
#define DTCODE_WSAConnect 27
#define DTCODE_WSACreateEvent 28
#define DTCODE_WSADuplicateSocketA 29
#define DTCODE_WSAEnumNetworkEvents 30
#define DTCODE_WSAEnumProtocolsA 31
#define DTCODE_WSAEventSelect 32
#define DTCODE_WSAGetLastError 33
#define DTCODE_WSAGetOverlappedResult 34
#define DTCODE_WSAGetQOSByName 35
#define DTCODE_WSAHtonl 36
#define DTCODE_WSAHtons 37
#define DTCODE_WSAIoctl 38
#define DTCODE_WSAIsBlocking 39
#define DTCODE_WSAJoinLeaf 40
#define DTCODE_WSANtohl 41
#define DTCODE_WSANtohs 42
#define DTCODE_WSARecv 43
#define DTCODE_WSARecvDisconnect 44
#define DTCODE_WSARecvFrom 45
#define DTCODE_WSAResetEvent 46
#define DTCODE_WSASend 47
#define DTCODE_WSASendDisconnect 48
#define DTCODE_WSASendTo 49
#define DTCODE_WSASetBlockingHook 50
#define DTCODE_WSASetEvent 51
#define DTCODE_WSASetLastError 52
#define DTCODE_WSASocketA 53
#define DTCODE_WSAStartup 54
#define DTCODE_WSAUnhookBlockingHook 55
#define DTCODE_WSAWaitForMultipleEvents 56
#define DTCODE_gethostbyaddr 57
#define DTCODE_gethostbyname 58
#define DTCODE_gethostname 59
#define DTCODE_getprotobyname 60
#define DTCODE_getprotobynumber 61
#define DTCODE_getservbyname 62
#define DTCODE_getservbyport 63
#define DTCODE_inet_addr 64
#define DTCODE_inet_ntoa 65
#define DTCODE_WSAAsyncGetHostByAddr 66
#define DTCODE_WSAAsyncGetHostByName 67
#define DTCODE_WSAAsyncGetProtoByName 68
#define DTCODE_WSAAsyncGetProtoByNumber 69
#define DTCODE_WSAAsyncGetServByName 70
#define DTCODE_WSAAsyncGetServByPort 71
#define DTCODE_WSACancelAsyncRequest 72
#define DTCODE_WSPAccept 73
#define DTCODE_WSPAsyncSelect 74
#define DTCODE_WSPBind 75
#define DTCODE_WSPCancelBlockingCall 76
#define DTCODE_WSPCleanup 77
#define DTCODE_WSPCloseSocket 78
#define DTCODE_WSPConnect 79
#define DTCODE_WSPDuplicateSocket 80
#define DTCODE_WSPEnumNetworkEvents 81
#define DTCODE_WSPEventSelect 82
#define DTCODE_WSPGetOverlappedResult 83
#define DTCODE_WSPGetPeerName 84
// The  WSPGetProcTable  function  has  been removed, but the code numbers have
// been kept the same.
// #define DTCODE_WSPGetProcTable 85
#define DTCODE_WSPGetSockName 86
#define DTCODE_WSPGetSockOpt 87
#define DTCODE_WSPGetQOSByName 88
#define DTCODE_WSPIoctl 89
#define DTCODE_WSPJoinLeaf 90
#define DTCODE_WSPListen 91
#define DTCODE_WSPRecv 92
#define DTCODE_WSPRecvDisconnect 93
#define DTCODE_WSPRecvFrom 94
#define DTCODE_WSPSelect 95
#define DTCODE_WSPSend 96
#define DTCODE_WSPSendDisconnect 97
#define DTCODE_WSPSendTo 98
#define DTCODE_WSPSetSockOpt 99
#define DTCODE_WSPShutdown 100
#define DTCODE_WSPSocket 101
#define DTCODE_WSPStartup 102
#define DTCODE_WPUCloseEvent 103
#define DTCODE_WPUCloseSocketHandle 104
#define DTCODE_WPUCreateEvent 105
#define DTCODE_WPUCreateSocketHandle 106
#define DTCODE_WSCDeinstallProvider 107
#define DTCODE_WSCInstallProvider 108
#define DTCODE_WPUModifyIFSHandle 109
#define DTCODE_WPUQueryBlockingCallback 110
#define DTCODE_WPUQuerySocketHandleContext 111
#define DTCODE_WPUQueueApc 112
#define DTCODE_WPUResetEvent 113
#define DTCODE_WPUSetEvent 114
#define DTCODE_WSCEnumProtocols 115
#define DTCODE_WPUGetProviderPath 116
#define DTCODE_WPUPostMessage 117
#define DTCODE_WPUFDIsSet 118
#define DTCODE_WSADuplicateSocketW 119
#define DTCODE_WSAEnumProtocolsW 120
#define DTCODE_WSASocketW 121
#define DTCODE___WSAFDIsSet 122
#define DTCODE_WSAAddressToStringA 123
#define DTCODE_WSAAddressToStringW 124
#define DTCODE_WSAStringToAddressA 125
#define DTCODE_WSAStringToAddressW 126
#define DTCODE_WSALookupServiceBeginA 127
#define DTCODE_WSALookupServiceBeginW 128
#define DTCODE_WSALookupServiceNextA 129
#define DTCODE_WSALookupServiceNextW 130
#define DTCODE_WSALookupServiceEnd 131
#define DTCODE_WSAGetAddressByNameA 132
#define DTCODE_WSAGetAddressByNameW 133
#define DTCODE_WSAInstallServiceClassA 134
#define DTCODE_WSAInstallServiceClassW 135
#define DTCODE_WSASetServiceA 136
#define DTCODE_WSASetServiceW 137
#define DTCODE_WSARemoveServiceClass 138
#define DTCODE_WSAGetServiceClassInfoA 139
#define DTCODE_WSAGetServiceClassInfoW 140
#define DTCODE_WSAEnumNameSpaceProvidersA 141
#define DTCODE_WSAEnumNameSpaceProvidersW 142
#define DTCODE_WSAGetServiceClassNameByClassIdA 143
#define DTCODE_WSAGetServiceClassNameByClassIdW 144
#define DTCODE_WSPAddressToString 145
#define DTCODE_WSPStringToAddress 146
#define DTCODE_NSPLookupServiceBegin 147
#define DTCODE_NSPLookupServiceNext 148
#define DTCODE_NSPLookupServiceEnd 149
#define DTCODE_NSPGetAddressByName 150
#define DTCODE_NSPInstallServiceClass 151
#define DTCODE_NSPSetService 152
#define DTCODE_NSPRemoveServiceClass 153
#define DTCODE_NSPGetServiceClassInfo 154
#define DTCODE_NSPEnumNameSpaceProviders 155
#define DTCODE_NSPGetServiceClassNameByClassId 156

#define MAX_DTCODE DTCODE_NSPGetServiceClassNameByClassId

#endif


DTHOOK.CPP

/*++

     Copyright (c) 1996 Intel Corporation
     Copyright 1996 - 1998 Microsoft Corporation
     All Rights Reserved

     Permission is granted to use, copy and distribute this software and
     its documentation for any purpose and without fee, provided, that
     the above copyright notice and this statement appear in all copies.
     Intel makes no representations about the suitability of this
     software for any purpose.  This software is provided "AS IS."

     Intel specifically disclaims all warranties, express or implied,
     and all liability, including consequential and other indirect
     damages, for the use of this software, including liability for
     infringement of any proprietary rights, and including the
     warranties of merchantability and fitness for a particular purpose.
     Intel does not assume any responsibility for any errors which may
     appear in this software nor any responsibility to update it.

  Module Name:

    dthook.cpp

  Abstract:

    This module contains the hooks that allow specially-compiled
    versions of layered provier DLL call into the debug/trace DLL.

--*/

#include "precomp.h"

//
// Static Globals
//

// Function pointers to the Debug/Trace DLL entry points
static LPFNWSANOTIFY PreApiNotifyFP = NULL;
static LPFNWSANOTIFY PostApiNotifyFP = NULL;

// Handle to the Debug/Trace DLL module
static HMODULE       DTDll = NULL;

// Static string to pass to Debug/Trace notification functions
static char LibName[] = "lsp";

//
// Functions
//

LPFNWSANOTIFY
GetPreApiNotifyFP(void)
/*++

  Function Description:

      Returns a pointer to the WSAPreApiNotify function exported by
      the Debug/Trace DLL.  This variable is global to this file only,
      and is initialized during DT_Initialize().

  Arguments:

      None.

  Return Value:

      Returns whatever is stored in PreApiNotifyFP.

--*/
{
    return(PreApiNotifyFP);
}

LPFNWSANOTIFY
GetPostApiNotifyFP(void)
/*++

  Function Description:

      Returns a pointer to the WSAPreApiNotify function exported by
      the Debug/Trace DLL.  This variable is global to this file only,
      and is initialized during DT_Initialize().

  Arguments:

      None.

  Return Value:

      Returns whatever is stored in PreApiNotifyFP.

--*/
{
    return(PostApiNotifyFP);
}

void
DTHookInitialize(void)
/*++

  Function Description:

      Intializes this hook module.  Loads the Debug/Trace DLL, if
      possible, and sets the global function pointers to point to the
      entry points exported by that DLL.  If the DLL can't be loaded,
      the function just returns and the function pointers are left at
      NULL.

      This function MUST be called before any of the hook functions
      are called, or the hook functions will not work.

  Arguments:

      None.

  Return Value:

      None.

--*/
{
    DTDll = (HMODULE)LoadLibrary("mydt_dll");

    if (DTDll == NULL) {
        return;
    }

    PreApiNotifyFP = (LPFNWSANOTIFY)GetProcAddress(
        DTDll,
        "WSAPreApiNotify");

    PostApiNotifyFP = (LPFNWSANOTIFY)GetProcAddress(
        DTDll,
        "WSAPostApiNotify");
}

void
DTHookShutdown(void)
/*++

  Function Description:

      Should be called to shutdown Debug/Tracing.  The function
      pointers are set to NULL, and the DLL is unloaded from memory.

  Arguments:

      None.

  Return Value:

      None.

--*/
{
    if (DTDll != NULL) {
        FreeLibrary(DTDll);
    }

    PreApiNotifyFP = NULL;
    PostApiNotifyFP = NULL;
}


DTHOOK.H

/*++

     Copyright c 1996 Intel Corporation
     All Rights Reserved

     Permission is granted to use, copy and distribute this software and
     its documentation for any purpose and without fee, provided, that
     the above copyright notice and this statement appear in all copies.
     Intel makes no representations about the suitability of this
     software for any purpose.  This software is provided "AS IS."

     Intel specifically disclaims all warranties, express or implied,
     and all liability, including consequential and other indirect
     damages, for the use of this software, including liability for
     infringement of any proprietary rights, and including the
     warranties of merchantability and fitness for a particular purpose.
     Intel does not assume any responsibility for any errors which may
     appear in this software nor any responsibility to update it.

  Module Name:

    dthook.h

  Abstract:

    Header file containing definitions, function prototypes, and other
    stuff for the Debug/Trace.

--*/

#ifndef DTHOOK_H
#define DTHOOK_H

#include "warnoff.h"

#include <winsock2.h>
#include "dbgtrace.h"

//
// Function Declarations
//

LPFNWSANOTIFY
GetPreApiNotifyFP(void);

LPFNWSANOTIFY
GetPostApiNotifyFP(void);

void
DTHookInitialize(void);

void
DTHookShutdown(void);

#ifdef DEBUG_TRACING

#define PREAPINOTIFY(x) \
    ( GetPreApiNotifyFP()  ? ( (*(GetPreApiNotifyFP())) x ) : FALSE)
#define POSTAPINOTIFY(x) \
    if ( GetPostApiNotifyFP() ) { \
         (VOID) ( (*(GetPostApiNotifyFP())) x ); \
    } else

#else

#define PREAPINOTIFY(x) FALSE
#define POSTAPINOTIFY(x)

#endif  // DEBUG_TRACING
#endif  // DTHOOK_H


DWORKER.CPP

/*++

     Copyright (c) 1996 Intel Corporation
     Copyright 1996 - 1998 Microsoft Corporation
     All Rights Reserved

     Permission is granted to use, copy and distribute this software and
     its documentation for any purpose and without fee, provided, that
     the above copyright notice and this statement appear in all copies.
     Intel makes no representations about the suitability of this
     software for any purpose.  This software is provided "AS IS."

     Intel specifically disclaims all warranties, express or implied,
     and all liability, including consequential and other indirect
     damages, for the use of this software, including liability for
     infringement of any proprietary rights, and including the
     warranties of merchantability and fitness for a particular purpose.
     Intel does not assume any responsibility for any errors which may
     appear in this software nor any responsibility to update it.

Module Name:

Abstract:

--*/

#include "precomp.h"

PDOVERLAPPEDSTRUCTMGR gOverlappedManager;

VOID
CALLBACK
APCProc(
    DWORD Context
    )
/*++
Routine Description:

    This routine unpacks the context value passed to WPUQueueApc() and calls
    the users completion function. This function is called in the clients
    thread context.

Arguments:

    Context - The context value passed to WPUQueueApc().

Return Value:

    None

--*/
{
    LPOVERLAPPED                        lpOverlapped;
    LPWSAOVERLAPPED_COMPLETION_ROUTINE  UserCompletionRoutine;

    lpOverlapped = (LPOVERLAPPED) Context;
    UserCompletionRoutine =
        (LPWSAOVERLAPPED_COMPLETION_ROUTINE)lpOverlapped->Internal;
    lpOverlapped->Internal = lpOverlapped->Offset; // To make sure it
                                                   // in no longer is
                                                   // WSS_OPERATION_IN_PROGRESS
    UserCompletionRoutine (
        lpOverlapped->Offset,
        lpOverlapped->InternalHigh,
        lpOverlapped,
        lpOverlapped->OffsetHigh
        );

}

VOID
CALLBACK
OverlappedCompletionProc(
    DWORD  dwError,
    DWORD  cbTransferred,
    LPWSAOVERLAPPED lpOverlapped,
    DWORD dwFlags
    )
/*++
Routine Description:

    This routine is the completion routine for all overlapped operations
    initiated with the lower level provider.

Arguments:

    dwError - The error code for the overlapped operation.

    cbTransferred - The number of bytes transfered by the overlapped
                    operation.

    lpOverlapped - a pointer to the WSAOVERLAPPED struct associated with the
                   overlapped operation.

    dwFlags -  Not Used must be zero.

Return Value:

    None

--*/
{
    INT                                Errno;
    PINTERNALOVERLAPPEDSTRUCT   OverlappedStruct;
DWORD   OperationType;
PDPROVIDER   Provider;
SOCKET   ProviderSocket, Socket;
WSATHREADID   UserThreadId;
LPWSAOVERLAPPED_COMPLETION_ROUTINE UserCompletionRoutine;
LPWSAOVERLAPPED   UserOverlappedStruct;

    // Get the stored overlapped operation parameters
OverlappedStruct = gOverlappedManager->GetInternalOverlappedStructure(
lpOverlapped);
    // If the completed operation was a recieve operation copy the internal
    // buffers into the users buffers.
    if (OverlappedStruct != NULL){
OperationType = OverlappedStruct->iolOperationType;
Socket = OverlappedStruct->iolSocket;
Provider = OverlappedStruct->iolProvider;
ProviderSocket = OverlappedStruct->iolProviderSocket;
UserCompletionRoutine = OverlappedStruct->iolUserCompletionRoutine;
UserOverlappedStruct = OverlappedStruct->iolUserOverlappedStruct;
UserThreadId = OverlappedStruct->iolUserThreadId;

if (dwError==WSA_IO_PENDING)
            Provider->WSPGetOverlappedResult (
                    ProviderSocket,
                    lpOverlapped,
                    &cbTransferred,
                    FALSE,
                    &dwFlags,
                    (int *)&dwError);

        switch (OperationType) {
case WSP_RECV:
        case WSP_RECVFROM:
            gBufferManager->CopyBuffer(
(OverlappedStruct->iolInternalBufferCount <= MAX_FAST_BUFS)
? &OverlappedStruct->iolInternalBuffers[0]
: OverlappedStruct->iolpInternalBuffers,
                OverlappedStruct->iolInternalBufferCount,
0,
cbTransferred,
(OverlappedStruct->iolUserBufferCount <= MAX_FAST_BUFS)
? &OverlappedStruct->iolUserBuffers[0]
: OverlappedStruct->iolpUserBuffers,
                OverlappedStruct->iolUserBufferCount,
                0);
            gBufferManager->FreeBuffer(
(OverlappedStruct->iolInternalBufferCount <= MAX_FAST_BUFS)
? &OverlappedStruct->iolInternalBuffers[0]
: OverlappedStruct->iolpInternalBuffers,
                OverlappedStruct->iolInternalBufferCount);
if (OverlappedStruct->iolUserBufferCount > MAX_FAST_BUFS)
delete OverlappedStruct->iolpUserBuffers;
if (OverlappedStruct->iolInternalBufferCount > MAX_FAST_BUFS)
delete OverlappedStruct->iolpInternalBuffers;
break;

case WSP_SEND:
case WSP_SENDTO:
if (OverlappedStruct->iolUserBufferCount > MAX_FAST_BUFS)
delete OverlappedStruct->iolpUserBuffers;
break;

        case WSP_IOCTL:
if ((OverlappedStruct->iolIoControlCode==SIO_GET_EXTENSION_FUNCTION_POINTER)
&& (NO_ERROR==Errno)) {
Provider->InterceptExtensions (
OverlappedStruct->iolInputBuffer,
OverlappedStruct->iolOutputBuffer,
&Errno);
}
break;
}

        gOverlappedManager->FreeOverlappedStruct (lpOverlapped);

        // If the user requested completion routine notification of the I/O
        // completion queue an APC to the user thread else signal the users
        // event.
        if (UserCompletionRoutine){
            UserOverlappedStruct->InternalHigh = cbTransferred;
            UserOverlappedStruct->Offset = dwError;
            UserOverlappedStruct->OffsetHigh = dwFlags;

            // Changing Internal field to anything other than
            // WSS_OPERATON_IN_PROGRESS indicates that operation
            // is completed and results can be read by WSPGetOverlappedResult,
            // That's why we set this field last (after updating all others)
            // Note that event if address of user completion routine is
            // equal to WSS_OPERATON_IN_PROGRESS (numericall), we are still
            // safe, because we'll update it again in our completion routine
            // Also, generally if application specifies completion routine,
            // it should not use WSAGetOverlappedResult, so essentially by
            // doing all of this we are just making our code bullet proof
            UserOverlappedStruct->Internal = (DWORD)UserCompletionRoutine;

            gUpCallTable.lpWPUQueueApc(
                &UserThreadId,
                APCProc,
                (DWORD)UserOverlappedStruct,
                &Errno);

        } //if
        else{
            UserOverlappedStruct->Offset = dwError;
            UserOverlappedStruct->OffsetHigh = dwFlags;
                // Internal and InternalHigh fields will be updated
                // by the WPUCompleteOverlappedRequest
            lpWPUCompleteOverlappedRequest (
Socket,
                UserOverlappedStruct,
                dwError,
                cbTransferred,
                &Errno);
        } //else
    } //if
}

VOID
InitiateOverlappedOperation (
    PINTERNALOVERLAPPEDSTRUCT           OverlappedStruct,
    LPWSAOVERLAPPED_COMPLETION_ROUTINE  CompletionProc,
    LPWSATHREADID                       thread_id
    )
/*++
Routine Description:

    Initiates overlapped operation with provider.

Arguments:

    OverlappedStruct    - packet operation parameters
    CompletionProc      - routine to be invoked upon completion
    thread_id           - id of the thread to which to post APC if any
Return Value:

    NONE
--*/
{
    DWORD                     BytesTransfered;
    INT                       Errno;
    INT                       ReturnCode;

    switch (OverlappedStruct->iolOperationType) {
    case WSP_RECV:
        ReturnCode = OverlappedStruct->iolProvider->WSPRecv(
            OverlappedStruct->iolProviderSocket,
(OverlappedStruct->iolInternalBufferCount <= MAX_FAST_BUFS)
? &OverlappedStruct->iolInternalBuffers[0]
: OverlappedStruct->iolpInternalBuffers,
            OverlappedStruct->iolInternalBufferCount,
            &BytesTransfered,
            &OverlappedStruct->iolFlags,
            &OverlappedStruct->iolInternalOverlappedStruct,
            CompletionProc,
            thread_id,
            &Errno);
        break;

    case WSP_RECVFROM:
        ReturnCode = OverlappedStruct->iolProvider->WSPRecvFrom(
            OverlappedStruct->iolProviderSocket,
(OverlappedStruct->iolInternalBufferCount <= MAX_FAST_BUFS)
? &OverlappedStruct->iolInternalBuffers[0]
: OverlappedStruct->iolpInternalBuffers,
            OverlappedStruct->iolInternalBufferCount,
            &BytesTransfered,
            &OverlappedStruct->iolFlags,
            OverlappedStruct->iolSockAddr,
            OverlappedStruct->iolSockAddrLenPtr,
            &OverlappedStruct->iolInternalOverlappedStruct,
            CompletionProc,
            thread_id,
            &Errno);
        break;

    case WSP_SEND:
         ReturnCode = OverlappedStruct->iolProvider->WSPSend(
            OverlappedStruct->iolProviderSocket,
(OverlappedStruct->iolUserBufferCount <= MAX_FAST_BUFS)
? &OverlappedStruct->iolUserBuffers[0]
: OverlappedStruct->iolpUserBuffers,
            OverlappedStruct->iolUserBufferCount,
            &BytesTransfered,
            OverlappedStruct->iolFlags,
            &OverlappedStruct->iolInternalOverlappedStruct,
            CompletionProc,
            thread_id,
            &Errno);
        break;
    case WSP_SENDTO:
        ReturnCode = OverlappedStruct->iolProvider->WSPSendTo(
            OverlappedStruct->iolProviderSocket,
(OverlappedStruct->iolUserBufferCount <= MAX_FAST_BUFS)
? &OverlappedStruct->iolUserBuffers[0]
: OverlappedStruct->iolpUserBuffers,
            OverlappedStruct->iolUserBufferCount,
            &BytesTransfered,
            OverlappedStruct->iolFlags,
            OverlappedStruct->iolSockAddr,
            OverlappedStruct->iolSockAddrLen,
            &OverlappedStruct->iolInternalOverlappedStruct,
            CompletionProc,
            thread_id,
            &Errno);
        break;

    case WSP_IOCTL:
        ReturnCode = OverlappedStruct->iolProvider->WSPIoctl(
            OverlappedStruct->iolProviderSocket,
            OverlappedStruct->iolIoControlCode,
            OverlappedStruct->iolInputBuffer,
            OverlappedStruct->iolInputBufferLength,
            OverlappedStruct->iolOutputBuffer,
            OverlappedStruct->iolOutputBufferLength,
            &BytesTransfered,
            &OverlappedStruct->iolInternalOverlappedStruct,
            CompletionProc,
            thread_id,
            &Errno);
        break;
case ACCEPT_EX:
        ReturnCode = OverlappedStruct->iolProvider->AcceptEx (
            OverlappedStruct->iolListenSocket,
            OverlappedStruct->iolAcceptSocket,
            OverlappedStruct->iolOutputBuffer,
            OverlappedStruct->iolOutputBufferLength,
            OverlappedStruct->iolLocalAddressLength,
            OverlappedStruct->iolRemoteAddressLength,
            &BytesTransfered,
            &OverlappedStruct->iolInternalOverlappedStruct,
            &Errno);
break;
case TRANSMIT_FILE:
        ReturnCode = OverlappedStruct->iolProvider->TransmitFile (
            OverlappedStruct->iolProviderSocket,
            OverlappedStruct->iolFileHandle,
            OverlappedStruct->iolBytesToWrite,
            OverlappedStruct->iolBytesPerSend,
            &OverlappedStruct->iolInternalOverlappedStruct,
((OverlappedStruct->iolTransmitBuffers.HeadLength!=0)
|| (OverlappedStruct->iolTransmitBuffers.TailLength!=0))
? &OverlappedStruct->iolTransmitBuffers
: NULL,
            OverlappedStruct->iolReserved,
            &Errno);
break;
default:
assert (FALSE);
break;
    } //switch

    // Complete ourselves if provider fails right away
    if ((ReturnCode!=NO_ERROR) && (Errno!=WSA_IO_PENDING))
        OverlappedCompletionProc (
            Errno,
            0,
            &OverlappedStruct->iolInternalOverlappedStruct,
            0);

}

DWORD
WorkerThreadProc(
    DWORD  Context
    )
/*++
Routine Description:

    Thread procedure passed to CreatThread().

Arguments:

    Context - Context value passed to CreateThread().  The context value is the
              worker thread object.

Return Value:

    The Return value of the worker thread

--*/

{
    PDWORKERTHREAD            Thread;
    HINSTANCE                 HModule;
    DWORD                     ReturnCode;
    HModule = LoadLibraryA (gLibraryName);

    Thread = (PDWORKERTHREAD) Context;
    ReturnCode = Thread->WorkerThreadProc();
    delete Thread;

    DEBUGF( DBG_TRACE,
            ("Exiting worker thread.\n"));
    FreeLibraryAndExitThread (HModule, ReturnCode);
    return 0;   // Keep compiler happy
}

DWORKERTHREAD::DWORKERTHREAD()
/*++
Routine Description:

    Creates any internal state.

Arguments:

    None

Return Value:

    None

--*/

{
    m_exit_thread = FALSE;
    m_thread_count = NULL;
    m_wakeup_semaphore = NULL;
    m_completion_port = NULL;

    InitializeCriticalSection(
        &m_overlapped_operation_queue_lock);

    InitializeListHead(
        &m_overlapped_operation_queue);
}

VOID
DWORKERTHREAD::Destroy (
    )
/*++
Routine Description:

    Initiates destruction of the DWORKERTHREAD object.
    This can't be done syncronously in destructor because this
    will require waiting for thread object to exit which we want
    to avoid.

Arguments:

    NONE

Return Value:

    NONE
--*/
{
    // If we made it through Initialize. Wake up our threads tell them to exit.
    if (m_thread_count>0){
        m_exit_thread = TRUE;
        if (m_completion_port) {
            static OVERLAPPED overlapped;
            PostQueuedCompletionStatus (m_completion_port,
                                    0,
                                    INVALID_SOCKET,
                                    &overlapped
                                    );
        }
        else {
            ReleaseSemaphore (m_wakeup_semaphore, m_thread_count, NULL);
        }
        m_thread_count = 0;
    }
}

DWORKERTHREAD::~DWORKERTHREAD()
/*++
Routine Description:

    destroys any internal state.

Arguments:

    None

Return Value:

    None

--*/
{

    if (m_completion_port) {
        CloseHandle (m_completion_port);
        m_completion_port = NULL;
    }
    else if (m_wakeup_semaphore){
        CloseHandle(m_wakeup_semaphore);
        m_wakeup_semaphore = NULL;
    } //if

    if (gOverlappedManager) {
        delete(gOverlappedManager);
        gOverlappedManager = NULL;
    }

    DeleteCriticalSection(
        &m_overlapped_operation_queue_lock);

    DEBUGF( DBG_TRACE,
            ("Destroyed worker thread object\n"));
}

INT
DWORKERTHREAD::Initialize(

    )
/*++
Routine Description:

    Initializes the DWORKERTHREAD object.

Arguments:

    NONE

Return Value:

    If no error occurs, Initialize() returns NO_ERROR.  Otherwise the value
    SOCKET_ERROR  is  returned,  and  a  specific  error  code  is available in
    lpErrno.

--*/
{
    INT ReturnCode =WSAENOBUFS;
    DWORD           ThreadId;
    HANDLE          hThread;
    SYSTEM_INFO     info;
DWORDmax_threads;

    DEBUGF( DBG_TRACE,
            ("Initializing worker thread \n"));

    m_completion_port = CreateIoCompletionPort (
                                INVALID_HANDLE_VALUE,
                                NULL,
                                0,
                                0);
    if (m_completion_port!=NULL) {
        ReturnCode = NO_ERROR;
// Don't need extra worker threads if we use completion
// port -> IO will be initiated/executed in the context
// of the original client thread and the worker thread
// will only be used to receive and redirect completion
// notification.
max_threads = 1;
}
    else {
        // Create the semaphore we will use to communicat with the worker thread.
        m_wakeup_semaphore = CreateSemaphore(
            NULL,
            0,
            MAXLONG,
            NULL);
        if ( m_wakeup_semaphore){
GetSystemInfo (&info);
max_threads = info.dwNumberOfProcessors;
            ReturnCode = NO_ERROR;
        } //if
    } // if

    if (NO_ERROR == ReturnCode){
        gOverlappedManager = new DOVERLAPPEDSTRUCTMGR;
        if (gOverlappedManager!=NULL) {
            ReturnCode = gOverlappedManager->Initialize();
        }
        else
            ReturnCode = WSAENOBUFS;
    }

    //
    // Create the worker threads.
    //
    if (NO_ERROR == ReturnCode){
        for (m_thread_count=0; m_thread_count<max_threads; m_thread_count++) {
            hThread = CreateThread(
                NULL,
                0,
                (LPTHREAD_START_ROUTINE)::WorkerThreadProc,
                this,
                0,
                &ThreadId);
            if (hThread!=NULL)
                CloseHandle (hThread);
            else
                break;
        } // for
if (m_thread_count>0)
ReturnCode = NO_ERROR;
else
ReturnCode = WSAENOBUFS;

    } //if

    if (NO_ERROR != ReturnCode){
        //Cleanup any resources we may have allocated.
        if (m_thread_count>0){
            m_exit_thread = TRUE;
            ReleaseSemaphore(m_wakeup_semaphore, m_thread_count, NULL);
        } //if

        if (gOverlappedManager) {
            delete gOverlappedManager;
            gOverlappedManager = NULL;
        }

        if (m_wakeup_semaphore) {
            CloseHandle (m_wakeup_semaphore);
            m_wakeup_semaphore = NULL;
        } // if

    } //if

    return(ReturnCode);
} //Initailize

DWORD
DWORKERTHREAD::WorkerThreadProc()
/*++
Routine Description:

    The thread procedure for this object.

Arguments:

    NONE

Return Value:

    NO_ERROR

--*/
{
    INT   Errno;

    gUpCallTable.lpWPUOpenCurrentThread(
        &m_thread_id,
        &Errno);

    while (!m_exit_thread){
        if (m_completion_port) {
            BOOL            result;
            LPOVERLAPPED    lpOverlapped;
            DWORD           key, cbTransferred;
            result = GetQueuedCompletionStatus (
                        m_completion_port,
                        &cbTransferred,
                        &key,
                        &lpOverlapped,
                        INFINITE
                        );
            if (m_exit_thread) {
                DEBUGF (DBG_TRACE, ("Worker thread received signal to exit.\n"));
                break;
            } // if
            else if ((key!=-1) && (lpOverlapped!=NULL)) {
                OverlappedCompletionProc (
                    WSA_IO_PENDING,
                    cbTransferred,
                    lpOverlapped,
                    0);
            } //else if

        } // if
        else {
            DWORD                       ReturnCode;
            PINTERNALOVERLAPPEDSTRUCT   OverlappedStruct;
            ReturnCode = WaitForSingleObjectEx(
                m_wakeup_semaphore,
                INFINITE,
                TRUE);
            if (m_exit_thread) {
                DEBUGF (DBG_TRACE, ("Worker thread received signal to exit.\n"));
                break;
            } // if
            else if (ReturnCode == WAIT_OBJECT_0){

                // Is there a queued overlapped operation that needs to be
                // initiated.
                OverlappedStruct = NextOverlappedOperation();

                if (OverlappedStruct){
                    InitiateOverlappedOperation (
                        OverlappedStruct,
                        OverlappedCompletionProc,
                        &m_thread_id);
                } //if
            } // else if
        } //else
    } //while

    gUpCallTable.lpWPUCloseThread(
        &m_thread_id,
        &Errno);

    return(NO_ERROR);
}

INT
DWORKERTHREAD::QueueOverlappedRecv(
    PDSOCKET                           Socket,
    LPWSABUF                           UserBuffers,
    DWORD                              UserBufferCount,
    LPDWORD                            UserBytesRecvd,
    LPDWORD                            UserFlags,
    LPWSAOVERLAPPED                    UserOverlappedStruct,
    LPWSAOVERLAPPED_COMPLETION_ROUTINE UserCompletionRoutine,
    LPWSATHREADID                      UserThreadId,
    LPWSABUF                           InternalBuffers,
    DWORD                              InternalBufferCount,
    LPINT                              Errno
    )
/*++

Routine Description:

    this routine allocates an internal overlapped structure stores its
    arguments in the allocated structure and enqueues the structure for the
    worker thread to complet the I/O operation.

Arguments:

    Socket      - Socket object

    UserBuffers - The pointer to the user buffer(s).

    UserBufferCount - The number of user buffers.

    UserBytesRecvd - The pointer to the user BytesRecvd parameter.

    UserFlags - A pointer to the user flags argument.

    UserOverlappedStruct - The user overlapped struct pointer.

    UserCompletionRoutine - The user overlapped completion routine.

    UserThreadId - The user thread ID.

    InternalBuffers - A pointer to our internal buffer(s).

    InternalBufferCount - The number of internal buffers.

    Errno - A pointer to the user errno parameter.

Return Value:

    NO_ERROR on success else a valid winsock2 error code.

--*/
{
    INT ReturnCode;

    PINTERNALOVERLAPPEDSTRUCT OverlappedStruct;

    ReturnCode = SOCKET_ERROR;
    *Errno = WSAENOBUFS;

    OverlappedStruct =
        gOverlappedManager->AllocateOverlappedStruct();
    if (OverlappedStruct){
OverlappedStruct->iolOperationType = WSP_RECV;
OverlappedStruct->iolSocket = Socket->GetSocketHandle ();
OverlappedStruct->iolProvider = Socket->GetDProvider();
OverlappedStruct->iolProviderSocket = Socket->GetProviderSocket ();
OverlappedStruct->iolUserOverlappedStruct = UserOverlappedStruct;
OverlappedStruct->iolUserCompletionRoutine = UserCompletionRoutine;
OverlappedStruct->iolUserThreadId = *UserThreadId;
if (UserBufferCount<=MAX_FAST_BUFS)
memcpy (OverlappedStruct->iolUserBuffers, UserBuffers,
sizeof (WSABUF)*UserBufferCount);
else {
OverlappedStruct->iolpUserBuffers = new WSABUF[UserBufferCount];
if (OverlappedStruct->iolpUserBuffers==NULL) {
gOverlappedManager->FreeOverlappedStruct (
&OverlappedStruct->iolInternalOverlappedStruct);
return ReturnCode;
}
memcpy (OverlappedStruct->iolpUserBuffers, UserBuffers,
sizeof (WSABUF)*UserBufferCount);
}
OverlappedStruct->iolUserBufferCount = UserBufferCount;
if (InternalBufferCount<=MAX_FAST_BUFS)
memcpy (OverlappedStruct->iolInternalBuffers, InternalBuffers,
sizeof (WSABUF)*InternalBufferCount);
else {
OverlappedStruct->iolpInternalBuffers = new WSABUF[InternalBufferCount];
if (OverlappedStruct->iolpInternalBuffers==NULL) {
if (UserBufferCount>MAX_FAST_BUFS)
delete OverlappedStruct->iolpUserBuffers;
gOverlappedManager->FreeOverlappedStruct (
&OverlappedStruct->iolInternalOverlappedStruct);
return ReturnCode;
}
memcpy (OverlappedStruct->iolpInternalBuffers, InternalBuffers,
sizeof (WSABUF)*InternalBufferCount);
}
OverlappedStruct->iolInternalBufferCount = InternalBufferCount;
OverlappedStruct->iolFlags = *UserFlags;
        AddOverlappedOperation(
            Socket,
            OverlappedStruct);
        *Errno = WSA_IO_PENDING;

    } //if
    return(ReturnCode);
}

INT
DWORKERTHREAD::QueueOverlappedRecvFrom(
    PDSOCKET                           Socket,
    LPWSABUF                           UserBuffers,
    DWORD                              UserBufferCount,
    LPDWORD                            UserBytesRecvd,
    LPDWORD                            UserFlags,
    struct sockaddr FAR *              UserFrom,
    LPINT                              UserFromLen,
    LPWSAOVERLAPPED                    UserOverlappedStruct,
    LPWSAOVERLAPPED_COMPLETION_ROUTINE UserCompletionRoutine,
    LPWSATHREADID                      UserThreadId,
    LPWSABUF                           InternalBuffers,
    DWORD                              InternalBufferCount,
    LPINT                              Errno
    )
/*++

Routine Description:

    this routine allocates an internal overlapped structure stores its
    arguments in the allocated structure and enqueues the structure for the
    worker thread to complet the I/O operation.

Arguments:

    Socket      - Socket object

    UserBuffers - The pointer to the user buffer(s).

    UserBufferCount - The number of user buffers.

    UserBytesRecvd - The pointer to the user BytesRecvd parameter.

    UserFlags - A pointer to the user flags argument.

    UserFrom  - A pinter to the user sockaddr structure,

    UserFromLen - A pointer to the length of UserFrom.

    UserOverlappedStruct - The user overlapped struct pointer.

    UserCompletionRoutine - The user overlapped completion routine.

    UserThreadId - The user thread ID.

    InternalBuffers - A pointer to our internal buffer(s).

    InternalBufferCount - The number of internal buffers.

    Errno - A pointer to the user errno parameter.

Return Value:

    NO_ERROR on success else a valid winsock2 error code.

--*/
{
    INT                       ReturnCode;
    PINTERNALOVERLAPPEDSTRUCT OverlappedStruct;

    ReturnCode = SOCKET_ERROR;
    *Errno = WSAENOBUFS;

    OverlappedStruct =
        gOverlappedManager->AllocateOverlappedStruct();
    if (OverlappedStruct){
OverlappedStruct->iolOperationType = WSP_RECVFROM;
OverlappedStruct->iolSocket = Socket->GetSocketHandle ();
OverlappedStruct->iolProvider = Socket->GetDProvider();
OverlappedStruct->iolProviderSocket = Socket->GetProviderSocket ();
OverlappedStruct->iolUserOverlappedStruct = UserOverlappedStruct;
OverlappedStruct->iolUserCompletionRoutine = UserCompletionRoutine;
OverlappedStruct->iolUserThreadId = *UserThreadId;
if (UserBufferCount<=MAX_FAST_BUFS)
memcpy (OverlappedStruct->iolUserBuffers, UserBuffers,
sizeof (WSABUF)*UserBufferCount);
else {
OverlappedStruct->iolpUserBuffers = new WSABUF[UserBufferCount];
if (OverlappedStruct->iolpUserBuffers==NULL) {
gOverlappedManager->FreeOverlappedStruct (
&OverlappedStruct->iolInternalOverlappedStruct);
return ReturnCode;
}
memcpy (OverlappedStruct->iolpUserBuffers, UserBuffers,
sizeof (WSABUF)*UserBufferCount);
}
OverlappedStruct->iolUserBufferCount = UserBufferCount;
if (InternalBufferCount<=MAX_FAST_BUFS)
memcpy (OverlappedStruct->iolInternalBuffers, InternalBuffers,
sizeof (WSABUF)*InternalBufferCount);
else {
OverlappedStruct->iolpInternalBuffers = new WSABUF[InternalBufferCount];
if (OverlappedStruct->iolpInternalBuffers==NULL) {
if (UserBufferCount>MAX_FAST_BUFS)
delete OverlappedStruct->iolpUserBuffers;
gOverlappedManager->FreeOverlappedStruct (
&OverlappedStruct->iolInternalOverlappedStruct);
return ReturnCode;
}
memcpy (OverlappedStruct->iolpInternalBuffers, InternalBuffers,
sizeof (WSABUF)*InternalBufferCount);
}
OverlappedStruct->iolInternalBufferCount = InternalBufferCount;
OverlappedStruct->iolFlags = *UserFlags;
        OverlappedStruct->iolSockAddr = UserFrom;
        OverlappedStruct->iolSockAddrLenPtr = UserFromLen;

        AddOverlappedOperation(
            Socket,
            OverlappedStruct);
        *Errno = WSA_IO_PENDING;

    } //if
    return(ReturnCode);
}

INT
DWORKERTHREAD::QueueOverlappedSend(
    PDSOCKET                           Socket,
    LPWSABUF                           UserBuffers,

DWORD                              UserBufferCount,
    LPDWORD                            UserBytesSent,
    DWORD                              UserFlags,
    LPWSAOVERLAPPED                    UserOverlappedStruct,
    LPWSAOVERLAPPED_COMPLETION_ROUTINE UserCompletionRoutine,
    LPWSATHREADID                      UserThreadId,
    LPINT           Errno
    )
/*++

Routine Description:

    this routine allocates an internal overlapped structure stores its
    arguments in the allocated structure and enqueues the structure for the
    worker thread to complet the I/O operation.

Arguments:

    Socket      - Socket object

    UserBuffers - The pointer to the user buffer(s).

    UserBufferCount - The number of user buffers.

    UserBytesSent - The pointer to the user BytesSent parameter.

    UserFlags - The user flags .

    UserOverlappedStruct - The user overlapped struct pointer.

    UserCompletionRoutine - The user overlapped completion routine.

    UserThreadId - The user thread ID.

    InternalBuffers - A pointer to our internal buffer(s).

    InternalBufferCount - The number of internal buffers.

    Errno - A pointer to the user errno parameter.

Return Value:

    NO_ERROR on success else a valid winsock2 error code.

--*/
{
    INT                       ReturnCode;
    PINTERNALOVERLAPPEDSTRUCT OverlappedStruct;

    ReturnCode = SOCKET_ERROR;
    *Errno = WSAENOBUFS;

    OverlappedStruct =
        gOverlappedManager->AllocateOverlappedStruct();
    if (OverlappedStruct){
OverlappedStruct->iolOperationType = WSP_SEND;
OverlappedStruct->iolSocket = Socket->GetSocketHandle ();
OverlappedStruct->iolProvider = Socket->GetDProvider();
OverlappedStruct->iolProviderSocket = Socket->GetProviderSocket ();
OverlappedStruct->iolUserOverlappedStruct = UserOverlappedStruct;
OverlappedStruct->iolUserCompletionRoutine = UserCompletionRoutine;
OverlappedStruct->iolUserThreadId = *UserThreadId;
if (UserBufferCount<=MAX_FAST_BUFS)
memcpy (OverlappedStruct->iolUserBuffers, UserBuffers,
sizeof (WSABUF)*UserBufferCount);
else {
OverlappedStruct->iolpUserBuffers = new WSABUF[UserBufferCount];
if (OverlappedStruct->iolpUserBuffers==NULL) {
gOverlappedManager->FreeOverlappedStruct (
&OverlappedStruct->iolInternalOverlappedStruct);
return ReturnCode;
}
memcpy (OverlappedStruct->iolpUserBuffers, UserBuffers,
sizeof (WSABUF)*UserBufferCount);
}
OverlappedStruct->iolUserBufferCount = UserBufferCount;
OverlappedStruct->iolFlags = UserFlags;

        AddOverlappedOperation(
            Socket,
            OverlappedStruct);

        *Errno = WSA_IO_PENDING;

    } //if
    return(ReturnCode);
}

INT
DWORKERTHREAD::QueueOverlappedSendTo(
    PDSOCKET                           Socket,
    LPWSABUF                           UserBuffers,
    DWORD                              UserBufferCount,
    LPDWORD                            UserBytesSent,
    DWORD                              UserFlags,
    const struct sockaddr FAR *        UserTo,
    INT                                UserToLen,
    LPWSAOVERLAPPED                    UserOverlappedStruct,
    LPWSAOVERLAPPED_COMPLETION_ROUTINE UserCompletionRoutine,
    LPWSATHREADID                      UserThreadId,
    LPINT                              Errno
    )
/*++

Routine Description:

    this routine allocates an internal overlapped structure stores its
    arguments in the allocated structure and enqueues the structure for the
    worker thread to complet the I/O operation.

Arguments:

    Socket      - Socket object

    UserBuffers - The pointer to the user buffer(s).

    UserBufferCount - The number of user buffers.

    UserBytesRecvd - The pointer to the user BytesRecvd parameter.

    UserFlags - A pointer to the user flags argument.

    UserTo - A pointer to the user sockaddr structure.

    UserToLen - The length of the user sockaddr structure.

    UserOverlappedStruct - The user overlapped struct pointer.

    UserCompletionRoutine - The user overlapped completion routine.

    UserThreadId - The user thread ID.

    InternalBuffers - A pointer to our internal buffer(s).

    InternalBufferCount - The number of internal buffers.

    Errno - A pointer to the user errno parameter.

Return Value:

    NO_ERROR on success else a valid winsock2 error code.

--*/
{
    INT                       ReturnCode;
    PINTERNALOVERLAPPEDSTRUCT OverlappedStruct;

    ReturnCode = SOCKET_ERROR;
    *Errno = WSAENOBUFS;

    OverlappedStruct =
        gOverlappedManager->AllocateOverlappedStruct();
    if (OverlappedStruct){
OverlappedStruct->iolOperationType = WSP_SENDTO;
OverlappedStruct->iolSocket = Socket->GetSocketHandle ();
OverlappedStruct->iolProvider = Socket->GetDProvider();
OverlappedStruct->iolProviderSocket = Socket->GetProviderSocket ();
OverlappedStruct->iolUserOverlappedStruct = UserOverlappedStruct;
OverlappedStruct->iolUserCompletionRoutine = UserCompletionRoutine;
OverlappedStruct->iolUserThreadId = *UserThreadId;
if (UserBufferCount<=MAX_FAST_BUFS)
memcpy (OverlappedStruct->iolUserBuffers, UserBuffers,
sizeof (WSABUF)*UserBufferCount);
else {
OverlappedStruct->iolpUserBuffers = new WSABUF[UserBufferCount];
if (OverlappedStruct->iolpUserBuffers==NULL) {
gOverlappedManager->FreeOverlappedStruct (
&OverlappedStruct->iolInternalOverlappedStruct);
return ReturnCode;
}
memcpy (OverlappedStruct->iolpUserBuffers, UserBuffers,
sizeof (WSABUF)*UserBufferCount);
}
OverlappedStruct->iolUserBufferCount = UserBufferCount;
OverlappedStruct->iolFlags = UserFlags;
        OverlappedStruct->iolSockAddr = (struct sockaddr FAR *)UserTo;
        OverlappedStruct->iolSockAddrLen = UserToLen;

        AddOverlappedOperation(
            Socket,
            OverlappedStruct);

        *Errno = WSA_IO_PENDING;
    } //if
    return(ReturnCode);
}

INT
DWORKERTHREAD::QueueOverlappedIoctl(
    PDSOCKET                           Socket,
    DWORD                              dwIoControlCode,
    LPVOID                             lpvInBuffer,
    DWORD                              cbInBuffer,
    LPVOID                             lpvOutBuffer,
    DWORD                              cbOutBuffer,
    LPDWORD                            lpcbBytesReturned,
    LPWSAOVERLAPPED                    UserOverlappedStruct,
    LPWSAOVERLAPPED_COMPLETION_ROUTINE UserCompletionRoutine,
    LPWSATHREADID                      UserThreadId,
    LPINT                              Errno
    )
/*++

Routine Description:

    this routine allocates an internal overlapped structure stores its
    arguments in the allocated structure and enqueues the structure for the
    worker thread to complet the I/O operation.

Arguments:

    Socket      - Socket object

    UserBuffers - The pointer to the user buffer(s).

    UserBufferCount - The number of user buffers.

    UserBytesRecvd - The pointer to the user BytesRecvd parameter.

    UserFlags - A pointer to the user flags argument.

    UserTo - A pointer to the user sockaddr structure.

    UserToLen - The length of the user sockaddr structure.

    UserOverlappedStruct - The user overlapped struct pointer.

    UserCompletionRoutine - The user overlapped completion routine.

    UserThreadId - The user thread ID.

    InternalBuffers - A pointer to our internal buffer(s).

    InternalBufferCount - The number of internal buffers.

    Errno - A pointer to the user errno parameter.

Return Value:

    NO_ERROR on success else a valid winsock2 error code.

--*/
{
    INT                       ReturnCode;
    PINTERNALOVERLAPPEDSTRUCT OverlappedStruct;

    ReturnCode = SOCKET_ERROR;
    *Errno = WSAENOBUFS;

    OverlappedStruct =
        gOverlappedManager->AllocateOverlappedStruct();
    if (OverlappedStruct){
OverlappedStruct->iolOperationType = WSP_IOCTL;
OverlappedStruct->iolSocket = Socket->GetSocketHandle ();
OverlappedStruct->iolProvider = Socket->GetDProvider();
OverlappedStruct->iolProviderSocket = Socket->GetProviderSocket ();
OverlappedStruct->iolUserOverlappedStruct = UserOverlappedStruct;
OverlappedStruct->iolUserCompletionRoutine = UserCompletionRoutine;
OverlappedStruct->iolUserThreadId = *UserThreadId;
OverlappedStruct->iolInputBuffer = lpvInBuffer;
OverlappedStruct->iolInputBufferLength = cbInBuffer;
OverlappedStruct->iolOutputBuffer = lpvOutBuffer;
OverlappedStruct->iolOutputBufferLength = cbOutBuffer;
OverlappedStruct->iolIoControlCode = dwIoControlCode;

        AddOverlappedOperation(
            Socket,
            OverlappedStruct);

        *Errno = WSA_IO_PENDING;
    } //if
    return(ReturnCode);
}

INT
DWORKERTHREAD::QueueOverlappedAcceptEx(
PDSOCKET   ListenSocket,
PDSOCKET   AcceptSocket,
LPVOID   lpOutputBuffer,
DWORD   dwReceiveDataLength,
DWORD   dwLocalAddressLength,
DWORD   dwRemoteAddressLength,
LPWSAOVERLAPPED   UserOverlappedStruct,
LPINT   Errno
) {
    INT                       ReturnCode;
    PINTERNALOVERLAPPEDSTRUCT OverlappedStruct;

    ReturnCode = SOCKET_ERROR;
    *Errno = WSAENOBUFS;

    OverlappedStruct =
        gOverlappedManager->AllocateOverlappedStruct();
    if (OverlappedStruct){
OverlappedStruct->iolOperationType = ACCEPT_EX;
OverlappedStruct->iolSocket = ListenSocket->GetSocketHandle ();
OverlappedStruct->iolProvider = ListenSocket->GetDProvider();
OverlappedStruct->iolListenSocket = ListenSocket->GetProviderSocket ();
OverlappedStruct->iolAcceptSocket = AcceptSocket->GetProviderSocket ();
OverlappedStruct->iolUserOverlappedStruct = UserOverlappedStruct;
OverlappedStruct->iolUserCompletionRoutine = NULL;
OverlappedStruct->iolOutputBuffer = lpOutputBuffer;
OverlappedStruct->iolOutputBufferLength = dwReceiveDataLength;
OverlappedStruct->iolLocalAddressLength = dwLocalAddressLength;
OverlappedStruct->iolRemoteAddressLength = dwRemoteAddressLength;

        AddOverlappedOperation(
            ListenSocket,
            OverlappedStruct);

        *Errno = WSA_IO_PENDING;
    } //if
    return(ReturnCode);
}

INT
DWORKERTHREAD::QueueOverlappedTransmitFile(
PDSOCKETSocket,
HANDLEhFile,
DWORDnNumberOfBytesToWrite,
DWORDnNumberOfBytesPerSend,
LPWSAOVERLAPPEDUserOverlappedStruct,
LPTRANSMIT_FILE_BUFFERSlpTransmitBuffers,
DWORDdwReserved,
    LPINT                               Errno
)
{
    INT                       ReturnCode;
    PINTERNALOVERLAPPEDSTRUCT OverlappedStruct;

    ReturnCode = SOCKET_ERROR;
    *Errno = WSAENOBUFS;

    OverlappedStruct =
        gOverlappedManager->AllocateOverlappedStruct();
    if (OverlappedStruct){
OverlappedStruct->iolOperationType = TRANSMIT_FILE;
OverlappedStruct->iolSocket = Socket->GetSocketHandle ();
OverlappedStruct->iolProvider = Socket->GetDProvider();
OverlappedStruct->iolProviderSocket = Socket->GetProviderSocket ();
OverlappedStruct->iolUserOverlappedStruct = UserOverlappedStruct;
OverlappedStruct->iolUserCompletionRoutine = NULL;
OverlappedStruct->iolFileHandle = hFile;
OverlappedStruct->iolBytesToWrite = nNumberOfBytesToWrite;
OverlappedStruct->iolBytesPerSend = nNumberOfBytesPerSend;
if (lpTransmitBuffers)
OverlappedStruct->iolTransmitBuffers = *lpTransmitBuffers;
else
OverlappedStruct->iolTransmitBuffers.HeadLength =
OverlappedStruct->iolTransmitBuffers.TailLength = 0;

OverlappedStruct->iolReserved = dwReserved;
        AddOverlappedOperation(
            Socket,
            OverlappedStruct);

        *Errno = WSA_IO_PENDING;
    } //if
    return(ReturnCode);
}

VOID
DWORKERTHREAD::AddOverlappedOperation(
    IN PDSOCKET                  Socket,
    IN PINTERNALOVERLAPPEDSTRUCT OverlappedOperation
    )
/*++
Routine Description:

    This routine adds an internal overlapped structure to the queue of requests
    to be completed by the worker thread.

Arguments:

    Socket              - A pointer to socket object
    OverlappedOperation - A pointer to an internal overlapped structure that
                          describes the operation to be performed by the worker
                          thread.

Return Value:

    NONE
--*/
{

    // If we are using completion ports or user requested
    // completion via APC, initiate operation in the
    // context of the current thread
    if (m_completion_port!=NULL) {
        if (Socket->GetCompletionContext ()==INVALID_SOCKET) {
            CreateIoCompletionPort (
                (HANDLE)OverlappedOperation->iolProviderSocket,
                m_completion_port,
                OverlappedOperation->iolSocket,
                0);
            Socket->SetCompletionContext (OverlappedOperation->iolSocket);
        }
        InitiateOverlappedOperation (
            OverlappedOperation,
            NULL,
            &OverlappedOperation->iolUserThreadId);
    }
    else {
        EnterCriticalSection(&m_overlapped_operation_queue_lock);
        InsertTailList(
            &m_overlapped_operation_queue,
            &OverlappedOperation->iolListLinkage);
        LeaveCriticalSection(&m_overlapped_operation_queue_lock);
        ReleaseSemaphore (m_wakeup_semaphore, 1, NULL);
    }
}

PINTERNALOVERLAPPEDSTRUCT
DWORKERTHREAD::NextOverlappedOperation()
/*++
Routine Description:

    This routine returns the first internal overlapped structure from the queue
    of requests to be completed by the worker thread.

Arguments:

    NONE

Return Value:

    A pointer to an internal overlapped structure or NULL

--*/
{
    PINTERNALOVERLAPPEDSTRUCT ReturnValue;
    PLIST_ENTRY               ListEntry;

    ReturnValue = NULL;

    EnterCriticalSection(&m_overlapped_operation_queue_lock);

    if (!IsListEmpty(&m_overlapped_operation_queue)){
        ListEntry = RemoveHeadList(
            &m_overlapped_operation_queue);
        ReturnValue = CONTAINING_RECORD(
            ListEntry,
            INTERNALOVERLAPPEDSTRUCT,
            iolListLinkage);
    } //if

    LeaveCriticalSection(&m_overlapped_operation_queue_lock);
    return(ReturnValue);
}


DWORKER.H

/*++

     Copyright c 1996 Intel Corporation
     All Rights Reserved

     Permission is granted to use, copy and distribute this software and
     its documentation for any purpose and without fee, provided, that
     the above copyright notice and this statement appear in all copies.
     Intel makes no representations about the suitability of this
     software for any purpose.  This software is provided "AS IS."

     Intel specifically disclaims all warranties, express or implied,
     and all liability, including consequential and other indirect
     damages, for the use of this software, including liability for
     infringement of any proprietary rights, and including the
     warranties of merchantability and fitness for a particular purpose.
     Intel does not assume any responsibility for any errors which may
     appear in this software nor any responsibility to update it.

Module Name:

dworker.h

Abstract:

   This module contins the interface to the DWORKERTHREAD class for
   LSP.DLL.

--*/

#ifndef _DWORKERTHREAD_
#define _DWORKERTHREAD_

#include <winsock2.h>
#include "llist.h"
#include "doverlap.h"
#include "classfwd.h"

#define WSP_RECV       0x00000001
#define WSP_RECVFROM   0x00000002
#define WSP_SEND       0x00000004
#define WSP_SENDTO     0x00000008
#define WSP_IOCTL      0x00000010
#define TRANSMIT_FILE  0x00000020
#define ACCEPT_EX   0x00000040

class DWORKERTHREAD
{
  public:

    DWORKERTHREAD();

    INT
    Initialize();

    VOID
    Destroy ();

    ~DWORKERTHREAD();

    BOOL
    RegisterSocketHandle (
        SOCKET  socket
        );

    DWORD
    WorkerThreadProc();

    LPWSATHREADID
    GetThreadId();

    INT
    QueueOverlappedRecv(
        PDSOCKET                           Socket,
        LPWSABUF                           UserBuffers,
        DWORD                              UserBufferCount,
        LPDWORD                            UserBytesRecvd,
        LPDWORD                            UserFlags,
        LPWSAOVERLAPPED                    UserOverlappedStruct,
        LPWSAOVERLAPPED_COMPLETION_ROUTINE UserlpCompletionRoutine,
        LPWSATHREADID                      UserThreadId,
        LPWSABUF                           InternalBuffers,
        DWORD                              InternalBufferCount,
        LPINT                              Errno
        );

    INT
    QueueOverlappedRecvFrom(
        PDSOCKET                           Socket,
        LPWSABUF                           UserBuffers,
        DWORD                              UserBufferCount,
        LPDWORD                            UserBytesRecvd,
        LPDWORD                            UserFlags,
        struct sockaddr FAR *              UserFrom,
        LPINT                              UserFromlen,
        LPWSAOVERLAPPED                    UserOverlappedStruct,
        LPWSAOVERLAPPED_COMPLETION_ROUTINE UserlpCompletionRoutine,
        LPWSATHREADID                      UserThreadId,
        LPWSABUF                           InternalBuffers,
        DWORD                              InternalBufferCount,
        LPINT                              Errno
        );
    INT
    QueueOverlappedSend(
        PDSOCKET                           Socket,
        LPWSABUF                           UserBuffers,
        DWORD                              UserBufferCount,
        LPDWORD                            UserBytesSent,
        DWORD                            UserFlags,
        LPWSAOVERLAPPED                    UserOverlappedStruct,
        LPWSAOVERLAPPED_COMPLETION_ROUTINE UserlpCompletionRoutine,
        LPWSATHREADID                      UserThreadId,
        LPINT           Errno
        );

    INT
    QueueOverlappedSendTo(
        PDSOCKET                           Socket,
        LPWSABUF                           UserBuffers,
        DWORD                              UserBufferCount,
        LPDWORD                            UserBytesSent,
        DWORD                              UserFlags,
        const struct sockaddr FAR *        UserTo,
        INT                                UserTolen,
        LPWSAOVERLAPPED                    UserOverlappedStruct,
        LPWSAOVERLAPPED_COMPLETION_ROUTINE UserlpCompletionRoutine,
        LPWSATHREADID                      UserThreadId,
        LPINT                              Errno
        );

    INT
    QueueOverlappedIoctl(
        PDSOCKET                           Socket,
        DWORD                              dwIoControlCode,
        LPVOID                             lpvInBuffer,
        DWORD                              cbInBuffer,
        LPVOID                             lpvOutBuffer,
        DWORD                              cbOutBuffer,
        LPDWORD                            lpcbBytesReturned,
        LPWSAOVERLAPPED                    UserOverlappedStruct,
        LPWSAOVERLAPPED_COMPLETION_ROUTINE UserlpCompletionRoutine,
        LPWSATHREADID                      UserThreadId,
        LPINT                              Errno
        );

INT
QueueOverlappedAcceptEx(
PDSOCKET   ListenSocket,
PDSOCKET   AcceptSocket,
LPVOID   lpOutputBuffer,
DWORD   dwReceiveDataLength,
DWORD   dwLocalAddressLength,
DWORD   dwRemoteAddressLength,
LPWSAOVERLAPPED   UserOverlappedStruct,
        LPINT                              Errno
);

INT
QueueOverlappedTransmitFile(
PDSOCKET   Socket,
HANDLE   hFile,
DWORD   nNumberOfBytesToWrite,
DWORD   nNumberOfBytesPerSend,
LPWSAOVERLAPPED   UserOverlappedStruct,
LPTRANSMIT_FILE_BUFFERS   lpTransmitBuffers,
DWORD   dwReserved,
        LPINT                              Errno
);
  private:
    VOID
    AddOverlappedOperation(
        IN PDSOCKET                  Socket,
        IN PINTERNALOVERLAPPEDSTRUCT OverlappedOperation
        );

    PINTERNALOVERLAPPEDSTRUCT
    NextOverlappedOperation();

    DWORD m_thread_count;
    // Number of worker threads.

    HANDLE m_completion_port;
    // The handle to NT completion port used for accepting of completion
    // indication from providers in worker thread(s)

    HANDLE m_wakeup_semaphore;
    // The handle to the WIN32 semaphore used to communicate with our worker
    // thread(s) if completion port is not available (Win9x).

    BOOL   m_exit_thread;
    // A boolean to tell our threads to exit at object destruction time.

    LIST_ENTRY       m_overlapped_operation_queue;

    CRITICAL_SECTION m_overlapped_operation_queue_lock;

    WSATHREADID  m_thread_id;
    // The thread ID for the worker thread.

};   // class DWORKERTHREAD

inline
LPWSATHREADID
DWORKERTHREAD::GetThreadId()
{
    return(&m_thread_id);
}

#endif // _DWORKERTHREAD_


GLOBALS.H

/*++

     Copyright c 1996 Intel Corporation
     All Rights Reserved

     Permission is granted to use, copy and distribute this software and
     its documentation for any purpose and without fee, provided, that
     the above copyright notice and this statement appear in all copies.
     Intel makes no representations about the suitability of this
     software for any purpose.  This software is provided "AS IS."

     Intel specifically disclaims all warranties, express or implied,
     and all liability, including consequential and other indirect
     damages, for the use of this software, including liability for
     infringement of any proprietary rights, and including the
     warranties of merchantability and fitness for a particular purpose.
     Intel does not assume any responsibility for any errors which may
     appear in this software nor any responsibility to update it.

Module Name:

    globals.h

Abstract:

    This module contains the external definitions of the global objects
    contained in the layered service provider sample

--*/
#ifndef _GLOBALS_H_
#define _GLOBALS_H_

//
// Global variables from SPI.CPP
//

// The WinSock2 UpCallTable.
extern WSPUPCALLTABLEgUpCallTable;
extern LPWPUCOMPLETEOVERLAPPEDREQUESTlpWPUCompleteOverlappedRequest;

// Variables to track Startup/Cleanup Pairing.
extern CRITICAL_SECTION  gInitCriticalSection;
extern DWORD             gStartupCount;

// The catalog of providers
extern PDCATALOG gProviderCatalog;

// The buffer manager for providers that modify the data stream
extern PDBUFFERMANAGER gBufferManager;

extern char gLibraryName[MAX_PATH];

extern HINSTANCE  HDllInstance;

extern DWORD  TlsIndex;

#endif // _GLOBALS_H_


INST_LSP.CPP

/*++

     Copyright (c) 1996 Intel Corporation
     Copyright 1996 - 1998 Microsoft Corporation
     All Rights Reserved

     Permission is granted to use, copy and distribute this software and
     its documentation for any purpose and without fee, provided, that
     the above copyright notice and this statement appear in all copies.
     Intel makes no representations about the suitability of this
     software for any purpose.  This software is provided "AS IS."

     Intel specifically disclaims all warranties, express or implied,
     and all liability, including consequential and other indirect
     damages, for the use of this software, including liability for
     infringement of any proprietary rights, and including the
     warranties of merchantability and fitness for a particular purpose.
     Intel does not assume any responsibility for any errors which may
     appear in this software nor any responsibility to update it.

Module Name:

    inst_lsp.cpp

Abstract:

    This module contains the installation routines for the winsock2 layered
    service provider sample.

--*/

#define WIN32_LEAN_AND_MEAN

#include "warnoff.h"
#include <windows.h>

#include "ws2spi.h"
#include <rpc.h>
#include <stdio.h>
#include <stdlib.h>
#include "sporder.h"
#include "install.h"
#define LAYERED_PROVIDER_NAME L"LAYERED_PROVIDER"

VOID
UninstallMyProvider()
{
    INT Errno;

    WSCDeinstallProvider(
        &LayeredProviderGuid,
        &Errno);
}

INT
InstallMyProvider(
    PDWORD CatalogId
    )
/*++
--*/
{
    WSAPROTOCOL_INFOW  proto_info;
    int               install_result;
    int               install_error;

    // Create a PROTOCOL_INFO to install for our provider DLL.
    proto_info.dwServiceFlags1 = 0;
    proto_info.dwServiceFlags2 = 0;
    proto_info.dwServiceFlags3 = 0;
    proto_info.dwServiceFlags4 = 0;
    proto_info.dwProviderFlags = PFL_HIDDEN;
    proto_info.ProviderId      = LayeredProviderGuid;
    proto_info.dwCatalogEntryId = 0;   // filled in by system
    proto_info.ProtocolChain.ChainLen = LAYERED_PROTOCOL;
        // Do  not  need  to  fill  in  chain  for LAYERED_PROTOCOL or
        // BASE_PROTOCOL
    proto_info.iVersion = 0;
    proto_info.iAddressFamily = AF_INET;
    proto_info.iMaxSockAddr = 16;
    proto_info.iMinSockAddr = 16;
    proto_info.iSocketType = SOCK_STREAM;
    proto_info.iProtocol = IPPROTO_TCP;   // mimic TCP/IP
    proto_info.iProtocolMaxOffset = 0;
    proto_info.iNetworkByteOrder = BIGENDIAN;
    proto_info.iSecurityScheme = SECURITY_PROTOCOL_NONE;
    proto_info.dwMessageSize = 0;  // stream-oriented
    proto_info.dwProviderReserved = 0;
    wcscpy(
        proto_info.szProtocol,
        LAYERED_PROVIDER_NAME);

    install_result = WSCInstallProvider(
        &LayeredProviderGuid,
        L"lsp.dll",                   // lpszProviderDllPath
        & proto_info,                 // lpProtocolInfoList
        1,                            // dwNumberOfEntries
        & install_error);             // lpErrno
    *CatalogId = proto_info.dwCatalogEntryId;

    return(install_result);

} // Install_My_Layer

INT
InstallNewChain(
    LPWSAPROTOCOL_INFOW BaseProtocolInfoBuff,
    DWORD               LayeredProviderCatalogId,
    HKEY                ConfigRegisteryKey
    )
{
    WSAPROTOCOL_INFOW ProtocolChainProtoInfo;
    WCHAR             DebugPrefix[] = L"LAYERED ";
    INT               ReturnCode;
    INT               Errno;
    UUID              NewChainId;
    RPC_STATUS        Status;
    PUCHAR            GuidString;
    HKEY              NewKey;
    DWORD             KeyDisposition;
    BOOL              Continue;

    ReturnCode = NO_ERROR;

    // We are only layering on top of base providers

    if (BaseProtocolInfoBuff->ProtocolChain.ChainLen == BASE_PROTOCOL){
        Continue = FALSE;

        // Get a new GUID for the protocol chain we are about to install
        Status = UuidCreate(
            &NewChainId);
        if (RPC_S_OK == Status){

            //Get the string representaion of the GUID
            Status = UuidToString(
                &NewChainId,
                &GuidString);
            if (RPC_S_OK == Status){
                // Write down the GUID  in the registry so we know who to
                // uninstall
                RegCreateKeyEx(
                    ConfigRegisteryKey,                 // hkey
                    (LPCSTR)GuidString,                 // lpszSubKey
                    0,                                  // dwReserved
                    NULL,                               // lpszClass
                    REG_OPTION_NON_VOLATILE,            // fdwOptions
                    KEY_ALL_ACCESS,                     // samDesired
                    NULL,                               // lpSecurityAttributes
                    & NewKey,                           // phkResult
                    & KeyDisposition                    // lpdwDisposition
                    );
                RpcStringFree(&GuidString);

                Continue =TRUE;
            } //if
            else{
                printf("UuidToString() Failed\n");
            } //else
        } //if
        else{
            printf("UuidCreate Failed\n");
        } //else

        if (Continue){

            ProtocolChainProtoInfo = *BaseProtocolInfoBuff;

            ProtocolChainProtoInfo.ProviderId = NewChainId;

            wcscpy(
                ProtocolChainProtoInfo.szProtocol,
                DebugPrefix);
            wcscat(
                ProtocolChainProtoInfo.szProtocol,
                BaseProtocolInfoBuff->szProtocol);

            ProtocolChainProtoInfo.ProtocolChain.ChainLen = 2;
            ProtocolChainProtoInfo.ProtocolChain.ChainEntries[0] =
                LayeredProviderCatalogId;
            ProtocolChainProtoInfo.ProtocolChain.ChainEntries[1] =
                BaseProtocolInfoBuff->dwCatalogEntryId;

            ReturnCode = WSCInstallProvider(
                &NewChainId,
                L"lsp.dll",
                &ProtocolChainProtoInfo,
                1,
                &Errno);
            if (ReturnCode==0)
                printf ("Installed over %ls.\n",
                     BaseProtocolInfoBuff->szProtocol);
            else
                printf ("Installation over %ls failed with error %ld.\n",
                     BaseProtocolInfoBuff->szProtocol, Errno);
        } //if
    } //if
    return(ReturnCode);
}

int
main( int argc, char** argv)
{
    LPWSAPROTOCOL_INFOW   ProtocolInfoBuff = NULL;
    DWORD                ProtocolInfoBuffSize = 0;
    INT                  ErrorCode;
    INT                  EnumResult;
    LONG                 lresult;
    HKEY                 NewKey;
    DWORD                KeyDisposition;
    GUID                 ProviderID;
    INT                  Index;
    DWORD                CatalogEntryId;
    CHAR                 GuidStringBuffer[40];
    DWORD                GuidStringBufferLen;
    FILETIME             FileTime;
    BOOL                 EntryIdFound;
DWORD *CatIdBuff;
DWORD nCatIds;

    // See if we are installing or deinstalling
    lresult = RegOpenKeyEx(
        HKEY_LOCAL_MACHINE,                     // hkey
        CONFIGURATION_KEY,                      // lpszSubKey
        0,                                      // dwReserved
        KEY_ALL_ACCESS,                        // samDesired
        & NewKey                               // phkResult
        );

    if (ERROR_SUCCESS == lresult){
        // The layered provider is installed so we are going uninstall.

        //
        // Enumerate all the provider IDs we stored on install and deinstall
        // the providers
        //
        printf("Removing Installed Layered Providers\n");

        Index = 0;
        GuidStringBufferLen = sizeof(GuidStringBuffer);
        lresult = RegEnumKeyEx(
            NewKey,               //hKey
            Index,                // Index of subkey
            &GuidStringBuffer[0],    // Buffer to hold key name
            &GuidStringBufferLen,  // Length of buffer
            NULL,                 // Reserved
            NULL,                 // Class buffer
            NULL,                 // Class buffer length
            &FileTime              // Last write time
            );

        printf("Removing layered provider protocol chains\n");
        while (lresult != ERROR_NO_MORE_ITEMS){
            UuidFromString(
                (PUCHAR) GuidStringBuffer,
                &ProviderID);
            // Deinstall the provider chain we installed
            WSCDeinstallProvider(
                &ProviderID,
                &ErrorCode);
            // Delete our registry key
            RegDeleteKey(
                NewKey,
                &GuidStringBuffer[0]);

            GuidStringBufferLen = sizeof(GuidStringBuffer);
        lresult = RegEnumKeyEx(
            NewKey,               //hKey
            Index,                // Index of subkey
            &GuidStringBuffer[0],    // Buffer to hold key name
            &GuidStringBufferLen,  // Length of buffer
            NULL,                 // Reserved
            NULL,                 // Class buffer
            NULL,                 // Class buffer length
            &FileTime              // Last write time
            );

        } //while

        // Clen up the registry
        RegCloseKey(
            NewKey);
        RegDeleteKey(
            HKEY_LOCAL_MACHINE,
            CONFIGURATION_KEY);

        // Uninstall the real provider
        UninstallMyProvider();
    } //if
    else{

        RegCreateKeyEx(
            HKEY_LOCAL_MACHINE,                 // hkey
            CONFIGURATION_KEY,                  // lpszSubKey
            0,                                  // dwReserved
            NULL,                               // lpszClass
            REG_OPTION_NON_VOLATILE,            // fdwOptions
            KEY_ALL_ACCESS,                     // samDesired
            NULL,                               // lpSecurityAttributes
            & NewKey,                           // phkResult
            & KeyDisposition                    // lpdwDisposition
            );
        // Install a dummy PROTOCOL_INFO for the layered provider.
        lresult = InstallMyProvider(
            &CatalogEntryId);
        if (NO_ERROR == lresult){
            //
            // Enumerate the installed providers and chains
            //
            printf("Scanning Installed Providers\n");
            // Call WSCEnumProtocols with a zero length buffer so we know what
            // size to  send in to get all the installed PROTOCOL_INFO
            // structs.
            WSCEnumProtocols(
                NULL,                     // lpiProtocols
                ProtocolInfoBuff,         // lpProtocolBuffer
                & ProtocolInfoBuffSize,   // lpdwBufferLength
                & ErrorCode);             // lpErrno

            ProtocolInfoBuff = (LPWSAPROTOCOL_INFOW)
                malloc(ProtocolInfoBuffSize);
            if (ProtocolInfoBuff){
                printf("Installing Layered Providers\n");

                EnumResult = WSCEnumProtocols(
                    NULL,                     // lpiProtocols
                    ProtocolInfoBuff,         // lpProtocolBuffer
                    & ProtocolInfoBuffSize,   // lpdwBufferLength
                    & ErrorCode);

                if (SOCKET_ERROR != EnumResult){

                    // Find our provider entry to get our catalog entry ID
                    EntryIdFound = FALSE;
                    for (Index =0; Index < EnumResult; Index++){
                        if (memcmp (&ProtocolInfoBuff[Index].ProviderId,
                                &LayeredProviderGuid,
                                sizeof (LayeredProviderGuid))==0){

                            CatalogEntryId =
                                ProtocolInfoBuff[Index].dwCatalogEntryId;
                            EntryIdFound = TRUE;
                        } //if
                    } //for
                    if (EntryIdFound){
                        for (Index =0; Index < EnumResult; Index++){
                            InstallNewChain(
                                &ProtocolInfoBuff[Index],
                                CatalogEntryId,
                                NewKey);
                        } //for
free (ProtocolInfoBuff);

//
// Enumerate the installed providers and chains
//
printf("Preparing To Reoder Installed Chains\n");
// Call WSCEnumProtocols with a zero length buffer so we know what
// size to  send in to get all the installed PROTOCOL_INFO
// structs.
ProtocolInfoBuffSize = 0;
WSCEnumProtocols(
NULL,                     // lpiProtocols
NULL,  // lpProtocolBuffer
& ProtocolInfoBuffSize,   // lpdwBufferLength
& ErrorCode);             // lpErrno

ProtocolInfoBuff = (LPWSAPROTOCOL_INFOW)
malloc(ProtocolInfoBuffSize);
if (ProtocolInfoBuff){
printf("Reodering Installed Chains\n");

EnumResult = WSCEnumProtocols(
NULL,                     // lpiProtocols
ProtocolInfoBuff,         // lpProtocolBuffer
& ProtocolInfoBuffSize,   // lpdwBufferLength
& ErrorCode);

if (SOCKET_ERROR != EnumResult){
// Allocate buffer to hold catalog ID array
CatIdBuff = (DWORD *)
malloc (sizeof (DWORD)*EnumResult);
if (CatIdBuff!=NULL) {
// Put our catalog chains first
nCatIds = 0;
for (Index =0; Index < EnumResult; Index++){
if ((ProtocolInfoBuff[Index].ProtocolChain.ChainLen>1)
&& (ProtocolInfoBuff[Index].ProtocolChain.ChainEntries[0]==CatalogEntryId))
CatIdBuff[nCatIds++] = ProtocolInfoBuff[Index].dwCatalogEntryId;
}

// Put the rest next
for (Index =0; Index < EnumResult; Index++){
if ((ProtocolInfoBuff[Index].ProtocolChain.ChainLen<=1)
|| (ProtocolInfoBuff[Index].ProtocolChain.ChainEntries[0]!=CatalogEntryId))
CatIdBuff[nCatIds++] = ProtocolInfoBuff[Index].dwCatalogEntryId;
}
// Save new protocol order
printf ("Saving New Protocol Order\n");
ErrorCode = WSCWriteProviderOrder (CatIdBuff, nCatIds);
if (ErrorCode!=NO_ERROR)
printf ("Reodering failed with error %ld", ErrorCode);
}
}
}
                    } //if
                } //if
            } //if
        } //if
    } //else
    return(0);
}


INSTALL.H

/*++

     Copyright c 1996 Intel Corporation
     All Rights Reserved

     Permission is granted to use, copy and distribute this software and
     its documentation for any purpose and without fee, provided, that
     the above copyright notice and this statement appear in all copies.
     Intel makes no representations about the suitability of this
     software for any purpose.  This software is provided "AS IS."

     Intel specifically disclaims all warranties, express or implied,
     and all liability, including consequential and other indirect
     damages, for the use of this software, including liability for
     infringement of any proprietary rights, and including the
     warranties of merchantability and fitness for a particular purpose.
     Intel does not assume any responsibility for any errors which may
     appear in this software nor any responsibility to update it.

Module Name:

    install.h

Abstract:

    This module contains the defininitions common the the winsock2 layered
    service provider sample and its installation application

--*/

#ifndef _INSTALL_H_
#define _INSTALL_H_

#include <windows.h>

#define CONFIGURATION_KEY TEXT("SOFTWARE\\WinSock2\\Layered Provider Sample")

GUID LayeredProviderGuid = { /* 5a21f160-df30-11cf-8927-00aa00539f1c */
    0x5a21f160,
    0xdf30,
    0x11cf,
    {0x89, 0x27, 0x00, 0xaa, 0x00, 0x53, 0x9f, 0x1c}
};

#endif // _INSTALL_H_


LLIST.H

/*++

Module Name:

    llist.h

Abstract:

    This  module  is  a  standalone  collection  of  linked-list definition and
    manipulation  macros  originally  defined  within  the  Windows NT
    development.

--*/

#ifndef _LLIST_
#define _LLIST_
#include <winsock2.h>

#if !defined( _WINNT_ )
//
//  From NTDEF.H.
//

//  Doubly  linked  list  structure.   Can be used as either a list head, or as
//  link storage within a linked-list item.

typedef struct _LIST_ENTRY {
   struct _LIST_ENTRY *Flink;
   struct _LIST_ENTRY *Blink;
} LIST_ENTRY, *PLIST_ENTRY, *RESTRICTED_POINTER PRLIST_ENTRY;

// LONG
// FIELD_OFFSET(
//     IN <typename>   type,
//     IN <fieldname>  field
//     );
#define FIELD_OFFSET(type, field)    ((LONG)&(((type *)0)->field))
/*++

Routine Description:

    Calculates  the  byte offset of a field in a structure of type "type".  The
    offset is from the beginning of the containing structure.

    Note  that  since  this macro uses compile-time type knowledge, there is no
    equivalent C procedure for this macro.

Arguments:

    type  - Supplies the type name of the containing structure.

    field - Supplies  the  field  name  of  the  field  whose  offset  is to be
            computed.

Return Value:

    Returns  the byte offset of the named field within a structure of the named
    type.
--*/

// <typename> FAR *
// CONTAINING_RECORD(
//     IN PVOID       address,
//     IN <typename>  type,
//     IN <fieldname> field
//     );
#define CONTAINING_RECORD(address, type, field) ((type FAR *)( \
                                          (PCHAR)(address) - \
                                          (PCHAR)(&((type *)0)->field)))
/*++

Routine Description:

    Retrieves  a  typed  pointer to a linked list item given the address of the
    link  storage  structure  embedded in the linked list item, the type of the
    linked  list  item,  and  the  field  name  of  the  embedded  link storage
    structure.

    Note  that  since  this macro uses compile-time type knowledge, there is no
    equivalent C procedure for this macro.

Arguments:

    address - Supplies  the  address of a LIST_ENTRY structure embedded in an a
              linked list item.

    type    - Supplies  the  type  name  of  the  containing  linked  list item
              structure.

    field   - Supplies  the  field  name  if  the LIST_ENTRY structure embedded
              within the linked list item structure.

Return Value:

    Returns a pointer to the linked list item.
--*/

#endif  // !defined( _WINNT_ )

//
//  From NTRTL.H.
//

//  Doubly-linked list manipulation routines.  Implemented as macros
//  but logically these are procedures.

// VOID
// InitializeListHead(
//     IN PLIST_ENTRY ListHead
//     );
#define InitializeListHead(ListHead) (\
    (ListHead)->Flink = (ListHead)->Blink = (ListHead))
/*++

Routine Description:

    Initializes  a  PLIST_ENTRY  structure to be the head of an initially empty
    linked list.

Arguments:

    ListHead - Supplies a reference to the structure to be initialized.

Return Value:

    None
--*/

// BOOLEAN
// IsListEmpty(
//     IN PLIST_ENTRY ListHead
//     );
#define IsListEmpty(ListHead) \
    ((ListHead)->Flink == (ListHead))
/*++

Routine Description:

    Determines whether or not a list is empty.

Arguments:

    ListHead - Supplies  a  reference  to  the  head  of  the linked list to be
               examined.

Return Value:

    TRUE  - The linked list is empty.

    FALSE - The linked list contains at least one item.
--*/

// PLIST_ENTRY
// RemoveHeadList(
//     IN PLIST_ENTRY ListHead
//     );
#define RemoveHeadList(ListHead) \
    (ListHead)->Flink;\
    {RemoveEntryList((ListHead)->Flink)}
/*++

Routine Description:

    Removes  the  "head" (first) item from a linked list, returning the pointer
    to  the  removed  entry's embedded linkage structure.  Attempting to remove
    the  head  item  from  a  (properly  initialized)  linked  list is a no-op,
    returning the pointer to the head of the linked list.

    The  caller  may  use  the  CONTAINING_RECORD macro to amplify the returned
    linkage structure pointer to the containing linked list item structure.

Arguments:

    ListHead - Supplies  a  reference  to  the  head  of  the linked list to be
               operated upon.

Return Value:

    Returns  a pointer to the newly removed linked list item's embedded linkage
    structure, or the linked list head in the case of an empty list.
--*/

// PLIST_ENTRY
// RemoveTailList(
//     IN PLIST_ENTRY ListHead
//     );
#define RemoveTailList(ListHead) \
    (ListHead)->Blink;\
    {RemoveEntryList((ListHead)->Blink)}
/*++

Routine Description:

    Removes the "tail" (last) item from a linked list, returning the pointer to
    the  removed  entry's embedded linkage structure.  Attempting to remove the
    tail  item  from a (properly initialized) linked list is a no-op, returning
    the pointer to the head of the linked list.

    The  caller  may  use  the  CONTAINING_RECORD macro to amplify the returned
    linkage structure pointer to the containing linked list item structure.

Arguments:

    ListHead - Supplies  a  reference  to  the  head  of  the linked list to be
               operated upon.

Return Value:

    Returns  a pointer to the newly removed linked list item's embedded linkage
    structure, or the linked list head in the case of an empty list.
--*/

// VOID
// RemoveEntryList(
//     IN PLIST_ENTRY Entry
//     );
#define RemoveEntryList(Entry) {\
    PLIST_ENTRY _EX_Blink;\
    PLIST_ENTRY _EX_Flink;\
    _EX_Flink = (Entry)->Flink;\
    _EX_Blink = (Entry)->Blink;\
    _EX_Blink->Flink = _EX_Flink;\
    _EX_Flink->Blink = _EX_Blink;\
    }
/*++

Routine Description:

    Removes  an  item  from a linked list.  Attempting to remove the head of an
    empty list is a no-op.

Arguments:

    Entry - Supplies  a reference to the linkage structure embedded in a linked
            list item structure.

Return Value:

    None
--*/

// VOID
// InsertTailList(
//     IN PLIST_ENTRY ListHead,
//     IN PLIST_ENTRY Entry
//     );
#define InsertTailList(ListHead,Entry) {\
    PLIST_ENTRY _EX_Blink;\
    PLIST_ENTRY _EX_ListHead;\
    _EX_ListHead = (ListHead);\
    _EX_Blink = _EX_ListHead->Blink;\
    (Entry)->Flink = _EX_ListHead;\
    (Entry)->Blink = _EX_Blink;\
    _EX_Blink->Flink = (Entry);\
    _EX_ListHead->Blink = (Entry);\
    }
/*++

Routine Description:

    Inserts a new item as the "tail" (last) item of a linked list.

Arguments:

    ListHead - Supplies  a  reference  to  the  head  of  the linked list to be
               operated upon.

    Entry    - Supplies  a  reference  to the linkage structure embedded in the
               linked list item to be added to the linked list.

Return Value:

    None
--*/

// VOID
// InsertHeadList(
//     IN PLIST_ENTRY ListHead,
//     IN PLIST_ENTRY Entry
//     );
#define InsertHeadList(ListHead,Entry) {\
    PLIST_ENTRY _EX_Flink;\
    PLIST_ENTRY _EX_ListHead;\
    _EX_ListHead = (ListHead);\
    _EX_Flink = _EX_ListHead->Flink;\
    (Entry)->Flink = _EX_Flink;\
    (Entry)->Blink = _EX_ListHead;\
    _EX_Flink->Blink = (Entry);\
    _EX_ListHead->Flink = (Entry);\
    }
/*++

Routine Description:

    Inserts a new item as the "head" (first) item of a linked list.

Arguments:

    ListHead - Supplies  a  reference  to  the  head  of  the linked list to be
               operated upon.

    Entry    - Supplies  a  reference  to the linkage structure embedded in the
               linked list item to be added to the linked list.

Return Value:

    None
--*/

//
//
//  PSINGLE_LIST_ENTRY
//  PopEntryList(
//      PSINGLE_LIST_ENTRY ListHead
//      );
//

#define PopEntryList(ListHead) \
    (ListHead)->Next;\
    {\
        PSINGLE_LIST_ENTRY FirstEntry;\
        FirstEntry = (ListHead)->Next;\
        if (FirstEntry != NULL) {     \
            (ListHead)->Next = FirstEntry->Next;\
        }                             \
    }

//
//  VOID
//  PushEntryList(
//      PSINGLE_LIST_ENTRY ListHead,
//      PSINGLE_LIST_ENTRY Entry
//      );
//

#define PushEntryList(ListHead,Entry) \
    (Entry)->Next = (ListHead)->Next; \
    (ListHead)->Next = (Entry)

// Samples:
//
// //
// //  Define a list head.
// //
//
// LIST_ENTRY FooList;
//
// //
// //  Define a structure that will be on the list.
// //
// //  NOTE:  For debugging purposes, it usually makes life simpler to make the
// //  LIST_ENTRY  the  first  field  of  the  structure,  but  this  is  not a
// //  requirement.
// //
//
// typedef struct _FOO
// {
//     LIST_ENTRY FooListEntry;
//     .
//     .
//     .
//
// } FOO, * PFOO;
//
// //
// //  Initialize an empty list.
// //
//
// InitializeListHead( &FooList );
//
// //
// //  Create an object, append it to the end of the list.
// //
//
// PFOO foo;
//
// foo = ALLOC( sizeof(FOO) );
// {check for errors, initialize FOO structure}
//
// InsertTailList( &FooList, &foo->FooListEntry );
//
// //
// //  Scan list and delete selected items.
// //
//
// PFOO foo;
// PLIST_ENTRY listEntry;
//
// listEntry = FooList.Flink;
//
// while( listEntry != &FooList )
// {
//     foo = CONTAINING_RECORD( listEntry,
//                              FOO,
//                              FooListEntry );
//     listEntry = listEntry->Flink;
//
//     if( SomeFunction( foo ) )
//     {
//         RemoveEntryList( &foo->FooListEntry );
//      FREE( foo );
//     }
// }
//
// //
// //  Purge all items from a list.
// //
//
// PFOO foo;
// PLIST_ENTRY listEntry;
//
// while( !IsListEmpty( &FooList ) )
// {
//     listEntry = RemoveHeadList( &FooList );
//     foo = CONTAINING_RECORD( listEntry,
//                              FOO,
//                              FooListEntry );
//
//     FREE( foo );
// }

#endif  // _LLIST_


NOWARN.H

/*++

  Copyright 1996 - 1998 Microsoft Corporation.

    Copyright (c) 1995 Intel Corporation

Module Name:

    nowarn.h

Abstract:

    This header contains pragmas to turn off compiler warnings that
    may be safely ignored.

--*/

/* nonstandard extension 'single line comment' was used */
#pragma warning(disable: 4001)

// argument differs in indirection to slightly different base types
#pragma warning(disable: 4057)

// unreferenced formal parameter
#pragma warning(disable: 4100)

// named type definition in parentheses
#pragma warning(disable: 4115)

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

// nonstandard extension used : benign typedef redefinition
#pragma warning(disable: 4209)

// nonstandard extension used : bit field types other than int
#pragma warning(disable: 4214)

// unreferenced inline function has been removed
#pragma warning(disable: 4514)

// Note: Creating precompiled header
#pragma warning(disable: 4699)


PRECOMP.H

/*++

     Copyright c 1996 Intel Corporation
     All Rights Reserved

     Permission is granted to use, copy and distribute this software and
     its documentation for any purpose and without fee, provided, that
     the above copyright notice and this statement appear in all copies.
     Intel makes no representations about the suitability of this
     software for any purpose.  This software is provided "AS IS."

     Intel specifically disclaims all warranties, express or implied,
     and all liability, including consequential and other indirect
     damages, for the use of this software, including liability for
     infringement of any proprietary rights, and including the
     warranties of merchantability and fitness for a particular purpose.
     Intel does not assume any responsibility for any errors which may
     appear in this software nor any responsibility to update it.

Module Name:

    precomp.h

Abstract:

    This file includes all the headers required to build lsp.dll
    to ease the process of building a precompiled header.

--*/

//#pragma message("")
//#pragma message("******** PLEASE READ THIS MESSAGE **********")
//#pragma message("Caution, this LSP code is provided 'as is,' an example ")
//#pragma message("that illustrates the process of developing a Layered Service Provider.")
//#pragma message("It does not take into account certain design considerations")
//#pragma message("such as handling both TCP and UDP in the same LSP, nor does it")
//#pragma message("prevent multiple threads on thunked 16 bit WinSock calls. This example")
//#pragma message("should be used as a guide, not a direct implementation")
//#pragma message("")

//#error "Please remove this line from precomp.h to compile this file"

#ifndef _PRECOMP_
#define _PRECOMP_

#define WIN32_LEAN_AND_MEAN

//
// Turn off "declspec" decoration of entrypoints defined in WINSOCK2.H.
//

#define WINSOCK_API_LINKAGE

#include "nowarn.h"

#include <winsock2.h>
#include <ws2spi.h>
#include <wtypes.h>
#include <assert.h>
#include <winnt.h>
#include <stdlib.h>
#include "trace.h"
#include "llist.h"
#include "dprovide.h"
#include "dsocket.h"
#include "dcatalog.h"
#include "dcatitem.h"
#include "dworker.h"
#include "dasyncw.h"
#include "doverlap.h"
#include "dbuffmgr.h"
#include "dbgtrace.h"
#include "dthook.h"
#include "globals.h"

#endif  // _PRECOMP_


CLASSFWD.H

/*++

     Copyright c 1996 Intel Corporation
     All Rights Reserved

     Permission is granted to use, copy and distribute this software and
     its documentation for any purpose and without fee, provided, that
     the above copyright notice and this statement appear in all copies.
     Intel makes no representations about the suitability of this
     software for any purpose.  This software is provided "AS IS."

     Intel specifically disclaims all warranties, express or implied,
     and all liability, including consequential and other indirect
     damages, for the use of this software, including liability for
     infringement of any proprietary rights, and including the
     warranties of merchantability and fitness for a particular purpose.
     Intel does not assume any responsibility for any errors which may
     appear in this software nor any responsibility to update it.

Module Name:

    classfwd.h

Abstract:

    This module contains forward definitions the for classes used in the
    WinSock2 layered service provider sample.

--*/

#ifndef _CLASSFWD_
#define _CLASSFWD_

#include <winsock2.h>

class DSOCKET;
typedef DSOCKET FAR * PDSOCKET;

class DPROVIDER;
typedef DPROVIDER FAR * PDPROVIDER;

class PROTO_CATALOG_ITEM;
typedef PROTO_CATALOG_ITEM  FAR * PPROTO_CATALOG_ITEM;

class DCATALOG;
typedef DCATALOG FAR * PDCATALOG;

class DWORKERTHREAD;
typedef DWORKERTHREAD FAR * PDWORKERTHREAD;

class DASYNCWINDOW;
typedef DASYNCWINDOW FAR * PDASYNCWINDOW;

class DOVERLAPPEDSTRUCTMGR;
typedef DOVERLAPPEDSTRUCTMGR FAR * PDOVERLAPPEDSTRUCTMGR;

class DBUFFERMANAGER;
typedef DBUFFERMANAGER FAR * PDBUFFERMANAGER;

#endif  // _CLASSFWD_


SPI.CPP

/*++

     Copyright (c) 1996 Intel Corporation
     Copyright 1996 - 1998 Microsoft Corporation
     All Rights Reserved

     Permission is granted to use, copy and distribute this software and
     its documentation for any purpose and without fee, provided, that
     the above copyright notice and this statement appear in all copies.
     Intel makes no representations about the suitability of this
     software for any purpose.  This software is provided "AS IS."

     Intel specifically disclaims all warranties, express or implied,
     and all liability, including consequential and other indirect
     damages, for the use of this software, including liability for
     infringement of any proprietary rights, and including the
     warranties of merchantability and fitness for a particular purpose.
     Intel does not assume any responsibility for any errors which may
     appear in this software nor any responsibility to update it.

Module Name:

    SPI.CPP :

Abstract:

    Contains all the entry points for the WS2SPI.  This module implements a
    sample WinSock2 layered service provider.

--*/

#include "precomp.h"

// The WinSock2 UpcallTable.
WSPUPCALLTABLEgUpCallTable;
LPWPUCOMPLETEOVERLAPPEDREQUESTlpWPUCompleteOverlappedRequest;

// Variables to track Startup/Cleanup Pairing.
CRITICAL_SECTION  gInitCriticalSection;
DWORD             gStartupCount=0;

// The catalog of providers
PDCATALOG gProviderCatalog;

// The worker thread for overlapped functions
#define UNINITIALIZED_THREAD    ((PDWORKERTHREAD)-1)
PDWORKERTHREAD gWorkerThread=UNINITIALIZED_THREAD;

// The window for translation of async select messages
#define UNINITIALIZED_WINDOW    ((PDASYNCWINDOW)-1)
PDASYNCWINDOW gAsyncWindow=UNINITIALIZED_WINDOW;

// The buffer manager for providers that modify the data stream
PDBUFFERMANAGER gBufferManager;

char    gLibraryName[MAX_PATH] ="LSP.DLL";

#define SetBlockingProvider(Provider)\
(TlsIndex!=0xFFFFFFFF)\
? TlsSetValue (TlsIndex, Provider)\
: NULL

#define GetBlockingProvider()\
(TlsIndex!=0xFFFFFFFF)\
? TlsGetValue (TlsIndex)\
: NULL

PDASYNCWINDOW
GetAsyncWindow (
    )
/*++
Routine Description:

Gets the pointer to async window object (creates and initializes
the object if not already created)
Arguments:
None
Return Value:
Pointer to async window object if successfull, NULL otherwise

--*/
{
// Quick check if object was already initialized (it can be NULL
// if we failed initialization in which case we do not
// want to retry)
    if (gAsyncWindow!=UNINITIALIZED_WINDOW)
        ;
    else {
        EnterCriticalSection (&gInitCriticalSection);
// Recheck under protection of the critical section
        if (gAsyncWindow == UNINITIALIZED_WINDOW) {
            PDASYNCWINDOW   asyncWindow;
// Attempt to create and initialize the object
// If creation or initialization fails, set global object
// pointer to NULL to prevent further initialization attepmts
            asyncWindow = new DASYNCWINDOW;
            if (asyncWindow!=NULL) {
                if (asyncWindow->Initialize()==NO_ERROR) {
                    gAsyncWindow = asyncWindow;
                }
                else {
                    delete asyncWindow;
                    gAsyncWindow = NULL;
                }
            }
        }
        LeaveCriticalSection (&gInitCriticalSection);
    }
    return gAsyncWindow;
}

PDWORKERTHREAD
GetWorkerThread (
    )
/*++
Routine Description:

Gets the pointer to worker thread object (creates and initializes
the object if not already created)
Arguments:
None
Return Value:
Pointer to worker thread object if successfull, NULL otherwise

--*/
{
// Quick check if object was already initialized (it can be NULL
// if we failed initialization in which case we do not
// want to retry)
    if (gWorkerThread!=UNINITIALIZED_THREAD)
        ;
    else {
        EnterCriticalSection (&gInitCriticalSection);
// Recheck under protection of the critical section
        if (gWorkerThread == UNINITIALIZED_THREAD) {
            PDWORKERTHREAD  workerThread;
// Attempt to create and initialize the object
// If creation or initialization fails, set global object
// pointer to NULL to prevent further initialization attepmts
            workerThread = new DWORKERTHREAD;
            if (workerThread!=NULL) {
                if (workerThread->Initialize()==NO_ERROR) {
                    gWorkerThread = workerThread;
                }
                else {
                    delete workerThread;
                    gWorkerThread = NULL;
                }
            }
        }
        LeaveCriticalSection (&gInitCriticalSection);
    }
    return gWorkerThread;
}

SOCKET
WSPAPI
WSPAccept(
    IN SOCKET s,
    OUT struct sockaddr FAR *addr,
    OUT INT FAR *addrlen,
    IN LPCONDITIONPROC lpfnCondition,
    IN DWORD dwCallbackData,
    OUT INT FAR *lpErrno
    )
/*++
Routine Description:

    Conditionally  accept a connection based on the return value of a condition
    function, and optionally create and/or join a socket group.

Arguments:

    s              - A  descriptor  identiying  a socket which is listening for
                     connections after a WSPListen().

    addr           - An optional pointer to a buffer which receives the address
                     of   the  connecting  entity,  as  known  to  the  service
                     provider.   The  exact  format  of  the  addr arguement is
                     determined  by  the  address  family  established when the
                     socket was created.

    addrlen        - An  optional  pointer  to  an  integer  which contains the
                     length of the address addr.

    lpfnCondition  - The  procedure  instance address of an optional, WinSock 2
                     client  supplied  condition  function  which  will make an
                     accept/reject  decision  based  on  the caller information
                     passed  in  as  parameters,  and optionally creaetd and/or
                     join  a  socket group by assigning an appropriate value to
                     the result parameter of this function.

    dwCallbackData - Callback data to be passed back to the WinSock 2 client as
                     a  condition  function  parameter.   This parameter is not
                     interpreted by the service provider.

    lpErrno        - A pointer to the error code.

Return Value:

    If  no  error occurs, WSPAccept() returns a value of type SOCKET which is a
    descriptor  for  the accepted socket.  Otherwise, a value of INVALID_SOCKET
    is returned, and a specific error code is available in lpErrno.

--*/
{
    INT          ReturnValue;
    PDPROVIDER   Provider;
    PDSOCKET     Socket;
    PDSOCKET     NewSocket;
    SOCKET       ProviderSocket;
    SOCKET       NewProviderSocket;
DWORD ThisProviderCatalogEntryId;

    // Debug/Trace stuff

    if (PREAPINOTIFY(( DTCODE_WSPAccept,
                       &ReturnValue,
                       gLibraryName,
                       &s,
                       &addr,
                       &addrlen,
                       &lpfnCondition,
                       &dwCallbackData,
                       &lpErrno)) ) {
        return(ReturnValue);
    }

    //
    // Get our DSOCKET object
    //
    ReturnValue = gUpCallTable.lpWPUQuerySocketHandleContext(
        s,
        (DWORD*)&Socket,
        lpErrno);

    if (SOCKET_ERROR != ReturnValue){

        Provider = Socket->GetDProvider();
        ProviderSocket = Socket->GetProviderSocket();

SetBlockingProvider (Provider);
        NewProviderSocket = Provider->WSPAccept(
            ProviderSocket,
            addr,
            addrlen,
            lpfnCondition,
            dwCallbackData,
            lpErrno);
SetBlockingProvider (NULL);

        if (INVALID_SOCKET != NewProviderSocket){

            //
            // Create a new socket object and initialize it.
            NewSocket = new DSOCKET;
            if (NewSocket){
ThisProviderCatalogEntryId =
gProviderCatalog->GetLocalProvider()->GetProtocolInfo()->dwCatalogEntryId;

                ReturnValue = gUpCallTable.lpWPUCreateSocketHandle(
                    ThisProviderCatalogEntryId,
                    (DWORD) NewSocket,
                    lpErrno);
                DEBUGF( DBG_TRACE,
                        ("Accept Returning Socket %X\n", ReturnValue));

                if (INVALID_SOCKET != ReturnValue){
                    NewSocket->Initialize(
                        Provider,
                        NewProviderSocket,
                        Socket->GetCatalogEntryId(),
                        ReturnValue);

                } //if
                else{
                    delete NewSocket;
Provider->WSPCloseSocket (NewProviderSocket, lpErrno);
                } //else
            } //if
        } //if
    } //if

    POSTAPINOTIFY(( DTCODE_WSPAccept,
                    &ReturnValue,
                    gLibraryName,
                    &s,
                    &addr,
                    &addrlen,
                    &lpfnCondition,
                    &dwCallbackData,
                    &lpErrno));

    return(ReturnValue);
}

BOOL
PASCAL FAR
WSPAcceptEx (
IN SOCKET sListenSocket,
IN SOCKET sAcceptSocket,
IN PVOID lpOutputBuffer,
IN DWORD dwReceiveDataLength,
IN DWORD dwLocalAddressLength,
IN DWORD dwRemoteAddressLength,
OUT LPDWORD lpdwBytesReceived,
IN LPOVERLAPPED lpOverlapped
)
/*++
Routine Description:
Accepts a new connection, returns the local and remote address,
and receives the first block of data sent by the client application.

Arguments:
sListenSocket- A descriptor identifying a socket that has
already been called with the listen function.
A server application waits for attempts to
connect on this socket.
sAcceptSocket- A descriptor identifying a socket on which to
accept an incoming connection. This socket must
not be bound or connected.
lpOutputBuffer- A pointer to a buffer that receives the first
block of data sent on a new connection,
the local address of the server, and the remote
address of the client. The receive data is
written to the first part of the buffer starting
at offset zero, while the addresses are written
to the latter part of the buffer. This parameter
must be specified.
dwReceiveDataLength - The number of bytes in the buffer that will be
used for receiving data. If this parameter is
specified as zero, then no receive operation is
performed in conjunction with accepting the connection.
Instead, the AcceptEx function completes as soon
as a connection arrives without waiting for any
data.
dwLocalAddressLength -  The number of bytes reserved for the local
address information. This must be at least
16 bytes more than the maximum address length
for the transport protocol in use.
dwRemoteAddressLength - The number of bytes reserved for the remote
address information. This must be at least
16 bytes more than the maximum address length
for the transport protocol in use.
lpdwBytesReceived- A pointer to a DWORD that receives the count
of bytes received. This is set only if the
operation completes synchronously.
If it returns ERROR_IO_PENDING and is completed
later, then this DWORD is never set and you must
obtain the number of bytes read from the
completion notification mechanism.
lpOverlapped- An OVERLAPPED structure that is used to process
the request. This parameter must be specified;
it cannot be NULL.

Return Value:

    If no error occurs, the AcceptEx function completed successfully
and a value of TRUE is returned.
If the function fails, AcceptEx returns FALSE. The WSAGetLastError
function can then be called to return extended error information.
If WSAGetLastError returns ERROR_IO_PENDING, then the operation
was successfully initiated and is still in progress

--*/

  {
PDSOCKETListenSocket, AcceptSocket;
INTErrno, ReturnValue;
PDPROVIDERProvider;
SOCKETProviderListenSocket, ProviderAcceptSocket;
    //
    // Get our DSOCKET objects
    //
    ReturnValue = gUpCallTable.lpWPUQuerySocketHandleContext(
        sListenSocket,
        (DWORD*)&ListenSocket,
        &Errno);

    if (SOCKET_ERROR != ReturnValue){
ReturnValue = gUpCallTable.lpWPUQuerySocketHandleContext(
sAcceptSocket,
(DWORD*)&AcceptSocket,
&Errno);

if (SOCKET_ERROR != ReturnValue){
if (lpOverlapped) {
if ((lpOverlapped->hEvent==NULL)
|| ResetEvent ((HANDLE)((DWORD)lpOverlapped->hEvent&0xFFFFFFFE))) {
PDWORKERTHREAD  Thread = GetWorkerThread ();
if (Thread!=NULL) {
// Setup the user overlapped struct
lpOverlapped->Internal = WSS_OPERATION_IN_PROGRESS;
lpOverlapped->InternalHigh = 0;
ReturnValue = Thread->QueueOverlappedAcceptEx(
ListenSocket,
AcceptSocket,
lpOutputBuffer,
dwReceiveDataLength,
dwLocalAddressLength,
dwRemoteAddressLength,
lpOverlapped,
&Errno);
}
else {
ReturnValue = SOCKET_ERROR;
Errno = WSAENOBUFS;
}
}
else {
ReturnValue = SOCKET_ERROR;
Errno = WSA_INVALID_PARAMETER;
}
}
else {
Provider = ListenSocket->GetDProvider ();
ProviderListenSocket = ListenSocket->GetProviderSocket ();
ProviderAcceptSocket = AcceptSocket->GetProviderSocket ();

SetBlockingProvider (Provider);
ReturnValue = Provider->AcceptEx (
ProviderListenSocket,
ProviderAcceptSocket,
lpOutputBuffer,
dwReceiveDataLength,
dwLocalAddressLength,
dwRemoteAddressLength,
lpdwBytesReceived,
lpOverlapped,
&Errno);
SetBlockingProvider (NULL);
}
}
}
if (ReturnValue==NO_ERROR)
return TRUE;
else {
SetLastError (Errno);
return FALSE;
}
}

INT
WSPAPI
WSPAddressToString(
    IN     LPSOCKADDR lpsaAddress,
    IN     DWORD dwAddressLength,
    IN     LPWSAPROTOCOL_INFOW lpProtocolInfo,
    OUT    LPWSTR lpszAddressString,
    IN OUT LPDWORD lpdwAddressStringLength,
    OUT    LPINT lpErrno )
/*++

Routine Description:

    WSPAddressToString() converts a SOCKADDR structure into a human-readable
    string representation of the address.  This is intended to be used mainly
    for display purposes. If the caller wishes the translation to be done by a
    particular provider, it should supply the corresponding WSAPROTOCOL_INFO
    struct in the lpProtocolInfo parameter.

Arguments:

    lpsaAddress - points to a SOCKADDR structure to translate into a string.

    dwAddressLength - the length of the Address SOCKADDR.

    lpProtocolInfo - (optional) the WSAPROTOCOL_INFO struct for a particular
                     provider.

    lpszAddressString - a buffer which receives the human-readable address
                        string.

    lpdwAddressStringLength - on input, the length of the AddressString buffer.
                              On output, returns the length of  the string
                              actually copied into the buffer.

Return Value:

    The return value is 0 if the operation was successful.  Otherwise the value
    SOCKET_ERROR is returned
--*/
{
    INT                 ReturnValue;
    PDPROVIDER          Provider;
PPROTO_CATALOG_ITEM BaseProviderCatalogEntry;

    if (PREAPINOTIFY(( DTCODE_WSPAddressToString,
                       &ReturnValue,
                       gLibraryName,
                       &lpsaAddress,
                       &dwAddressLength,
                       &lpProtocolInfo,
                       &lpszAddressString,
                       &lpdwAddressStringLength,
                       &lpErrno)) ){
        return(ReturnValue);
    } //if

    //
    // Get the catlog entry for the next provider in the chain
    //
    ReturnValue = gProviderCatalog->FindNextProviderInChain(
        lpProtocolInfo,
&Provider,
&BaseProviderCatalogEntry);

    if (NO_ERROR == ReturnValue){

        //
        ReturnValue = Provider->WSPAddressToString(
            lpsaAddress,
            dwAddressLength,
            BaseProviderCatalogEntry
? BaseProviderCatalogEntry->GetProtocolInfo()
: lpProtocolInfo,
            lpszAddressString,
            lpdwAddressStringLength,
            lpErrno);
    } //if

    POSTAPINOTIFY(( DTCODE_WSPAddressToString,
                    &ReturnValue,
                    gLibraryName,
                    &lpsaAddress,
                    &dwAddressLength,
                    &lpProtocolInfo,
                    &lpszAddressString,
                    &lpdwAddressStringLength,
                    &lpErrno));

    return(ReturnValue);
}

INT
WSPAPI
WSPAsyncSelect(
    IN SOCKET s,
    IN HWND hWnd,
    IN unsigned int wMsg,
    IN long lEvent,
    OUT INT FAR *lpErrno
    )
/*++
Routine Description:

    Request  Windows  message-based  event notification of network events for a
    socket.

Arguments:

    s       - A  descriptor identiying a socket for which event notification is
              required.

    hWnd    - A  handle  identifying  the window which should receive a message
              when a network event occurs.

    wMsg    - The message to be sent when a network event occurs.

    lEvent  - bitmask  which specifies a combination of network events in which
              the WinSock client is interested.

    lpErrno - A pointer to the error code.

Return Value:

    The  return  value  is 0 if the WinSock client's declaration of interest in
    the  netowrk event set was successful.  Otherwise the value SOCKET_ERROR is
    returned, and a specific error code is available in lpErrno.

--*/
{
    INT ReturnValue;
    PDSOCKET     Socket;

    if (PREAPINOTIFY(( DTCODE_WSPAsyncSelect,
                       &ReturnValue,
                       gLibraryName,
                       &s,
                       &hWnd,
                       &wMsg,
                       &lEvent,
                       &lpErrno)) ) {
        return(ReturnValue);
    }

    //
    // Get our DSOCKET object
    //
    ReturnValue = gUpCallTable.lpWPUQuerySocketHandleContext(
        s,
        (DWORD*)&Socket,
        lpErrno);
    if (SOCKET_ERROR != ReturnValue){
        PDASYNCWINDOW   AsyncWindow = GetAsyncWindow ();
        if (AsyncWindow!=NULL) {
            Socket->RegisterAsyncOperation(
                hWnd,
                wMsg,
                lEvent);

            *lpErrno = NO_ERROR;
            // Register this socket with the worker thread.
            *lpErrno = AsyncWindow->RegisterSocket(Socket);

            if (NO_ERROR == *lpErrno){
                ReturnValue = NO_ERROR;
            } //if
            else{
                ReturnValue = SOCKET_ERROR;
            } //else
        }
        else {
            ReturnValue = SOCKET_ERROR;
            *lpErrno = WSAENOBUFS;
        }

    } //if

    POSTAPINOTIFY(( DTCODE_WSPAsyncSelect,
                    &ReturnValue,
                    gLibraryName,
                    &s,
                    &hWnd,
                    &wMsg,
                    &lEvent,
                    &lpErrno));

    return(ReturnValue);
}

INT
WSPAPI
WSPBind(
    IN SOCKET s,
    IN const struct sockaddr FAR *name,
    IN INT namelen,
    OUT INT FAR *lpErrno
    )
/*++
Routine Description:

    Associate a local address (i.e. name) with a socket.

Arguments:

    s       - A descriptor identifying an unbound socket.

    name    - The  address  to assign to the socket.  The sockaddr structure is
              defined as follows:

              struct sockaddr {
                  u_short sa_family;
                  char    sa_data[14];
              };

              Except  for  the sa_family field,
sockaddr contents are epxressed
              in network byte order.

    namelen - The length of the name.

    lpErrno - A pointer to the error code.

Return Value:

    If   no   erro   occurs,  WSPBind()  returns  0.   Otherwise, it  returns
    SOCKET_ERROR, and a specific error code is available in lpErrno.

--*/
{
    INT ReturnValue;
    PDPROVIDER   Provider;
    PDSOCKET     Socket;
    SOCKET       ProviderSocket;

    if (PREAPINOTIFY(( DTCODE_WSPBind,
                       &ReturnValue,
                       gLibraryName,
                       &s,
                       &name,
                       &namelen,
                       &lpErrno)) ) {

        return(ReturnValue);
    }

    //
    // Get our DSOCKET object
    //
    ReturnValue = gUpCallTable.lpWPUQuerySocketHandleContext(
        s,
        (DWORD*)&Socket,
        lpErrno);

    if (SOCKET_ERROR != ReturnValue){
        Provider = Socket->GetDProvider();
        ProviderSocket = Socket->GetProviderSocket();

        ReturnValue = Provider->WSPBind(
            ProviderSocket,
            name,
            namelen,
            lpErrno);

        POSTAPINOTIFY(( DTCODE_WSPBind,
                        &ReturnValue,
                        gLibraryName,
                        &s,
                        &name,
                        &namelen,
                        &lpErrno));
    }

    return(ReturnValue);

}

INT
WSPAPI
WSPCancelBlockingCall(OUT INT FAR *lpErrno)
/*++
Routine Description:

    Cancel a blocking call which is currently in progress.

Arguments:

    lpErrno - A pointer to the error code.

Return Value:

    The  value  returned  by  WSPCancelBlockingCall() is 0 if the operation was
    successfully canceled.  Otherwise the value SOCKET_ERROR is returned,
and a
    specific error code is available in lpErrno.

--*/
{
      INT ReturnValue;
  PDPROVIDERProvider;

      if (PREAPINOTIFY(( DTCODE_WSPCancelBlockingCall,
                         &ReturnValue,
                         gLibraryName,
                         &lpErrno)) ) {
          return(ReturnValue);
      }

  Provider = (PDPROVIDER)TlsGetValue (TlsIndex);
  if (Provider!=NULL) {
  ReturnValue = Provider->WSPCancelBlockingCall(
  lpErrno);
  }
  else {
  ReturnValue = SOCKET_ERROR;
  *lpErrno = WSAEINVAL;
  }

      POSTAPINOTIFY(( DTCODE_WSPCancelBlockingCall,
                      &ReturnValue,
                      gLibraryName,
                      &lpErrno));

      return(ReturnValue);
}

INT
WSPAPI
WSPCleanup(
    OUT INT FAR *lpErrno
    )
/*++
Routine Description:

    Terminate use of the WinSock service provider.

Arguments:

    lpErrno - A pointer to the error code.

Return Value:

    The  return  value  is  0 if the operation has been successfully initiated.
    Otherwise  the  value SOCKET_ERROR is returned,
and a specific error number
    is available in lpErrno.

--*/

{
    INT          ReturnValue;

    if (PREAPINOTIFY(( DTCODE_WSPCleanup,
                       &ReturnValue,
                       gLibraryName,
                       &lpErrno)) ) {
        return(ReturnValue);
    }

    EnterCriticalSection(&gInitCriticalSection);
    if (gStartupCount > 0){
        gStartupCount--;
        if (gStartupCount == 0){

            DEBUGF( DBG_TRACE,
                    ("Tearing down layered provider\n"));

            if (gAsyncWindow!=UNINITIALIZED_WINDOW) {
                if (gAsyncWindow!=NULL)
                    //Kill the worker thread
                    gAsyncWindow->Destroy ();
                gAsyncWindow = UNINITIALIZED_WINDOW;
            }

            if (gWorkerThread!=UNINITIALIZED_THREAD) {
                if (gWorkerThread!=NULL)
                    //Kill the worker thread
                    gWorkerThread->Destroy ();
                gWorkerThread = UNINITIALIZED_THREAD;
            }

// To let threads wake up and figure out that
// they being cleaned up
Sleep (100);

            DSOCKET::DSocketClassCleanup ();

            // Kill the ProviderCatalog
            delete(gProviderCatalog);

            //Kill the buffer manager
            delete(gBufferManager);
            DEBUGF( DBG_TRACE,
                    ("Tearing down Complete\n"));

        } //if
    } //if
    ReturnValue = NO_ERROR;
    *lpErrno = NO_ERROR;

    LeaveCriticalSection(&gInitCriticalSection);

    POSTAPINOTIFY(( DTCODE_WSPCleanup,
                    &ReturnValue,
                    gLibraryName,
                    &lpErrno));

    return(ReturnValue);

}

INT
WSPAPI
WSPCloseSocket(
    IN SOCKET s,
    OUT INT FAR *lpErrno
    )
/*++
Routine Description:

    Close a socket.

Arguments:

    s       - A descriptor identifying a socket.

    lpErrno - A pointer to the error code.

Return Value:

    If  no  erro  occurs, WSPCloseSocket()  returns  0.  Otherwise, a value of
    SOCKET_ERROR  is  returned,  and  a  specific  error  code  is available in
    lpErrno.

--*/
{
    INT ReturnValue;
    PDSOCKET     Socket;
    PDPROVIDER   Provider;
    SOCKET       ProviderSocket;

    if (PREAPINOTIFY(( DTCODE_WSPCloseSocket,
                       &ReturnValue,
                       gLibraryName,
                       &s,
                       &lpErrno)) ) {
        return(ReturnValue);
    }

    //
    // Get our DSOCKET object
    //
    ReturnValue = gUpCallTable.lpWPUQuerySocketHandleContext(
        s,
        (DWORD*)&Socket,
        lpErrno);
    if (SOCKET_ERROR != ReturnValue){
        Provider = Socket->GetDProvider();
        ProviderSocket = Socket->GetProviderSocket();
        DEBUGF( DBG_TRACE,
                ("Closing socket %X\n",s));

        Socket->Remove ();

        ReturnValue = Provider->WSPCloseSocket(
            ProviderSocket,
            lpErrno);

if (NO_ERROR==ReturnValue) {
gUpCallTable.lpWPUCloseSocketHandle(
Socket->GetSocketHandle(),
lpErrno);

delete(Socket);

ReturnValue = NO_ERROR;
}
    } //if

    POSTAPINOTIFY(( DTCODE_WSPCloseSocket,
                    &ReturnValue,
                    gLibraryName,
                    &s,
                    &lpErrno));

    return(ReturnValue);

}

INT
WSPAPI
WSPConnect(
    IN SOCKET s,
    IN const struct sockaddr FAR *name,
    IN INT namelen,
    IN LPWSABUF lpCallerData,
    IN LPWSABUF lpCalleeData,
    IN LPQOS lpSQOS,
    IN LPQOS lpGQOS,
    OUT INT FAR *lpErrno
    )
/*++
Routine Description:

    Establish a connection to a peer,
exchange connect data,
and specify needed
    quality of service based on the supplied flow spec.

Arguments:

    s            - A descriptor identifying an unconnected socket.

    name         - The name of the peer to which the socket is to be connected.

    namelen      - The length of the name.

    lpCallerData - A  pointer to the user data that is to be transferred to the
                   peer during connection established.

    lpCalleeData - A pointer to a buffer into which may be copied any user data
                   received from the peer during connection establishment.

    lpSQOS       - A  pointer  to  the  flow  specs  for socket s, one for each
                   direction.

    lpGQOS       - A  pointer  to  the  flow  specs  for  the  socket group (if
                   applicable).

    lpErrno      - A pointer to the error code.

Return Value:

    If  no  error  occurs, WSPConnect()  returns NO_ERROR.  Otherwise, it
    returns SOCKET_ERROR, and a specific erro rcode is available in lpErrno.

--*/
{
    INT ReturnValue;
    PDSOCKET     Socket;
    PDPROVIDER   Provider;
    SOCKET       ProviderSocket;

    if (PREAPINOTIFY(( DTCODE_WSPConnect,
                       &ReturnValue,
                       gLibraryName,
                       &s,
                       &name,
                       &namelen,

&lpCallerData,
                       &lpCalleeData,
                       &lpSQOS,
                       &lpGQOS,
                       &lpErrno)) ) {
        return(ReturnValue);
    }

    //
    // Get our DSOCKET object
    //
    ReturnValue = gUpCallTable.lpWPUQuerySocketHandleContext(
        s,
        (DWORD*)&Socket,
        lpErrno);
    if (SOCKET_ERROR != ReturnValue){
        Provider = Socket->GetDProvider();
        ProviderSocket = Socket->GetProviderSocket();

SetBlockingProvider (Provider);
        ReturnValue = Provider->WSPConnect(
            ProviderSocket,
            name,
            namelen,
            lpCallerData,
            lpCalleeData,
            lpSQOS,
            lpGQOS,
            lpErrno);
SetBlockingProvider (NULL);
    }//if

    POSTAPINOTIFY(( DTCODE_WSPConnect,
                    &ReturnValue,
                    gLibraryName,
                    &s,
                    &name,
                    &namelen,
                    &lpCallerData,
                    &lpCalleeData,
                    &lpSQOS,
                    &lpGQOS,
                    &lpErrno));

    return(ReturnValue);

}

INT
WSPAPI
WSPDuplicateSocket(
    IN SOCKET s,
    IN DWORD dwProcessID,
    OUT LPWSAPROTOCOL_INFOW lpProtocolInfo,
    OUT INT FAR *lpErrno
    )
/*++
Routine Description:

    descriptor for a shared socket.

Arguments:

    s              - Specifies the local socket descriptor.

    dwProcessID    - Specifies  the  ID  of  the  target  process for which the
                     shared socket will be used.

    lpProtocolInfo - A  pointer  to  a  buffer  allocated by the client that is
                     large enough to contain a WSAPROTOCOL_INFOA struct.  The
                     service  provider copies the protocol info struct contents
                     to this buffer.

    lpErrno        - A pointer to the error code

Return Value:

    If  no  error  occurs, WPSDuplicateSocket()  returns zero.  Otherwise, the
    value of SOCKET_ERROR is returned, and a specific error number is available
    in lpErrno.

--*/
{
    INTReturnValue=SOCKET_ERROR;
PDPROVIDERProvider;
PDSOCKETSocket;
SOCKETProviderSocket;
PPROTO_CATALOG_ITEMChainCatalogItem;
DWORDReserved;

    if (PREAPINOTIFY(( DTCODE_WSPDuplicateSocket,
                     &ReturnValue,
                     gLibraryName,
                     &s,
                     &dwProcessID,
                     &lpProtocolInfo,
                     &lpErrno)) ) {
      return(ReturnValue);
    }

    //
    // Get our DSOCKET object
    //
    ReturnValue = gUpCallTable.lpWPUQuerySocketHandleContext(
        s,
        (DWORD*)&Socket,
        lpErrno);
    if (SOCKET_ERROR != ReturnValue){
// Get chain catalog entry
ReturnValue = gProviderCatalog->GetCatalogItemFromCatalogEntryId(
Socket->GetCatalogEntryId(),
&ChainCatalogItem);
        if (NO_ERROR==ReturnValue) {
// Call next provider to duplicate its socket
Provider = Socket->GetDProvider();
ProviderSocket = Socket->GetProviderSocket();

ReturnValue = Provider->WSPDuplicateSocket(
ProviderSocket,
dwProcessID,
lpProtocolInfo,
lpErrno);
if (NO_ERROR==ReturnValue) {
// Replace returned protocol infor
// with chain protocol info and
// carefully restore provider reserved field.
Reserved = lpProtocolInfo->dwProviderReserved;
*lpProtocolInfo = *ChainCatalogItem->GetProtocolInfo ();
lpProtocolInfo->dwProviderReserved = Reserved;
}

        } // if
    } //if

    POSTAPINOTIFY(( DTCODE_WSPDuplicateSocket,
                  &ReturnValue,
                  gLibraryName,
                  &s,
                  &dwProcessID,
                  &lpProtocolInfo,
                  &lpErrno));

    return(ReturnValue);

}

INT
WSPAPI
WSPEnumNetworkEvents(
    IN SOCKET s,
    OUT WSAEVENT hEventObject,
    OUT LPWSANETWORKEVENTS lpNetworkEvents,
    OUT INT FAR *lpErrno
    )
/*++
Routine Description:

    Report occurrences of network events for the indicated socket.

Arguments:

    s               - A descriptor identifying the socket.

    hEventObject    - An optional handle identifying an associated event object
                      to be reset.

    lpNetworkEvents - A  pointer  to  a WSANETWORKEVENTS struct which is filled
                      with   a  record  of  occurred  network  events  and  any
                      associated error codes.

    lpErrno         - A pointer to the error code.

Return Value:

    The  return  value  is  NO_ERROR  if  the  operation  was  successful.
    Otherwise  the  value SOCKET_ERROR is returned, and a specific error number
    is available in lpErrno.

--*/
{
    INT ReturnValue;
    PDSOCKET     Socket;
    PDPROVIDER   Provider;
    SOCKET       ProviderSocket;

    if (PREAPINOTIFY(( DTCODE_WSPEnumNetworkEvents,
                       &ReturnValue,
                       gLibraryName,
                       &s,
                       &hEventObject,
                       &lpNetworkEvents,
                       &lpErrno)) ) {
        return(ReturnValue);
    }

    //
    // Get our DSOCKET object
    //
    ReturnValue = gUpCallTable.lpWPUQuerySocketHandleContext(
        s,
        (DWORD*)&Socket,
        lpErrno);
    if (SOCKET_ERROR != ReturnValue){
        Provider = Socket->GetDProvider();
        ProviderSocket = Socket->GetProviderSocket();

        ReturnValue = Provider->WSPEnumNetworkEvents(
            ProviderSocket,
            hEventObject,
            lpNetworkEvents,
            lpErrno);
    } //if

    POSTAPINOTIFY(( DTCODE_WSPEnumNetworkEvents,
                    &ReturnValue,
                    gLibraryName,
                    &s,
                    &hEventObject,
                    &lpNetworkEvents,
                    &lpErrno));

    return(ReturnValue);

}

INT
WSPAPI
WSPEventSelect(
    IN SOCKET s,
    IN OUT WSAEVENT hEventObject,
    IN long lNetworkEvents,
    OUT INT FAR *lpErrno
    )
/*++
Routine Description:

    Specify  an  event object to be associated with the supplied set of network
    events.

Arguments:

    s              - A descriptor identifying the socket.

    hEventObject   - A  handle  identifying  the  event object to be associated
                     with the supplied set of network events.

    lNetworkEvents - A  bitmask  which  specifies  the  combination  of network
                     events in which the WinSock client has interest.

    lpErrno        - A pointer to the error code.

Return Value:

    The return value is 0 if the WinSock client's specification of the network
    events and the associated event object was successful. Otherwise the value
    SOCKET_ERROR is returned, and a specific error number is available in
    lpErrno

--*/
{
    INT ReturnValue;
    PDSOCKET     Socket;
    PDPROVIDER   Provider;
    SOCKET       ProviderSocket;

    if (PREAPINOTIFY(( DTCODE_WSPEventSelect,
                       &ReturnValue,
                       gLibraryName,
                       &s,
                       &hEventObject,
                       &lNetworkEvents,
                       &lpErrno)) ) {
        return(ReturnValue);
    }

    //
    // Get our DSOCKET object
    //
    ReturnValue = gUpCallTable.lpWPUQuerySocketHandleContext(
        s,
        (DWORD*)&Socket,
        lpErrno);
    if (SOCKET_ERROR != ReturnValue){
        Provider = Socket->GetDProvider();
        ProviderSocket = Socket->GetProviderSocket();

        ReturnValue = Provider->WSPEventSelect(
            ProviderSocket,
            hEventObject,
            lNetworkEvents,
            lpErrno);
    } //if

    POSTAPINOTIFY(( DTCODE_WSPEventSelect,
                    &ReturnValue,
                    gLibraryName,
                    &s,
                    &hEventObject,
                    &lNetworkEvents,
                    &lpErrno));

    return(ReturnValue);

}

INT
WSPAPI
WSPGetOverlappedResult(
    IN SOCKET s,
    IN LPWSAOVERLAPPED lpOverlapped,
    IN LPDWORD lpcbTransfer,
    IN BOOL fWait,
    OUT LPDWORD lpdwFlags,
    OUT INT FAR *lpErrno
    )
/*++
Routine Description:

    Returns the results of an overlapped operation on the specified socket.

Arguments:

    s            - Identifies  the  socket.   This  is the same socket that was
                   specified  when  the  overlapped  operation was started by a
                   call to WSPRecv(), WSPRecvFrom(), WSPSend(), WSPSendTo(), or
                   WSPIoctl().

    lpOverlapped - Points to a WSAOVERLAPPED structure that was specified
                   when the overlapped operation was started.

    lpcbTransfer - Points to a 32-bit variable that receives the number of
                   bytes that were actually transferred by a send or receive
                   operation, or by WSPIoctl().

    fWait        - Specifies  whether  the function should wait for the pending
                   overlapped  operation  to  complete.   If TRUE, the function
                   does  not return until the operation has been completed.  If
                   FALSE  and  the  operation  is  still  pending, the function
                   returns FALSE and lperrno is WSA_IO_INCOMPLETE.

    lpdwFlags    - Points  to  a  32-bit variable that will receive one or more
                   flags   that  supplement  the  completion  status.   If  the
                   overlapped   operation   was   initiated  via  WSPRecv()  or
                   WSPRecvFrom(), this parameter will contain the results value
                   for lpFlags parameter.

    lpErrno      - A pointer to the error code.

Return Value:

    If WSPGetOverlappedResult() succeeds,the return value is TRUE.  This means
    that the overlapped operation has completed successfully and that the value
    pointed  to  by lpcbTransfer has been updated.  If WSPGetOverlappedResult()
    returns  FALSE,  this  means  that  either the overlapped operation has not
    completed  or  the  overlapped operation completed but with errors, or that
    completion  status  could  not  be  determined due to errors in one or more
    parameters  to  WSPGetOverlappedResult().  On failure, the value pointed to
    by  lpcbTransfer  will  not be updated.  lpErrno indicates the cause of the
    failure (either of WSPGetOverlappedResult() or of the associated overlapped
    operation).

--*/
{
    INT ReturnValue;

    if (PREAPINOTIFY(( DTCODE_WSPGetOverlappedResult,
                       &ReturnValue,
                       gLibraryName,
                       &s,
                       &lpOverlapped,
                       &lpcbTransfer,
                       &fWait,
                       &lpdwFlags,
                       &lpErrno)) ) {
        return(ReturnValue);
    }

    if (lpOverlapped->Internal!=WSS_OPERATION_IN_PROGRESS) {
        *lpcbTransfer = lpOverlapped->InternalHigh;
        *lpdwFlags = lpOverlapped->Offset;
        *lpErrno = lpOverlapped->OffsetHigh;
        ReturnValue = lpOverlapped->OffsetHigh==0;
    }
    else if (fWait) {
        ReturnValue = WaitForSingleObject (lpOverlapped->hEvent, INFINITE);
        if ((ReturnValue==WAIT_OBJECT_0)
                && (lpOverlapped->Internal!=WSS_OPERATION_IN_PROGRESS)) {
            *lpcbTransfer = lpOverlapped->InternalHigh;
            *lpdwFlags = lpOverlapped->Offset;
            *lpErrno = lpOverlapped->OffsetHigh;
            ReturnValue = lpOverlapped->OffsetHigh==0;
        }
        else {
            ReturnValue = FALSE;
            *lpErrno = WSASYSCALLFAILURE;
        }
    }
    else {
        *lpErrno = WSA_IO_PENDING;
        ReturnValue = FALSE;
    }

    POSTAPINOTIFY(( DTCODE_WSPGetOverlappedResult,
                    &ReturnValue,
                    gLibraryName,
                    &s,
                    &lpOverlapped,
                    &lpcbTransfer,
                    &fWait,
                    &lpdwFlags,
                    &lpErrno));

    return(ReturnValue);
}

INT
WSPAPI
WSPGetPeerName(
    IN SOCKET s,
    OUT struct sockaddr FAR *name,
    OUT INT FAR *namelen,
    OUT INT FAR *lpErrno
    )
/*++
Routine Description:

    Get the address of the peer to which a socket is connected.

Arguments:

    s       - A descriptor identifying a connected socket.

    name    - A  pointer  to  the structure which is to receive the name of the
              peer.

    namelen - A  pointer  to  an integer which, on input, indicates the size of
              the  structure  pointed  to  by name, and on output indicates the
              size of the returned name.

    lpErrno - A pointer to the error code.

Return Value:

    If  no  error occurs, WSPGetPeerName() returns NO_ERROR.  Otherwise, a
    value  of  SOCKET_ERROR is returned, and a specific error code is available
    in lpErrno

--*/
{
    INT ReturnValue;
    PDSOCKET     Socket;
    PDPROVIDER   Provider;
    SOCKET       ProviderSocket;

    if (PREAPINOTIFY(( DTCODE_WSPGetPeerName,
                       &ReturnValue,
                       gLibraryName,
                       &s,
                       &name,
                       &namelen,
                       &lpErrno)) ) {
        return(ReturnValue);
    }

    //
    // Get our DSOCKET object
    //
    ReturnValue = gUpCallTable.lpWPUQuerySocketHandleContext(
        s,
        (DWORD*)&Socket,
        lpErrno);
    if (SOCKET_ERROR != ReturnValue){
        Provider = Socket->GetDProvider();
        ProviderSocket = Socket->GetProviderSocket();

        ReturnValue = Provider->WSPGetPeerName(
        ProviderSocket,
        name,
        namelen,
        lpErrno);

    } //if

    POSTAPINOTIFY(( DTCODE_WSPGetPeerName,
                    &ReturnValue,
                    gLibraryName,
                    &s,
                    &name,
                    &namelen,
                    &lpErrno));

    return(ReturnValue);
}

INT
WSPAPI
WSPGetQOSByName(
    IN SOCKET s,
    IN LPWSABUF lpQOSName,
    IN LPQOS lpQOS,
    OUT INT FAR *lpErrno
    )
/*++
Routine Description:

    Initializes a QOS structure based on a named template.

Arguments:

    s         - A descriptor identifying a socket.

    lpQOSName - Specifies the QOS template name.

    lpQOS     - A pointer to the QOS structure to be filled.

    lpErrno   - A pointer to the error code.

Return Value:

    If the function succeeds, the return value is TRUE.  If the function fails,
    the  return  value  is  FALSE, and  a  specific error code is available in
    lpErrno.

--*/
{
    INT ReturnValue;
    PDSOCKET     Socket;
    PDPROVIDER   Provider;
    SOCKET       ProviderSocket;

    if (PREAPINOTIFY(( DTCODE_WSPGetQOSByName,
                       &ReturnValue,
                       gLibraryName,
                       &s,
                       &lpQOSName,
                       &lpQOS,
                       &lpErrno)) ) {
        return(ReturnValue);
    }

    //
    // Get our DSOCKET object
    //
    ReturnValue = gUpCallTable.lpWPUQuerySocketHandleContext(
        s,
        (DWORD*)&Socket,
        lpErrno);
    if (SOCKET_ERROR != ReturnValue){
        Provider = Socket->GetDProvider();
        ProviderSocket = Socket->GetProviderSocket();

        ReturnValue = Provider->WSPGetQOSByName(
            ProviderSocket,
            lpQOSName,
            lpQOS,
            lpErrno);
    } //if

    POSTAPINOTIFY(( DTCODE_WSPGetQOSByName,
                    &ReturnValue,
                    gLibraryName,
                    &s,
                    &lpQOSName,
                    &lpQOS,
                    &lpErrno));

    return(ReturnValue);
}

INT
WSPAPI
WSPGetSockName(
    IN SOCKET s,
    OUT struct sockaddr FAR *name,
    OUT INT FAR *namelen,
    OUT INT FAR *lpErrno
    )
/*++
Routine Description:

    Get the local name for a socket.

Arguments:

    s       - A descriptor identifying a bound socket.

    name    - A pointer to a structure used to supply the address (name) of the
              socket.

    namelen - A  pointer  to  an integer which, on input, indicates the size of
              the  structure  pointed  to  by name, and on output indicates the
              size of the returned name

    lpErrno - A Pointer to the error code.

Return Value:

    If  no  error occurs, WSPGetSockName() returns NO_ERROR.  Otherwise, a
    value  of  SOCKET_ERROR is returned, and a specific error code is available
    in lpErrno.

--*/
{
    INT ReturnValue;
    PDSOCKET     Socket;
    PDPROVIDER   Provider;
    SOCKET       ProviderSocket;

    if (PREAPINOTIFY(( DTCODE_WSPGetSockName,
                       &ReturnValue,
                       gLibraryName,
                       &s,
                       &name,
                       &namelen,
                       &lpErrno)) ) {
        return(ReturnValue);
    }

    //
    // Get our DSOCKET object
    //
    ReturnValue = gUpCallTable.lpWPUQuerySocketHandleContext(
        s,
        (DWORD*)&Socket,
        lpErrno);
    if (SOCKET_ERROR != ReturnValue){
        Provider = Socket->GetDProvider();
        ProviderSocket = Socket->GetProviderSocket();

        ReturnValue = Provider->WSPGetSockName(
            ProviderSocket,
            name,
            namelen,
            lpErrno);
    } //if

    POSTAPINOTIFY(( DTCODE_WSPGetSockName,
                    &ReturnValue,
                    gLibraryName,
                    &s,
                    &name,
                    &namelen,
                    &lpErrno));

    return(ReturnValue);
}

 INT
WSPAPI
WSPGetSockOpt(
    IN SOCKET s,
    IN INT level,
    IN INT optname,
    OUT char FAR *optval,
    OUT INT FAR *optlen,
    OUT INT FAR *lpErrno
    )
/*++
Routine Description:

    Retrieve a socket option.

Arguments:

    s       - A descriptor identifying a socket.

    level   - The  level  at  which the option is defined; the supported levels
              include SOL_SOCKET (See annex for more protocol-specific levels.)

    optname - The socket option for which the value is to be retrieved.

    optval  - A  pointer  to  the  buffer  in which the value for the requested
              option is to be returned.

    optlen  - A pointer to the size of the optval buffer.

    lpErrno - A pointer to the error code.

Return Value:

    If  no  error  occurs,  WSPGetSockOpt()  returns  0.  Otherwise, a value of
    SOCKET_ERROR  is  returned,  and  a  specific  error  code  is available in
    lpErrno.

--*/
{
    INT ReturnValue;
    PDSOCKET     Socket;
    PDPROVIDER   Provider;
    SOCKET       ProviderSocket;

    if (PREAPINOTIFY(( DTCODE_WSPGetSockOpt,
                       &ReturnValue,
                       gLibraryName,
                       &s,
                       &level,
                       &optname,
                       &optval,
                       &optlen,
                       &lpErrno)) ) {
        return(ReturnValue);
    }

    //
    // Get our DSOCKET object
    //
    ReturnValue = gUpCallTable.lpWPUQuerySocketHandleContext(
        s,
        (DWORD*)&Socket,
        lpErrno);
    if (SOCKET_ERROR != ReturnValue){
        Provider = Socket->GetDProvider();
        ProviderSocket = Socket->GetProviderSocket();

        ReturnValue = Provider->WSPGetSockOpt(
            ProviderSocket,
            level,
            optname,
            optval,
            optlen,
            lpErrno);
    } //if

    POSTAPINOTIFY(( DTCODE_WSPGetSockOpt,
                    &ReturnValue,
                    gLibraryName,
                    &s,
                    &level,
                    &optname,
                    &optval,
                    &optlen,
                    &lpErrno));

    return(ReturnValue);
}

INT
WSPAPI
WSPIoctl(
    IN SOCKET s,
    IN DWORD dwIoControlCode,
    IN LPVOID lpvInBuffer,
    IN DWORD cbInBuffer,
    IN LPVOID lpvOutBuffer,
    IN DWORD cbOutBuffer,
    IN LPDWORD lpcbBytesReturned,
    IN LPWSAOVERLAPPED lpOverlapped,
    IN LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine,
    IN LPWSATHREADID lpThreadId,
    OUT INT FAR *lpErrno
    )
/*++
Routine Description:

    Control the mode of a socket.

Arguments:

    s                   - Handle to a socket

    dwIoControlCode     - Control code of operation to perform

    lpvInBuffer         - Address of input buffer

    cbInBuffer          - Size of input buffer

    lpvOutBuffer        - Address of output buffer

    cbOutBuffer         - Size of output buffer

    lpcbBytesReturned   - A pointer to the size of output buffer's contents.

    lpOverlapped        - Address of WSAOVERLAPPED structure

    lpCompletionRoutine - A  pointer  to the completion routine called when the
                          operation has been completed.

    lpThreadId          - A  pointer to a thread ID structure to be used by the
                          provider

    lpErrno             - A pointer to the error code.

Return Value:

    If  no error occurs and the operation has completed immediately, WSPIoctl()
    returns  0.   Note  that in this case the completion routine, if specified,
    will  have  already  been  queued.   Otherwise, a value of SOCKET_ERROR is
    returned, and  a  specific  error code is available in lpErrno.  The error
    code  WSA_IO_PENDING  indicates  that  an  overlapped  operation  has  been
    successfully  initiated  and  that  conpletion will be indicated at a later
    time.   Any  other  error  code  indicates that no overlapped operation was
    initiated and no completion indication will occur.

--*/
{
    INT ReturnValue;
    PDSOCKET     Socket;
    PDPROVIDER   Provider;
    SOCKET       ProviderSocket;

    if (PREAPINOTIFY(( DTCODE_WSPIoctl,
                       &ReturnValue,
                       gLibraryName,
                       &s,
                       &dwIoControlCode,
                       &lpvInBuffer,
                       &cbInBuffer,
                       &lpvOutBuffer,
                       &cbOutBuffer,
                       &lpcbBytesReturned,
                       &lpOverlapped,
                       &lpCompletionRoutine,
                       &lpThreadId,
                       &lpErrno)) ) {
        return(ReturnValue);
    }

    //
    // Get our DSOCKET object
    //
    ReturnValue = gUpCallTable.lpWPUQuerySocketHandleContext(
        s,
        (DWORD*)&Socket,
        lpErrno);
    if (SOCKET_ERROR != ReturnValue){
        if (lpOverlapped){
            if (lpCompletionRoutine
                    || (lpOverlapped->hEvent==NULL)
                    || ResetEvent ((HANDLE)((DWORD)lpOverlapped->hEvent&0xFFFFFFFE))) {
                PDWORKERTHREAD  Thread = GetWorkerThread ();
                if (Thread!=NULL) {
                    // Setup the user overlapped struct
                    lpOverlapped->Internal = WSS_OPERATION_IN_PROGRESS;
                    lpOverlapped->InternalHigh = 0;
                    ReturnValue = Thread->QueueOverlappedIoctl(
                        Socket,
                        dwIoControlCode,
                        lpvInBuffer,
                        cbInBuffer,
                        lpvOutBuffer,
                        cbOutBuffer,
                        lpcbBytesReturned,
                        lpOverlapped,
                        lpCompletionRoutine,
                        lpThreadId,
                        lpErrno);
                }
                else {
                    ReturnValue = SOCKET_ERROR;
                    *lpErrno = WSAENOBUFS;
                }
            }
            else {
                ReturnValue = SOCKET_ERROR;
                *lpErrno = WSA_INVALID_PARAMETER;
            }
        }
        else {
            Provider = Socket->GetDProvider ();
            ProviderSocket = Socket->GetProviderSocket ();

            ReturnValue = Provider->WSPIoctl(
                ProviderSocket,
                dwIoControlCode,
                lpvInBuffer,
                cbInBuffer,
                lpvOutBuffer,
                cbOutBuffer,
                lpcbBytesReturned,
                lpOverlapped,
                lpCompletionRoutine,
                lpThreadId,
                lpErrno);
if ((dwIoControlCode==SIO_GET_EXTENSION_FUNCTION_POINTER)
&& (NO_ERROR==ReturnValue)) {
ReturnValue = Provider->InterceptExtensions (
lpvInBuffer,
lpvOutBuffer,
lpErrno);
}
        }
    } //if

    POSTAPINOTIFY(( DTCODE_WSPIoctl,
                    &ReturnValue,
                    gLibraryName,
                    &s,
                    &dwIoControlCode,
                    &lpvInBuffer,
                    &cbInBuffer,
                    &lpvOutBuffer,
                    &cbOutBuffer,
                    &lpcbBytesReturned,
                    &lpOverlapped,
                    &lpCompletionRoutine,
                    &lpThreadId,
                    &lpErrno));

    return(ReturnValue);
}

SOCKET
WSPAPI
WSPJoinLeaf(
    IN SOCKET s,
    IN const struct sockaddr FAR *name,
    IN INT namelen,
    IN LPWSABUF lpCallerData,
    IN LPWSABUF lpCalleeData,
    IN LPQOS lpSQOS,
    IN LPQOS lpGQOS,
    IN DWORD dwFlags,
    OUT INT FAR *lpErrno
    )
/*++
Routine Description:

    Join  a  leaf  node  into  a multipoint session, exchange connect data, and
    specify needed quality of service based on the supplied flow specs.

Arguments:

    s            - A descriptor identifying a multipoint socket.

    name         - The name of the peer to which the socket is to be joined.

    namelen      - The length of the name.

    lpCallerData - A  pointer to the user data that is to be transferred to the
                   peer during multipoint session establishment.

    lpCalleeData - A  pointer  to  the user data that is to be transferred back
                   from the peer during multipoint session establishment.

    lpSQOS       - A  pointer  to  the  flow  specs  for socket s, one for each
                   direction.

    lpGQOS       - A  pointer  to  the  flow  specs  for  the  socket group (if
                   applicable).

    dwFlags      - Flags  to  indicate  that  the socket is acting as a sender,
                   receiver, or both.

    lpErrno      - A pointer to the error code.

Return Value:

    If no error occurs, WSPJoinLeaf() returns a value of type SOCKET which is a
    descriptor  for the newly created multipoint socket.  Otherwise,a value of
    INVALID_SOCKET  is  returned, and  a  specific  error code is available in
    lpErrno.

--*/
{
    INT ReturnValue;
    PDSOCKET     Socket, NewSocket;
    PDPROVIDER   Provider;
    SOCKET       ProviderSocket, NewProviderSocket;
DWORD ThisProviderCatalogEntryId;

    if (PREAPINOTIFY(( DTCODE_WSPJoinLeaf,
                       &ReturnValue,
                       gLibraryName,
                       &s,
                       &name,
                       &namelen,
                       &lpCallerData,
                       &lpCalleeData,
                       &lpSQOS,
                       &lpGQOS,
                       &dwFlags,
                       &lpErrno)) ) {
        return(ReturnValue);
    }

    //
    // Get our DSOCKET object
    //
    ReturnValue = gUpCallTable.lpWPUQuerySocketHandleContext(
        s,
        (DWORD*)&Socket,
        lpErrno);
    if (SOCKET_ERROR != ReturnValue){
        Provider = Socket->GetDProvider();
        ProviderSocket = Socket->GetProviderSocket();

        NewProviderSocket = Provider->WSPJoinLeaf(
            ProviderSocket,
            name,
            namelen,
            lpCallerData,
            lpCalleeData,
            lpSQOS,
            lpGQOS,
            dwFlags,

lpErrno);
if (NewProviderSocket!=INVALID_SOCKET) {
if (NewProviderSocket!=ProviderSocket) {
//
// Create a new socket object and initialize it.
NewSocket = new DSOCKET;
if (NewSocket){
ThisProviderCatalogEntryId =
gProviderCatalog->GetLocalProvider()->GetProtocolInfo()->dwCatalogEntryId;

ReturnValue = gUpCallTable.lpWPUCreateSocketHandle(
ThisProviderCatalogEntryId,
(DWORD) NewSocket,
lpErrno);
DEBUGF( DBG_TRACE,
("JoinLeaf Returning Socket %X\n", ReturnValue));

if (INVALID_SOCKET != ReturnValue){
NewSocket->Initialize(
Provider,
NewProviderSocket,
Socket->GetCatalogEntryId(),
ReturnValue);

} //if
else{
delete NewSocket;
Provider->WSPCloseSocket (NewProviderSocket, lpErrno);
} //else
}
}
else
ReturnValue = s;
}
else
ReturnValue = INVALID_SOCKET;
    } //if

    POSTAPINOTIFY(( DTCODE_WSPJoinLeaf,
                    &ReturnValue,
                    gLibraryName,
                    &s,
                    &name,
                    &namelen,
                    &lpCallerData,
                    &lpCalleeData,
                    &lpSQOS,
                    &lpGQOS,
                    &dwFlags,
                    &lpErrno));

    return(ReturnValue);

}

INT
WSPAPI
WSPListen(
    IN SOCKET s,
    IN INT backlog,
    OUT INT FAR *lpErrno
    )
/*++
Routine Description:

    Establish a socket to listen for incoming connections.

Arguments:

    s       - A descriptor identifying a bound,
unconnected socket.

    backlog - The  maximum length to which the queue of pending connections may
              grow.   If  this  value  is  SOMAXCONN,
then the service provider
              should set the backlog to a maximum "reasonable" value.

    lpErrno - A pointer to the error code.

Return Value:

    If  no  error  occurs, WSPListen()  returns  0.   Otherwise, a  value  of
    SOCKET_ERROR  is  returned, and  a  specific  error  code  is available in
    lpErrno.

--*/
{
    INT ReturnValue;
    PDSOCKET     Socket;
    PDPROVIDER   Provider;
    SOCKET       ProviderSocket;

    if (PREAPINOTIFY(( DTCODE_WSPListen,
                       &ReturnValue,
                       gLibraryName,
                       &s,
                       &backlog,
                       &lpErrno)) ) {
        return(ReturnValue);
    }

    //
    // Get our DSOCKET object
    //
    ReturnValue = gUpCallTable.lpWPUQuerySocketHandleContext(
        s,
        (DWORD*)&Socket,
        lpErrno);
    if (SOCKET_ERROR != ReturnValue){
        Provider = Socket->GetDProvider();
        ProviderSocket = Socket->GetProviderSocket();

        ReturnValue = Provider->WSPListen(
            ProviderSocket,
            backlog,
            lpErrno);
    } //if

    POSTAPINOTIFY(( DTCODE_WSPListen,
                    &ReturnValue,
                    gLibraryName,
                    &s,
                    &backlog,
                    &lpErrno));

    return(ReturnValue);
}

INT
WSPAPI
WSPRecv(
    IN SOCKET s,
    IN LPWSABUF lpBuffers,
    IN DWORD dwBufferCount,
    IN LPDWORD lpNumberOfBytesRecvd,
    IN OUT LPDWORD lpFlags,
    IN LPWSAOVERLAPPED lpOverlapped,
    IN LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine,
    IN LPWSATHREADID lpThreadId,
    OUT INT FAR *lpErrno
    )
/*++
Routine Description:

    Receive data on a socket.

Arguments:

    s                    - A descriptor identifying a connected socket.

    lpBuffers            - A  pointer  to  an array of WSABUF structures.  Each
                           WSABUF  structure contains a pointer to a buffer and
                           the length of the buffer.

    dwBufferCount        - The  number  of  WSABUF  structures in the lpBuffers
                           array.

    lpNumberOfBytesRecvd - A  pointer  to  the number of bytes received by this
                           call.

    lpFlags              - A pointer to flags.

    lpOverlapped         - A pointer to a WSAOVERLAPPED structure.

    lpCompletionRoutine  - A  pointer to the completion routine called when the
                           receive operation has been completed.

    lpThreadId           - A pointer to a thread ID structure to be used by the
                           provider in a subsequent call to WPUQueueApc().

    lpErrno              - A pointer to the error code.

Return Value:

    If  no  error  occurs  and the receive operation has completed immediately,
    WSPRecv() returns the number of bytes received.  If the connection has been
    closed, it  returns  0.  Note that in this case the completion routine, if
    specified,  will   have  already  been  queued.   Otherwise, a  value  of
    SOCKET_ERROR  is  returned, and  a  specific  error  code  is available in
    lpErrno.   The  error  code WSA_IO_PENDING indicates that the overlapped an
    operation  has  been  successfully  initiated  and  that completion will be
    indicated  at  a  later  time.   Any  other  error  code  indicates that no
    overlapped  operations  was  initiated  and  no  completion indication will
    occur.
--*/
{
    INT ReturnValue;
    PDSOCKET         Socket;
    PDPROVIDER       Provider;
    SOCKET           ProviderSocket;
    LPWSABUF         InternalBuffers;
    DWORD            InternalBufferCount;

    if (PREAPINOTIFY(( DTCODE_WSPRecv,
                       &ReturnValue,
                       gLibraryName,
                       &s,
                       &lpBuffers,
                       &dwBufferCount,
                       &lpNumberOfBytesRecvd,
                       &lpFlags,
                       &lpOverlapped,
                       &lpCompletionRoutine,
                       &lpThreadId,
                       &lpErrno)) ) {
        return(ReturnValue);
    }

    //
    // Get our DSOCKET object
    //
    ReturnValue = gUpCallTable.lpWPUQuerySocketHandleContext(
        s,
        (DWORD*)&Socket,
        lpErrno);
    if (SOCKET_ERROR != ReturnValue){

        // Get Internal buffers to send down to the lower provider.
        ReturnValue = gBufferManager->AllocBuffer(
            lpBuffers,
            dwBufferCount,
            &InternalBuffers,
            &InternalBufferCount);

        if (NO_ERROR == ReturnValue){
            //Is this a overlapped operation.
            if (lpOverlapped){
                if (lpCompletionRoutine
                        || (lpOverlapped->hEvent==NULL)
                        || ResetEvent ((HANDLE)((DWORD)lpOverlapped->hEvent&0xFFFFFFFE))) {
                    PDWORKERTHREAD  Thread = GetWorkerThread ();
                    if (Thread!=NULL) {
                        // Setup the user overlapped struct
                        lpOverlapped->Internal = WSS_OPERATION_IN_PROGRESS;
                        lpOverlapped->InternalHigh = 0;
                        ReturnValue = Thread->QueueOverlappedRecv(
                            Socket,
                            lpBuffers,
                            dwBufferCount,
                            lpNumberOfBytesRecvd,
                            lpFlags,
                            lpOverlapped,
                            lpCompletionRoutine,
                            lpThreadId,
                            InternalBuffers,
                            InternalBufferCount,
                            lpErrno);
                    }
                    else {
                        ReturnValue = SOCKET_ERROR;
                        *lpErrno = WSAENOBUFS;
                    }
                }
                else {
                    ReturnValue = SOCKET_ERROR;
                    *lpErrno = WSA_INVALID_PARAMETER;
                }
            } //if
            else{
                Provider = Socket->GetDProvider();
                ProviderSocket = Socket->GetProviderSocket();

SetBlockingProvider (Provider);
                ReturnValue = Provider->WSPRecv(
                    ProviderSocket,
                    InternalBuffers,
                    InternalBufferCount,
                    lpNumberOfBytesRecvd,
                    lpFlags,
                    lpOverlapped,
                    lpCompletionRoutine,
                    lpThreadId,
                    lpErrno);
SetBlockingProvider (NULL);
if (ReturnValue==NO_ERROR) {
gBufferManager->CopyBuffer (InternalBuffers,
InternalBufferCount,
0,
*lpNumberOfBytesRecvd,
lpBuffers,
dwBufferCount,
0);
}
gBufferManager->FreeBuffer (InternalBuffers, InternalBufferCount);
            } //else
        } //if
    } //if

    POSTAPINOTIFY(( DTCODE_WSPRecv,
                    &ReturnValue,
                    gLibraryName,
                    &s,
                    &lpBuffers,
                    &dwBufferCount,
                    &lpNumberOfBytesRecvd,
                    &lpFlags,
                    &lpOverlapped,
                    &lpCompletionRoutine,
                    &lpThreadId,
                    &lpErrno));

    return(ReturnValue);
}

 INT
WSPAPI
WSPRecvDisconnect(
    IN SOCKET s,
    IN LPWSABUF lpInboundDisconnectData,
    OUT INT FAR *lpErrno
    )
/*++
Routine Description:

    Terminate  reception  on  a socket, and retrieve the disconnect data if the
    socket is connection-oriented.

Arguments:

    s                       - A descriptor identifying a socket.

    lpInboundDisconnectData - A  pointer to a buffer into which disconnect data
                              is to be copied.

    lpErrno                 - A pointer to the error code.

Return Value:

    If  no error occurs, WSPRecvDisconnect() returns NO_ERROR.  Otherwise,
    a value of SOCKET_ERROR is returned, and a specific error code is available
    in lpErrno.

--*/
{
    INT ReturnValue;
    PDSOCKET     Socket;
    PDPROVIDER   Provider;
    SOCKET       ProviderSocket;

    if (PREAPINOTIFY(( DTCODE_WSPRecvDisconnect,
                       &ReturnValue,
                       gLibraryName,
                       &s,
                       &lpInboundDisconnectData,
                       &lpErrno)) ) {
        return(ReturnValue);
    }

    //
    // Get our DSOCKET object
    //
    ReturnValue = gUpCallTable.lpWPUQuerySocketHandleContext(
        s,
        (DWORD*)&Socket,
        lpErrno);

    if (SOCKET_ERROR != ReturnValue){
        Provider = Socket->GetDProvider();
        ProviderSocket = Socket->GetProviderSocket();

        ReturnValue = Provider->WSPRecvDisconnect(
            ProviderSocket,
            lpInboundDisconnectData,
            lpErrno);
    } //if

    POSTAPINOTIFY(( DTCODE_WSPRecvDisconnect,
                    &ReturnValue,
                    gLibraryName,
                    &s,
                    &lpInboundDisconnectData,
                    &lpErrno));

    return(ReturnValue);

}

INT
WSPAPI
WSPRecvFrom(
    IN  SOCKET s,
    IN  LPWSABUF lpBuffers,
    IN  DWORD dwBufferCount,
    IN  LPDWORD lpNumberOfBytesRecvd,
    IN  OUT LPDWORD lpFlags,
    OUT struct sockaddr FAR *  lpFrom,
    IN  LPINT lpFromlen,
    IN  LPWSAOVERLAPPED lpOverlapped,
    IN  LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine,
    IN LPWSATHREADID lpThreadId,
    OUT INT FAR *lpErrno
    )
/*++
Routine Description:

    Receive a datagram and store the source address.

Arguments:

    s                    - A descriptor identifying a socket.

    lpBuffers            - A  pointer  to  an array of WSABUF structures.  Each
                           WSABUF  structure contains a pointer to a buffer and
                           the length of the buffer.

    dwBufferCount        - The  number  of  WSABUF  structures in the lpBuffers
                           array.

    lpNumberOfBytesRecvd - A  pointer  to  the number of bytes received by this
                           call.

    lpFlags              - A pointer to flags.

    lpFrom               - An  optional pointer to a buffer which will hold the
                           source address upon the completion of the overlapped
                           operation.

    lpFromlen            - A  pointer  to the size of the from buffer, required
                           only if lpFrom is specified.

    lpOverlapped         - A pointer to a WSAOVERLAPPED structure.

    CompletionRoutine    - A  pointer to the completion routine called when the
                           receive operation has been completed.

    lpThreadId           - A pointer to a thread ID structure to be used by the
                           provider in a subsequent call to WPUQueueApc().

    lpErrno              - A pointer to the error code.

Return Value:

    If  no  error  occurs  and the receive operation has completed immediately,
    WSPRecvFrom()  returns the number of bytes received.  If the connection has
    been  closed, it returns 0.  Note that in this case the completion routine,
    if  specified  will  have  already  been  queued.   Otherwise,  a  value of
    SOCKET_ERROR  is  returned, and  a  specific  error  code  is available in
    lpErrno.   The  error  code  WSA_IO_PENDING  indicates  that the overlapped
    operation  has  been  successfully  initiated  and  that completion will be
    indicated  at  a  later  time.   Any  other  error  code  indicates that no
    overlapped  operations  was  initiated  and  no  completion indication will
    occur.

--*/
{
    INT              ReturnValue;
    PDSOCKET         Socket;
    PDPROVIDER       Provider;
    SOCKET           ProviderSocket;
    LPWSABUF         InternalBuffers;
    DWORD            InternalBufferCount;

    if (PREAPINOTIFY(( DTCODE_WSPRecvFrom,
                       &ReturnValue,
                       gLibraryName,
                       &s,
                       &lpBuffers,
                       &dwBufferCount,
                       &lpNumberOfBytesRecvd,
                       &lpFlags,
                       &lpFrom,
                       &lpFromlen,
                       &lpOverlapped,
                       &lpCompletionRoutine,
                       &lpThreadId,
                       &lpErrno)) ) {
        return(ReturnValue);
    }

    //
    // Get our DSOCKET object
    //
    ReturnValue = gUpCallTable.lpWPUQuerySocketHandleContext(
        s,
        (DWORD*)&Socket,
        lpErrno);
    if (SOCKET_ERROR != ReturnValue){

        // Get Internal buffers to send down to the lower provider.
        ReturnValue = gBufferManager->AllocBuffer(
            lpBuffers,
            dwBufferCount,
            &InternalBuffers,
            &InternalBufferCount);

        if (NO_ERROR == ReturnValue){
            //Is this a overlapped operation.
            if (lpOverlapped){
                if (lpCompletionRoutine
                        || (lpOverlapped->hEvent==NULL)
                        || ResetEvent ((HANDLE)((DWORD)lpOverlapped->hEvent&0xFFFFFFFE))) {
                    PDWORKERTHREAD  Thread = GetWorkerThread ();
                    if (Thread!=NULL) {
                        // Setup the user overlapped struct
                        lpOverlapped->Internal = WSS_OPERATION_IN_PROGRESS;
                        lpOverlapped->InternalHigh = 0;
                        ReturnValue = Thread->QueueOverlappedRecvFrom(
                            Socket,
                            InternalBuffers,
                            InternalBufferCount,
                            lpNumberOfBytesRecvd,
                            lpFlags,
                            lpFrom,
                            lpFromlen,
                            lpOverlapped,
                            lpCompletionRoutine,
                            lpThreadId,
                            InternalBuffers,
                            InternalBufferCount,
                            lpErrno);
                    }
                    else {
                        ReturnValue = SOCKET_ERROR;
                        *lpErrno = WSAENOBUFS;
                    }
                }
                else {
                    ReturnValue = SOCKET_ERROR;
                    *lpErrno = WSA_INVALID_PARAMETER;
                }
            } //if
            else{
                Provider = Socket->GetDProvider();
                ProviderSocket = Socket->GetProviderSocket();

SetBlockingProvider (Provider);
                ReturnValue = Provider->WSPRecvFrom(
                    ProviderSocket,
                    lpBuffers,
                    dwBufferCount,
                    lpNumberOfBytesRecvd,
                    lpFlags,
                    lpFrom,
                    lpFromlen,
                    lpOverlapped,
                    lpCompletionRoutine,
                    lpThreadId,
                    lpErrno);
SetBlockingProvider (NULL);
if (ReturnValue==NO_ERROR) {
gBufferManager->CopyBuffer (InternalBuffers,
InternalBufferCount,
0,
*lpNumberOfBytesRecvd,
lpBuffers,
dwBufferCount,
0);
}
gBufferManager->FreeBuffer (InternalBuffers, InternalBufferCount);
            } //else
        } //if
    } //if

    POSTAPINOTIFY(( DTCODE_WSPRecvFrom,
                    &ReturnValue,
                    gLibraryName,
                    &s,
                    &lpBuffers,
                    &dwBufferCount,
                    &lpNumberOfBytesRecvd,
                    &lpFlags,
                    &lpFrom,
                    &lpFromlen,
                    &lpOverlapped,
                    &lpCompletionRoutine,
                    &lpThreadId,
                    &lpErrno));

    return(ReturnValue);
}

typedef struct association
{
    SOCKET  ProviderSocket;
    SOCKET  UserSocket;
} SOCKETASSOCIATION, *PSOCKETASSOCIATION;

typedef struct
{
    UINT                AssociationCount;
    PSOCKETASSOCIATION  Associations;
} SOCKETMAP, *PSOCKETMAP;

INT
TransferUserFdSetToProviderFdSet(
    IN  fd_set * UserSet,
    OUT fd_set * ProviderSet,
    OUT PSOCKETMAP SocketMap,
    OUT LPINT    Errno)
{
    INT ReturnCode;
    UINT Index;
    PDSOCKET Socket;

    ReturnCode= NO_ERROR;
    SocketMap->AssociationCount = 0;
    SocketMap->Associations     = NULL;
    ProviderSet->fd_count       = 0;

    if (UserSet && (UserSet->fd_count > 0)){
        if (UserSet->fd_count > FD_SETSIZE){
            *Errno = WSAENOBUFS;
            return(SOCKET_ERROR);
        } //if
        SocketMap->Associations = (PSOCKETASSOCIATION)new BYTE[
            (sizeof(SOCKETASSOCIATION) * UserSet->fd_count)];
        if (SocketMap->Associations){

            for (Index=0;Index < UserSet->fd_count  ;Index++ ){

                ReturnCode = gUpCallTable.lpWPUQuerySocketHandleContext(
                    UserSet->fd_array[Index],
                    (DWORD*)&Socket,
                    Errno);
                if (NO_ERROR != ReturnCode){
                    delete(SocketMap->Associations);
                    SocketMap->Associations = NULL;
                    *Errno = WSAEINVAL;
                    break;
                } //if

                SocketMap->Associations[Index].ProviderSocket =
                    Socket->GetProviderSocket();
                SocketMap->Associations[Index].UserSocket =
                    UserSet->fd_array[Index];
                ProviderSet->fd_array[Index] =
                    SocketMap->Associations[Index].ProviderSocket;

                ProviderSet->fd_count++;
                SocketMap->AssociationCount++;
            } //for
        } //if
        else{
             ReturnCode =SOCKET_ERROR;
            *Errno = WSAENOBUFS;
        } //else
    } //if
    return(ReturnCode);
}

INT
TransferProviderFdSetToUserFdSet(
    IN  fd_set *   UserSet,
    OUT fd_set *   ProviderSet,
    IN  PSOCKETMAP SocketMap,
    OUT LPINT      Errno)
{
    INT ReturnCode;
    UINT ProviderIndex;
    UINT AssociationIndex;

    ReturnCode= NO_ERROR;

    if (UserSet) {
        UserSet->fd_count = 0;
        for (ProviderIndex = 0;
             ProviderIndex < ProviderSet->fd_count;
             ProviderIndex++){

            for (AssociationIndex =0;
                 AssociationIndex < SocketMap->AssociationCount;
                 AssociationIndex++){

                if (ProviderSet->fd_array[ProviderIndex] ==
                    SocketMap->Associations[AssociationIndex].ProviderSocket){

                    UserSet->fd_array[ProviderIndex] =
                        SocketMap->Associations[AssociationIndex].UserSocket;
                    UserSet->fd_count++;
                } //if
            } //for
        } //for
        delete SocketMap->Associations;
    } //if

    return(ReturnCode);

}

INT
WSPAPI
WSPSelect(
    IN INT nfds,
    IN OUT fd_set FAR *readfds,
    IN OUT fd_set FAR *writefds,
    IN OUT fd_set FAR *exceptfds,
    IN const struct timeval FAR *timeout,
    OUT INT FAR *lpErrno
    )
/*++
Routine Description:

    Determine the status of one or more sockets.

Arguments:

    nfds      - This  argument  is  ignored  and  included only for the sake of
                compatibility.

    readfds   - An  optional  pointer  to  a  set  of sockets to be checked for
                readability.

    writefds  - An  optional  pointer  to  a  set  of sockets to be checked for
                writability

    exceptfds - An  optional  pointer  to  a  set  of sockets to be checked for
                errors.

    timeout   - The  maximum  time  for  WSPSelect()  to  wait, or  NULL for a
                blocking operation.

    lpErrno   - A pointer to the error code.

Return Value:

    WSPSelect()  returns  the  total  number of descriptors which are ready and
    contained  in  the  fd_set  structures, 0  if  the  time limit expired, or
    SOCKET_ERROR  if an error occurred.  If the return value is SOCKET_ERROR, a
    specific error code is available in lpErrno.

--*/
{
    INT ReturnValue;
    PDPROVIDER   Provider;
    SOCKET       SocketHandle;
    BOOL         FoundSocket=FALSE;
    PDSOCKET     Socket;
    fd_set       InternalReadfds;
    fd_set       InternalWritefds;
    fd_set       InternalExceptfds;
    SOCKETMAP    ReadMap;
    SOCKETMAP    WriteMap;
    SOCKETMAP    ExceptMap;

    if (PREAPINOTIFY(( DTCODE_WSPSelect,
                       &ReturnValue,
                       gLibraryName,
                       &nfds,
                       &readfds,
                       &writefds,
                       &exceptfds,
                       &timeout,
                       &lpErrno)) ) {
        return(ReturnValue);
    }

    // Look for a socket in the three fd_sets handed in. The first
    // socket found will be used to select the service provider to
    // service this call
    if (readfds && readfds->fd_count){

        SocketHandle = readfds->fd_array[0];
        FoundSocket = TRUE;
    } //if

    if (!FoundSocket && writefds && writefds->fd_count ){

        SocketHandle = writefds->fd_array[0];
        FoundSocket = TRUE;
    } //if

    if (!FoundSocket && exceptfds && exceptfds->fd_count ){

        SocketHandle = exceptfds->fd_array[0];
        FoundSocket = TRUE;
    } //if
    if (FoundSocket){
        //
        // Get our DSOCKET object
        //
        ReturnValue = gUpCallTable.lpWPUQuerySocketHandleContext(
            SocketHandle,
            (DWORD*)&Socket,
            lpErrno);
        if (SOCKET_ERROR != ReturnValue){
            Provider = Socket->GetDProvider();

            TransferUserFdSetToProviderFdSet(
                readfds,
                &InternalReadfds,
                &ReadMap,
                lpErrno);
            TransferUserFdSetToProviderFdSet(
                writefds,
                &InternalWritefds,
                &WriteMap,
                lpErrno);
            TransferUserFdSetToProviderFdSet(
                exceptfds,
                &InternalExceptfds,
                &ExceptMap,
                lpErrno);
            if (NO_ERROR == *lpErrno){
SetBlockingProvider (Provider);
                ReturnValue = Provider->WSPSelect(
                    nfds,
                    &InternalReadfds,
                    &InternalWritefds,
                    &InternalExceptfds,
                    timeout,
                    lpErrno);
SetBlockingProvider (NULL);
                TransferProviderFdSetToUserFdSet(
                    readfds,
                    &InternalReadfds,
                    &ReadMap,
                    lpErrno);
                TransferProviderFdSetToUserFdSet(
                    writefds,
                    &InternalWritefds,
                    &WriteMap,
                    lpErrno);
                TransferProviderFdSetToUserFdSet(
                    exceptfds,
                    &InternalExceptfds,
                    &ExceptMap,
                    lpErrno);
                DEBUGF( DBG_TRACE,
                        ("Select Returns %X\n",ReturnValue));

            } //if
            else{
                DEBUGF( DBG_TRACE,
                        ("**Select failed**\n"));

                ReturnValue = SOCKET_ERROR;
            } //else
        }
    } //if
    else{
        ReturnValue = SOCKET_ERROR;
        *lpErrno    = WSAEINVAL;
    } //else/if

    POSTAPINOTIFY(( DTCODE_WSPSelect,
                    &ReturnValue,
                    gLibraryName,
                    &nfds,
                    &readfds,
                    &writefds,
                    &exceptfds,
                    &timeout,
                    &lpErrno));

    return(ReturnValue);
}

 INT
WSPAPI
WSPSend(
    IN SOCKET s,
    IN LPWSABUF lpBuffers,
    IN DWORD dwBufferCount,
    IN LPDWORD lpNumberOfBytesSent,
    IN DWORD dwFlags,
    IN LPWSAOVERLAPPED lpOverlapped,
    IN LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine,
    IN LPWSATHREADID lpThreadId,
    OUT INT FAR *lpErrno
    )
/*++
Routine Description:

    Send data on a connected socket.

Arguments:

    s                   - A descriptor identifying a connected socket.

    lpBuffers           - A  pointer  to  an  array of WSABUF structures.  Each
                          WSABUF  structure  contains a pointer to a buffer and
                          the length of the buffer.

    dwBufferCount       - The  number  of  WSABUF  structures  in the lpBuffers
                          array.

    lpNumberOfBytesSent - A pointer to the number of bytes sent by this call.

    dwFlags             - Flags.

    lpOverlapped        - A pointer to a WSAOVERLAPPED structure.

    lpCompletionRoutine - A  pointer  to the completion routine called when the
                          send operation has been completed.

    lpThreadId          - A  pointer to a thread ID structure to be used by the
                          provider in a subsequent call to WPUQueueApc().

    lpErrno             - A pointer to the error code.

Return Value:

    If  no  error  occurs  and  the  send  operation has completed immediately,
    WSPSend() returns the number of bytes received.  If the connection has been
    closed,  it  returns  0.  Note that in this case the completion routine, if
    specified, will   have  already  been  queued.   Otherwise, a  value  of
    SOCKET_ERROR  is  returned, and  a  specific  error  code  is available in
    lpErrno.   The  error  code  WSA_IO_PENDING  indicates  that the overlapped
    operation  has  been  successfully  initiated  and  that completion will be
    indicated  at  a  later  time.   Any  other  error  code  indicates that no
    overlapped operation was initiated and no completion indication will occur.

--*/
{
    INT              ReturnValue;
    PDSOCKET         Socket;
    PDPROVIDER       Provider;
    SOCKET           ProviderSocket;
    LPWSABUF         InternalBuffers;
    DWORD            InternalBufferCount;

    if (PREAPINOTIFY(( DTCODE_WSPSend,
                       &ReturnValue,
                       gLibraryName,
                       &s,
                       &lpBuffers,
                       &dwBufferCount,

&lpNumberOfBytesSent,
                       &dwFlags,
                       &lpOverlapped,
                       &lpCompletionRoutine,
                       &lpThreadId,
                       &lpErrno)) ) {
        return(ReturnValue);
    }

    //
    // Get our DSOCKET object
    //
    ReturnValue = gUpCallTable.lpWPUQuerySocketHandleContext(
        s,
        (DWORD*)&Socket,
        lpErrno);
    if (SOCKET_ERROR != ReturnValue){

        // Get Internal buffers to send down to the lower provider.
        ReturnValue = gBufferManager->AllocBuffer(
            lpBuffers,
            dwBufferCount,
            &InternalBuffers,
            &InternalBufferCount);

        if (NO_ERROR == ReturnValue){
            // Copy the user buffers
            ReturnValue = gBufferManager->CopyBuffer(
                lpBuffers,
                dwBufferCount,
0,
0xFFFFFFFF,// Max bytes to copy
                InternalBuffers,
                InternalBufferCount,
                0);
        } //if

        if (NO_ERROR == ReturnValue){
            //Is this a overlapped operation.
            if (lpOverlapped){
                if (lpCompletionRoutine
                        || (lpOverlapped->hEvent==NULL)
                        || ResetEvent ((HANDLE)((DWORD)lpOverlapped->hEvent&0xFFFFFFFE))) {
                    PDWORKERTHREAD  Thread = GetWorkerThread ();
                    if (Thread!=NULL) {
                        // Setup the user overlapped struct
                        lpOverlapped->Internal = WSS_OPERATION_IN_PROGRESS;
                        lpOverlapped->InternalHigh = 0;
                        ReturnValue = Thread->QueueOverlappedSend(
                                Socket,
                                InternalBuffers,
                                InternalBufferCount,
                                lpNumberOfBytesSent,
                                dwFlags,
                                lpOverlapped,
                                lpCompletionRoutine,
                                lpThreadId,
                                lpErrno);
                    }
                    else {
                        ReturnValue = SOCKET_ERROR;
                        *lpErrno = WSAENOBUFS;
                    }
                }
                else {
                    ReturnValue = SOCKET_ERROR;
                    *lpErrno = WSA_INVALID_PARAMETER;
                }

            } //if
            else{
                Provider = Socket->GetDProvider();
                ProviderSocket = Socket->GetProviderSocket();

SetBlockingProvider (Provider);
                ReturnValue = Provider->WSPSend(
                    ProviderSocket,
                    InternalBuffers,
                    InternalBufferCount,
                    lpNumberOfBytesSent,
                    dwFlags,
                    lpOverlapped,
                    lpCompletionRoutine,
                    lpThreadId,
                    lpErrno);
SetBlockingProvider (NULL);
gBufferManager->FreeBuffer (InternalBuffers, InternalBufferCount);
            } //else
        } //if
    } //if
    POSTAPINOTIFY(( DTCODE_WSPSend,
                    &ReturnValue,
                    gLibraryName,
                    &s,
                    &lpBuffers,
                    &dwBufferCount,
                    &lpNumberOfBytesSent,
                    &dwFlags,
                    &lpOverlapped,
                    &lpCompletionRoutine,
                    &lpThreadId,
                    &lpErrno));

    return(ReturnValue);
}

 INT
WSPAPI
WSPSendDisconnect(
    IN SOCKET s,
    IN LPWSABUF lpOutboundDisconnectData,
    OUT INT FAR *lpErrno
    )
/*++
Routine Description:

    Initiate  termination  of the connection for the socket and send disconnect
    data.

Arguments:

    s                        - A descriptor identifying a socket.

    lpOutboundDisconnectData - A pointer to the outgoing disconnect data.

    lpErrno                  - A pointer to the error code.

Return Value:

    If  no  error occurs, WSPSendDisconnect() returns 0.  Otherwise, a value of
    SOCKET_ERROR  is  returned, and  a  specific  error  code  is available in
    lpErrno.

--*/
{
    INT ReturnValue;
    PDSOCKET     Socket;
    PDPROVIDER   Provider;
    SOCKET       ProviderSocket;

    if (PREAPINOTIFY(( DTCODE_WSPSendDisconnect,
                       &ReturnValue,
                       gLibraryName,
                       &s,
                       &lpOutboundDisconnectData,
                       &lpErrno)) ) {
        return(ReturnValue);
    }

    //
    // Get our DSOCKET object
    //
    ReturnValue = gUpCallTable.lpWPUQuerySocketHandleContext(
        s,
        (DWORD*)&Socket,
        lpErrno);
    if (SOCKET_ERROR != ReturnValue){
        Provider = Socket->GetDProvider();
        ProviderSocket = Socket->GetProviderSocket();

        ReturnValue = Provider->WSPSendDisconnect(
            ProviderSocket,
            lpOutboundDisconnectData,
            lpErrno);
    } //if

    POSTAPINOTIFY(( DTCODE_WSPSendDisconnect,
                    &ReturnValue,
                    gLibraryName,
                    &s,
                    &lpOutboundDisconnectData,
                    &lpErrno));

    return(ReturnValue);

}

INT
WSPAPI
WSPSendTo(
    IN SOCKET s,
    IN LPWSABUF lpBuffers,
    IN DWORD dwBufferCount,
    IN LPDWORD lpNumberOfBytesSent,
    IN DWORD dwFlags,
    IN const struct sockaddr FAR *  lpTo,
    IN INT iTolen,
    IN LPWSAOVERLAPPED lpOverlapped,
    IN LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine,
    IN LPWSATHREADID lpThreadId,
    OUT INT FAR *lpErrno
    )
/*++
Routine Description:

    Send data to a specific destination using overlapped I/O.

Arguments:

    s                   - A descriptor identifying a socket.

    lpBuffers           - A  pointer  to  an  array of WSABUF structures.  Each
                          WSABUF  structure  contains a pointer to a buffer and
                          the length of the buffer.

    dwBufferCount       - The  number  of  WSABUF  structures  in the lpBuffers
                          array.

    lpNumberOfBytesSent - A pointer to the number of bytes sent by this call.

    dwFlags             - Flags.

    lpTo                - An  optional  pointer  to  the  address of the target
                          socket.

    iTolen              - The size of the address in lpTo.

    lpOverlapped        - A pointer to a WSAOVERLAPPED structure.

    lpCompletionRoutine - A  pointer  to the completion routine called when the
                          send operation has been completed.

    lpThreadId          - A  pointer to a thread ID structure to be used by the
                          provider in a subsequent call to WPUQueueApc().

    lpErrno             - A pointer to the error code.

Return Value:

    If  no  error  occurs  and the receive operation has completed immediately,
    WSPSendTo()  returns  the  number of bytes received.  If the connection has
    been  closed,it returns 0.  Note that in this case the completion routine,
    if  specified, will  have  already  been  queued.   Otherwise, a value of
    SOCKET_ERROR  is  returned, and  a  specific  error  code  is available in
    lpErrno.   The  error  code  WSA_IO_PENDING  indicates  that the overlapped
    operation  has  been  successfully  initiated  and  that completion will be
    indicated  at  a  later  time.   Any  other  error  code  indicates that no
    overlapped operation was initiated and no completion indication will occur.

--*/

{
    INT ReturnValue;
    PDSOCKET     Socket;
    PDPROVIDER   Provider;
    SOCKET       ProviderSocket;
    LPWSABUF     InternalBuffers;
    DWORD        InternalBufferCount;

    if (PREAPINOTIFY(( DTCODE_WSPSendTo,
                       &ReturnValue,
                       gLibraryName,
                       &s,
                       &lpBuffers,
                       &lpNumberOfBytesSent,
                       &dwFlags,
                       &lpTo,
                       &iTolen,
                       &lpOverlapped,
                       &lpCompletionRoutine,
                       &lpThreadId,
                       &lpErrno)) ) {
        return(ReturnValue);
    }

    //
    // Get our DSOCKET object
    //
    ReturnValue = gUpCallTable.lpWPUQuerySocketHandleContext(
        s,
        (DWORD*)&Socket,
        lpErrno);
    if (SOCKET_ERROR != ReturnValue){
        // Get Internal buffers to send down to the lower provider.
        ReturnValue = gBufferManager->AllocBuffer(
            lpBuffers,
            dwBufferCount,
            &InternalBuffers,
            &InternalBufferCount);

        if (NO_ERROR == ReturnValue){
            // Copy the user buffers
            ReturnValue = gBufferManager->CopyBuffer(
                lpBuffers,
                dwBufferCount,
0,
0xFFFFFFFF,// Max bytes to copy
                InternalBuffers,
                InternalBufferCount,
                0);
        } //if

        if (NO_ERROR == ReturnValue){
            //Is this a overlapped operation.
            if (lpOverlapped){
                if (lpCompletionRoutine
                        || (lpOverlapped->hEvent==NULL)
                        || ResetEvent ((HANDLE)((DWORD)lpOverlapped->hEvent&0xFFFFFFFE))) {
                    PDWORKERTHREAD  Thread = GetWorkerThread ();
                    if (Thread!=NULL) {
                        // Setup the user overlapped struct
                        lpOverlapped->Internal = WSS_OPERATION_IN_PROGRESS;
                        lpOverlapped->InternalHigh = 0;
                        ReturnValue = Thread->QueueOverlappedSendTo(
                            Socket,
                            InternalBuffers,
                            InternalBufferCount,
                            lpNumberOfBytesSent,
                            dwFlags,
                            lpTo,
                            iTolen,
                            lpOverlapped,
                            lpCompletionRoutine,
                            lpThreadId,
                            lpErrno);
                    }
                    else {
                        ReturnValue = SOCKET_ERROR;
                        *lpErrno = WSAENOBUFS;
                    }
                }
                else {
                    ReturnValue = SOCKET_ERROR;
                    *lpErrno = WSA_INVALID_PARAMETER;
                }
            } //if
            else{
                Provider = Socket->GetDProvider();
                ProviderSocket = Socket->GetProviderSocket();

SetBlockingProvider (Provider);
                ReturnValue = Provider->WSPSendTo(
                    ProviderSocket,
                    lpBuffers,
                    dwBufferCount,
                    lpNumberOfBytesSent,
                    dwFlags,
                    lpTo,
                    iTolen,
                    lpOverlapped,
                    lpCompletionRoutine,
                    lpThreadId,
                    lpErrno);
SetBlockingProvider (NULL);
gBufferManager->FreeBuffer (InternalBuffers, InternalBufferCount);
            } //else
        } //if
    } //if

    POSTAPINOTIFY(( DTCODE_WSPSendTo,
                    &ReturnValue,
                    gLibraryName,
                    &s,
                    &lpBuffers,
                    &lpNumberOfBytesSent,
                    &dwFlags,
                    &lpTo,
                    &iTolen,
                    &lpOverlapped,
                    &lpCompletionRoutine,
                    &lpThreadId,
                    &lpErrno));

    return(ReturnValue);
}

 INT
WSPAPI
WSPSetSockOpt(
    IN SOCKET s,
    IN INT level,
    IN INT optname,
    IN const char FAR *optval,
    IN INT optlen,
    OUT INT FAR *lpErrno
    )
/*++
Routine Description:

    Set a socket option.

Arguments:

    s       - A descriptor identifying a socket.

    level   - The  level  at  which the option is defined; the supported levels
              include   SOL_SOCKET.   (See  annex  for  more  protocol-specific
              levels.)

    optname - The socket option for which the value is to be set.

    optval  - A  pointer  to  the  buffer  in which the value for the requested
              option is supplied.

    optlen  - The size of the optval buffer.

    lpErrno - A pointer to the error code.

Return Value:

    If  no  error  occurs, WSPSetSockOpt()  returns  0.  Otherwise, a value of
    SOCKET_ERROR  is  returned, and  a  specific  error  code  is available in
    lpErrno.

--*/
{
    INT ReturnValue;
    PDSOCKET     Socket;
    PDPROVIDER   Provider;
    SOCKET       ProviderSocket;

    if (PREAPINOTIFY(( DTCODE_WSPSetSockOpt,
                       &ReturnValue,
                       gLibraryName,
                       &s,
                       &level,
                       &optname,
                       &optval,
                       &optlen,
                       &lpErrno)) ) {
        return(ReturnValue);
    }

    //
    // Get our DSOCKET object
    //
    ReturnValue = gUpCallTable.lpWPUQuerySocketHandleContext(
        s,
        (DWORD*)&Socket,
        lpErrno);
    if (SOCKET_ERROR != ReturnValue){
        Provider = Socket->GetDProvider();
        ProviderSocket = Socket->GetProviderSocket();

        ReturnValue = Provider->WSPSetSockOpt(
            ProviderSocket,
            level,
            optname,
            optval,
            optlen,
            lpErrno);
    } //if

    POSTAPINOTIFY(( DTCODE_WSPSetSockOpt,
                    &ReturnValue,
                    gLibraryName,
                    &s,
                    &level,
                    &optname,
                    &optval,
                    &optlen,
                    &lpErrno));

    return(ReturnValue);

}

INT
WSPAPI
WSPShutdown(
    IN SOCKET s,
    IN INT how,
    OUT INT FAR *lpErrno
    )
/*++
Routine Description:

    Disable sends and/or receives on a socket.

Arguments:

    s       - A descriptor identifying a socket.

    how     - A  flag  that describes what types of operation will no longer be
              allowed.

    lpErrno - A pointer to the error code.

Return Value:

    If  no  error  occurs, WSPShutdown()  returns  0.   Otherwise, a value of
    SOCKET_ERROR  is  returned, and  a  specific  error  code  is available in
    lpErrno.

--*/
{
    INT ReturnValue;
    PDSOCKET     Socket;
    PDPROVIDER   Provider;
    SOCKET       ProviderSocket;

    if (PREAPINOTIFY(( DTCODE_WSPShutdown,
                       &ReturnValue,
                       gLibraryName,
                       &s,
                       &how,
                       &lpErrno)) ) {
        return(ReturnValue);
    }

    //
    // Get our DSOCKET object
    //
    ReturnValue = gUpCallTable.lpWPUQuerySocketHandleContext(
        s,
        (DWORD*)&Socket,
        lpErrno);
    if (SOCKET_ERROR != ReturnValue){
        Provider = Socket->GetDProvider();
        ProviderSocket = Socket->GetProviderSocket();
        DEBUGF( DBG_TRACE,
                ("Shutdown socket %X\n",s));

        ReturnValue = Provider->WSPShutdown(
            ProviderSocket,
            how,
            lpErrno);
    } //if

    POSTAPINOTIFY(( DTCODE_WSPShutdown,
                    &ReturnValue,
                    gLibraryName,
                    &s,
                    &how,
                    &lpErrno));

    return(ReturnValue);
}

SOCKET
WSPAPI
WSPSocket(
    IN int af,
    IN int type,
    IN int protocol,
    IN LPWSAPROTOCOL_INFOW lpProtocolInfo,
    IN GROUP g,
    IN DWORD dwFlags,
    OUT INT FAR *lpErrno
    )
/*++
Routine Description:

    Initialize  internal  data  and  prepare sockets for usage.  Must be called
    before any other socket routine.

Arguments:

    lpProtocolInfo - Supplies  a pointer to a WSAPROTOCOL_INFOA struct that
                     defines  the characteristics of the socket to be created.

    g              - Supplies  the identifier of the socket group which the new
                     socket is to join.

    dwFlags        - Supplies the socket attribute specification.

    lpErrno        - Returns the error code

Return Value:

    WSPSocket() returns zero if successful.  Otherwise it returns an error code
    as outlined in the SPI.

--*/
{
    INT                 ReturnValue;
    INT                 NextProviderSocket;
    PDPROVIDER          Provider;
    PDSOCKET            Socket;
DWORDThisProviderCatalogEntryId;
PPROTO_CATALOG_ITEMBaseProviderCatalogEntry;

    // Debug/Trace stuff
    if (PREAPINOTIFY(( DTCODE_WSPSocket,
                       &ReturnValue,
                       gLibraryName,
                       &af,
                       &type,
                       &protocol,
                       &lpProtocolInfo,
                       &g,
                       &dwFlags,
                       &lpErrno)) ) {
        return(ReturnValue);
    }

    //
    // Get the catlog entry for the next provider in the chain
    //
    ReturnValue = gProviderCatalog->FindNextProviderInChain(
        lpProtocolInfo,
        &Provider,
&BaseProviderCatalogEntry);

if (NO_ERROR==ReturnValue) {
WSAPROTOCOL_INFOWProtocolInfo;
LPWSAPROTOCOL_INFOWlpPassedInfo;

// If next provider is base, get it its own protocol info
if (BaseProviderCatalogEntry) {
ProtocolInfo = *BaseProviderCatalogEntry->GetProtocolInfo();
// Carefully restore reserved field.
ProtocolInfo.dwProviderReserved = lpProtocolInfo->dwProviderReserved;
lpPassedInfo = &ProtocolInfo;
}
else
lpPassedInfo = lpProtocolInfo;

        ReturnValue = Provider->WSPSocket(
            af,
            type,
            protocol,
lpPassedInfo,
            g,
            dwFlags,
            lpErrno);

if (ReturnValue != INVALID_SOCKET){
NextProviderSocket = ReturnValue;

//
// Create our socket object
//

Socket = new DSOCKET;
if (Socket){
ThisProviderCatalogEntryId =
gProviderCatalog->GetLocalProvider()->GetProtocolInfo()->dwCatalogEntryId;

ReturnValue = gUpCallTable.lpWPUCreateSocketHandle(
ThisProviderCatalogEntryId,
(DWORD) Socket,
lpErrno);
DEBUGF( DBG_TRACE,
("Socket Returning Socket %X\n", ReturnValue));

if (INVALID_SOCKET != ReturnValue){
Socket->Initialize(
Provider,
NextProviderSocket,
lpProtocolInfo->dwCatalogEntryId,
ReturnValue);

                } //if
                else{
                    delete(Socket);
                } //else
            } //if
        } //if
        else{
            *lpErrno = ReturnValue;
            ReturnValue = INVALID_SOCKET;
        } //else
    } //if
    else{
        *lpErrno = ReturnValue;
        ReturnValue = INVALID_SOCKET;
    } //else

    // Debug/Trace stuff
    POSTAPINOTIFY(( DTCODE_WSPSocket,
                    &ReturnValue,
                    gLibraryName,
                    &af,
                    &type,
                    &protocol,
                    &lpProtocolInfo,
                    &g,
                    &dwFlags,
                    &lpErrno));

    return(ReturnValue);
}

INT
WSPAPI
WSPStringToAddress(
    IN     LPWSTR AddressString,
    IN     INT AddressFamily,
    IN     LPWSAPROTOCOL_INFOW lpProtocolInfo,
    OUT    LPSOCKADDR lpAddress,
    IN OUT LPINT lpAddressLength,
    IN OUT LPINT lpErrno )
/*++

Routine Description:

    WSPStringToAddress() converts a human-readable string to a socket address
    structure (SOCKADDR) suitable for pass to Windows Sockets routines which
    take such a structure.  If the caller wishes the translation to be done by
    a particular provider, it should supply the corresponding WSAPROTOCOL_INFO
    struct in the lpProtocolInfo parameter.

Arguments:

    AddressString - points to the zero-terminated human-readable string to
                    convert.

    AddressFamily - the address family to which the string belongs.

    lpProtocolInfo - (optional) the WSAPROTOCOL_INFO struct for a particular
                     provider.

    Address - a buffer which is filled with a single SOCKADDR structure.

    lpAddressLength - The length of the Address buffer.  Returns the size of
                      the resultant SOCKADDR structure.

Return Value:

    The return value is 0 if the operation was successful.  Otherwise the value
    SOCKET_ERROR is returned.

--*/
{
    INT                 ReturnValue;
    PDPROVIDER          Provider;
PPROTO_CATALOG_ITEMBaseProviderCatalogEntry;

    if (PREAPINOTIFY(( DTCODE_WSPAddressToString,
                       &ReturnValue,
                       gLibraryName,
                       &AddressString,
                       &AddressFamily,
                       &lpProtocolInfo,
                       &lpAddress,
                       &lpAddressLength,
                       &lpErrno)) ){
        return(ReturnValue);
    } //if

    //
    // Get the catlog entry for the next provider in the chain
    //
    ReturnValue = gProviderCatalog->FindNextProviderInChain(
        lpProtocolInfo,
        &Provider,
&BaseProviderCatalogEntry);

    if (NO_ERROR == ReturnValue){
        ReturnValue = Provider->WSPStringToAddress(
            AddressString,
            AddressFamily,
        BaseProviderCatalogEntry
? BaseProviderCatalogEntry->GetProtocolInfo()
: lpProtocolInfo,
            lpAddress,
            lpAddressLength,
            lpErrno);
    } //if

    POSTAPINOTIFY(( DTCODE_WSPAddressToString,
                    &ReturnValue,
                    gLibraryName,
                    &AddressString,
                    &AddressFamily,
                    &lpProtocolInfo,
                    &lpAddress,
                    &lpAddressLength,
                    &lpErrno));

    return(ReturnValue);

}

int
WSPAPI
WSPStartup(
    WORD wVersion,
    LPWSPDATA lpWSPData,
    LPWSAPROTOCOL_INFOW lpProtocolInfo,
    WSPUPCALLTABLE UpcallTable,
    LPWSPPROC_TABLE lpProcTable )
/*++

Routine Description:

    Initiate use of a WinSock service provider by a client.

Arguments:

    wVersionRequested - The highest version of WinSock SPI support that the
                        caller  can use. The high order byte specifies the
                        minor version (revision) number; the low-order byte
                        specifies the major version number.

    lpWSPData - A pointer to the WSPDATA data structure that is to receive
                details of the WinSock service provider.

    lpProtocolInfo - A pointer to a WSAPROTOCOL_INFO struct that defines the
                     characteristics of the desired protocol.  This is
                     especially useful when a single provider DLL is capable of
                     instantiating multiple different service providers.

    UpcallTableThe WinSock 2 DLLs upcall dispatch table.

    lpProcTable - A pointer to the table of SPI function pointers.

Return Value:

--*/
{
HMODULEhWS2_32;
    INTReturnCode;

    EnterCriticalSection(&gInitCriticalSection);

    ReturnCode = NO_ERROR;

// Save the WinSock2 upcall table
gUpCallTable = UpcallTable;

    if (gStartupCount == 0){
ReturnCode = WSAVERNOTSUPPORTED;
        // This is the first time that WSPStartup() has been called so lets get
        // ourselves ready to do bussiness

// First check for new exported function
hWS2_32 = GetModuleHandle (TEXT("ws2_32.dll"));
if (hWS2_32!=NULL) {
lpWPUCompleteOverlappedRequest =
(LPWPUCOMPLETEOVERLAPPEDREQUEST)
GetProcAddress (hWS2_32, "WPUCompleteOverlappedRequest");

if (lpWPUCompleteOverlappedRequest!=NULL) {
ReturnCode = WSASYSNOTREADY;

// Initialize the sockets
DSOCKET::DSocketClassInitialize ();

//
// Init the provider catalog
//
gProviderCatalog = new DCATALOG;
if (gProviderCatalog){
ReturnCode = gProviderCatalog->Initialize();
} //if

//
// Init the buffer manager
//
if (NO_ERROR == ReturnCode){
gBufferManager = new DBUFFERMANAGER;
if (gBufferManager){
ReturnCode = gBufferManager->Initialize();
// If we succeded incremant the startup count
if (NO_ERROR == ReturnCode){
gStartupCount++;
} //if
} //if
} //if
} //if
} // if
} // if
    LeaveCriticalSection(&gInitCriticalSection);

if (ReturnCode==NO_ERROR) {
//
// Fill in the clients proceedure table with our entry points.
//

lpProcTable->lpWSPAccept = WSPAccept;
lpProcTable->lpWSPAddressToString = WSPAddressToString;
lpProcTable->lpWSPAsyncSelect = WSPAsyncSelect;
lpProcTable->lpWSPBind = WSPBind;
lpProcTable->lpWSPCancelBlockingCall = WSPCancelBlockingCall;
lpProcTable->lpWSPCleanup = WSPCleanup;
lpProcTable->lpWSPCloseSocket = WSPCloseSocket;
lpProcTable->lpWSPConnect = WSPConnect;
lpProcTable->lpWSPDuplicateSocket = WSPDuplicateSocket;
lpProcTable->lpWSPEnumNetworkEvents = WSPEnumNetworkEvents;
lpProcTable->lpWSPEventSelect = WSPEventSelect;
lpProcTable->lpWSPGetOverlappedResult = WSPGetOverlappedResult;
lpProcTable->lpWSPGetPeerName = WSPGetPeerName;
lpProcTable->lpWSPGetSockName = WSPGetSockName;
lpProcTable->lpWSPGetSockOpt = WSPGetSockOpt;
lpProcTable->lpWSPGetQOSByName = WSPGetQOSByName;
lpProcTable->lpWSPIoctl = WSPIoctl;
lpProcTable->lpWSPJoinLeaf = WSPJoinLeaf;
lpProcTable->lpWSPListen = WSPListen;
lpProcTable->lpWSPRecv = WSPRecv;
lpProcTable->lpWSPRecvDisconnect = WSPRecvDisconnect;
lpProcTable->lpWSPRecvFrom = WSPRecvFrom;
lpProcTable->lpWSPSelect = WSPSelect;
lpProcTable->lpWSPSend = WSPSend;
lpProcTable->lpWSPSendDisconnect = WSPSendDisconnect;
lpProcTable->lpWSPSendTo = WSPSendTo;
lpProcTable->lpWSPSetSockOpt = WSPSetSockOpt;
lpProcTable->lpWSPShutdown = WSPShutdown;
lpProcTable->lpWSPSocket = WSPSocket;
lpProcTable->lpWSPStringToAddress = WSPStringToAddress;

}

    // Set the version info
    lpWSPData->wVersion = MAKEWORD(2,2);
    lpWSPData->wHighVersion = MAKEWORD(2,2);

    return(ReturnCode);
}

BOOL
PASCAL FAR
WSPTransmitFile (
IN SOCKET hSocket,
IN HANDLE hFile,
IN DWORD nNumberOfBytesToWrite,
IN DWORD nNumberOfBytesPerSend,
IN LPOVERLAPPED lpOverlapped,
IN LPTRANSMIT_FILE_BUFFERS lpTransmitBuffers,
IN DWORD dwReserved
) {
PDSOCKETSocket;
INTErrno, ReturnValue;
PDPROVIDERProvider;
SOCKETProviderSocket;
    //
    // Get our DSOCKET object
    //
    ReturnValue = gUpCallTable.lpWPUQuerySocketHandleContext(
        hSocket,
        (DWORD*)&Socket,
        &Errno);

if (SOCKET_ERROR != ReturnValue){
if (lpOverlapped) {
if ((lpOverlapped->hEvent==NULL)
|| ResetEvent ((HANDLE)((DWORD)lpOverlapped->hEvent&0xFFFFFFFE))) {
PDWORKERTHREAD  Thread = GetWorkerThread ();
if (Thread!=NULL) {
// Setup the user overlapped struct
lpOverlapped->Internal = WSS_OPERATION_IN_PROGRESS;
lpOverlapped->InternalHigh = 0;
ReturnValue = Thread->QueueOverlappedTransmitFile(
Socket,
hFile,
nNumberOfBytesToWrite,
nNumberOfBytesPerSend,
lpOverlapped,
lpTransmitBuffers,
dwReserved,
&Errno);
}
else {
ReturnValue = SOCKET_ERROR;
Errno = WSAENOBUFS;
}
}
else {
ReturnValue = SOCKET_ERROR;
Errno = WSA_INVALID_PARAMETER;
}
}
else {
Provider = Socket->GetDProvider ();
ProviderSocket = Socket->GetProviderSocket ();
ReturnValue = Provider->TransmitFile (
ProviderSocket,
hFile,
nNumberOfBytesToWrite,
nNumberOfBytesPerSend,
lpOverlapped,
lpTransmitBuffers,
dwReserved,
&Errno);
}
}
if (ReturnValue==NO_ERROR)
return TRUE;
else {
SetLastError (Errno);
return FALSE;

}
}


TRACE.CPP

/*++

     Copyright (c) 1996 Intel Corporation
     Copyright 1996 - 1998 Microsoft Corporation
     All Rights Reserved

     Permission is granted to use, copy and distribute this software and
     its documentation for any purpose and without fee, provided, that
     the above copyright notice and this statement appear in all copies.
     Intel makes no representations about the suitability of this
     software for any purpose.  This software is provided "AS IS."

     Intel specifically disclaims all warranties, express or implied,
     and all liability, including consequential and other indirect
     damages, for the use of this software, including liability for
     infringement of any proprietary rights, and including the
     warranties of merchantability and fitness for a particular purpose.
     Intel does not assume any responsibility for any errors which may
     appear in this software nor any responsibility to update it.

Module Name:

    TRACE.CPP :

Abstract:

    This module implements the traceing functions used in the winsock2 layered
    service provider example.

--*/
#define WIN32_LEAN_AND_MEAN
#include "nowarn.h"

#include <windows.h>
#include <memory.h>
#include <stdio.h>
#include <stdarg.h>
#include <io.h>
#include <malloc.h>

/* because windows.h is brain damaged and turns this one back on */
#pragma warning(disable: 4001)

#include "trace.h"

#ifdef TRACING

//
// Internal functions
//
BOOL  InitMemoryBuffers(VOID);

//
// Module local variables
//

// The buffers to use to format debug messages.
LPSTR g_CurrentMessage=NULL;
LPSTR g_PreviousMessage=NULL;

// Critcal section to keep multiple thread from walking on each others output
CRITICAL_SECTION OutputRoutine;

// Where should the output go? If it is a file what is the file name
int iTraceDestination=TRACE_TO_AUX;
char TraceFile[] = "trace.log";

// The default output level
DWORD debugLevel=0xFFFFFFFF;

VOID
PrintDebugString(
                 char *Format,
                 ...
                 )
/*++
  Routine Description:

  This routine outputs a debug messages.  Debug messages are routed
  to a file or a debug window depnding on the value of a global
  variable defined in this module

  Arguments:

  Format - A "printf()" compatable format specification.

  ... - Additional arguments to "printf()" format specification.

  Returns:

  NONE

  --*/
{
va_list ArgumentList; // argument list for varargs processing
static HANDLE OutputFile =NULL; // file descriptor for file output
static OFSTRUCT of; // open file struct for file output
static int RepeatCount=0; // Count of repeated output statements
static BOOL LogOpened = FALSE; // have we opened the file for
                                     // output
    static BOOL TraceInited = FALSE;
    DWORD  BytesWritten;

if (!TraceInited)
{
        HANDLE InitMutex;
        // Create the mutex to protect the rest of the init code
        InitMutex = CreateMutex(
            NULL,  // Use default security attributes
            FALSE, // We don't want automatic ownership
            "TraceMutextName");
        if (!InitMutex)
        {
            // We failed to create the mutex there is nothign else we
            // can do so return.  This will cause the debug output to
            // be silently lost.
            return;
        } //if

        // Wait on mutex
        WaitForSingleObject( InitMutex,
                             INFINITE);

        // Check to see if init is still needed
        if (!TraceInited)
        {
            // Init the critical section to be used to protect the
            // output portion of this routine.
            InitializeCriticalSection( &OutputRoutine );
// Note:
// DeleteCriticalSection will not be called on this critical section
// we are reling on the OS to clean this one up.  We are doing this so
// the user of this module does not have to Init/DeInit the module explicitly.

            // allocate buffers to hold debug messages
            if (InitMemoryBuffers())
            {
                TraceInited = TRUE;
            } //if
        } //if

        // Signal the mutex and delete this threads handle to the mutex
        ReleaseMutex(InitMutex);
        CloseHandle(InitMutex);
}

    // Here is where all the heavy lifting starts
    EnterCriticalSection( &OutputRoutine );

    // print the user message to our buffer
va_start(ArgumentList, Format);
vsprintf(g_CurrentMessage, Format, ArgumentList);
va_end(ArgumentList);

    // Is the current debug message the same as the last debug
    // message?  If the two messages are the same just increment the
    // count of message repeats and return.  This keeps the system
    // from being flooded with debug messages that may be being
    // generated from inside a loop.

if(lstrcmp(g_CurrentMessage, g_PreviousMessage))
{
if (iTraceDestination == TRACE_TO_FILE)
{
            if (!LogOpened)
            {
                OutputFile =
                CreateFile( TraceFile,
                            GENERIC_WRITE,     // open for writing
                            FILE_SHARE_WRITE,  // Share the file with others
                            NULL,              // default security
                            OPEN_ALWAYS,       // Use file if it exsits
                            FILE_ATTRIBUTE_NORMAL, // Use a normal file
                            NULL);             // No template

                if (OutputFile != INVALID_HANDLE_VALUE)
                {
                    LogOpened = TRUE;
                } //if
            } //if

            if (LogOpened)
            {
                if (RepeatCount > 0)
                {
                    wsprintf(g_PreviousMessage,
 "Last Message Repeated < %d > times\n",
 RepeatCount);

                    WriteFile(OutputFile,
                              g_PreviousMessage,
                              lstrlen(g_PreviousMessage),
                              &BytesWritten,
                              NULL);
                } //if

                // Write the current message to the trace file
                WriteFile(OutputFile,
                          g_CurrentMessage,
                          lstrlen(g_CurrentMessage),
                          &BytesWritten,
                          NULL);

                // Flush debug output to file
                FlushFileBuffers( TraceFile );

                //reset the repeat count
                RepeatCount =0;
            } //if
}

if( iTraceDestination == TRACE_TO_AUX)
{
if(RepeatCount > 0)
{
wsprintf(g_PreviousMessage,
 "Last Message Repeated < %d > times\n",
 RepeatCount);
OutputDebugString(g_PreviousMessage);
RepeatCount = 0;
}
            // Send message to AUX device
OutputDebugString(g_CurrentMessage);
}
        // Store off this message
lstrcpy(g_PreviousMessage, g_CurrentMessage);
}
else
{
RepeatCount++;
}
    LeaveCriticalSection( &OutputRoutine );
}

BOOL
InitMemoryBuffers(
                  VOID
                  )
/*++
  Routine Description:

  Initailizes the memory buffers used by this module.

  Arguments:

  NONE

  Returns:

  TRUE if all memory buffers are successfully created, Otherwise FALSE.

  --*/
{
    BOOL ReturnCode=FALSE;

    g_CurrentMessage = (LPSTR)malloc(TRACE_OUTPUT_BUFFER_SIZE);
    if (g_CurrentMessage)
    {
        g_PreviousMessage = (LPSTR)malloc(TRACE_OUTPUT_BUFFER_SIZE);
        if (g_PreviousMessage)
        {
            ReturnCode=TRUE;
        } //if
        else
        {
            free( g_CurrentMessage );
        } //else
    } //if
    return(ReturnCode);
}

#endif  // TRACING


TRACE.H

/*++

     Copyright c 1996 Intel Corporation
     All Rights Reserved

     Permission is granted to use, copy and distribute this software and
     its documentation for any purpose and without fee, provided, that
     the above copyright notice and this statement appear in all copies.
     Intel makes no representations about the suitability of this
     software for any purpose.  This software is provided "AS IS."

     Intel specifically disclaims all warranties, express or implied,
     and all liability, including consequential and other indirect
     damages, for the use of this software, including liability for
     infringement of any proprietary rights, and including the
     warranties of merchantability and fitness for a particular purpose.
     Intel does not assume any responsibility for any errors which may
     appear in this software nor any responsibility to update it.

Module Name:

    SPI.CPP :

Abstract:

    This file defines a macros for tracing and the function prototypes for the
    actual output functions. If the symbol TRACING is not defined  all the
    macros expands to ((void)0).

    There are three global variables that control the behavior of the tracing
    macros/functions.  debugLevel is a 32 bit bitmask that determine controls
    what level of debug messages is output. iTraceDestination controls whether
    the debug output goes to a file or to the aux device. if iTraceDestination
    == TRACE_TO_FILE szTraceFile must contain the filename.

--*/

#ifndef __TRACE_H__
#define __TRACE_H__

extern
#if defined(__cplusplus)
"C"
#endif  // defined(__cplusplus)
VOID PrintDebugString(char *format, ...);

//
// defines for where the debug output should go
//
#define TRACE_TO_FILE    0
#define TRACE_TO_AUX     1

// the size of the string buffers used as arg to wsprintf
// in trace.c
#define TRACE_OUTPUT_BUFFER_SIZE  1024

// Debug level masks
#define DBG_TRACE       0x00000001
#define DBG_WARN        0x00000002
#define DBG_ERR         0x00000004
#define DBG_MEMORY      0x00000008
#define DBG_LIST        0x00000010
#define DBG_FUNCTION    0x00000020

#if defined(TRACING)

extern
#if defined(__cplusplus)
"C"
#endif  // defined(__cplusplus)
DWORD debugLevel;

//
// This macro creates debug output depending on the debug mask "sev" and
// calls PrintDebugString output function. PrintDebugString makes the
// descision on whether the output goes into a file or to the aux device.
//
#define  DEBUGF(sev, var_args)                                                \
{                                                                             \
   if ((sev) & debugLevel) {                                                  \
      switch (sev) {                                                          \
         case DBG_TRACE:                                                      \
            PrintDebugString("-| TRACE   :: ");                               \
            PrintDebugString(" %s : %d |-\n", __FILE__, __LINE__ );           \
            PrintDebugString var_args ;                                       \
         break;                                                               \
         case DBG_WARN:                                                       \
            PrintDebugString("-| WARNING :: ");                               \
            PrintDebugString(" %s : %d |-\n", __FILE__, __LINE__ );           \
            PrintDebugString var_args ;                                       \
            break;                                                            \
         case DBG_ERR:                                                        \
            PrintDebugString("-| ERROR   :: ");                               \
            PrintDebugString(" %s : %d |-\n", __FILE__, __LINE__ );           \
            PrintDebugString var_args;                                        \
            break;                                                            \
        case DBG_MEMORY:                                                      \
            PrintDebugString("-| MEMORY  :: ");                               \
            PrintDebugString(" %s : %d |-\n", __FILE__, __LINE__ );           \
            PrintDebugString var_args ;                                       \
            break;                                                            \
        case DBG_LIST:                                                        \
            PrintDebugString("-| LIST    :: ");                               \
            PrintDebugString(" %s : %d |-\n", __FILE__, __LINE__ );\
            PrintDebugString var_args ;                                       \
            break;                                                            \
        case DBG_FUNCTION:                                                    \
            PrintDebugString var_args;                                        \
            break;                                                            \
      }                                                                       \
   }                                                                          \
}                                                                             \

#define ALLOC_LOG( pointer, size)                                             \
    DEBUGF( DBG_MEMORY ,("MEMORY %lX size %X Allocated \n",                   \
                         (pointer),(size)))                                   \

#define DEALLOC_LOG(pointer, size)                                            \
DEBUGF( DBG_MEMORY ,("MEMORY %lX size %X Deallocated \n",                     \
                         (pointer),(size)))                                   \

#define LIST_ADD_LOG(list, element)                                           \
    DEBUGF( DBG_LIST ,("LIST %lX element %lX Added \n",                       \
                       (list),(element)))                                     \

#define LIST_DEL_LOG(list, element)                                           \
    DEBUGF( DBG_LIST ,("LIST %lX element %lX Deleted \n",                     \
                       (list),(element)))                                     \

#define ENTER_FUNCTION(name)                                                  \
DEBUGF( DBG_FUNCTION,name)                                \

#define EXIT_FUNCTION(name)                                                   \
DEBUGF( DBG_FUNCTION,name)                                \

#else // TRACING
     // make sure that these are defined if tracing is turned off
#define DEBUGF(sev, va)                     ((void)0)
#define LIST_ADD_LOG(list, element)         ((void)0)
#define LIST_DEL_LOG(list, element)         ((void)0)
#define ENTER_FUNCTION(name)                ((void)0)
#define EXIT_FUNCTION(name)                 ((void)0)
#define ALLOC_LOG( pointer, size)                                             \
    DEBUGF( DBG_MEMORY ,("",                                                  \
                         (pointer),(size)))                                   \

#define DEALLOC_LOG(pointer, size)\
    DEBUGF( DBG_MEMORY ,("",\
                         (pointer),(size)))  \

#endif // TRACING

#endif // __TRACE_H__


WARNOFF.H

/*++

    Intel Corporation Proprietary Information
    Copyright (c) 1995 Intel Corporation

    This listing is supplied under the terms of a license agreement with
    Intel Corporation and may not be used, copied, nor disclosed except in
    accordance with the terms of that agreeement.

Module Name:

    warnoff.h

Abstract:

    This header contains pragmas to turn off compiler warnings that
    may be safely ignored.

--*/

/*#ifndef _WARNOFF_
  #define _WARNOFF_
  */
/* nonstandard extension 'single line comment' was used */
#pragma warning(disable: 4001)

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

// nonstandard extension used : bit field types other than int
#pragma warning(disable: 4214)

// Note: Creating precompiled header
#pragma warning(disable: 4699)

// unreferenced inline function has been removed
#pragma warning(disable: 4514)

// unreferenced formal parameter
//#pragma warning(disable: 4100)

// 'type' differs in indirection to slightly different base
// types from 'other type'
#pragma warning(disable: 4057)

// named type definition in parentheses
#pragma warning(disable: 4115)

// nonstandard extension used : benign typedef redefinition
#pragma warning(disable: 4209)

/*
#endif // _WARNOFF_
*/


NULLPDEF.H

/*++

Copyright (c) 1996 Intel Corp

Module Name:

    nullpdef.h

Abstract:

    This  file  provides  common  externally  visible  definitions for the null
    provider.

Author:

    Paul Drews (Intel) 01-30-1996

Notes:

    $Revision:   1.1  $

    $Modtime:   05 Apr 1996 12:17:12  $

Revision History:

    most-recent-revision-date email-name
        description

    01-30-1996 Paul Drews
        Created
--*/

#ifndef _NULLPDEF_
#define _NULLPDEF_

// Define  the  artificial  "address family" that identifies the null provider.
// This  is  technically  illegal,  since one is supposed to get address family
// identifiers allocated through a clearing house.  But for the purpose of this
// simple  example  it  will  suffice to define one that is some small distance
// past the currently known set.

#define AF_SPECIAL_NULL  (AF_MAX + 5)

#endif  // _NULLPDEF_


NULLPROV.CPP

/*++

Copyright 1996 - 1998 Microsoft Corporation

Copyright (c) 1996 Intel Corp

Module Name:

    nullprov.cpp

Abstract:

    This  module  provides  a  "null" provider for the purpose of demonstrating
    what  it  takes  to  get  a  service  provider  installed and started.  The
    provider pretty much does nothing but pretend to send and receive data.

--*/

//
// The Service Provider Interface was widened to Unicode in version 2.2.
//  All of the parameters that involve strings in this module all provided
//  in their "transparent" form.  They are switched to the wide character
//  version, at compile time, by the following define:
//

#define UNICODE

#include <windows.h>
#include "ws2spi.h"

#include "nullpdef.h"

// Global Upcall table with WinSock 2 DLL upcall entry points.
WSPUPCALLTABLE g_upcalls;

// Global protocol info struct of the one protocol we support.
WSAPROTOCOL_INFOA  g_protoinfo;

SOCKET WSPAPI WSPAccept(
    SOCKET s,
    struct sockaddr FAR * addr,
    LPINT addrlen,
    LPCONDITIONPROC lpfnCondition,
    DWORD dwCallbackData,
    LPINT lpErrno
    )
/*++
--*/
{
    // Not implemented
    return(INVALID_SOCKET);
} // lpWSPAccept

INT WSPAPI WSPAddressToString(
    LPSOCKADDR lpsaAddress,
    DWORD dwAddressLength,
    LPWSAPROTOCOL_INFO lpProtocolInfo,
    LPTSTR lpszAddressString,
    LPDWORD lpdwAddressStringLength,
    LPINT lpErrno
    )
/*++
--*/
{
    // Not implemented
    return(SOCKET_ERROR);
} // lpWSPAddressToString

int WSPAPI WSPAsyncSelect(
    SOCKET s,
    HWND hWnd,
    unsigned int wMsg,
    long lEvent,
    LPINT lpErrno
    )
/*++
--*/
{
    // Not implemented
    return(SOCKET_ERROR);
} // lpWSPAsyncSelect

int WSPAPI WSPBind(
    SOCKET s,
    const struct sockaddr FAR * name,
    int namelen,
    LPINT lpErrno
    )
/*++
Implementation:

    A  simple  implementation  is  supplied.  The implementation simply returns
    success without really doing anything.
--*/
{
    * lpErrno = ERROR_SUCCESS;
    return(ERROR_SUCCESS);

} // lpWSPBind

int WSPAPI WSPCancelBlockingCall(
    LPINT lpErrno
    )
/*++
Implementation:

    A  simple  implementation  is  supplied.  The implementation simply returns
    success without really doing anything.
--*/
{
    * lpErrno = ERROR_SUCCESS;
    return(ERROR_SUCCESS);

} // lpWSPCancelBlockingCall

int WSPAPI WSPCleanup(
    LPINT lpErrno
    )
/*++
Implementation

    A  simple  implementation  is  supplied.  The implementation simply returns
    success  without  really  doing  anything.   This is technically incorrect,
    since  we  should really maintain an up/down count of Startup/Cleanup pairs
    and  return  an  error  code  if  WSPCleanup  is called when there is not a
    WSPStartup outstanding.
--*/
{
    * lpErrno = ERROR_SUCCESS;
    return(ERROR_SUCCESS);

} // lpWSPCleanup

int WSPAPI WSPCloseSocket(
    SOCKET s,
    LPINT lpErrno
    )
/*++
Implementation

    Perform  an  upcall to the WinSock 2 DLL to inform it of the removal of the
    socket handle.  Return the result.
--*/
{
    int close_result;

    close_result = (* g_upcalls.lpWPUCloseSocketHandle)(
        s,         // s
        lpErrno);  // lpErrno
    return(close_result);

} // lpWSPCloseSocket

int WSPAPI WSPConnect(
    SOCKET s,
    const struct sockaddr FAR * name,
    int namelen,
    LPWSABUF lpCallerData,
    LPWSABUF lpCalleeData,
    LPQOS lpSQOS,
    LPQOS lpGQOS,
    LPINT lpErrno
    )
/*++
Implementation

    A  simple implementation is supplied.  It indicates that no callee data was
    retrieved and returns success.
--*/
{
    if (lpCalleeData != NULL) {
        lpCalleeData->len = 0;
    }
    * lpErrno = ERROR_SUCCESS;
    return(ERROR_SUCCESS);

} // lpWSPConnect

int WSPAPI WSPDuplicateSocket(
    SOCKET s,
    DWORD dwProcessId,
    LPWSAPROTOCOL_INFO lpProtocolInfo,
    LPINT lpErrno
    )
/*++
--*/
{
    // Not implemented
    return(SOCKET_ERROR);
} // lpWSPDuplicateSocket

int WSPAPI WSPEnumNetworkEvents(
    SOCKET s,
    WSAEVENT hEventObject,
    LPWSANETWORKEVENTS lpNetworkEvents,
    LPINT lpErrno
    )
/*++
--*/
{
    // Not implemented
    return(SOCKET_ERROR);
} // lpWSPEnumNetworkEvents

int WSPAPI WSPEventSelect(
    SOCKET s,
    WSAEVENT hEventObject,
    long lNetworkEvents,
    LPINT lpErrno
    )
/*++
--*/
{
    // Not implemented
    return(SOCKET_ERROR);
} // lpWSPEventSelect

int WSPAPI WSPGetOverlappedResult(
    SOCKET s,
    LPWSAOVERLAPPED lpOverlapped,
    LPDWORD lpcbTransfer,
    BOOL fWait,
    LPDWORD lpdwFlags,
    LPINT lpErrno
    )
/*++
--*/
{
    // Not implemented
    return(FALSE);
} // lpWSPGetOverlappedResult

int WSPAPI WSPGetPeerName(
    SOCKET s,
    struct sockaddr FAR * name,
    LPINT namelen,
    LPINT lpErrno
    )
/*++
--*/
{
    // Not implemented
    return(SOCKET_ERROR);
} // lpWSPGetPeerName

int WSPAPI WSPGetSockName(
    SOCKET s,
    struct sockaddr FAR * name,
    LPINT namelen,
    LPINT lpErrno
    )
/*++
--*/
{
    // Not implemented
    return(SOCKET_ERROR);
} // lpWSPGetSockName

int WSPAPI WSPGetSockOpt(
    SOCKET s,
    int level,
    int optname,
    char FAR * optval,
    LPINT optlen,
    LPINT lpErrno
    )
/*++
--*/
{
    // Not implemented
    return(SOCKET_ERROR);
} // lpWSPGetSockOpt

BOOL WSPAPI WSPGetQOSByName(
    SOCKET s,
    LPWSABUF lpQOSName,
    LPQOS lpQOS,
    LPINT lpErrno
    )
/*++
--*/
{
    // Not implemented
    return(FALSE);
} // lpWSPGetQOSByName

int WSPAPI WSPIoctl(
    SOCKET s,
    DWORD dwIoControlCode,
    LPVOID lpvInBuffer,
    DWORD cbInBuffer,
    LPVOID lpvOutBuffer,
    DWORD cbOutBuffer,
    LPDWORD lpcbBytesReturned,
    LPWSAOVERLAPPED lpOverlapped,
    LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine,
    LPWSATHREADID lpThreadId,
    LPINT lpErrno
    )
/*++
Not implemented,
--*/
{
    // Not implemented
    return(SOCKET_ERROR);

} // lpWSPIoctl

SOCKET WSPAPI WSPJoinLeaf(
    SOCKET s,
    const struct sockaddr FAR * name,
    int namelen,
    LPWSABUF lpCallerData,
    LPWSABUF lpCalleeData,
    LPQOS lpSQOS,
    LPQOS lpGQOS,
    DWORD dwFlags,
    LPINT lpErrno
    )
/*++
--*/
{
    // Not implemented
    return(INVALID_SOCKET);
} // lpWSPJoinLeaf

int WSPAPI WSPListen(
    SOCKET s,
    int backlog,
    LPINT lpErrno
    )
/*++
--*/
{
    // Not implemented
    return(SOCKET_ERROR);
} // lpWSPListen

int WSPAPI WSPRecv(
    SOCKET s,
    LPWSABUF lpBuffers,
    DWORD dwBufferCount,
    LPDWORD lpNumberOfBytesRecvd,
    LPDWORD lpFlags,
    LPWSAOVERLAPPED lpOverlapped,
    LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine,
    LPWSATHREADID lpThreadId,
    LPINT lpErrno
    )
/*++
Implementation

    A  simple  implementation  is  supplied.   The  number of bytes received is
    simply  filled  in as the number requested.  No actual data is deposited in
    the  buffers.   The  client  will  "see"  whatever data happens to be there
    already.

    This  simple implementation also violates the specification in that it does
    not  support  overlapped  I/O.  It simply returns an error indication if an
    attempt is made to use overlapped I/O features.
--*/
{
    if ((lpOverlapped != NULL) ||
        (lpCompletionRoutine != NULL)) {
        // Overlapped I/O and its animal friends not implemented.
        *lpErrno = WSAEINVAL;
        return(SOCKET_ERROR);
    }

    // Pretend to receive some data
    * lpNumberOfBytesRecvd = lpBuffers[0].len;
    * lpFlags = 0;
    * lpErrno = ERROR_SUCCESS;
    return(ERROR_SUCCESS);

} // lpWSPRecv

int WSPAPI WSPRecvDisconnect(
    SOCKET s,
    LPWSABUF lpInboundDisconnectData,
    LPINT lpErrno
    )
/*++
--*/
{
    // Not implemented
    return(SOCKET_ERROR);
} // lpWSPRecvDisconnect

int WSPAPI WSPRecvFrom(
    SOCKET s,
    LPWSABUF lpBuffers,
    DWORD dwBufferCount,
    LPDWORD lpNumberOfBytesRecvd,
    LPDWORD lpFlags,
    struct sockaddr FAR * lpFrom,
    LPINT lpFromlen,
    LPWSAOVERLAPPED lpOverlapped,
    LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine,
    LPWSATHREADID lpThreadId,
    LPINT lpErrno
    )
/*++
Implementation

    A  simple  implementation  is  supplied.   The  number of bytes received is
    simply  filled  in as the number requested.  The "received from" address is
    filled  in  with  a length of zero.  This might be an unexpected result, in
    which case it could be changed to fill in some dummy constant address.

    This  simple implementation also violates the specification in that it does
    not  support  overlapped  I/O.  It simply returns an error indication if an
    attempt is made to use overlapped I/O features.
--*/
{
    if ((lpOverlapped != NULL) ||
        (lpCompletionRoutine != NULL)) {
        // Overlapped I/O and its animal friends not implemented.
        *lpErrno = WSAEINVAL;
        return(SOCKET_ERROR);
    }

    // Pretend to receive some data
    * lpNumberOfBytesRecvd = lpBuffers[0].len;
    * lpFlags = 0;
    if (lpFromlen != NULL) {
        * lpFromlen = 0;
    }
    * lpErrno = ERROR_SUCCESS;
    return(ERROR_SUCCESS);

} // lpWSPRecvFrom

int WSPAPI WSPSelect(
    int nfds,
    fd_set FAR * readfds,
    fd_set FAR * writefds,
    fd_set FAR * exceptfds,
    const struct timeval FAR * timeout,
    LPINT lpErrno
    )
/*++
--*/
{
    // Not implemented
    return(SOCKET_ERROR);
} // lpWSPSelect

int WSPAPI WSPSend(
    SOCKET s,
    LPWSABUF lpBuffers,
    DWORD dwBufferCount,
    LPDWORD lpNumberOfBytesSent,
    DWORD dwFlags,
    LPWSAOVERLAPPED lpOverlapped,
    LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine,
    LPWSATHREADID lpThreadId,
    LPINT lpErrno
    )
/*++
Implementation

    A  simple  implementation is supplied.  This implementation simply computes
    the total number of bytes the client is attempting to send and fills in the
    result to indicate that all the requested bytes were successfully sent.

    This  simple implementation also violates the specification in that it does
    not  support  overlapped  I/O.  It simply returns an error indication if an
    attempt is made to use overlapped I/O features.
--*/
{
    DWORD bytes_sent;
    DWORD buf_index;

    if ((lpOverlapped != NULL) ||
        (lpCompletionRoutine != NULL)) {
        // Overlapped I/O and its animal friends not implemented.
        *lpErrno = WSAEINVAL;
        return(SOCKET_ERROR);
    }

    bytes_sent = 0;
    for (buf_index = 0; buf_index < dwBufferCount; buf_index++) {
        bytes_sent += lpBuffers[buf_index].len;
    } // for buf_index
    * lpNumberOfBytesSent = bytes_sent;
    * lpErrno = ERROR_SUCCESS;
    return(ERROR_SUCCESS);

} // lpWSPSend

int WSPAPI WSPSendDisconnect(
    SOCKET s,
    LPWSABUF lpOutboundDisconnectData,
    LPINT lpErrno
    )
/*++
Implementation

    A  simple  implementation is supplied.  This implementation simply computes
    the total number of bytes the client is attempting to send and fills in the
    result to indicate that all the requested bytes were successfully sent.

    This  simple implementation also violates the specification in that it does
    not  support  overlapped  I/O.  It simply returns an error indication if an
    attempt is made to use overlapped I/O features.
--*/
{
    // Not implemented
    return(SOCKET_ERROR);
} // lpWSPSendDisconnect

int WSPAPI WSPSendTo(
    SOCKET s,
    LPWSABUF lpBuffers,
    DWORD dwBufferCount,
    LPDWORD lpNumberOfBytesSent,
    DWORD dwFlags,
    const struct sockaddr FAR * lpTo,
    int iTolen,
    LPWSAOVERLAPPED lpOverlapped,
    LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine,
    LPWSATHREADID lpThreadId,
    LPINT lpErrno
    )
/*++
--*/
{
    DWORD bytes_sent;
    DWORD buf_index;

    if ((lpOverlapped != NULL) ||
        (lpCompletionRoutine != NULL)) {
        // Overlapped I/O and its animal friends not implemented.
        *lpErrno = WSAEINVAL;
        return(SOCKET_ERROR);
    }

    bytes_sent = 0;
    for (buf_index = 0; buf_index < dwBufferCount; buf_index++) {
        bytes_sent += lpBuffers[buf_index].len;
    } // for buf_index
    * lpNumberOfBytesSent = bytes_sent;
    * lpErrno = ERROR_SUCCESS;
    return(ERROR_SUCCESS);

} // lpWSPSendTo

int WSPAPI WSPSetSockOpt(
    SOCKET s,
    int level,
    int optname,
    const char FAR * optval,
    int optlen,
    LPINT lpErrno
    )
/*++
--*/
{
    // Not implemented
    return(SOCKET_ERROR);
} // lpWSPSetSockOpt

int WSPAPI WSPShutdown(
    SOCKET s,
    int how,
    LPINT lpErrno
    )
/*++
--*/
{
    // Not implemented
    return(SOCKET_ERROR);
} // lpWSPShutdown

SOCKET WSPAPI WSPSocket(
    int af,
    int type,
    int protocol,
    LPWSAPROTOCOL_INFO lpProtocolInfo,
    GROUP g,
    DWORD dwFlags,
    LPINT lpErrno
    )
/*++
Implementation

    A  simple  implementation  is  supplied.   The procedure simply performs an
    upcall  to  the  WinSock  2 DLL to allocate a socket handle.  The allocated
    socket handle is returned as the new socket handle.

    Note that this implementation does not keep any state information about the
    "socket"  it  allocated.   Consequently,  it simply uses 0 as the "context"
    value  associated with the socket handle.  If it were desired to keep state
    information  for  the  socket,  the  implementation  would  allocate a data
    structure  to  store the state information, cast the data structure pointer
    into a DWORD, and use this value as the dwContext.  When the implementation
    needed    to    access    the    state    information,    it   would   call
    WPUQuerySocketHandleContext  and  cast  the  retrieved  DWORD  back  to the
    appropriate data structure pointer.
--*/
{
    SOCKET  new_socket;

    new_socket = (* g_upcalls.lpWPUCreateSocketHandle)(
        g_protoinfo.dwCatalogEntryId,  // dwCatalogEntryId
        0,                             // dwContext
        lpErrno);                      // lpErrno

    return(new_socket);

} // lpWSPSocket

INT WSPAPI WSPStringToAddress(
    LPTSTR AddressString,
    INT AddressFamily,
    LPWSAPROTOCOL_INFO lpProtocolInfo,
    LPSOCKADDR lpAddress,
    LPINT lpAddressLength,
    LPINT     lpErrno
    )
/*++
--*/
{
    // Not implemented
    return(SOCKET_ERROR);
} // lpWSPStringToAddress

int WSPAPI WSPStartup(
    WORD wVersionRequested,
    LPWSPDATA lpWSPData,
    LPWSAPROTOCOL_INFO lpProtocolInfo,
    WSPUPCALLTABLE UpcallTable,
    LPWSPPROC_TABLE lpProcTable
    )
/*++
Implementation

    A  simple implementation is provided.  This just fills in required WSPData,
    a  procedure  table,  and returns success.  Technically this implementation
    violates  the  specification  since  it should maintain an up/down count of
    WSPStartup/WSPCleanup pairs.
--*/
{
    // Make  sure  that  the version requested is == 2.0 because that is all we
    // support.
    //
    // The  low  byte  is  the  major  version  and  the high byte is the minor
    // version.

    if ( LOBYTE( wVersionRequested ) < 2) {
        return WSAVERNOTSUPPORTED;
    }

    lpWSPData->wVersion = MAKEWORD( 2, 0 );
    lpWSPData->wHighVersion = MAKEWORD( 2, 0 );

#define szDESCRIPTION "Null Provider for WinSock 2 preliminary sample source"
    memcpy (lpWSPData->szDescription, szDESCRIPTION, sizeof szDESCRIPTION);

    memcpy (&g_protoinfo, lpProtocolInfo, sizeof WSAPROTOCOL_INFO);

    g_upcalls = UpcallTable;

    lpProcTable->lpWSPAccept = WSPAccept;
    lpProcTable->lpWSPAddressToString = WSPAddressToString;
    lpProcTable->lpWSPAsyncSelect = WSPAsyncSelect;
    lpProcTable->lpWSPBind = WSPBind;
    lpProcTable->lpWSPCancelBlockingCall = WSPCancelBlockingCall;
    lpProcTable->lpWSPCleanup = WSPCleanup;
    lpProcTable->lpWSPCloseSocket = WSPCloseSocket;
    lpProcTable->lpWSPConnect = WSPConnect;
    lpProcTable->lpWSPDuplicateSocket = WSPDuplicateSocket;
    lpProcTable->lpWSPEnumNetworkEvents = WSPEnumNetworkEvents;
    lpProcTable->lpWSPEventSelect = WSPEventSelect;
    lpProcTable->lpWSPGetOverlappedResult = WSPGetOverlappedResult;
    lpProcTable->lpWSPGetPeerName = WSPGetPeerName;
    lpProcTable->lpWSPGetSockName = WSPGetSockName;
    lpProcTable->lpWSPGetSockOpt = WSPGetSockOpt;
    lpProcTable->lpWSPGetQOSByName = WSPGetQOSByName;
    lpProcTable->lpWSPIoctl = WSPIoctl;
    lpProcTable->lpWSPJoinLeaf = WSPJoinLeaf;
    lpProcTable->lpWSPListen = WSPListen;
    lpProcTable->lpWSPRecv = WSPRecv;
    lpProcTable->lpWSPRecvDisconnect = WSPRecvDisconnect;
    lpProcTable->lpWSPRecvFrom = WSPRecvFrom;
    lpProcTable->lpWSPSelect = WSPSelect;
    lpProcTable->lpWSPSend = WSPSend;
    lpProcTable->lpWSPSendDisconnect = WSPSendDisconnect;
    lpProcTable->lpWSPSendTo = WSPSendTo;
    lpProcTable->lpWSPSetSockOpt = WSPSetSockOpt;
    lpProcTable->lpWSPShutdown = WSPShutdown;
    lpProcTable->lpWSPSocket = WSPSocket;
    lpProcTable->lpWSPStringToAddress = WSPStringToAddress;

    return(ERROR_SUCCESS);

} // WSPStartup


SRTEST.CPP

/*++

Copyright 1996 - 1998 Microsoft Corporation

Copyright (c) 1996 Intel Corp

Module Name:

    perftest.cpp

Abstract:

    This  module  contains  demonstration code which illustrates how to install
    and  use  a  service provider.  This demonstration code is designed to work
    together only with the temporary sample skeleton "null" service provider.

--*/

#include <windows.h>
#include "nullpdef.h"
#include "ws2spi.h"

//
//  Unique GUID generated with UUIDGEN:
//   ba657007-d615-11cf-9f5c-08003e301875
//
//  WARNING:  If you use this sample code, you must generate your own
//   GUID.  Under no circumstances should you use this GUID.
//

GUID ProviderIDGuid = { 0xba657007, 0xd615, 0x11cf, 0x9f, 0x5c,
                     0x08, 0x00, 0x3e, 0x30, 0x18, 0x75 };

//
// Dummy name as unicode string
//

#define SAMPLE_NAME_WSTRING L"Null Provider protocol for sample"

// The  nominal  size  of  send  and  receive operations.  Since we expect this
// sample code to be used only with a "null" provider that does not really send
// or receive data, the value is arbitrary and has no real effect.

#define SEND_RECEIVE_BYTES  100

static
void
Test_Send(
    IN  SOCKET  SendSock,
    IN  DWORD   NumTries
    )
/*++
Routine Description:

    This  procedure  performs the indicated number of send operations through a
    socket.

Arguments:

    SendSock - Supplies the socket to be used for the "send" operations.

    NumTries - Supplies the number of tries.

Return Value:

    none

Implementation:

    We  use  an "unrolled" loop to perform a large number of calls back-to-back
    without  loop turnaround overhead.  Then we perform the remaining number of
    calls.   This  should  help  reduce  the  overall  contribution of the loop
    overhead,  which  might  be  relevant  if this were being used as part of a
    performance measurement.
--*/
{
    DWORD    unrolled_iterations;
    DWORD    remaining_iterations;
    DWORD    i;
    int      send_result;
    WSABUF   send_bufs;
    CHAR     data_space[SEND_RECEIVE_BYTES];
    DWORD    bytes_sent;

#define SEND_ONCE \
    send_result = WSASend(                       \
        SendSock,      /* s                   */ \
        & send_bufs,   /* lpBuffers           */ \
        1,             /* dwBufferCount       */ \
        & bytes_sent,  /* lpNumberOfBytesSent */ \
        0,             /* dwFlags             */ \
        NULL,          /* lpOverlapped        */ \
        NULL)          /* lpCompletionRoutine */

#define UNROLLED_LENGTH 50

    unrolled_iterations  = NumTries / UNROLLED_LENGTH;
    remaining_iterations = NumTries % UNROLLED_LENGTH;
    send_bufs.len = SEND_RECEIVE_BYTES;
    send_bufs.buf = data_space;

    for (i = 0; i < unrolled_iterations; i++) {
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
    } // for i

    for (i = 0; i < remaining_iterations; i++) {
        SEND_ONCE;
    } // for i

} // Test_Send

static
SOCKET
Create_Test_Socket(
    void)
/*++
Routine Description:

    This  procedure  creates a socket to be used for the tests through the null
    provider.

Arguments:

    none

Return Value:

    The  procedure  returns  the  newly  created  socket  if  it is successful,
    otherwise it returns INVALID_SOCKET.
--*/
{
    SOCKET  new_socket;

    new_socket = WSASocket(
        AF_SPECIAL_NULL,   // af
        SOCK_STREAM,       // type
        0,                 // protocol
        NULL,              // lpProtocolInfo
        0,                 // g
        0);                // dwFlags
    return(new_socket);

} // Create_Test_Socket

static
void
Destroy_Test_Socket(
    IN  SOCKET  TestSock
    )
/*++
Routine Description:

    This  procedure  closes  a socket that was being used for tests through the
    null provider.

Arguments:

    TestSock - Supplies the socket to be closed.

Return Value:

    none
--*/
{
    int    close_result;
    DWORD  last_error;

    close_result = closesocket(
        TestSock);
    if (close_result != ERROR_SUCCESS) {
        last_error = GetLastError();
        last_error = GetLastError();  // Redundant for single-step convenience.
    }

} // Destroy_Test_Socket

static
DWORD
Establish_Provider(
    void
    )
/*++
Routine Description:

    This procedure makes sure the null provider is installed and installs it if
    it is not already installed.

    Normally  an  application  program  would  NOT actually install a provider.
    Installation  was  bundled into this application example simply to make the
    example compact.

Arguments:

    none

Return Value:

--*/
{
    LPWSAPROTOCOL_INFOW  proto_buf;
    DWORD                size_guess;
    BOOL                 still_trying;
    BOOL                 got_protos;
    int                  enum_result;
    int                  error_code;
    BOOL                 found_null_prov;

    size_guess = 0;
    got_protos = FALSE;
    still_trying = TRUE;
    proto_buf = NULL;

    // We  call  WSCEnumProtocols  twice.   The first call determines how big a
    // buffer  we will actually need for the results.  The second call actually
    // retrieves  the  results.  In a situation such as this, I generally write
    // the  sequence  of  two  calls  as  a  loop,  since this way the call and
    // error-check code has to be written only once.
    while (still_trying) {
        enum_result = WSCEnumProtocols(
            NULL,           // lpiProtocols
            proto_buf,      // lpProtocolBuffer
            & size_guess,   // lpdwBufferLength
            & error_code);  // lpErrno
        if (enum_result != SOCKET_ERROR) {
            got_protos = TRUE;
            still_trying = FALSE;
        }
        else {
            if (error_code == WSAENOBUFS) {
                delete proto_buf;
                proto_buf = (LPWSAPROTOCOL_INFOW) new char[size_guess];
                if (proto_buf == NULL) {
                    still_trying = FALSE;
                }
            }
            else {
                still_trying = FALSE;
            }
        }
    } // while still_trying

    found_null_prov = FALSE;
    if (got_protos) {
        int  i;

        for (i = 0; i < enum_result; i++) {
            if (proto_buf[i].iAddressFamily == AF_SPECIAL_NULL) {
                found_null_prov = TRUE;
                break;
            }
        }
    }
    delete proto_buf;

    if (! found_null_prov) {
        WSAPROTOCOL_INFOW proto_info;
        int               install_result;
        int               install_error;

        proto_info.dwServiceFlags1 = 0;
        proto_info.dwServiceFlags2 = 0;
        proto_info.dwServiceFlags3 = 0;
        proto_info.dwServiceFlags4 = 0;
        proto_info.dwProviderFlags = PFL_MATCHES_PROTOCOL_ZERO;
        proto_info.dwCatalogEntryId = 0;   // filled in by system
        proto_info.ProtocolChain.ChainLen = BASE_PROTOCOL;
        proto_info.iVersion = 0;
        proto_info.iAddressFamily = AF_SPECIAL_NULL;
        proto_info.iMaxSockAddr = 16;
        proto_info.iMinSockAddr = 16;
        proto_info.iSocketType = SOCK_STREAM;
        proto_info.iProtocol = 0;
        proto_info.iProtocolMaxOffset = 0;
        proto_info.iNetworkByteOrder = BIGENDIAN;
        proto_info.iSecurityScheme = SECURITY_PROTOCOL_NONE;
        proto_info.dwMessageSize = 0;  // stream-oriented
        proto_info.dwProviderReserved = 0;

        proto_info.ProviderId = ProviderIDGuid ; // filled in by system.

        // copy unicode string into name parameter
        memcpy(proto_info.szProtocol, SAMPLE_NAME_WSTRING, sizeof SAMPLE_NAME_WSTRING);

        install_result = WSCInstallProvider(
            &ProviderIDGuid,   // lpProviderID
            L"nullprov.dll",   // lpszProviderDllPath (unicode string)
            & proto_info,      // lpProtocolInfoList
            1,                 // dwNumberOfEntries
            & install_error);  // lpErrno
        if (install_result != ERROR_SUCCESS) {
            install_error = install_error;
            // assert(install_result == ERROR_SUCCESS);
        }
    } // if not found_null_prov

    return(TRUE);

} // Establish_Provider

void
main(
    void
    )
/*++
--*/
{
    SOCKET   test_sock;
    int      start_return;
    WSADATA  winsock_data;
    DWORD    last_error;
    int      clean_return;
    DWORD    num_tries;

    Establish_Provider();

    start_return = WSAStartup(
        MAKEWORD(2,0),    // wVersionRequested
        & winsock_data);  // lpWSAData

    if (start_return == ERROR_SUCCESS) {
        test_sock = Create_Test_Socket();
        // To  keep  this  sample  simple,  this  demonstration program and the
        // matching  null  provider  do  not  bother  binding or connecting the
        // socket  before  doing  "send" operations.  With a real provider, one
        // should  bind  and possibly also connect the socket before proceeding
        // with a "send".
        if (test_sock != INVALID_SOCKET) {
            num_tries = 10;
            Test_Send(
                test_sock,   // SendSock
                num_tries);  // NumTries
            Destroy_Test_Socket(
                test_sock);
        } // if test_sock != NULL
        else {
            last_error = GetLastError();
            // The  following  is  just  a  redundant executable statement that
            // allows   me   to   see   the   return  value  conveniently  when
            // single-stepping.
            last_error = GetLastError();
        }
    } // if start_return == ERROR_SUCCESS
    else {
        last_error = GetLastError();
        last_error = GetLastError();  // Redundant for single-step convenience.
    }

    clean_return = WSACleanup();
    clean_return = clean_return; // Redundant for single-step convenience.

} // main


README.TXT

NetWare Interface

SUMMARY
=======

CONNECT, DGRecv, DGSend, LISTEN, and PING are parts of the NWLink sample
suite.

MORE INFORMATION
================

CONNECT: Is a simple example of using SPX commands.

DGRecv: Opens a socket, binds to the socket, and waits for a datagram
        packet.

DGSend: Opens a socket, binds to the socket, and sends a packet repeatedly.

LISTEN: Opens an SPX socket, binds to the socket, and listens for a
        connection.

PING: Opens a socket, binds to the socket, receives a packet, and sends that
packet back to the original sender.


CONNECT.C

/****************************************************************************\
*  dgrecv.c -- sample program demonstrating NWLink.
*
*       Microsoft Developer Support
*       Copyright 1992 - 1998 Microsoft Corporation
*
*  This program is a simple example of using SPX connect.
*
****************************************************************************/
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <wsipx.h>
#include "../testlib/testlib.h"

SOCKADDR_IPX addr;
SOCKADDR_IPX baddr;
SOCKADDR_IPX caddr;

UCHAR ch = 0;

/*
*   Function Prototypes
*/

extern int main(int, char **);
extern int net_init(SOCKET *);
extern int make_connection(SOCKET);
extern int do_send_receive(SOCKET);

/****************************************************************************
*
*    FUNCTION:  main( int argc, char **argv )
*
*    PURPOSE:   This is the main entry for the program
*
*
*    ARGUMENTS:argc = Number of arguments
*               argv = Array of ptrs to cmd line args
*
*
* RETURNS:   Exit code for the program
*
*\***************************************************************************/
int main(int argc, char **argv)
{
    SOCKET s;

    /*
    *   Set up our default values before checking command line
    */

    memcpy(Remote_Node_Number, "\x08\x00\x2B\x2E\x98\xA8", 6);
    memcpy(Remote_Socket_Number, "\x05\x00", 2);
    Socket_Type = SOCK_STREAM;
    Protocol = NSPROTO_SPX;
    Remote_Address_Family = AF_NS;
    Sleep_Time = 250;

    /*
    *   Get any command line options
    */

    parse_cmd_line(argc, argv);

    /*
    *   Initialize the network and set up the socket
    */

    if (net_init(&s))
        return 1;

    /*
    *   Try to connect to our server
    */

    if (make_connection(s))
        return 1;

    /*
    *   Send/receive data to/from server
    */

    do_send_receive(s);

    /*
    *   All done - close up
    */

    if (verbose)
        printf("calling closesocket(socket = %d)\n", s);

    closesocket(s);
    return 0;
}

/****************************************************************************
*
*    FUNCTION:  net_init( SOCKET *skt )
*
*    PURPOSE:   Initializes the WinSock stuff and sets up our socket.
*
*
*    ARGUMENTS:SOCKET * => struct to fill in with opened socket.
*
* RETURNS:   0 if ok
*1 if error
*
*\***************************************************************************/
int net_init(SOCKET *skt)
{
    SOCKET s;
    int rc, addrlen;
    WSADATA wsdata;
    WORD    wVersionRequested;

    wVersionRequested = MAKEWORD(1,1);

    /*
    *   Register with the socket library
    */

    rc = WSAStartup(wVersionRequested, &wsdata);

    if (verbose)
        printf("WSAStartup returned 0x%X\n", rc);

    if (rc) {
        printf("WSAStartup failed: error = %d\n", rc);
        return 1;
    }

    if (verbose) {
        printf("contents of wsdata structure: \n");
        print_wsa(&wsdata);
        printf("calling socket(address family = %d, socket type = %d, protocol = %d)\n", Local_Address_Family, Socket_Type, Protocol);
    }

    /*
    *   Open a STREAMING socket
    */

    s = socket(Local_Address_Family, Socket_Type, Protocol);

    if (verbose)
        printf("socket() returned 0x%X (%d)\n", s, s);

    if (s == INVALID_SOCKET) {
        dos_net_perror("socket call failed");
        return 1;
    }

    /*
    *   Bind to any address
    */

    addr.sa_family = Local_Address_Family;
    memcpy(&addr.sa_netnum, Local_Network_Number, 4);
    memcpy(&addr.sa_nodenum, Local_Node_Number, 6);
    memcpy(&addr.sa_socket, Local_Socket_Number, 2);

    rc = bind(s, (const struct sockaddr FAR *)&addr, 16);

    if (verbose)
        printf("bind() returned 0x%X\n", rc);

    if (rc == SOCKET_ERROR) {
        dos_net_perror("bind call failed");
        return 1;
    }

    if (verbose)
        printf("calling getsockname(socket = %d)\n", s);

    addrlen = 16;
    rc = getsockname(s, (struct sockaddr *) &addr, &addrlen);

    if (verbose)
        printf("getsockname() returned 0x%X\n", rc);

    if (rc == SOCKET_ERROR) {
        dos_net_perror("Error binding to socket");
        closesocket(s);
        return 1;
    }

    if (verbose) {
        printf("addrlen = %d\n", addrlen);
        print_netaddr(addr.sa_netnum, "Bound address = ", "\n");
    }

    /*
    *   Build the address of the node to connect to
    */

    memcpy(&caddr.sa_netnum, Remote_Network_Number, 4);
    memcpy(&caddr.sa_nodenum, Remote_Node_Number, 6);
    memcpy(&caddr.sa_socket, Remote_Socket_Number, 2);
    caddr.sa_family = AF_NS;

    /*
    *   Set up socket for return
    */

    *skt = s;

    return 0;
}

/****************************************************************************
*
*    FUNCTION:  make_connection( SOCKET s )
*
*    PURPOSE:   Establishes a connection with our server.
*
*    ARGUMENTS:SOCKET socket to use for connection
*
* RETURNS:   0 if ok
*1 if error
*
*\***************************************************************************/
int make_connection(SOCKET s)
{
    int rc, addrlen;

    /*
    *   Connect
    */

    if (verbose)
        printf("calling connect(socket = %d)\n", s);

    rc = connect(s, (const struct sockaddr FAR *)&caddr, 16);

    if (verbose)
        printf("connect() returned 0x%X\n", rc);

    if (rc == SOCKET_ERROR) {
        dos_net_perror("connect call failed");
        return 1;
    }

    printf("Connect OK\n");

    /*
    *   Print out address we connected to
    */

    if (verbose)
        printf("calling getpeername(socket = %d)\n", s);

    addrlen = 16;
    rc = getpeername(s, (struct sockaddr *) &caddr, &addrlen);

    if (verbose)
        printf("getpeername() returned 0x%X\n", rc);

    if (verbose) {
        printf("addrlen = %d\n", addrlen);
        print_netaddr(caddr.sa_netnum, "Remote Address = ", "\n");
    }

    return 0;
}
/****************************************************************************
*
*    FUNCTION:  do_send_receive( SOCKET *s )
*
*    PURPOSE:   Alternately sends/receives data to/from the server.
*
*    ARGUMENTS:SOCKET socket to transmit on
*
* RETURNS:   0 if ok
*1 if error
*
*\***************************************************************************/
int do_send_receive(SOCKET s)
{
    int rc, errflag = 0;
    int sndpkts = 0, rcvpkts = 0;
    LPSTR sendbuf;

    /*
    *   Allocate a send buffer
    */

    if (verbose)
        printf("Allocating %d bytes for send buffer \n");

    sendbuf = malloc(Send_Length);

    if (!sendbuf) {
        printf("Error allocating %d bytes for send buffer\n");
        return 1;
    }

    /*
    *   Send data and recv it back
    */

    while (1) {

        /*
        *   Fill the buffer with our current character
        */

        memset(sendbuf, ch, Send_Length);

        /*
        *   Send data
        */

        if (verbose)
    printf("calling send(socket = %d, length = %d)\n", s, Send_Length);

        rc = send(s, sendbuf, Send_Length, 0);

        if (verbose)
            printf("send() returned 0x%X\n", rc);

        if (rc == SOCKET_ERROR) {
            dos_net_perror("\nsend() call failed");
            errflag++;
            break;
        }

        if (verbose)
            printf("Sent packet %d: sent %d bytes\n", sndpkts++, rc);
        else
            printf("\rSent packet %d: sent %d bytes... ", sndpkts++, rc);

        /*
        *   Receive the data back
        */

        if (verbose)
            printf("calling recv(socket = %d, length = %d)\n", s, Send_Length);

        rc = recv(s, sendbuf, Send_Length, 0);

        if (verbose)
            printf("recv() returned %d\n", rc);

        if (rc == SOCKET_ERROR) {
            dos_net_perror("\nrecv() call failed");
            errflag++;
            break;
        }

        if (!rc) {
            printf("connection has been lost\n");
            break;
        }

        printf("Received %d bytes", rc);

        if (verbose)
            printf("\n");

        /*
        *   See if our buffer has the same data in it
        */

        rc = mem_check(sendbuf, ch++, Send_Length);

        if (rc)
            printf("Data compare error: packet = %d, offset = %d\n", (sndpkts-1), rc);
        else if (verbose)
            printf("Data compares okay\n");

        /*
        *   If we are to send just 1, break out
        */

        if (No_Loop)
            break;

        /*
        *   Pause a little while (don't trash the network)
        */

        Sleep(Sleep_Time);
    }

    if (verbose)
        printf("\nFreeing send buffer\n");

    free(sendbuf);

    return errflag;
}


DGRECV.C

/****************************************************************************\
*  dgrecv.c -- sample program demonstrating NWLink.
*
*       Microsoft Developer Support
*       Copyright 1992 - 1998 Microsoft Corporation
*
*  This program is a simple example of opening a socket,
*  binding to the socket, and waiting for a datagram packet.
*
****************************************************************************/
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <wsipx.h>
#include <wsnwlink.h>
#include "../testlib/testlib.h"

/*
*   Sockaddr structures
*/

SOCKADDR_IPX addr;
SOCKADDR_IPX baddr;
SOCKADDR_IPX raddr;

/*
*   Function Prototypes
*/

extern int main(int, char **);
extern int net_init(SOCKET *);
extern int dg_recv(SOCKET);

/****************************************************************************
*
*    FUNCTION:  main( int argc, char **argv )
*
*    PURPOSE:   This is the main entry for the program
*
*
*    ARGUMENTS:argc = Number of arguments
*               argv = Array of ptrs to cmd line args
*
*
* RETURNS:   Exit code for the program
*
*\***************************************************************************/
int main(int argc, char **argv)
{
    SOCKET s;

    /*
    *   Initialize our default values before checking the command line
    */

    *Local_Socket_Number = 0x06;
    *(Local_Socket_Number+1) = 0x00;

    /*
    *   Parse the command line to set up any command line options
    */

    parse_cmd_line(argc, argv);

    /*
    *   Initialize the network and set up our socket
    */

    if (net_init(&s))
        return 1;

    /*
    *   Do the receive thing
    */

    if (dg_recv(s))
        return 1;

    /*
    *   All Done - Close up the socket and exit
    */

    if (verbose)
printf("Calling closesocket()\n");

    closesocket(s);

    return 0;
}

/****************************************************************************
*
*    FUNCTION:  net_init( SOCKET *skt )
*
*    PURPOSE:   Initializes the WinSock stuff and sets up our socket.
*
*
*    ARGUMENTS:SOCKET * => struct to receive our socket info
*
* RETURNS:   0 if ok
*1 if error
*
*\***************************************************************************/
int net_init(SOCKET *skt)
{
    SOCKET s;
    int rc, addrlen = 16;
    WSADATA wsdata;
    WORD    wVersionRequested;

    if (verbose)
        printf("Calling WSAStartup(), ");

    /*
    *   Initialize with the WINSOCK library
    */

    wVersionRequested = MAKEWORD(1,1);
    rc = WSAStartup(wVersionRequested, &wsdata);

    if (verbose)
        printf("return = 0x%X (%d)\n", rc, rc);

    if (rc) {
        printf("WSAStartup failed: error code = %d\n", rc);
        return 1;
    }

    if (verbose) {
        printf("Contents of wsadata struct:\n");
        print_wsa(&wsdata);
    }

    /*
    *   Open a DATAGRAM socket with IPX
    */

    if (verbose)
        printf("Calling socket(address family = %d, socket type = %d, protocol = %d)\n", Local_Address_Family, Socket_Type, Protocol);

    s = socket(Local_Address_Family, Socket_Type, Protocol);

    if (verbose)
printf("socket() returned 0x%X (%d)\n", s, s);

    if (s == INVALID_SOCKET) {
        dos_net_perror("Socket call failed");
        exit(1);
    }

    /*
    *    Bind to a socket.  We dont care what socket we bind to,
    *    so we will send down all 0's
    */

    addr.sa_family = Local_Address_Family;

    memcpy(&addr.sa_netnum, Local_Network_Number, 4);
    memcpy(&addr.sa_nodenum, Local_Node_Number, 6);
    memcpy(&addr.sa_socket, Local_Socket_Number, 2);

    if (verbose) {
        printf("calling bind(), local address =\n  ");
        print_saddr(&addr);
    }

    rc = bind(s, (struct sockaddr *) &addr, 16);

    if (verbose)
        printf("\nbind() returned 0x%X (%d)\n", rc, rc);

    if (rc == SOCKET_ERROR) {
        dos_net_perror("Error binding to socket");
        closesocket(s);
        return 1;
    }

    /*
    *   Get the address we bound to and print it out
    */

    if (verbose)
        printf("calling getsockname(socket = %d), ", s);

    rc = getsockname(s, (struct sockaddr *) &baddr, &addrlen);

    if (verbose)
        printf("return = 0x%lX (%d)\n", rc, rc);

    if (rc == SOCKET_ERROR) {
        dos_net_perror("Error getting socket name");
        closesocket(s);
        return 1;
    }

    /*
    *   Set the packet type to send for this socket
    */

    if (verbose)
        printf("Calling setsockopt for packet type %d\n", Local_Packet_Type);

    rc = setsockopt(s, NSPROTO_IPX, IPX_PTYPE, (const char *) &Local_Packet_Type, 4);

    if (rc == SOCKET_ERROR)
        dos_net_perror("setsockopt() call failed");

    if (Filter_Packet_Type) {

        /*
        *   Set the packet type for this socket
        */

        if (verbose)
            printf("Calling setsockopt to filter packet type %d\n", Filter_Packet_Type);

rc = setsockopt(s, NSPROTO_IPX, IPX_FILTERPTYPE, (const char *) &Filter_Packet_Type, sizeof(int));
printf("RC from FILTER SOCKOPT = %d\n", rc);

        if (rc == SOCKET_ERROR)
            dos_net_perror("setsockopt() call failed setting filter packet type");
    }
    /*
    *   Print out the network address
    */

    if (verbose) {
        printf("addrlen = %d\n", addrlen);
        print_netaddr(baddr.sa_netnum, "  Bound to address ", "\n");
    }

    *skt = s;

    return 0;
}

/****************************************************************************
*
*    FUNCTION:  dg_recv( SOCKET s )
*
*    PURPOSE:   Receives datagrams.
*
*    ARGUMENTS:SOCKET   socket to receive on
*
* RETURNS:   0 if ok
*               1 if error
*
*\***************************************************************************/
int dg_recv(SOCKET s)
{
    int rc, errflag = 0;
    int addrlen = 16;
    UINT dgrams = 0;
    LPSTR recvbuf;

    if (verbose)
        printf("allocating %d bytes for receive buffer\n", Receive_Length);

    /*
    *   Set up the data buffer to send
    */

    recvbuf = (LPSTR)malloc(Send_Length);

    if (!recvbuf) {
        printf("Error allocating %d bytes for receive buffer\n", Receive_Length);
        return 1;
    }

    if (verbose) {
        printf("calling recvfrom(socket = %d, length = %d),\n", s, Receive_Length);
    }
    else
        printf("Waiting for call...\n");

    while (1) {
        /*
        *   Receive a packet from anyone
        */

        rc = recvfrom(s, recvbuf, Receive_Length, 0, (struct sockaddr *)&raddr, &addrlen);

        if (rc == SOCKET_ERROR) {
            dos_net_perror("recvfrom() failed");
            errflag++;
            break;
        }

        if (verbose) {
            printf("Received %d bytes from \n  ", rc);
            print_saddr(&raddr);
            printf("\n");
        }
        else
            printf("\rReceived packet %d, length = %d bytes", ++dgrams, rc);

        if (No_Loop)
            break;
    }

    if (verbose)
        printf("Freeing receive buffer\n");

    free(recvbuf);

    return errflag;
}


DGSEND.C

/****************************************************************************\
*  dgsend.c -- sample program demonstrating NWLink.
*
*       Microsoft Developer Support
*       Copyright 1992 - 1998 Microsoft Corporation
*
*  This program is a simple example of opening a socket,
*  binding to the socket, and sending a packet repeatedly.
*
****************************************************************************/
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <wsipx.h>
#include <wsnwlink.h>
#include "..\testlib\testlib.h"

/*
*   Sockaddr structures
*/

SOCKADDR_IPX addr;
SOCKADDR_IPX baddr;
SOCKADDR_IPX saddr;

/*
*   Data for sending
*/

char send_data[] = "This is an IPX packet from NT";

/*
*   Function Prototypes
*/
extern int main(int, char **);
extern int net_init(SOCKET *);
extern int enable_broadcasts(SOCKET);
extern void build_dest_addr(SOCKET, PSOCKADDR_IPX);
extern int dg_send(SOCKET);

/****************************************************************************
*
*    FUNCTION:  main( int argc, char **argv )
*
*    PURPOSE:   This is the main entry for the program
*
*
*    ARGUMENTS:argc = Number of arguments
*               argv = Array of ptrs to cmd line args
*
*
* RETURNS:   Exit code for the program
*
*\***************************************************************************/
int main(int argc, char **argv)
{
    SOCKET s;

    /*
    *   Fill in any default values before checking the command line
    */

    *Remote_Socket_Number = 0x06;
    *(Remote_Socket_Number+1) = 0x00;
    Sleep_Time = 500;

    /*
    *   Parse the command line to set up any command line options
    */

    parse_cmd_line(argc, argv);

    if (net_init(&s))
        return 1;

    if (!No_Broadcast) {
        if (enable_broadcasts(s))
            return 1;
    }

    build_dest_addr(s, &saddr);

    /*
    *   Send some datagrams
    */

    dg_send(s);

    /*
    *   All Done - Close up the socket and exit
    */

    if (verbose)
printf("Calling closesocket()\n");

    closesocket(s);
    return 0;
}

/****************************************************************************
*
*    FUNCTION:  net_init( SOCKET *skt )
*
*    PURPOSE:   Initializes the WinSock stuff and sets up our socket.
*
*
*    ARGUMENTS:SOCKET * => struct to receive our socket info
*
* RETURNS:   0 if ok
*1 if error
*
*\***************************************************************************/
int net_init(SOCKET *skt)
{
    SOCKET s;
    WSADATA wsdata;
    WORD    wVersionRequested;

    int rc, addrlen = 16;

    if (verbose)
        printf("Calling WSAStartup(), ");

    /*
    *   Initialize with the WINSOCK library
    */

    wVersionRequested = MAKEWORD(1,1);
    rc = WSAStartup(wVersionRequested, &wsdata);

    if (verbose)
        printf("return = 0x%X (%d)\n", rc, rc);

    if (rc) {
        printf("WSAStartup failed: error code = %d\n", rc);
        return 1;
    }

    if (verbose) {
        printf("Contents of wsadata struct:\n");
        print_wsa(&wsdata);
    }

    /*
    *   Open a DATAGRAM socket with IPX
    */

    if (verbose)
        printf("Calling socket(address family = %d, socket type = %d, protocol = %d)\n", Local_Address_Family, Socket_Type, Protocol);

    s = socket(Local_Address_Family, Socket_Type, Protocol);

    if (verbose)
printf("socket() returned 0x%X (%d)\n", s, s);

    if (s == INVALID_SOCKET) {
        dos_net_perror("Socket call failed");
        exit(1);
    }

    /*
    *   Bind to a socket.  We dont care what socket we bind to,
    *   so we will send down all 0's
    */

    addr.sa_family = Local_Address_Family;

    memcpy(&addr.sa_netnum, Local_Network_Number, 4);
    memcpy(&addr.sa_nodenum, Local_Node_Number, 6);
    memcpy(&addr.sa_socket, Local_Socket_Number, 2);

    if (verbose) {
        printf("calling bind(), local address =\n  ");
        print_saddr(&addr);
    }

    rc = bind(s, (const struct sockaddr *) &addr, 16);

    if (verbose)
        printf("\nbind() returned 0x%X (%d)\n", rc, rc);

    if (rc == SOCKET_ERROR) {
        dos_net_perror("Error binding to socket");
        printf("Socket = 0x%lx\n", s);
        closesocket(s);
        return 1;
    }

    /*
    *   Get the address we bound to and print it out
    */

    if (verbose)
        printf("calling getsockname(socket = %d), ", s);

    rc = getsockname(s, (struct sockaddr *) &baddr, &addrlen);

    if (verbose)
        printf("return = 0x%lX (%d)\n", rc, rc);

    if (rc == SOCKET_ERROR) {
        dos_net_perror("Error getting socket name");
        closesocket(s);
        return 1;
    }

    /*
    *   Set the packet type for this socket
    */

    if (verbose)
        printf("Calling setsockopt for packet type %d\n", Local_Packet_Type);

    rc = setsockopt(s, NSPROTO_IPX, IPX_PTYPE, (const char *) &Local_Packet_Type, 4);

    if (rc == SOCKET_ERROR)
        dos_net_perror("setsockopt() call failed");

    /*
    *   Print out the network address
    */

    if (verbose) {
        printf("addrlen = %d\n", addrlen);
        print_netaddr(baddr.sa_netnum, "  Bound to address ", "\n");
    }

    *skt = s;

    return 0;
}

/****************************************************************************
*
*    FUNCTION:  enable_broadcasts( SOCKET s )
*
*    PURPOSE:   Sets the socket option to enable broadcast sends on it.
*
*
*    ARGUMENTS:SOCKET   socket to enable
*
* RETURNS:   0 if ok
*1 if error
*
*\***************************************************************************/
int enable_broadcasts(SOCKET s)
{
    int rc;
    BOOL optval = TRUE;

    /*
    *   Enable sending of broadcasts
    */

    /*
    *    NOTE:  This only needs to be done if you want to SEND
    *           broadcast packets.  Reception of broadcast
    *           packets will happen automatically.
    */

    if (verbose)
        printf("Setting socket option to broadcast, ");
rc = setsockopt(s, SOL_SOCKET, SO_BROADCAST, (char *)&optval, sizeof(int));
    if (verbose)
        printf("return = 0x%X (%d)\n", rc, rc);

    if (rc == SOCKET_ERROR) {
        dos_net_perror("Error enabling broadcast address");
        closesocket(s);
        return 1;
    }

    return 0;
}

/****************************************************************************
*
*    FUNCTION:  build_dest_addr( SOCKET s )
*
*    PURPOSE:   Fills in the destination address fields and sets the packet type.
*
*
*    ARGUMENTS:SOCKET       socket to set packet type for
*PSOCKADDR_NS => address struct to fill in
*
* RETURNS:   0 if ok
*1 if error
*
*\***************************************************************************/
void build_dest_addr(SOCKET s, PSOCKADDR_IPX psaddr)
{
    int rc;

    /*
    *   Build the dest. address
    */

    psaddr->sa_family = Remote_Address_Family;

    /*
    *   Set dest. network number
    */

    memcpy(&psaddr->sa_netnum, Remote_Network_Number, 4);

    /*
    *   Set dest. node address
    */

    memcpy(&psaddr->sa_nodenum, Remote_Node_Number, 6);

    /*
    *   Set the dest. socket number
    */

    memcpy(&psaddr->sa_socket, Remote_Socket_Number, 2);

    /*
    *   Set the packet type for this socket
    */

    if (verbose)
        printf("Calling setsockopt for packet type %d\n", Send_Packet_Type);

    rc = setsockopt(s, NSPROTO_IPX, IPX_PTYPE, (const char *) &Send_Packet_Type, sizeof(int));

    if (rc == SOCKET_ERROR)
        dos_net_perror("setsockopt() call failed");

    return;
}

/****************************************************************************
*
*    FUNCTION:  dg_send( SOCKET s )
*
*    PURPOSE:   Receives datagrams.
*
*    ARGUMENTS:SOCKET socket to transmit on
*
* RETURNS:   0 if ok
*               1 if error
*
*\***************************************************************************/
int dg_send(SOCKET s)
{
    LPSTR sendbuf;
    int rc, errflag = 0;
    UINT dgrms = 0;

    if (verbose)
        printf("allocating %d bytes for send buffer\n", Send_Length);

    /*
    *   Set up the data buffer to send
    */

    sendbuf = (LPSTR)malloc(Send_Length);

    if (!sendbuf) {
        printf("Error allocating %d bytes for send buffer\n", Send_Length);
        return 1;
    }

    /*
    *   Zero the buffer and copy as much of our data to it as possible
    */

    memset(sendbuf, 0, Send_Length);
    strncpy(sendbuf, send_data, Send_Length);

    while (1) {
        if (verbose) {
            printf("calling sendto(socket = %d, length = %d),\n", s, Send_Length);
            printf("destination address:\n  ");
            print_saddr(&saddr);
        }

        /*
        *   Send a packet to everybody
        */

        rc = sendto(s, sendbuf, Send_Length, 0, (const struct sockaddr *) &saddr, 16);

        if (verbose)
    printf("\nsendto() returned %d\n", rc);

        if (rc == SOCKET_ERROR) {
            dos_net_perror("Sendto() failed");
            errflag++;
            break;
        }
        else {
            printf("\rSent datagram %d, length = %d bytes", ++dgrms, rc);
            if (verbose)
                printf("\n");
        }

        /*
        *   If we are to send only one, break out
        */

        if (No_Loop)
            break;

        /*
        *   Sleep for a little while so we don't bombard the network
        */

        Sleep(Sleep_Time);
    }

    if (verbose)
        printf("Freeing send buffer\n");

    free(sendbuf);

    return errflag;
}


LISTEN.C

/****************************************************************************\
*  listen.c -- sample program demonstrating NWLink.
*
*       Microsoft Developer Support
*       Copyright 1992 - 1998 Microsoft Corporation
*
*  This program is a simple example of opening a SPX socket,
*  binding to the socket, and listening for a connection.
*
****************************************************************************/
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <wsipx.h>
#include "../../testlib/testlib.h"

/*
*   Sockaddr structures
*/

SOCKADDR_IPX addr;
SOCKADDR_IPX baddr;
SOCKADDR_IPX saddr;

/*
*   Function Prototypes
*/

extern int main(int, char **);
extern int net_init(SOCKET *);
extern int do_listen(SOCKET);
extern int wait_for_connection(SOCKET, SOCKET *);
extern int do_recv_send(SOCKET);

/****************************************************************************
*
*    FUNCTION:  main( int argc, char **argv )
*
*    PURPOSE:   This is the main entry for the program
*
*
*    ARGUMENTS:argc = Number of arguments
*               argv = Array of ptrs to cmd line args
*
*
* RETURNS:   Exit code for the program
*
*\***************************************************************************/
int main(int argc, char **argv)
{
    SOCKET s, s2;

    /*
    *   Set any default values before calling parse_cmd_line
    */

    *Local_Socket_Number = 0x05;
    *(Local_Socket_Number + 1) = 0x00;
    Socket_Type = SOCK_STREAM;
    Protocol = NSPROTO_SPX;

    /*
    *   Get any options from the command line
    */

    parse_cmd_line(argc, argv);

    /*
    *   Initialize the network and set up our socket
    */

    if (net_init(&s))
        return 1;

    /*
    *   Go listen for a call
    */

    if (do_listen(s))
        return 1;

    /*
    *   Then wait for a connection
    */

    if (wait_for_connection(s, &s2))
        return 1;

    /*
    *   Receive data then send it back
    */

    if (do_recv_send(s2))
        return 1;

    /*
    *   All done
    */

    if (verbose)
        printf("closing both sockets\n");

    closesocket(s2);
    closesocket(s);

    return 0;
}

/****************************************************************************
*
*    FUNCTION:  net_init( SOCKET *skt )
*
*    PURPOSE:   Initializes the WinSock stuff and sets up our socket.
*
*
*    ARGUMENTS:SOCKET * => struct to receive our socket info
*
* RETURNS:   0 if ok
*1 if error
*
*\***************************************************************************/
int net_init(SOCKET *skt)
{
    int rc, addrlen = 16;
    WSADATA wsdata;
    SOCKET s;
    WORD    wVersionRequested;

    wVersionRequested = MAKEWORD(1,1);

    /*
    *   Initialize with the WINSOCK library
    */

    if (verbose)
        printf("calling WSAStartup(), ");

    rc = WSAStartup(wVersionRequested, &wsdata);

    if (verbose)
        printf("return = 0x%X, (%d)\n", rc, rc);

    if (rc) {
        printf("WSAStartup failed: error code = %d\n", rc);
        return 1;
    }

    if (verbose) {
        printf("contents of wsdata struct: \n");
        print_wsa(&wsdata);
    }

    /*
    *   Open a STREAM socket with SPX
    */

    if (verbose)
        printf("calling socket(addresss family = %d, socket type = %d, protocol = %d)\n", Local_Address_Family, Socket_Type, Protocol);

    s = socket(Local_Address_Family, Socket_Type, Protocol);

    if (verbose)
        printf("socket() returned 0x%X (%d)\n", s, s);

    if (s == INVALID_SOCKET) {
        dos_net_perror("Socket call failed");
        exit(1);
    }

    /*
    *   Bind to a socket.  We want to bind to a well known
    *   socket so that who ever calls us will be able to.
    */

    addr.sa_family = Local_Address_Family;
    memcpy(&addr.sa_netnum, Local_Network_Number, 4);
    memcpy(&addr.sa_nodenum, Local_Node_Number, 6);
    memcpy(&addr.sa_socket, Local_Socket_Number, 2);

    if (verbose) {
        printf("calling bind(socket = %d): \n  ", s);
        print_saddr(&addr);
    }

    rc = bind(s, (const struct sockaddr *) &addr, 16);

    if (verbose)
        printf("bind() returned 0x%X (%d)\n", rc, rc);

    if (rc == SOCKET_ERROR) {
        dos_net_perror("Error binding to socket");
        closesocket(s);
        return 1;
    }

    if (verbose)
        printf("calling getsockname(socket = %d), ", s);

    /*
    *   Get the address we bound to and print it out
    */

    addrlen = 16;
    rc = getsockname(s, (struct sockaddr *) &baddr, &addrlen);

    if (verbose)
        printf("return = 0x%X (%d)\n", rc, rc);

    if (rc == SOCKET_ERROR) {
        dos_net_perror("Error getting socket name");
        closesocket(s);
        return 1;
    }

    /*
    *   Print out the network address
    */

    if (verbose) {
        printf("addrlen = %d\n", addrlen);
        print_netaddr(baddr.sa_netnum, "Bound address = ", "\n");
    }

    *skt = s;

    return 0;
}

/****************************************************************************
*
*    FUNCTION:  do_listen( SOCKET s )
*
*    PURPOSE:   Sets the socket up for listening.
*
*    ARGUMENTS:SOCKET socket to listen on
*
* RETURNS:   0 if ok
*1 if error
*
*\***************************************************************************/
int do_listen(SOCKET s)
{
    int rc;

    /*
    *   Enable this socket as a listen socket that can
    *   take <Backlog> connection indication(s) at a time.
    */

    if (verbose)
        printf("calling listen(socket = %d, backlog = %d), ", s, Backlog);

    rc = listen(s, Backlog);

    if (verbose)
        printf("return = 0x%X (%d)\n", rc, rc);

    if (rc == SOCKET_ERROR) {
        dos_net_perror("listen call failed");
        closesocket(s);
        return 1;
    }

    /*
    *   Wait for a connection and get the connecting socket
    */

    return 0;
}

/****************************************************************************
*
*    FUNCTION:  wait_for_connection( SOCKET s, SOCKET *callsock )
*
*    PURPOSE:   Waits for someone to connect.
*
*    ARGUMENTS:SOCKET socket we are listening on
*SOCKET * => area to store client socket info after
*            connect
*
* RETURNS:   0 if ok
*1 if error
*
*\***************************************************************************/
int wait_for_connection(SOCKET s, SOCKET *callsock)
{
    SOCKET s2;
    int addrlen = 16;

    /*
    *   Go wait for somebody to connect
    */

    if (verbose)
        printf("calling accept(socket = %d), ", s);
    else
        printf("Waiting for call...\n");

    s2 = accept(s, (struct sockaddr *) &saddr, &addrlen);

    if (verbose)
        printf("return (socket) = 0x%X (%d)\n", s2, s2);

    if (s2 == INVALID_SOCKET) {
        dos_net_perror("accept call failed");
        closesocket(s);
        return 1;
    }

    /*
    *   Print out who connected to us
    */

    if (verbose) {
        printf("addrlen = %d\n", addrlen);
        print_netaddr(saddr.sa_netnum, "Callers address = ", "\n");
    }

    *callsock = s2;

    return 0;
}

/****************************************************************************
*
*    FUNCTION:  do_recv_send( SOCKET s2 )
*
*    PURPOSE:   Waits for someone to connect.
*
*    ARGUMENTS:SOCKET socket to transmit on
*
* RETURNS:   0 if ok
*1 if error
*
*\***************************************************************************/
int do_recv_send(SOCKET s2)
{
    int rc, rcount = 0;
    int nbytes, errflag = 0;
    LPSTR recvbuf;

    if (verbose)
        printf("allocating %d bytes for receive buffer\n", Receive_Length);

    recvbuf = malloc(Receive_Length);

    if (!recvbuf) {
        printf("Error allocating %d bytes for receive buffer\n", Receive_Length);
        return 1;
    }

    /*
    *   Recv packets and send them back
    */

    while (1) {

        /*
        *   Receive data
        */

        if (verbose)
            printf("calling recv(socket = %d, receive length = %d)\n", s2, Receive_Length);

        nbytes = recv(s2, recvbuf, Receive_Length, 0);

        if (nbytes == SOCKET_ERROR) {
            dos_net_perror("recv call failed");
            errflag++;
            break;
        }

        if (verbose)
            printf("Received packet %d: received %d bytes\n", rcount++, nbytes);
        else
            printf("\rReceived packet %d: received %d bytes... ", rcount++, nbytes);

        /*
        *   Send the data back
        */

        if (verbose)
            printf("calling send(socket = %d, send length = %d)\n", s2, nbytes);

        rc = send(s2, recvbuf, nbytes, 0);

        if (rc == SOCKET_ERROR) {
            dos_net_perror("send call failed");
            errflag++;
            break;
        }

        printf("Sent %d bytes", rc);

        if (verbose)
            printf("\n");

        if (No_Loop)
            break;
    }

    if (verbose)
        printf("freeing receive buffer\n");

    free(recvbuf);

    return errflag;
}


LISTEN.C

/****************************************************************************\
*  listen.c -- sample program demonstrating NWLink.
*
*       Microsoft Developer Support
*       Copyright 1992 - 1998 Microsoft Corporation
*
*  This program is a simple example of opening a SPX socket,
*  binding to the socket, and listening for a connection.
*
****************************************************************************/
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <wsipx.h>
#include "../../testlib/testlib.h"

/*
*   Sockaddr structures
*/

SOCKADDR_IPX addr;
SOCKADDR_IPX baddr;
SOCKADDR_IPX saddr;

/*
*   Function Prototypes
*/

extern int main(int, char **);
extern int net_init(SOCKET *);
extern int do_listen(SOCKET);
extern int wait_for_connection(SOCKET, SOCKET *);
extern int do_recv_send(SOCKET);

/****************************************************************************
*
*    FUNCTION:  main( int argc, char **argv )
*
*    PURPOSE:   This is the main entry for the program
*
*
*    ARGUMENTS:argc = Number of arguments
*               argv = Array of ptrs to cmd line args
*
*
* RETURNS:   Exit code for the program
*
*\***************************************************************************/
int main(int argc, char **argv)
{
    SOCKET s, s2;

    /*
    *   Set any default values before calling parse_cmd_line
    */

    *Local_Socket_Number = 0x05;
    *(Local_Socket_Number + 1) = 0x00;
    Socket_Type = SOCK_STREAM;
    Protocol = NSPROTO_SPX;

    /*
    *   Get any options from the command line
    */

    parse_cmd_line(argc, argv);

    /*
    *   Initialize the network and set up our socket
    */

    if (net_init(&s))
        return 1;

    /*
    *   Go listen for a call
    */

    if (do_listen(s))
        return 1;

    /*
    *   Then wait for a connection
    */

    if (wait_for_connection(s, &s2))
        return 1;

    /*
    *   Receive data then send it back
    */

    if (do_recv_send(s2))
        return 1;

    /*
    *   All done
    */

    if (verbose)
        printf("closing both sockets\n");

    closesocket(s2);
    closesocket(s);

    return 0;
}

/****************************************************************************
*
*    FUNCTION:  net_init( SOCKET *skt )
*
*    PURPOSE:   Initializes the WinSock stuff and sets up our socket.
*
*
*    ARGUMENTS:SOCKET * => struct to receive our socket info
*
* RETURNS:   0 if ok
*1 if error
*
*\***************************************************************************/
int net_init(SOCKET *skt)
{
    int rc, addrlen = 16;
    int non_block = 1;
    WSADATA wsdata;
    SOCKET s;
    WORD    wVersionRequested;

    wVersionRequested = MAKEWORD(1,1);

    /*
    *   Initialize with the WINSOCK library
    */

    if (verbose)
        printf("calling WSAStartup(), ");

    rc = WSAStartup(wVersionRequested, &wsdata);

    if (verbose)
        printf("return = 0x%X, (%d)\n", rc, rc);

    if (rc) {
        printf("WSAStartup failed: error code = %d\n", rc);
        return 1;
    }

    if (verbose) {
        printf("contents of wsdata struct: \n");
        print_wsa(&wsdata);
    }

    /*
    *   Open a STREAM socket with SPX
    */

    if (verbose)
        printf("calling socket(addresss family = %d, socket type = %d, protocol = %d)\n", Local_Address_Family, Socket_Type, Protocol);

    s = socket(Local_Address_Family, Socket_Type, Protocol);

    if (verbose)
        printf("socket() returned 0x%X (%d)\n", s, s);

    if (s == INVALID_SOCKET) {
        dos_net_perror("Socket call failed");
        exit(1);
    }

    /*
    *   Set the socket as non-blocking
    */

    if (verbose)
        printf("Calling ioctlsocket(socket = %d, cmd = FIONBIO argp = %d), ", s, non_block);

    rc = ioctlsocket(s, FIONBIO, &non_block);

    if (verbose)
        printf("return = 0x%X (%d)\n", rc, rc);

    if (rc == SOCKET_ERROR) {
        dos_net_perror("ioctlsocket() call failed");
        closesocket(s);
        exit(1);
    }

    /*
    *   Bind to a socket.  We want to bind to a well known
    *   socket so that who ever calls us will be able to.
    */

    addr.sa_family = Local_Address_Family;
    memcpy(&addr.sa_netnum, Local_Network_Number, 4);
    memcpy(&addr.sa_nodenum, Local_Node_Number, 6);
    memcpy(&addr.sa_socket, Local_Socket_Number, 2);

    if (verbose) {
        printf("calling bind(socket = %d): \n  ", s);
        print_saddr(&addr);
    }

    rc = bind(s, (const struct sockaddr *) &addr, 16);

    if (verbose)
        printf("bind() returned 0x%X (%d)\n", rc, rc);

    if (rc == SOCKET_ERROR) {
        dos_net_perror("Error binding to socket");
        closesocket(s);
        return 1;
    }

    if (verbose)
        printf("calling getsockname(socket = %d), ", s);

    /*
    *   Get the address we bound to and print it out
    */

    addrlen = 16;
    rc = getsockname(s, (struct sockaddr *) &baddr, &addrlen);

    if (verbose)
        printf("return = 0x%X (%d)\n", rc, rc);

    if (rc == SOCKET_ERROR) {
        dos_net_perror("Error getting socket name");
        closesocket(s);
        return 1;
    }

    /*
    *   Print out the network address
    */

    if (verbose) {
        printf("addrlen = %d\n", addrlen);
        print_netaddr(baddr.sa_netnum, "Bound address = ", "\n");
    }

    *skt = s;

    return 0;
}

/****************************************************************************
*
*    FUNCTION:  do_listen( SOCKET s )
*
*    PURPOSE:   Sets the socket up for listening.
*
*    ARGUMENTS:SOCKET socket to listen on
*
* RETURNS:   0 if ok
*1 if error
*
*\***************************************************************************/
int do_listen(SOCKET s)
{
    int rc;

    /*
    *   Enable this socket as a listen socket that can
    *   take <Backlog> connection indication(s) at a time.
    */

    if (verbose)
        printf("calling listen(socket = %d, backlog = %d), ", s, Backlog);

    rc = listen(s, Backlog);

    if (verbose)
        printf("return = 0x%X (%d)\n", rc, rc);

    if (rc == SOCKET_ERROR) {
        dos_net_perror("listen call failed");
        closesocket(s);
        return 1;
    }

    /*
    *   Wait for a connection and get the connecting socket
    */

    return 0;
}

/****************************************************************************
*
*    FUNCTION:  wait_for_connection( SOCKET s, SOCKET *callsock )
*
*    PURPOSE:   Waits for someone to connect.
*
*    ARGUMENTS:SOCKET socket we are listening on
*SOCKET * => area to store client socket info after
*            connect
*
* RETURNS:   0 if ok
*1 if error
*
*\***************************************************************************/
int wait_for_connection(SOCKET s, SOCKET *callsock)
{
    SOCKET s2;
    int addrlen = 16;

    /*
    *   Poll the accept() call until it returns
    *   something other than WSAEWOULDBLOCK
    *   We will print out little '.' while we wait.
    */

    if (verbose)
        printf("Polling socket every 1/2 second");
    else
        printf("Waiting for call");

    do {
        s2 = accept(s, (struct sockaddr *) &saddr, &addrlen);

        /*
        *   Wait 1/2 second and try again
        */

        Sleep(500);

        printf(".");
    }
    while (s2 == INVALID_SOCKET && h_errno == WSAEWOULDBLOCK);

    printf("\n");

    if (verbose)
        printf("\naccept() returned, new socket = 0x%X (%d)\n", s2, s2);

    if (s2 == INVALID_SOCKET) {
        dos_net_perror("accept call failed");
        closesocket(s);
        return 1;
    }

    /*
    *   Print out who connected to us
    */

    if (verbose) {
        printf("addrlen = %d\n", addrlen);
        print_netaddr(saddr.sa_netnum, "Callers address = ", "\n");
    }

    *callsock = s2;

    return 0;
}

/****************************************************************************
*
*    FUNCTION:  do_recv_send( SOCKET s2 )
*
*    PURPOSE:   Waits for someone to connect.
*
*    ARGUMENTS:SOCKET socket to transmit on
*
* RETURNS:   0 if ok
*1 if error
*
*\***************************************************************************/
int do_recv_send(SOCKET s2)
{
    int rc, rcount = 0;
    int nbytes, errflag = 0;
    LPSTR recvbuf;

    if (verbose)
        printf("allocating %d bytes for receive buffer\n", Receive_Length);

    recvbuf = malloc(Receive_Length);

    if (!recvbuf) {
        printf("Error allocating %d bytes for receive buffer\n", Receive_Length);
        return 1;
    }

    /*
    *   Recv packets and send them back
    */

    while (1) {

        /*
        *   Receive data
        */

        if (verbose)
            printf("calling recv(socket = %d, receive length = %d)\n", s2, Receive_Length);

        nbytes = recv(s2, recvbuf, Receive_Length, 0);

        if (nbytes == SOCKET_ERROR) {
            dos_net_perror("recv call failed");
            errflag++;
            break;
        }

        if (verbose)
            printf("Received packet %d: received %d bytes\n", rcount++, nbytes);
        else
            printf("\rReceived packet %d: received %d bytes... ", rcount++, nbytes);

        /*
        *   Send the data back
        */

        if (verbose)
            printf("calling send(socket = %d, send length = %d)\n", s2, nbytes);

        rc = send(s2, recvbuf, nbytes, 0);

        if (rc == SOCKET_ERROR) {
            dos_net_perror("send call failed");
            errflag++;
            break;
        }

        printf("Sent %d bytes", rc);

        if (verbose)
            printf("\n");

        if (No_Loop)
            break;
    }

    if (verbose)
        printf("freeing receive buffer\n");

    free(recvbuf);

    return errflag;
}


PING.C

/****************************************************************************\
*  ping.c -- sample program demonstrating NWLink.
*
*       Microsoft Developer Support
*       Copyright 1992 - 1998 Microsoft Corporation
*
*  This program is a simple example of opening a socket,
*   binding to the socket, receiving a packet and sending
*  that packet back to the original sender.
*
****************************************************************************/
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <wsipx.h>
#include <wsnwlink.h>
#include "../testlib/testlib.h"

/*
*   Sockaddr structures
*/

SOCKADDR_IPX addr;
SOCKADDR_IPX baddr;
SOCKADDR_IPX raddr;
int addrlen;

/*
*   Function Prototypes
*/

extern int main(int, char **);
extern int net_init(SOCKET *);
extern int do_ping(SOCKET);

/****************************************************************************
*
*    FUNCTION:  main( int argc, char **argv )
*
*    PURPOSE:   This is the main entry for the program
*
*
*    ARGUMENTS:argc = Number of arguments
*               argv = Array of ptrs to cmd line args
*
*
* RETURNS:   Exit code for the program
*
*\***************************************************************************/
int main(int argc, char **argv)
{
    SOCKET s;

    /*
    *   Set our default values before calling parse_cmd_line
    */

    *Local_Socket_Number = 0x30;
    *(Local_Socket_Number+1) = 0x00;
    Receive_Length = 2048;

    /*
    *   Get any command line options
    */

    parse_cmd_line(argc, argv);

    /*
    *   Initialize the network and set up our socket
    */

    if (net_init(&s))
        return 1;

    do_ping(s);

    /*
    *   All done (We only get here on error)
    */

    if (verbose)
        printf("calling closesocket()");

    closesocket(s);
    return 0;
}

/****************************************************************************
*
*    FUNCTION:  net_init( SOCKET *skt )
*
*    PURPOSE:   Initializes the WinSock stuff and sets up our socket.
*
*
*    ARGUMENTS:SOCKET * => struct to receive our socket info
*
* RETURNS:   0 if ok
*1 if error
*
*\***************************************************************************/
int net_init(SOCKET *skt)
{
    int rc, addrlen;
    WSADATA wsdata;
    SOCKET s;
    WORD    wVersionRequested;

    /*
    *   Initialize with the WINSOCK library
    */

    if (verbose)
        printf("calling WSAStartup(), ");

    wVersionRequested = MAKEWORD(1,1);
    rc = WSAStartup(wVersionRequested, &wsdata);

    if (verbose)
        printf("return = 0x%X (%d)\n", rc, rc);

    if (rc) {
        printf("WSAStartup failed: error code = %d\n", rc);
        return 1;
    }

    if (verbose) {
        printf("contents of wsdata struct: \n");
        print_wsa(&wsdata);
    }

    if (verbose)
        printf("calling socket(address family = %d, socket type = %d, protocol = %d)\n", Local_Address_Family, Socket_Type, Protocol);

    /*
    *   Open a DATAGRAM socket with IPX
    */

    s = socket(AF_NS, SOCK_DGRAM, NSPROTO_IPX);

    if (verbose)
        printf("socket() returned 0x%lX\n", s);

    if (s == INVALID_SOCKET) {
        dos_net_perror("Socket call failed");
        exit(1);
    }

    /*
    *   Bind to a socket.  We want to bind to a well known
    *   socket so that the app. that sends us a packet will
    *   know where to send it.
    */

    addr.sa_family = Local_Address_Family;

    memcpy(&addr.sa_netnum, Local_Network_Number, 4);
    memcpy(&addr.sa_nodenum, Local_Node_Number, 6);
    memcpy(&addr.sa_socket, Local_Socket_Number, 2);

    if (verbose) {
        printf("calling bind():\n  ");
        print_saddr(&addr);
    }

    rc = bind(s, (const struct sockaddr *) &addr, 16);

    if (verbose)
        printf("bind() returned 0x%X\n", rc);

    if (rc == SOCKET_ERROR) {
        dos_net_perror("Error binding to socket");
        closesocket(s);
        return 1;
    }
    /*
    *   Set the packet type for this socket
    */

    if (verbose)
        printf("Calling setsockopt for packet type %d\n", Local_Packet_Type);

    rc = setsockopt(s, SOL_SOCKET, IPX_PTYPE, (const char *) &Local_Packet_Type, 4);

    if (rc == SOCKET_ERROR)
        dos_net_perror("setsockopt() call failed");

    /*
    *   Get the address we bound to and print it out
    */

    if (verbose)
        printf("Calling getsockname(socket = %d), ");

    addrlen = 16;
    rc = getsockname(s, (struct sockaddr *) &baddr, &addrlen);

    if (verbose)
        printf("return = 0x%X (%d)\n", rc, rc);

    if (rc == SOCKET_ERROR) {
        dos_net_perror("Error getting socket name");
        closesocket(s);
        return 1;
    }

    /*
    *   Print out the network address
    */

    if (verbose) {
        printf("addrlen = %d\n", addrlen);
        print_netaddr(baddr.sa_netnum, "Bound address = ", "\n");
    }

    if (verbose)
        printf("Allocating %d byte receive buffer\n", Receive_Length);

    /*
    *   Set up socket to send back
    */

    *skt = s;

    return 0;
}

/****************************************************************************
*
*    FUNCTION:  do_ping( SOCKET s )
*
*    PURPOSE:   This will receive a packet then send it back.
*
*    ARGUMENTS:SOCKET socket we are transmitting on.
*
* RETURNS:   0 if ok
*1 if error
*
*\***************************************************************************/
int do_ping(SOCKET s)
{
    LPSTR recvbuf;
    int nbytes, rc, errflag = 0;
    int rcvpkts = 0, sndpkts = 0;

    /*
    *   Allocate a buffer for receives
    */

    recvbuf = malloc(Receive_Length);

    if (!recvbuf) {
        printf("Error allocating %d bytes for receive buffer\n", Receive_Length);
        return 1;
    }

    /*
    *   This loop will receive a packet and then send
    *   it back to whoever sent it to us.
*
*      To exit the loop - hit CTRL-C.
    */

    while (1) {

        /*
        *   Recv a packet
        */

        /*
        *    NOTE:  If you want to know the packet type field of the
        *           packet you just received, look at the byte at
*           raddr.sa_ptype.  The addrlen returned will be
*           15.
*
        *           By using the addrlen field unchanged when sending
        *           the packet back, you will send the packet back with
        *           the same packet type that is was sent with originally
        */

        addrlen = 16;

        if (verbose)
            printf("calling recvfrom(socket = %d, len = %d)\n", s, Receive_Length);

        nbytes = recvfrom(s, recvbuf, Receive_Length, 0, (struct sockaddr *) &raddr, &addrlen);

        /*
        *   If error - print it and exit
        */

        if (nbytes == SOCKET_ERROR) {
            dos_net_perror("recvfrom() failed");
            errflag++;
            break;
        }

        if (verbose) {
            printf("received %d bytes, raddr = \n  ", nbytes);
            print_saddr(&raddr);
        }

        if (!verbose)
            printf("\rRecv packet number %d", ++rcvpkts);

        /*
        *   Send the data back
        */

        if (verbose)
            printf("calling sendto(socket = %d, len = %d\n", s, nbytes);

addrlen = 16;
        rc = sendto(s, recvbuf, nbytes, 0, (const struct sockaddr *) &raddr, addrlen);

        if (verbose)
            printf("sendto() returned 0x%X\n", rc);

        if (rc == SOCKET_ERROR) {
            dos_net_perror("sendto() failed");
            errflag++;
            break;
        }

        if (!verbose)
            printf(" : Send packet number %d", ++sndpkts);

    }

    if (verbose)
        printf("Freeing receive buffer\n");

    free(recvbuf);

    return errflag;
}


DOSPING.C

/*=====================================================================
P I N G . C

This is the DOS side ping program.  It uses Novell's IPX/SPX APIs,
requiring their SDK. It will send/receive 1-n IPX packets.
======================================================================*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>
#include <nxtdos.h>

#define TIMER (unsigned long far *) 0x0000046cL

char *progname;
IPXHeader *ipx;
int send_socket=0;
int recv_socket;
int datasize = 1;
ECB send_ecb;
ECB recv_ecb[10];
char mynodeaddr[6];
char sendb[2048];
char recvb[2048];

int ptype = 4;
int niters = 1;

/**
    This is the address we try to send to.  When
    we find a server, this address is replaced by the address
    of the exact server.  The address is made up of:
    bytes 1-4    = network number
    bytes 5-10   = node number
    bytes 11-12  = socket address

**/

char destaddr[12] = {
    0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x30, 0x00
};

/** Function Prototypes **/

void   hang_recvs(void);
void   cancel_recvs(void);
void   do_recv(int);
int    get_recv(void);
void   find_a_server(void);
void   print_address(char *);
void   print_network(char *);
void   print_nodeaddr(char *);
void   parse_cmd_line(int, char **);
void   swap_dwf(char *);
char * get_node_number(char * cmd);
char * get_network_number(char * cmd);
/********************************************************************
m a i n
*********************************************************************/
int main(int argc, char **argv)
{
    char *p;
    unsigned long start = *TIMER;
    unsigned long end;
    int cnt = 0;
    long sendpkt = 0L;
    long recvpkt = 0L;
    int ttime;
    float total = 0;

    progname = *argv;
    for (p = progname; *p; p++) {
    if (*p == '\\')
progname = p + 1;
    }

    /** Make sure IPX is loaded **/

    if (IPXInitialize()) {
printf("%s: IPX IS NOT LOADED\n", progname);
    exit(1);
    }
    recv_socket = *(short *)(destaddr + 10);
    parse_cmd_line(argc, argv);
    print_network(destaddr);
    print_nodeaddr(destaddr + 4);
    printf("packet type:    %d\n",ptype);
    printf("datasize:       %d\n", datasize);
    printf("n iterations:   %d\n", niters);

    if (IPXOpenSocket(&send_socket, 0)) {
printf("%s: ERROR: unable to open socket\n", progname);
exit(1);
    }
    recv_socket = send_socket;
    if (IPXGetLocalTarget(destaddr, send_ecb.immediateAddress, &ttime)) {
printf("%s: ERROR: unable to get local target\n", progname);
exit(1);
    }

    memcpy(mynodeaddr, send_ecb.immediateAddress, 6);

    /** Go find a server **/

    find_a_server();
hang_recvs();

    /** Do # iterations (default = 1) **/
while (cnt < niters) {
    int rcv;
/** send a packet **/
*(sendb + 5) = ptype;
    memcpy(sendb + 6, destaddr, 12);
    memcpy(send_ecb.immediateAddress, mynodeaddr, 6);
    send_ecb.socketNumber = send_socket;
    send_ecb.fragmentCount = 1;
    send_ecb.fragmentDescriptor[0].address = sendb;
    send_ecb.fragmentDescriptor[0].size = 30 + datasize;

IPXSendPacket(&send_ecb);

while (send_ecb.inUseFlag) /* wait for it to finish */
;

if (send_ecb.completionCode) {
cancel_recvs();
IPXCloseSocket(send_socket);
printf("%s: Error sending packet: %d\n", progname, send_ecb.completionCode);
exit(1);
}

    sendpkt++;
    total += 30 + datasize;     /* Count the bytes */
    printf("\rSend packet Number %d ", sendpkt);

    if ((rcv = get_recv()) == -1)
goto EXIT;

    do_recv(rcv);
    total += 30 + datasize;     /* Count the bytes */

    cnt++;
    recvpkt++;
    printf(": Recv packet Number %d ", recvpkt);
}

/** We did 1000 packets - print KB per second **/
EXIT:
end = *TIMER;
end = (end - start) / 18.2; /* Num seconds it took */

total = (float)((total / 1024.0) / end);

    /** All done - cancel recvs and exit **/
    cancel_recvs();
    IPXCloseSocket(send_socket);
    printf("\n%ld packets \n", recvpkt);
    if (recvpkt)
printf("Average time per iteration: %5.3f\n",total/recvpkt);
    exit(0);
}

/*page**************************************************************
h a n g _ r e c v s

Arguments - None

Returns - nothing
*******************************************************************/
void hang_recvs(void)
{
    int i;

    for (i = 0; i < 10; i++) {

/** Don't hang recv on one that is already hung **/

if (recv_ecb[i].inUseFlag)
    continue;

/** Hang a recv **/

memset(&recv_ecb[i], 0, sizeof(ECB));
recv_ecb[i].socketNumber = recv_socket;
recv_ecb[i].fragmentCount = 1;
recv_ecb[i].fragmentDescriptor[0].address = recvb;
recv_ecb[i].fragmentDescriptor[0].size = 2048;
IPXListenForPacket(&recv_ecb[i]);
    }

    /** All done **/

    return;
}

/*page**************************************************************
c a n c e l _ r e c v s

Arguments - None

Returns - nothing
*******************************************************************/
void cancel_recvs(void)
{
    int i;

    for (i = 0; i < 10; i++) {

/** If recv not in use - skip it **/

if (!recv_ecb[i].inUseFlag)
    continue;

/** Cancel the recv **/

IPXCancelEvent(&recv_ecb[i]);
    }

    /** All done **/

    return;
}

/*page**************************************************************
d o _ r e c v

Arguments - i = recv index to hang

Returns - nothing
*******************************************************************/
void do_recv(int i)
{
    memset(&recv_ecb[i], 0, sizeof(ECB));
    recv_ecb[i].socketNumber = recv_socket;
    recv_ecb[i].fragmentCount = 1;
    recv_ecb[i].fragmentDescriptor[0].address = recvb;
    recv_ecb[i].fragmentDescriptor[0].size = 2048;
    IPXListenForPacket(&recv_ecb[i]);
    return;
}

/*page**************************************************************
g e t _ r e c v

Arguments - none

Returns - -1 = User hit a key
  else = recv index that was hit
*******************************************************************/
int get_recv(void)
{
    int i;

    /** **/

    while (!kbhit()) {
for (i = 0; i < 10; i++) {
if (!recv_ecb[i].inUseFlag)
return i;
}
//      try resending the bugger...maybe we be out of synch?..bug,bug,bug???
    IPXSendPacket(&send_ecb);
    }

    /** Return that user hit a key **/

    return -1;
}

/*******************************************************************
function name:      f i n d _ a _ s e r v e r
    Description:    make sure can find the machine we want to send to
    Arguments:      none
    Returns:        none
*********************************************************************/
void find_a_server(void)
{
    long start;
    long end;
    ECB send_ecb;
    ECB recv_ecb;

    /** Setup the recv ecb and listen for a packet **/

    memset(&recv_ecb, 0, sizeof(ECB));
    recv_ecb.socketNumber = recv_socket;
    recv_ecb.fragmentCount = 1;
    recv_ecb.fragmentDescriptor[0].address = recvb;
    recv_ecb.fragmentDescriptor[0].size = 31;
    IPXListenForPacket(&recv_ecb);

    memset(&send_ecb, 0, sizeof(ECB));
    memcpy(sendb + 6, destaddr, 12);
    send_ecb.socketNumber = send_socket;
    memcpy(send_ecb.immediateAddress, mynodeaddr, 6);
    send_ecb.fragmentCount = 1;
    send_ecb.fragmentDescriptor[0].address = sendb;
    send_ecb.fragmentDescriptor[0].size = 31;

    IPXSendPacket(&send_ecb);

    while (send_ecb.inUseFlag && !kbhit())
;

    if (send_ecb.completionCode) {
printf("%s: ERROR: send completion code 0x%02x\n", progname, send_ecb.completionCode);
exit(1);
    }

    /** Wait awhile for the recv to pay off **/

    start = *TIMER;

    while (recv_ecb.inUseFlag && !kbhit() && (*TIMER - start) < 180L)
;
    end = *TIMER;

    /** If we timed out - then we couldn't find a server **/

    if (end - start >= 180L) {
IPXCancelEvent(&recv_ecb);
IPXCloseSocket(send_socket);
printf("%s: Unable to find a server\n", progname);
exit(1);
    }

    /** If recv still in use - error **/

    if (recv_ecb.inUseFlag) {
IPXCancelEvent(&recv_ecb);
IPXCloseSocket(send_socket);
printf("%s: Program terminated by a keystroke\n", progname);
exit(1);
    }

    /** If recv error - clean up and exit **/

    if (recv_ecb.completionCode) {
IPXCancelEvent(&recv_ecb);
IPXCloseSocket(send_socket);
printf("%s: Error receiving packet: %d\n", progname, recv_ecb.completionCode);
exit(1);
    }

    /** Get the server address and print it out **/

    memcpy(destaddr, recvb + 18, 12);   /* Set the address to send to */
    memcpy(mynodeaddr, recv_ecb.immediateAddress, 6);
    printf("Found server: ");
    print_address(destaddr);

    /** All done **/

    return;
}

/********************************************************************
p r i n t _ a d d r e s s

Arguments - netaddr = ptr to 14 bytes address to print

Returns - nothing
*********************************************************************/
void print_address(char *netaddr)
{
    int i;

    for (i = 0; i < 12; i++) {
if ((i == 4) || (i == 10))
    printf(" ");
printf("%02X", (unsigned char) *(netaddr + i));
}
    printf("\n");
    return;
}

void print_network(char *p)
{
    int i;
    printf("Network Number: ");
    for (i = 0; i < 4; i++)
printf("%02X", (unsigned char) *p++);
    printf("\n");
    return;
}

void print_nodeaddr(char *p)
{
    int i;

    printf("Node Address:   ");
    for (i = 0; i < 6; i++)
printf("%02X", (unsigned char) *p++);
    printf("\n");
    return;
}

/*-----------------------------------------------------------------
function name: parse_cmd_line
Description:
    Parse the command line for the parameters and host name.
Arguments:
       -i <number of iterations>
       -p <packet type>
       -d <data size>
  NOTE: must be < frame size - 14 (MAC length) - 30 (IPX header)
       -n <network number>
  NOTE: you can ignore this if on the same subnet.  Must be 4 hex
bytes
       -a <destination address>
  NOTE: must be 6 hex bytes representing the physical NIC address of
the remote machine.
       -s <socket>
  Two byte socket number
Returns:        Nothing
----------------------------------------------------------------------*/
void parse_cmd_line(int argc, char **argv)
{
    short socket;

    argv++;                      /* skip to first arg */
    while (--argc) {             /* do all args */
if (**argv == '-') {
    ++(*argv);
    switch(tolower(**argv)) {
case 'i':
    niters = atoi(++(*argv));
    if (niters < 0)
       niters = 1;
    break;
case 'p':
    ptype = atoi(++(*argv));
    if (ptype < 0 || ptype > 255) {
ptype = 4;
printf("%s: Invalid packet type.  Setting to default = %d\n",
progname,ptype);
    }
    break;
case 'd':  /*amount of data to send in a frame */
    datasize = atoi(++(*argv));
    if (!datasize) {
datasize = 1;
printf("%s: Datasize cannot be zero. Setting to default = 1\n", progname);
    }
    break;
case 's':  /* socket number, default = 0x3000 */
    socket = (short)strtol(++(*argv), NULL, 16);
    printf("New socket = 0x%x\n", socket);
    *(short *)(destaddr + 10) = socket;
    swap_dwf(destaddr + 10);
    break;
case 'n':  /* network number, default = local net (= 0) */
    ++(*argv);
    memcpy(destaddr, get_network_number(*argv),4);
    if (destaddr[0] == 'X') {
printf("\n****!\n%s is an incorrect net number.\n****!\n", *argv);
printf("%s E.G.: -nAABBCCDD (I.E.: 8 hex digits)\n");
exit(1);
    }
    break;
case 'a': /* remote address, have to know this */
    ++(*argv);
    memcpy(destaddr+4, get_node_number(*argv), 6);
    if (*(destaddr+4) == 'X') {
       printf("\n****!\n%s is an incorrect Node address.\n****!\n", *argv);
       printf("\n E.G.: -a 023C8DAADDCC (12 hex digits)\n");
       exit(1);
    }
    break;
default:
    printf("%s [-p] [-d] [-nxxxxxxxx] [-axxxxxxxxxxxx] [-sx]\n", progname);
    printf("     -p - packet type\n");
    printf("     -d - data size\n");
    printf("     -n - network number (in hex)\n");
    printf("     -a - address (in hex)\n");
    printf("     -s - socket number (in hex)\n");
    exit(0);
    }
    argv++;
}
    }
    return;
}
/**********************************************************************
    g e t _ h e x _ b y t e

    Converts the character passed in to a hexadecimal nibble.

    Arguments:    char    character to convert

    Returns:      UCHAR   hex nibble
**************************************************************************/
char get_hex_byte(char ch)
{
    if (ch >= '0' && ch <= '9')
    return (ch - '0');

    if (ch >= 'A' && ch <= 'F')
    return ((ch - 'A') + 0x0A);

    return -1;
}
/**********************************************************************
    g e t _ h e x _ s t r i n g

    Reads in a character string containing hex digits and converts
    it to a hexadecimal number.

    Arguments:    src   => source string to convert
  dest  => destination for hex number
  num   => number of chars to convert

    Returns:      nothing
**************************************************************************/
char get_hex_string(char * src, char * dest, int num)
{
    char *   q = src;
    char     hexbyte1,hexbyte2;

    strupr(q);
    while (num--)
      {hexbyte1 = get_hex_byte(*q++);
       hexbyte2 = get_hex_byte(*q++);
       if ( (hexbyte1 < 0) || (hexbyte2 < 0) )
      return -1;
       *dest++ = (hexbyte1 << 4) + hexbyte2;
      }

    return(0);
}
/*************************************************************************
    g e t _ n o d e _ n u m b e r

    Reads a node number from the given string.

    Arguments:    cmd  => string to read from

    Returns:      hex node number
**************************************************************************/
char * get_node_number(char * cmd)
{
    static char hex_num[6];

    memset(hex_num, 0, 6);

    if (strlen(cmd) != 12){
    hex_num[0] = 'X';
    return hex_num;
       }

    if (get_hex_string(cmd, hex_num, 6) < 0)
    hex_num[0] = 'X';
    return hex_num;
}
/**************************************************************************
    g e t _ n e t w o k _ n u m b e r

    Reads a network number from the given string.

    Arguments:    cmd   string to read from

    Returns:      hex network number
**************************************************************************/
char * get_network_number(char * cmd)
{
    static char hex_num[4];

    memset(hex_num, 0, 4);

    if (strlen(cmd) != 8) {
    hex_num[0] = 'X';
    return(hex_num);
    }

    if (get_hex_string(cmd, hex_num, 4) < 0)
    hex_num[0] = 'X';

    return hex_num;
}
/*******************************************************************
s w a p _ d w f

Swap the bytes pointed to by p.

Arguments - p = ptr to word to swap

Returns - nothing
********************************************************************/
void swap_dwf(char *p)
{
    char tmp;

    tmp = *p;
    *p = *(p + 1);
    *(p + 1) = tmp;
    return;
}


CMDLINE.C

/****************************************************************************\
*  cmdline.c -- sample program library demonstrating NWLink.
*
*       Microsoft Developer Support
*       Copyright 1992 - 1998 Microsoft Corporation
*
*  Demonstrates basic sockets programming with the Windows Sockets API
*  using the NWLink transport.
*
****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include "externs.h"
#include <wsipx.h>
#include "testlib.h"

/*
*   Global variables that can be set on the command line
*/

int     verbose = 0;
int     Socket_Type = SOCK_DGRAM;
int     Protocol = NSPROTO_IPX;
int     Backlog = 1;
int     No_Broadcast = 0;
int     No_Loop = 0;
int     Sleep_Time = 250;
int     Send_Length = 1024;
int     Receive_Length = 1024;
int     Local_Packet_Type = 0;
int     Send_Packet_Type = 9;
int     Filter_Packet_Type = 0;
int     Local_Address_Family = AF_NS;
int     Remote_Address_Family = AF_NS;
char    Local_Network_Number[4]  = {0x00, 0x00, 0x00, 0x00};
char    Local_Node_Number[6]     = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
char    Local_Socket_Number[2]   = {0x00, 0x00};
char    Remote_Network_Number[4] = {0x00, 0x00, 0x00, 0x00};
char    Remote_Node_Number[6]    = "\xFF\xFF\xFF\xFF\xFF\xFF";
char    Remote_Socket_Number[2]  = {0x12, 0x34};

/*
*   Function prototypes for this file
*/

void  usage(LPSTR);
void  dump_defaults(LPSTR);
void  get_hex_string(LPSTR, LPSTR, int);
UCHAR get_hex_byte(char);

/****************************************************************************
*
*    FUNCTION:  parse_cmd_line( int argc, char **argv )
*
*    DESCRIPTION:  Routine used by all of the WinSock test programs for NWLink.
*        Parses the command line and sets the respective global variables.
*
*    ARGUMENTS: char **     => array of command line arguments
*                int         number of command line arguments
*
* RETURNS:  nothing
*
*\***************************************************************************/
void parse_cmd_line(int argc, char **argv)
{
    LPSTR p;
    int num = 1;

    argc--;

    /*
    *   Parse each command line parameter
    */

    while (num <= argc) {
        /*
        *   If option starts with '-' or '/', skip it
        */

        p = strupr(argv[num++]);
        if (*p == '-' || *p == '/')
            p++;

        /*
        *   Help ?
        */

        printf("***> command: %s\n:",p);
        if (*p == 'h' || *p == 'H' || *p == '?' || !stricmp(p, "help"))
    usage(argv[0]);

        /*
        *   Verbose option?
        */

        if (!stricmp(p, "v") || !stricmp(p, "verbose")) {
            verbose++;

            printf("verbose is ON\n");
            continue;
        }

        /*
        *   Display default values ?
        */

        if (!strnicmp(p, "default", 7)) {
            p+= 7;

            dump_defaults(argv[0]);
        }

        /*
        *   Local address family ?
        */

        if (!strnicmp(p, "laf:", 4)) {
            p+= 4;

            /*
            *   Get the address family from the option
            */

            Local_Address_Family = atoi(p);

            if (verbose)
                printf("Local_Address_Family = %d\n", Local_Address_Family);
            continue;
        }

        /*
        *   Remote address family ?
        */

        if (!strnicmp(p, "raf:", 4)) {
            p+= 4;

            /*
            *   Get the address family from the option
            */

            Remote_Address_Family = atoi(p);

            if (verbose)
                printf("Remote_Address_Family = %d\n", Remote_Address_Family);
            continue;
        }

        /*
        *   Socket type ?
        */

        if (!strnicmp(p, "st:", 3)) {
            p+= 3;

            /*
            *   Get the socket type from the option
            */

            Socket_Type = atoi(p);
            if (verbose)
printf("Socket_Type = %d\n", Socket_Type);
            continue;
        }
#if(0)
        /*
        *   Socket family ?
        */

        if (!strnicmp(p, "sf:", 3)) {
            p+= 3;

            /*
            *   Get the socket family from the option
            */

            Socket_Family = get_socket_family(p);
            continue;
        }
#endif
        /*
        *   Protocol ?
        */

        if (!strnicmp(p, "proto:", 6)) {
            p+= 6;

            /*
            *   Get the protocol from the option
            */

            Protocol = atoi(p);

            if (verbose)
        printf("Protocol = %d\n", Protocol);
            continue;
        }

        /*
        *   Local network number ?
        */

        if (!strnicmp(p, "lnet:", 5)) {
            p+= 5;

            /*
            *   Get the local network number from the option
            */

            memcpy(Local_Network_Number, get_network_number(p), 4);
            if (verbose) {
                printf("Local_Network_Number = ");
                print_network_num(Local_Network_Number);
                printf("\n");
            }
            continue;
        }

        /*
        *   Local node number ?
        */

        if (!strnicmp(p, "lnode:", 6)) {
            p+= 6;

            /*
            *   Get the local network number from the option
            */

            memcpy(Local_Node_Number, get_node_number(p), 6);
            if (verbose) {
                printf("Local_Node_Number = ");
 print_node_num(Local_Node_Number);
                printf("\n");
            }
            continue;
        }

        /*
        *   Remote network number ?
        */

        if (!strnicmp(p, "rnet:", 5)) {
            p+= 5;

            /*
            *   Get the remote network number from the option
            */

            memcpy(Remote_Network_Number, get_network_number(p), 4);
            if (verbose) {
                printf("Remote_Network_Number = ");
                print_network_num(Remote_Network_Number);
                printf("\n");
            }
            continue;
        }

        /*
        *   Remote node number ?
        */

        if (!strnicmp(p, "rnode:", 6)) {
            p+= 6;

            /*
            *   Get the remote network number from the option
            */

            memcpy(Remote_Node_Number, get_node_number(p), 6);
            if (verbose) {
                printf("Remote_Node_Number = ");
 print_node_num(Remote_Node_Number);
                printf("\n");
            }
            continue;
        }

        /*
        *   Local socket number ?
        */

        if (!strnicmp(p, "lsock:", 6)) {
            p+= 6;

            /*
            *   Get the local socket number from the command line
            */

            memcpy(Local_Socket_Number, get_socket_number(p), 2);

    if (verbose) {
                printf("Local_Socket_Number = ");
                print_socket_num(Local_Socket_Number);
                printf("\n");
            }
            continue;
        }

        /*
        *   Remote socket number ?
        */

        if (!strnicmp(p, "rsock:", 6)) {
            p+= 6;

            /*
            *   Get the remote socket number from the command line
            */

            memcpy(Remote_Socket_Number, get_socket_number(p), 2);

    if (verbose) {
                printf("Remote_Socket_Number = ");
                print_socket_num(Remote_Socket_Number);
                printf("\n");
            }
            continue;
        }

        /*
        *   Send length ?
        */

        if (!strnicmp(p, "sendlen:", 8)) {
            p+= 8;

            /*
            *   Get the amount of data to send from the command line
            */

            Send_Length = atoi(p);
            if (verbose)
                printf("Send length = %d\n", Send_Length);

            continue;
        }

        /*
        *   Receive length ?
        */

        if (!strnicmp(p, "recvlen:", 8)) {
            p+= 8;

            /*
            *   Get the amount of data to send from the command line
            */

            Receive_Length = atoi(p);
            if (verbose)
                printf("Receive length = %d\n", Receive_Length);

            continue;
        }

        /*
        *   Send packet type ?
        */

        if (!strnicmp(p, "sptype:", 7)) {
    p+= 7;

            /*
            *   Get the packet type from the command line
            */

            Send_Packet_Type = atoi(p);

      if (verbose)
printf("Send_Packet_Type = %d\n", Send_Packet_Type);
            continue;
        }

        /*
        *   Local packet type ?
        */

        if (!strnicmp(p, "lptype:", 7)) {
    p+= 7;

            /*
            *   Get the packet type from the command line
            */

            Local_Packet_Type = atoi(p);

      if (verbose)
printf("Send_Packet_Type = %d\n", Send_Packet_Type);
            continue;
        }

        /*
        *   Filter packet type ?
        */

        if (!strnicmp(p, "fptype:", 7)) {
    p+= 7;

            /*
            *   Get the packet type from the command line
            */

            Filter_Packet_Type = atoi(p);

      if (verbose)
printf("Filter_Packet_Type = %d\n", Send_Packet_Type);
            continue;
        }

        /*
        *   Backlog size ?
        */

        if (!strnicmp(p, "backlog:", 8)) {
     p+= 8;

            Backlog = atoi(p);

            if (verbose)
                printf("Backlog = %d\n", Backlog);
            continue;
        }

        /*
        *   No broadcast flag ?
        */

        if (!strnicmp(p, "nobcast", 7)) {
            p+= 7;

            No_Broadcast++;

            if (verbose)
                printf("No broadcast flag is set\n");

            continue;
        }

        /*
        *   No loop flag ?
        */

        if (!strnicmp(p, "noloop", 6)) {
            p+= 6;

            No_Loop++;

            if (verbose)
                printf("No loop flag is set\n");

            continue;
        }

        /*
        *   Sleep time ?
        */

        if (!strnicmp(p, "sleep:", 6)) {
            p+= 6;

            Sleep_Time = atoi(p);

            if (verbose)
                printf("Sleep time = %d\n", Sleep_Time);

            continue;
        }

        if (*p)
            printf("Unknown command line parameter: %s\n", p);

    } /* while */

    if (verbose)
        printf("\n\n");
}

/****************************************************************************
*
*    FUNCTION:  usage( LPSTR progname )
*
*    PURPOSE:   Displays all allowable command line parameters.  (All may not be used
*       by the program linking to this library, however).
*
*    ARGUMENTS:LPSTR=> program name
*
* RETURNS:   exits with errorlevel 0
*
*\***************************************************************************/
void usage(LPSTR progname)
{
    printf("\n\nUsage: %s [options]\n", progname);
    printf("\n");
    printf("Valid options are:\n");
    printf("-v               Toggles verbose mode ON\n");
    printf("-laf:x           Sets local address family to x\n");
    printf("-raf:x           Sets remote address family to x\n");
    printf("-st:x            Sets socket type to x\n");
    printf("-proto:x         Sets protocol to x\n");
    printf("-sleep:x         Sets sleep time between sends to x milliseconds\n");
    printf("-backlog:x       Sets listen backlog size to x indications\n");
    printf("-nobcast         Sets flag so program won't set sockopt to broadcast\n");
    printf("-noloop          Sets flag so program won't loop\n");
    printf("-sptype:x        Sets send packet type to x\n");
    printf("-lptype:x        Sets local packet type to x\n");
    printf("-fptype:x        Sets filter for incoming packet type x\n");
    printf("-sendlen:x       Sets amount of data to send to x bytes\n");
    printf("-recvlen:x       Sets amount of data to receive to x bytes\n");
    printf("-lnet:xxxx       Sets local network number to xxxx (1)\n");
    printf("-lnode:xxxxxx    Sets local node number to xxxxxx (1)\n");
    printf("-rnet:xxxx       Sets remote network number to xxxx (1)\n");
    printf("-rnode:xxxxxx    Sets remote node number to xxxxxx (1)\n");
    printf("-rsock:xx        Sets remote socket number to xx (1)\n");
    printf("-default         Displays programs default values\n");
    printf("-?, -help, or -h Displays this screen\n");
    printf("\n");
    printf("Notes:  (1) Network numbers (network, node, and socket) must\n");
    printf("            be specified as a series of hexadecimal numbers,\n");
    printf("            for example: -lnet:04003BFF. \n");
    printf("\n");
    printf("        All values must come immediately after the ':'\n");
    printf("        -default and/or -v should be first\n");
    printf("        Not all programs make use of all the above parameters\n");

    exit(0);
}

/****************************************************************************
*
*    FUNCTION:  dump_defaults( LPSTR progname )
*
*    PURPOSE:   Prints out the current values of the command line options
*
*
*    ARGUMENTS:LPSTR=> program name
*
* RETURNS:   nothing
*
*\***************************************************************************/
void dump_defaults(LPSTR progname)
{
    printf("Default option values for %s:\n\n", progname);

    printf("verbose =                %d\n", verbose);
    printf("socket type =            %d\n", Socket_Type);
    printf("protocol =               %d\n", Protocol);
    printf("sleep time =             %d\n", Sleep_Time);
    printf("backlog =                %d\n", Backlog);
    printf("nobcast =                %d\n", No_Broadcast);
    printf("noloop =                 %d\n", No_Loop);
    printf("send length =            %d\n", Send_Length);
    printf("receive length =         %d\n", Receive_Length);
    printf("send packet type =       %d\n", Send_Packet_Type);
    printf("local packet type =      %d\n", Local_Packet_Type);
    printf("filter packet type =     %d\n", Filter_Packet_Type);
    printf("local address family =   %d\n", Local_Address_Family);
    printf("remote address family =  %d\n", Remote_Address_Family);
    printf("local network number =   ");
    print_network_num(Local_Network_Number);
    printf("\nlocal node number =      ");
    print_node_num(Local_Node_Number);
    printf("\nlocal socket number =    ");
    print_socket_num(Local_Socket_Number);
    printf("\nremote network number =  ");
    print_network_num(Remote_Network_Number);
    printf("\nremote node number =     ");
    print_node_num(Remote_Node_Number);
    printf("\nremote socket number =   ");
    print_socket_num(Remote_Socket_Number);
    printf("\n\n");
}

/****************************************************************************
*
*    FUNCTION:  get_network_number( LPSTR cmd )
*
*    PURPOSE:   Reads a network number from the given string
*
*
*    ARGUMENTS:LPSTR  => string to read from
*
* RETURNS:   LPSTR  => hex network number
*
*\***************************************************************************/
LPSTR get_network_number(LPSTR cmd)
{
    static char hex_num[4];

    memset(hex_num, 0, 4);

    if (strlen(cmd) != 8) {
        printf("Incorrect format for network number.\n");
        exit(1);
    }

    get_hex_string(cmd, hex_num, 4);

    return hex_num;
}

/****************************************************************************
*
*    FUNCTION:  get_node_number( LPSTR cmd )
*
*    PURPOSE:   Reads a node number from the given string
*
*
*    ARGUMENTS:LPSTR  => string to read from
*
* RETURNS:   LPSTR  => hex network number
*
*\***************************************************************************/
LPSTR get_node_number(LPSTR cmd)
{
    static char hex_num[6];

    memset(hex_num, 0, 6);

    if (strlen(cmd) != 12) {
        printf("Incorrect format for node number.\n");
        exit(1);
    }

    get_hex_string(cmd, hex_num, 6);

    return hex_num;
}

/****************************************************************************
*
*    FUNCTION:  get_socket_number( LPSTR cmd )
*
*    PURPOSE:   Reads a socket number from the given string
*
*
*    ARGUMENTS:LPSTR  => string to read from
*
* RETURNS:   LPSTR  => hex network number
*
*\***************************************************************************/
LPSTR get_socket_number(LPSTR cmd)
{
    static char hex_num[2];

    memset(hex_num, 0, 2);

    if (strlen(cmd) != 4) {
        printf("Incorrect format for socket number.\n");
        exit(1);
    }

    get_hex_string(cmd, hex_num, 2);

    return hex_num;
}

/****************************************************************************
*
*    FUNCTION:  get_hex_string( LPSTR src, LPSTR dest, int num )
*
*    PURPOSE:   Reads in a character string containing hex digits and converts
*it to a hexadecimal number.
*
*    ARGUMENTS:LPSTR  => source string
*LPSTR  => destination for hex number
*int    number of bytes to convert
*
* RETURNS:   nothing
*
*\***************************************************************************/
void get_hex_string(LPSTR src, LPSTR dest, int num)
{
    LPSTR q = src;

    while (num--)
        *dest++ = (get_hex_byte(*q++) << 4) + get_hex_byte(*q++);

}

/****************************************************************************
*
*    FUNCTION:  get_hex_byte( char ch )
*
*    PURPOSE:   Converts the character passed in to a hexadecimal nibble.
*
*    ARGUMENTS:char    character to convert
*
* RETURNS:   UCHAR   hex nibble
*
*\***************************************************************************/
UCHAR get_hex_byte(char ch)
{
    if (ch >= '0' && ch <= '9')
        return (ch - '0');

    if (ch >= 'A' && ch <= 'F')
        return ((ch - 'A') + 0x0A);

    printf("Illegal character %c in hex string\n", ch);
    exit(1);
}


DPERROR.C

/****************************************************************************\
*  dperror.c -- sample program demonstrating NWLink.
*
*       Microsoft Developer Support
*       Copyright 1992 - 1998 Microsoft Corporation
*
*  Demonstrates basic sockets programming with the Windows Sockets API
*  using the NWLink transport.
*
****************************************************************************/
#include <stdio.h>
#include <windows.h>
#include "externs.h"

/****************************************************************************
*
*    FUNCTION:  dos_net_perror( LPSTR p )
*
*    PURPOSE:   This will printf an error message to stdout showing the
*               last error for the calling process.  This should be
*               called IMMEDIATELY after a call has returned an error
*               condition.
*
*    ARGUMENTS:LPSTR=> user string to print first
*
* RETURNS:   nothing
*
*\***************************************************************************/
void dos_net_perror(LPSTR p)
{
    printf(dos_net_strerror(p));
    printf("\n");

    return;
}


DSTRERR.C

/****************************************************************************\
*  dstrerr.c -- sample program demonstrating NWLink.
*
*       Microsoft Developer Support
*       Copyright 1992 - 1998 Microsoft Corporation
*
*  Demonstrates basic sockets programming with the Windows Sockets API
*  using the NWLink transport.
*
****************************************************************************/
#include <windows.h>
#include <stdio.h>
#include "externs.h"

/****************************************************************************
*
*    FUNCTION:  dos_net_strerror( LPSTR p )
*
*    PURPOSE:   This will return an far char * to an error message
*               string preceded by the user string passed in and
*               seperated by a :.  The user string cannot be more
*               than xx bytes in length.
*
*    ARGUMENTS:LPSTR=> user string to print first
*
* RETURNS:   LPCSTR  => user string + error string
*
*\***************************************************************************/
LPCSTR dos_net_strerror(LPSTR p)
{
    int error = 0;
    static char return_string[256];

    /*
    *   Get the error number from the system
    */

    error = h_errno;

    /*
    *   Build the string to return
    */

    sprintf(return_string, "%s :(%d)%s", p, error, get_error_text(error));

    return (LPSTR)return_string;
}


ERRMSG.C

/****************************************************************************\
*  errmsg.c -- sample program demonstrating NWLink.
*
*       Original code from Micro Computer Systems, Inc.
*       Copyright(c) 1992  All Rights Reserved.
*       Microsoft Developer Support
*       Copyright 1992 - 1998 Microsoft Corporation
*
*  Demonstrates basic sockets programming with the Windows Sockets API
*  using the NWLink transport.
*
****************************************************************************/
#include <windows.h>
#include "externs.h"

/*
*   Messages we return if there was no error or we didn't have it
*   in our list.
*/

char msg_no_error[19] =      "No error occurred!";
char msg_unknown_error[14] = "Unknown error";

/*
*   Error structures - Windows error number (from winsock header) and
*   the corresponding description string.
*/

ERROR_STRUCT errlist[] = {
    {WSAEINTR,           "WSAEINTR - Interrupted"},
    {WSAEBADF, "WSAEBADF - Bad file number"},
    {WSAEFAULT,   "WSAEFAULT - Bad address"},
    {WSAEINVAL,          "WSAEINVAL - Invalid argument"},
    {WSAEMFILE,          "WSAEMFILE - Too many open files"},

/*
*    Windows Sockets definitions of regular Berkeley error constants
*/

    {WSAEWOULDBLOCK,   "WSAEWOULDBLOCK - Socket marked as non-blocking"},
    {WSAEINPROGRESS,     "WSAEINPROGRESS - Blocking call in progress"},
    {WSAEALREADY,        "WSAEALREADY - Command already completed"},
    {WSAENOTSOCK,        "WSAENOTSOCK - Descriptor is not a socket"},
    {WSAEDESTADDRREQ,    "WSAEDESTADDRREQ - Destination address required"},
    {WSAEMSGSIZE,        "WSAEMSGSIZE - Data size too large"},
    {WSAEPROTOTYPE,      "WSAEPROTOTYPE - Protocol is of wrong type for this socket"},
    {WSAENOPROTOOPT,     "WSAENOPROTOOPT - Protocol option not supported for this socket type"},
    {WSAEPROTONOSUPPORT, "WSAEPROTONOSUPPORT - Protocol is not supported"},
    {WSAESOCKTNOSUPPORT, "WSAESOCKTNOSUPPORT - Socket type not supported by this address family"},
    {WSAEOPNOTSUPP,      "WSAEOPNOTSUPP - Option not supported"},
    {WSAEPFNOSUPPORT,    "WSAEPFNOSUPPORT - "},
    {WSAEAFNOSUPPORT,    "WSAEAFNOSUPPORT - Address family not supported by this protocol"},
    {WSAEADDRINUSE,      "WSAEADDRINUSE - Address is in use"},
    {WSAEADDRNOTAVAIL,   "WSAEADDRNOTAVAIL - Address not available from local machine"},
    {WSAENETDOWN,        "WSAENETDOWN - Network subsystem is down"},
    {WSAENETUNREACH,     "WSAENETUNREACH - Network cannot be reached"},
    {WSAENETRESET,       "WSAENETRESET - Connection has been dropped"},
    {WSAECONNABORTED,    "WSAECONNABORTED - "},
    {WSAECONNRESET,      "WSAECONNRESET - "},
    {WSAENOBUFS,         "WSAENOBUFS - No buffer space available"},
    {WSAEISCONN,         "WSAEISCONN - Socket is already connected"},
    {WSAENOTCONN,        "WSAENOTCONN - Socket is not connected"},
    {WSAESHUTDOWN,       "WSAESHUTDOWN - Socket has been shut down"},
    {WSAETOOMANYREFS,    "WSAETOOMANYREFS - "},
    {WSAETIMEDOUT,       "WSAETIMEDOUT - Command timed out"},
    {WSAECONNREFUSED,    "WSAECONNREFUSED - Connection refused"},
    {WSAELOOP,           "WSAELOOP - "},
    {WSAENAMETOOLONG,    "WSAENAMETOOLONG - "},
    {WSAEHOSTDOWN,       "WSAEHOSTDOWN - "},
    {WSAEHOSTUNREACH,    "WSAEHOSTUNREACH - "},
    {WSAENOTEMPTY,       "WSAENOTEMPTY - "},
    {WSAEPROCLIM,        "WSAEPROCLIM - "},
    {WSAEUSERS,          "WSAEUSERS - "},
    {WSAEDQUOT,          "WSAEDQUOT - "},
    {WSAESTALE,          "WSAESTALE - "},
    {WSAEREMOTE,         "WSAEREMOTE - "},

/*
*    Extended Windows Sockets error constant definitions
*/

    {WSASYSNOTREADY,     "WSASYSNOTREADY - Network subsystem not ready"},
    {WSAVERNOTSUPPORTED, "WSAVERNOTSUPPORTED - Version not supported"},
    {WSANOTINITIALISED,  "WSANOTINITIALISED - WSAStartup() has not been successfully called"},

/*
*    Other error constants.
*/

    {WSAHOST_NOT_FOUND,  "WSAHOST_NOT_FOUND - Host not found"},
    {WSATRY_AGAIN,       "WSATRY_AGAIN - Host not found or SERVERFAIL"},
    {WSANO_RECOVERY,     "WSANO_RECOVERY - Non-recoverable error"},
    {WSANO_DATA,         "WSANO_DATA - (or WSANO_ADDRESS) - No data record of requested type"},
    {-1, NULL}
};


EXTERNS.H

#include <windows.h>
#include <wsipx.h>
#include "testlib.h"

typedef struct tag_error_struct{
    int     errnum;
    LPSTR   errstr;
} ERROR_STRUCT;

LPSTR get_network_number(LPSTR);
LPSTR get_node_number(LPSTR);
LPSTR get_socket_number(LPSTR);

char msg_no_error[];
char msg_unknown_error[];


GETERROR.C

/****************************************************************************\
*  geterror.c -- sample program demonstrating NWLink.
*
*       Original code from Micro Computer Systems, Inc.
*       Copyright(c) 1992  All Rights Reserved.
*       Microsoft Developer Support
*       Copyright 1992 - 1998 Microsoft Corporation
*
*  Demonstrates basic sockets programming with the Windows Sockets API
*  using the NWLink transport.
*
****************************************************************************/
#include <windows.h>
#include <stdio.h>
#include "externs.h"

#define WSAERRGAP1 28
#define WSAERRGAP2 19

extern ERROR_STRUCT errlist[];

/****************************************************************************
*
*    FUNCTION:  get_error_text( int error )
*
*    PURPOSE:   Gets the text string explaining the error number passed in.
*       If the error number is 0, or is not found, a respective
*message is returned.
*
*    ARGUMENTS:interror number to look up
*
* RETURNS:   LPCSTR=> char string having error message
*
*\***************************************************************************/
LPCSTR get_error_text(int error)
{
    int search = 0;

    /*
    *   No error
    */

    if (!error)
        return (LPCSTR)msg_no_error;

    /*
    *   Search through our array of error number / string pairs
    *   until we find a matching error number or get to the end
    *   of our list.  If we found a matching error number,
    *   return a LPSTR to the corresponding string.
    */

    while (errlist[search].errstr) {
        if (error == errlist[search].errnum)
            return errlist[search].errstr;
        search++;
    }

    /*
    * If we didn't have the error in our list, return unkown
    */

    return (LPCSTR)msg_unknown_error;
}


MEMORY.C

/****************************************************************************\
*  memory.c -- sample program demonstrating NWLink.
*
*       Microsoft Developer Support
*       Copyright 1992 - 1998 Microsoft Corporation
*
*  Demonstrates basic sockets programming with the Windows Sockets API
*  using the NWLink transport.
*
****************************************************************************/
#include <stdio.h>
#include <windows.h>
#include "externs.h"

/****************************************************************************
*
*    FUNCTION:  mem_check( LPSTR p, UCHAR ch, int len)
*
*    PURPOSE:   Makes sure that a buffer is filled with only the character
*       specified.
*
*    ARGUMENTS:LPSTR=> buffer to scan
*               char    character to check for
*               int     length of buffer to check
*
* RETURNS:   0 if buffer has only the specified character
*offset to non-matching character if found
*
*\***************************************************************************/
int mem_check(LPSTR p, UCHAR ch, int len)
{
    int buflen;

    buflen = len;

    while (len--) {
        if ((UCHAR)*p++ != ch) {
            return (buflen - len);
        }
    }

    return 0;
}


NETPRINT.C

/****************************************************************************\
*  wperror.c -- sample program library demonstrating NWLink.
*
*       Microsoft Developer Support
*       Copyright 1992 - 1998 Microsoft Corporation
*
*  Demonstrates basic sockets programming with the Windows Sockets API
*  using the NWLink transport.
*
****************************************************************************/
#include <stdio.h>
#include <windows.h>
#include <wsipx.h>

/****************************************************************************
*
*    FUNCTION:  print_network_num( char *addrp)
*
*    PURPOSE:   Print out a given network number
*
*    ARGUMENTS:addrp   Pointer to the network address
*
* RETURNS:   nothing.
*
*\***************************************************************************/
void print_network_num(char *addrp)
{

    printf("%02X%02X%02X%02X",
        (unsigned char)*addrp,
        (unsigned char)*(addrp + 1),
        (unsigned char)*(addrp + 2),
        (unsigned char)*(addrp + 3));

    return;
}

/****************************************************************************
*
*    FUNCTION:  print_node_num( char *addrp)
*
*    PURPOSE:   Print out a given node number
*
*    ARGUMENTS:addrp   Pointer to the network address
*
* RETURNS:   nothing.
*
*\***************************************************************************/
void print_node_num(char *addrp)
{

    printf("%02X%02X%02X%02X%02X%02X",
        (unsigned char)*addrp,
        (unsigned char)*(addrp + 1),
        (unsigned char)*(addrp + 2),
        (unsigned char)*(addrp + 3),
        (unsigned char)*(addrp + 4),
        (unsigned char)*(addrp + 5));

    return;
}

/****************************************************************************
*
*    FUNCTION:  print_socket_num( char *addrp)
*
*    PURPOSE:   Print out a given socket number
*
*    ARGUMENTS:addrp   Pointer to the network address
*
* RETURNS:   nothing.
*
*\***************************************************************************/
void print_socket_num(char *addrp)
{

    printf("%02X%02X",
        (unsigned char)*addrp,
        (unsigned char)*(addrp + 1));

    return;
}

/****************************************************************************
*
*    FUNCTION:  print_netaddr( char *addrp, char *prestr, char *poststr)
*
*    PURPOSE:   Print out a given network address
*
*    ARGUMENTS:addrp   = Ptr to the network address
*prestr  = String to print before addr
*poststr = String to print after addr
*
* RETURNS:   nothing.
*
*\***************************************************************************/
void print_netaddr(char *addrp, char *prestr, char *poststr)
{

    if (prestr)
        printf(prestr);

    print_network_num(addrp);
    printf(" - ");
    print_node_num(addrp+4);
    printf(" - ");
    print_socket_num(addrp+10);

    if (poststr)
        printf(poststr);

    return;
}

/****************************************************************************
*
*    FUNCTION:  print_saddr( PSOCKADDR_IPX p )
*
*    PURPOSE:   Prints out contents of the SOCKADDR_IPX struct passed in.
*
*    ARGUMENTS:PSOCKADDR_IPX => SOCKADDR_IPX struct to dump.
*
* RETURNS:   nothing.
*
*\***************************************************************************/
void print_saddr(PSOCKADDR_IPX p)
{
    printf("address family = %d  address = ", p->sa_family);
    print_netaddr(p->sa_netnum, NULL, NULL);
}

/****************************************************************************
*
*    FUNCTION:  print_wsa( LPWSADATA w )
*
*    PURPOSE:   Dumps out the contents of a WSA structure.
*
*    ARGUMENTS:umps out the contents of a WSA structure.
*
* RETURNS:   nothing.
*
*\***************************************************************************/
void print_wsa(LPWSADATA w)
{
    printf("  Version =         %ld\n", w->wVersion);
    printf("  High Version =    %ld\n", w->wHighVersion);
    printf("  Description =     %s\n", w->szDescription);
    printf("  System Status =   %s\n", w->szSystemStatus);
    printf("  Max sockets =     %d\n", w->iMaxSockets);
    printf("  Max UPD dg size = %d\n", w->iMaxUdpDg);
}


TESTLIB.H

/*
*   Globals for test programs
*/

extern int     verbose;
extern int     Socket_Type;
extern int     Protocol;
extern int     Sleep_Time;
extern int     Backlog;
extern int     No_Broadcast;
extern int     No_Loop;
extern int     Send_Length;
extern int     Receive_Length;
extern int     Local_Packet_Type;
extern int     Send_Packet_Type;
extern int     Filter_Packet_Type;
extern int     Local_Address_Family;
extern int     Remote_Address_Family;
extern char    Local_Network_Number[];
extern char    Local_Node_Number[];
extern char    Local_Socket_Number[];
extern char    Remote_Network_Number[];
extern char    Remote_Node_Number[];
extern char    Remote_Socket_Number[];

/*
*    Command line and network address print functions
*/

 /*   Parses command line and fills in above global variables  */
extern void parse_cmd_line(int, char **);

 /*   Prints full network address (network, node, and socket)  */
extern void print_netaddr(LPSTR, LPSTR, LPSTR);

 /*   Prints first 4 bytes of address passed in  */
extern void print_network_num(LPSTR);

 /*   Prints first 6 bytes of address passed in */
extern void print_node_num(LPSTR);

 /*   Prints first 2 bytes of address passed in */
extern void print_socket_num(LPSTR);

 /*   Prints contents of a SOCKADDR_IPX struct */
extern void print_saddr(PSOCKADDR_IPX);

 /*   Prints contents of a WSAData struct */
extern void print_wsa(LPWSADATA);

/*
*    Error print functions
*/

 /*   Returns string with passed in string + description of last h_errno */
extern LPCSTR dos_net_strerror(LPSTR);

 /* Prints given string followed by description of last h_errno to stdout */
extern void  dos_net_perror(LPSTR);

 /* Displays message box with given string followed by description of
 *   last h_errno.  Allows user to specify if messagebox should be
 *   task modal (last param should be 1 if so)
 */
extern void win_net_perror(HWND, LPSTR, int);

 /* Given h_errno, returns descriptive text */
extern LPCSTR get_error_text(int);

 /* Makes sure that a buffer is filled with only the character specified. */
extern int mem_check(LPSTR, UCHAR, int);


WPERROR.C

/****************************************************************************\
*  wperror.c -- sample program library demonstrating NWLink.
*
*       Microsoft Developer Support
*       Copyright 1992 - 1998 Microsoft Corporation
*
*  Demonstrates basic sockets programming with the Windows Sockets API
*  using the NWLink transport.
*
****************************************************************************/
#include <stdio.h>
#include <windows.h>
#include "externs.h"

/****************************************************************************
*
*    FUNCTION:  win_net_perror( HWND parent, LPSTR p, int tm_flag)
*
*    PURPOSE:   Similar to the perror() function. This will display
*   a dialog box witht an error message showing the last error for
*the calling process.  This should be called IMMEDIATELY after
* a call has returned an error condition.
*
*    ARGUMENTS:HWND    to the parent window
*               LPSTR=> user string to print first
*               intflag set if message box should be task modal
*
* RETURNS:   nothing.
*
*\***************************************************************************/
void win_net_perror(HWND parent, LPSTR p, int tm_flag)
{
    MessageBox(parent, dos_net_strerror(p), NULL,
        MB_OK | MB_ICONEXCLAMATION | (tm_flag ? MB_TASKMODAL : 0));

    return;
}


README.TXT

overlap.c:
---------
This TCP sample demonstrates the use of AcceptEx() and overlapped I/O to
multiplex different client connections in a single-threaded Win32 application.

AcceptEx is a Microsoft-specific extension to Windows Sockets.  It is not
part of the Windows Sockets specification.  If you use this function, you
will limit your application to only run over implementations of Windows
Sockets that support AcceptEx -- some implementations do not.

The server uses AcceptEx() to handle a number of asynchronous connection
requests effectively. When a connection is received, it is added to an array o
handles being monitored in a wait function.
The server multiplexes between the listening socket and the various client
connections.

When a connection is first established, the server queues an overlapped
ReadFile operation on the socket. On subsequent notification that this
operation has completed, the server queues a WriteFile. Thus, the server does
alternate reads and writes  the socket, until the client closes the
connection.

usage:
overlap -e <endpoint>  -i <interface>

where,
endpoint is the port to listen on.
interface is the IP address to bind to for multi-homed hosts.


OVERLAP.C

/******************************************************************************\
* Sample demonstrating use of Events in Overlapped (Asynchronous) I/O
*
* This code uses AcceptEx()
* YOU MUST HAVE SERVICE PACK 3 on NT 3.51 to use it !!!
*
*       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.
\******************************************************************************/

#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#include <mswsock.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define DEFAULT_PORT 5001
#define MAX_IO_PEND 10// maximum pending I/O requests

#define OP_READ 0x10
#define OP_WRITE 0x20

#define xmalloc(s) HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,(s))
#define xfree(p)   HeapFree(GetProcessHeap(),0,(p))

//
// This structure keeps some useful information
//
typedef struct _socklist {
SOCKET sock;
OVERLAPPED *overlap;
char Buffer[128];
}Socklist;

int curr_size; //current number of handles we are intersted in

int DoWait(HANDLE *,Socklist *) ;
void HandleEvent(int , HANDLE *,Socklist *) ;

void Usage(char *progname) {
fprintf(stderr,"Usage\n%s -e [endpoint] -i [interface]\n",
progname);
fprintf(stderr,"Where:\n");
fprintf(stderr,"\tendpoint is the port to listen on\n");
fprintf(stderr,"\tinterface is the ipaddr (in dotted decimal notation)");
fprintf(stderr," to bind to\n");
fprintf(stderr,"Defaults are 5001 and INADDR_ANY\n");
WSACleanup();
exit(1);
}

int main(int argc, char **argv) {

char *interface= NULL;
char *Buffer = xmalloc(256);
unsigned short port=DEFAULT_PORT;
int i;
struct sockaddr_in local;
WSADATA wsaData;
SOCKET listen_socket, accept_sock;
OVERLAPPED *Overlap;
DWORD bytes_read;
DWORD lasterror;
//
// Handles is the array that stores the Event Handles

HANDLE Handles[MAX_IO_PEND] ;

//
// socklist is a parallel array that keeps state information for
// each Handle.
Socklist socklist[MAX_IO_PEND];

/* Parse arguments */
if (argc >1) {
for(i=1;i <argc;i++) {
if ( (argv[i][0] == '-') || (argv[i][0] == '/') ) {
switch(tolower(argv[i][1])) {

case 'i':
interface = argv[++i];
break;
case 'e':
port = atoi(argv[++i]);
break;
default:
Usage(argv[0]);
break;
}
}
else
Usage(argv[0]);
}
}

if (WSAStartup(0x202,&wsaData) == SOCKET_ERROR) {
fprintf(stderr,"WSAStartup failed with error %d\n",WSAGetLastError());
WSACleanup();
return -1;
}

if (port == 0){
Usage(argv[0]);
}

local.sin_family = AF_INET;
local.sin_addr.s_addr = (!interface)?INADDR_ANY:inet_addr(interface);

/*
 * Port MUST be in Network Byte Order
 */
local.sin_port = htons(port);

listen_socket = socket(AF_INET,SOCK_STREAM,0); // TCP socket
if(listen_socket == INVALID_SOCKET) {
fprintf(stderr,"socket() failed with error %d\n",WSAGetLastError());
WSACleanup();
return -1;
}

if (bind(listen_socket,(struct sockaddr*)&local,sizeof(local) )
== SOCKET_ERROR) {
fprintf(stderr,"bind() failed with error %d\n",WSAGetLastError());
WSACleanup();
return -1;
}

if (listen(listen_socket,5) == SOCKET_ERROR) {
fprintf(stderr,"listen() failed with error %d\n",WSAGetLastError());
WSACleanup();
return -1;
}
printf("%s: Listening on port %d\n",argv[0],port);

//
// Add the listening socket to our state information for the handle.
//
socklist[0].sock = listen_socket;

curr_size =1;

for(i=1;i<MAX_IO_PEND;i++)
Handles[i] = INVALID_HANDLE_VALUE;

//
// The structure of the following loop is very similar to a situation
// where select() might be used.
// We use WaitForSingleObject to multiplex between incoming/outgoing
// data on existing connections.
//
// We don't queue an AcceptEx() until someone actually connects to
// the previous socket. This is to keep the code simple, not a limitation
// of the API itself.

while(1) {

// create a socket for AcceptEx()

accept_sock = socket(AF_INET,SOCK_STREAM,0);

//
// Allocate an overlapped structure.
// We use the Offset field to keep track of the socket handle
// we have accpeted a connection on, since there is no other
// way to pass information to GetOverlappedResult()
//
Overlap = xmalloc(sizeof(OVERLAPPED));
Overlap->Offset = accept_sock;

Overlap->hEvent = CreateEvent(NULL,
 TRUE,//manual reset
 FALSE, // initially non-signalled
 NULL);

if (!Overlap->hEvent) {
fprintf(stderr,"CreateEvent failed %d\n",GetLastError());
return -1;
}
//
// Set the appropriate array members
//
Handles[0] = Overlap->hEvent;
socklist[0].overlap = Overlap;

// AcceptEx()
if (!AcceptEx(listen_socket,
  accept_sock,
  Buffer,
  0, // read nothing from the socket
  sizeof(struct sockaddr_in) +16,
  sizeof(struct sockaddr_in) +16,
  &bytes_read,
  Overlap)){
lasterror=WSAGetLastError();
if(lasterror!=ERROR_IO_PENDING){
fprintf(stderr,"acceptex failed %d\n",lasterror);
return -1;
}
}
//
// This loop simple checks the handles to see which one is
// signalled.
// If error, exit.
// If there is a new incoming connection, we break to the outer loop
// queue another AcceptEx()
//
while(1){
i = DoWait(Handles,socklist);
if (i<0)
break;
HandleEvent(i,Handles,socklist);
if (i ==0)
break;
};
if (i < 0)
return -1;

}
}
/*
 * This is the main function that handles all the events occuring on the
 * different handles we are watching.
 *
 * Parameters:
 *          index: Index into the Handles[] array. Returned by DoWait()
 *          Handles: Array of Event Handles we are watching
 *          socklist: Helper parallel array of state information
 *
 */
void HandleEvent(int index, HANDLE *Handles,Socklist *socklist) {

OVERLAPPED *Overlap;
SOCKET newsock;
DWORD bytes,overlap_err=0,lasterr;

Overlap = socklist[index].overlap;
//
// Check the specified handle
//
// If a socket is closed by the other side, the error returned is
// ERROR_NETNAM_DELETED
//
if(!GetOverlappedResult(Handles[index], Overlap, &bytes, TRUE) ) {
fprintf(stderr,"GetOverlappedResult failed with error %d\n",
overlap_err=GetLastError());
if (overlap_err  != ERROR_NETNAME_DELETED)
return;
}
newsock = Overlap->Offset;
//
// If the other side closed the connection, close our socket and
// move the last element of the Handles[] array into our
// index.
//
// The array compaction is done so that we only pass valid handles
// in the first "curr_size" elements of the array to
// WaitForMultipleObjects(). The function will fail otherwise.

// We should NEVER get this for our listening socket
if (index && overlap_err == ERROR_NETNAME_DELETED) {
closesocket(newsock);
xfree(Overlap);
Handles[index] = Handles[curr_size-1];
socklist[index] = socklist[curr_size-1];
curr_size--;
return;
}

if( (index ==0) ) { //listening socket
if (curr_size >= MAX_IO_PEND) {
fprintf(stderr,"Too many pending requests\n");
return;
}
//
// Get the event handle used to queue the AcceptEx(),
// and re-use it to queue a ReadFile on the socket.
//

Handles[curr_size] = Overlap->hEvent;
socklist[curr_size].overlap = Overlap;

//
// The OffsetHigh field is used to keep track of what we are doing.
// This enables us to alternate ReadFile and WriteFile on a
// connection

Overlap->OffsetHigh = OP_READ;

if (!ReadFile((HANDLE)newsock, socklist[curr_size].Buffer,
sizeof(socklist[curr_size].Buffer),
&bytes,
Overlap) ) {
lasterr = GetLastError();

// Handle ERROR_NETNAME_DELETED specially
// Other errors are Not Good
//
if (lasterr && lasterr != ERROR_IO_PENDING &&
lasterr != ERROR_NETNAME_DELETED ) {
fprintf(stderr,"Inital ReadFile failed %d\n");
return;
}
if (lasterr == ERROR_NETNAME_DELETED) {
closesocket(newsock);
xfree(Overlap);
Handles[index] = Handles[curr_size];
socklist[index] = socklist[curr_size];
curr_size--;
return;
}

}
//
// Increment the last valid handle location in the Handles
// array.
curr_size++;
return;
}
//
// This possibly indicates a closed socket.
//
if (  (bytes == 0 ) && (Overlap->OffsetHigh == OP_READ) ){
closesocket(newsock);
xfree(Overlap);
Handles[index] = Handles[curr_size];
socklist[index] = socklist[curr_size];
curr_size--;
return;
}
//
//  If the previos operation was an OP_READ, queue WriteFile on the
//  socket
//
if (Overlap->OffsetHigh == OP_READ) { // ReadFile was queued
printf("Read buffer [%s]\n",socklist[index].Buffer);
printf("Echoing back to client\n");
if (!WriteFile((HANDLE)newsock, socklist[index].Buffer,
sizeof(socklist[index].Buffer),
&bytes,
Overlap) ) {
lasterr = GetLastError();
if (lasterr && lasterr != ERROR_IO_PENDING &&
lasterr != ERROR_NETNAME_DELETED ) {
fprintf(stderr,"WriteFile failed %d\n");
ExitProcess(1);
}
if ( (lasterr == ERROR_NETNAME_DELETED) || (!lasterr)) {
closesocket(newsock);
xfree(Overlap);
Handles[index] = Handles[curr_size];
socklist[index] = socklist[curr_size];
curr_size--;
return;
}
}
Overlap->OffsetHigh = OP_WRITE;
return;
}
//
// If we had a WriteFile queued, now do a ReadFile
//
else if (Overlap->OffsetHigh == OP_WRITE) { // WriteFile was queued
printf("Wrote %d bytes\n",bytes);
printf("Queueing read\n");
if (!ReadFile((HANDLE)newsock, socklist[index].Buffer,
sizeof(socklist[index].Buffer),
&bytes,
Overlap) ) {
lasterr =GetLastError();
if (lasterr && lasterr != ERROR_IO_PENDING) {
if (lasterr == ERROR_NETNAME_DELETED) {
closesocket(newsock);
xfree(Overlap);
Handles[index] = Handles[curr_size];
socklist[index] = socklist[curr_size];
curr_size--;
return;
}
fprintf(stderr,"ReadFile failed %d\n",GetLastError());
ExitProcess(1);
}
}
Overlap->OffsetHigh = OP_READ;
return;
}
else {
fprintf(stderr,"Unknown operation queued\n");
}

}
//
// This is the wait function used to keep track of events
//
int DoWait(HANDLE *Handles,Socklist *socklist ) {

DWORD wait_rc;
HANDLE hTemp;
Socklist socklTemp;

int i;

//
// Rotate the array, beginning at index 1, by one element.
// This ensures that all handles get a fair chance to be serviced.
//
// There is no way to detect how many handles were signalled when
// WaitForMultipleObjects() returns. We simply pick the first one and
// come back to this function later
// Without the rotation below, this has the potential for starving
// connections accepted later.
//
// Index 0 is avoided, since it is our listening socket.
//
for(i=1;i<curr_size-1;i++){

hTemp = Handles[i+1];
Handles[i+1] = Handles[i];
Handles[i] = hTemp;

socklTemp = socklist[i+1];
socklist[i+1] = socklist[i];
socklist[i] = socklTemp;
}

wait_rc = WaitForMultipleObjects(curr_size,Handles,FALSE,
INFINITE);

if (wait_rc == WAIT_FAILED) {
fprintf(stderr,"Wait failed Error %d\n",GetLastError());
return -1;
}

return (wait_rc - WAIT_OBJECT_0);
}


README.TXT

This sample illustrates the use of SOCK_RAW in Winsock 2.0 on Windows NT in
order to write an ICMP_ECHO utility (commonly seen in the 'ping' program  on
various systems).

Usage:
ping <hostname/IP address> <packet size>

Sends ICMP echo requests to host specified by the first argument. If a
packet size is specified, a buffer of that size is sent. The default size
is 32 bytes.


PING.C

/******************************************************************************\
* ping.c - Simple ping utility using SOCK_RAW
*
*       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.
\******************************************************************************/

#pragma pack(4)

#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#include <stdio.h>
#include <stdlib.h>

#define ICMP_ECHO 8
#define ICMP_ECHOREPLY 0

#define ICMP_MIN 8 // minimum 8 byte icmp packet (just header)

/* The IP header */
typedef struct iphdr {
unsigned int h_len:4;          // length of the header
unsigned int version:4;        // Version of IP
unsigned char tos;             // Type of service
unsigned short total_len;      // total length of the packet
unsigned short ident;          // unique identifier
unsigned short frag_and_flags; // flags
unsigned char  ttl;
unsigned char proto;           // protocol (TCP, UDP etc)
unsigned short checksum;       // IP checksum

unsigned int sourceIP;
unsigned int destIP;

}IpHeader;

//
// ICMP header
//
typedef struct _ihdr {
  BYTE i_type;
  BYTE i_code; /* type sub code */
  USHORT i_cksum;
  USHORT i_id;
  USHORT i_seq;
  /* This is not the std header, but we reserve space for time */
  ULONG timestamp;
}IcmpHeader;

#define STATUS_FAILED 0xFFFF
#define DEF_PACKET_SIZE 32
#define MAX_PACKET 1024

#define xmalloc(s) HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,(s))
#define xfree(p)   HeapFree (GetProcessHeap(),0,(p))

void fill_icmp_data(char *, int);
USHORT checksum(USHORT *, int);
void decode_resp(char *,int ,struct sockaddr_in *);

void Usage(char *progname){

  fprintf(stderr,"Usage:\n");
  fprintf(stderr,"%s <host> [data_size]\n",progname);
  fprintf(stderr,"datasize can be up to 1Kb\n");
  ExitProcess(STATUS_FAILED);

}
int main(int argc, char **argv){

  WSADATA wsaData;
  SOCKET sockRaw;
  struct sockaddr_in dest,from;
  struct hostent * hp;
  int bread,datasize;
  int fromlen = sizeof(from);
  int timeout = 1000;
  char *dest_ip;
  char *icmp_data;
  char *recvbuf;
  unsigned int addr=0;
  USHORT seq_no = 0;

  if (WSAStartup(MAKEWORD(2,1),&wsaData) != 0){
fprintf(stderr,"WSAStartup failed: %d\n",GetLastError());
ExitProcess(STATUS_FAILED);
  }

  if (argc <2 ) {
Usage(argv[0]);
  }
  sockRaw = WSASocket (AF_INET,
   SOCK_RAW,
   IPPROTO_ICMP,
   NULL, 0,0);

  if (sockRaw == INVALID_SOCKET) {
fprintf(stderr,"WSASocket() failed: %d\n",WSAGetLastError());
ExitProcess(STATUS_FAILED);
  }
  bread = setsockopt(sockRaw,SOL_SOCKET,SO_RCVTIMEO,(char*)&timeout,
  sizeof(timeout));
  if(bread == SOCKET_ERROR) {
  fprintf(stderr,"failed to set recv timeout: %d\n",WSAGetLastError());
ExitProcess(STATUS_FAILED);
  }
  timeout = 1000;
  bread = setsockopt(sockRaw,SOL_SOCKET,SO_SNDTIMEO,(char*)&timeout,
  sizeof(timeout));
  if(bread == SOCKET_ERROR) {
  fprintf(stderr,"failed to set send timeout: %d\n",WSAGetLastError());
ExitProcess(STATUS_FAILED);
  }
  memset(&dest,0,sizeof(dest));

  hp = gethostbyname(argv[1]);

  if (!hp){
addr = inet_addr(argv[1]);
  }
  if ((!hp)  && (addr == INADDR_NONE) ) {
fprintf(stderr,"Unable to resolve %s\n",argv[1]);
ExitProcess(STATUS_FAILED);
  }

  if (hp != NULL)
  memcpy(&(dest.sin_addr),hp->h_addr,hp->h_length);
  else
  dest.sin_addr.s_addr = addr;

  if (hp)
  dest.sin_family = hp->h_addrtype;
  else
  dest.sin_family = AF_INET;

  dest_ip = inet_ntoa(dest.sin_addr);

  if (argc >2) {
datasize = atoi(argv[2]);
if (datasize == 0)
  datasize = DEF_PACKET_SIZE;

  }
  else
  datasize = DEF_PACKET_SIZE;

  datasize += sizeof(IcmpHeader);

  icmp_data = xmalloc(MAX_PACKET);
  recvbuf = xmalloc(MAX_PACKET);

  if (!icmp_data) {
fprintf(stderr,"HeapAlloc failed %d\n",GetLastError());
ExitProcess(STATUS_FAILED);
  }

  memset(icmp_data,0,MAX_PACKET);
  fill_icmp_data(icmp_data,datasize);

  while(1) {
int bwrote;

((IcmpHeader*)icmp_data)->i_cksum = 0;
((IcmpHeader*)icmp_data)->timestamp = GetTickCount();

((IcmpHeader*)icmp_data)->i_seq = seq_no++;
((IcmpHeader*)icmp_data)->i_cksum = checksum((USHORT*)icmp_data,
datasize);

bwrote = sendto(sockRaw,icmp_data,datasize,0,(struct sockaddr*)&dest,
sizeof(dest));
if (bwrote == SOCKET_ERROR){
  if (WSAGetLastError() == WSAETIMEDOUT) {
  printf("timed out\n");
continue;
  }
  fprintf(stderr,"sendto failed: %d\n",WSAGetLastError());
  ExitProcess(STATUS_FAILED);
}
if (bwrote < datasize ) {
  fprintf(stdout,"Wrote %d bytes\n",bwrote);
}
bread = recvfrom(sockRaw,recvbuf,MAX_PACKET,0,(struct sockaddr*)&from,
 &fromlen);
if (bread == SOCKET_ERROR){
  if (WSAGetLastError() == WSAETIMEDOUT) {
  printf("timed out\n");
continue;
  }
  fprintf(stderr,"recvfrom failed: %d\n",WSAGetLastError());
  ExitProcess(STATUS_FAILED);
}
decode_resp(recvbuf,bread,&from);
Sleep(1000);

  }
  return 0;

}
/*
The response is an IP packet. We must decode the IP header to locate
the ICMP data
*/
void decode_resp(char *buf, int bytes,struct sockaddr_in *from) {

IpHeader *iphdr;
IcmpHeader *icmphdr;
unsigned short iphdrlen;

iphdr = (IpHeader *)buf;

iphdrlen = iphdr->h_len * 4 ; // number of 32-bit words *4 = bytes

if (bytes  < iphdrlen + ICMP_MIN) {
printf("Too few bytes from %s\n",inet_ntoa(from->sin_addr));
}

icmphdr = (IcmpHeader*)(buf + iphdrlen);

if (icmphdr->i_type != ICMP_ECHOREPLY) {
fprintf(stderr,"non-echo type %d recvd\n",icmphdr->i_type);
return;
}
if (icmphdr->i_id != (USHORT)GetCurrentProcessId()) {
fprintf(stderr,"someone else's packet!\n");
return ;
}
printf("%d bytes from %s:",bytes, inet_ntoa(from->sin_addr));
printf(" icmp_seq = %d. ",icmphdr->i_seq);
printf(" time: %d ms ",GetTickCount()-icmphdr->timestamp);
printf("\n");

}

USHORT checksum(USHORT *buffer, int size) {

  unsigned long cksum=0;

  while(size >1) {
cksum+=*buffer++;
size -=sizeof(USHORT);
  }

  if(size ) {
cksum += *(UCHAR*)buffer;
  }

  cksum = (cksum >> 16) + (cksum & 0xffff);
  cksum += (cksum >>16);
  return (USHORT)(~cksum);
}
/*
Helper function to fill in various stuff in our ICMP request.
*/
void fill_icmp_data(char * icmp_data, int datasize){

  IcmpHeader *icmp_hdr;
  char *datapart;

  icmp_hdr = (IcmpHeader*)icmp_data;

  icmp_hdr->i_type = ICMP_ECHO;
  icmp_hdr->i_code = 0;
  icmp_hdr->i_id = (USHORT)GetCurrentProcessId();
  icmp_hdr->i_cksum = 0;
  icmp_hdr->i_seq = 0;

  datapart = icmp_data + sizeof(IcmpHeader);
  //
  // Place some junk in the buffer.
  //
  memset(datapart,'E', datasize - sizeof(IcmpHeader));

}


README.TXT

Windows Sockets 2 Sample Quality of Service ( QOS ) Chat application.

This sample works as a Windows Sockets server or client.  It will listen
for connections, or it will initiate one based on a user menu action.  When the connection
becomes active, quality of service is setup on the connection.  Winsock 2 GQOS APIs
are used to signal the Quality of Service requirements across the network to the peer application.

This sample requires Windows Sockets 2.  Either run it on Windows NT
verison 5.0 or greater, or Win98 or greater.


CHATDLG.H

/*++

Copyright (c) 1995 Intel Corp

Module Name:

    chatdlg.h

Abstract:

    Header file containing function protoypes of functions in dialog.c.

--*/

BOOL APIENTRY
InetConnDlgProc(
    IN HWND DialogHandle,
    IN UINT Message,
    IN WPARAM WordParam,
    IN LPARAM LongParam);

BOOL APIENTRY
DefaultConnDlgProc(
    IN HWND DialogWindow,
    IN UINT Message,
    IN WPARAM WordParam,
    IN LPARAM LongParam);

BOOL APIENTRY
ChooseFamilyDlgProc(
    IN HWND DialogHandle,
    IN UINT Message,
    IN WPARAM WordParam,
    IN LPARAM LongParam);

BOOL APIENTRY
NameAndSubjectDlgProc(
    IN HWND DialogWindow,
    IN UINT Message,
    IN WPARAM WordParam,
    IN LPARAM LongParam);

BOOL APIENTRY
AcceptConnectionDlgProc(
    IN HWND DialogHandle,
    IN UINT Message,
    IN WPARAM WordParam,
    IN LPARAM LongParam);

BOOL APIENTRY
InetListenPortDlgProc(
    IN HWND DialogHandle,
    IN UINT Message,
    IN WPARAM WordParam,
    IN LPARAM LongParam);

BOOL APIENTRY
DefaultListenAddrDlgProc(
    IN HWND DialogHandle,
    IN UINT Message,
    IN WPARAM WordParam,
    IN LPARAM LongParam);


CHATSOCK.C

/*++

Copyright (c) 1995 Intel Corp

Module Name:

    chatsock.c

Abstract:

    Socket-related functions for the WinSock2 Chat sample application.

--*/

#include "nowarn.h"  /* turn off benign warnings */
#ifndef _WINSOCKAPI_
#define _WINSOCKAPI_   /* Prevent inclusion of winsock.h in windows.h */
#endif
#include <windows.h>
#include "nowarn.h"  /* some warnings may have been turned back on */
#include <winsock2.h>
#include <qos.h>
#include <stdlib.h>
#include <assert.h>
#include "qoschat.h"
#include "chatsock.h"
#include "chatdlg.h"

//
// Static Globals
//

// points to an array of WSAPROTOCOL_INFO structs
static LPWSAPROTOCOL_INFO InstalledProtocols = NULL;

// number of WSAPROTOCOL_INFO structs in the InstalledProtocols buffer
static int NumProtocols = 0;

// static array of sockets, one for each listening socket
static LISTENDATA ListeningSockets[MAX_LISTENING_SOCKETS];

// number of meaningful entries in ListeningSockets
static int NumFound = 0;

//
// Function Prototypes -- Internal Functions
//

DWORD
IOThreadFunc(
    IN LPVOID ParamPtr);

BOOL
HandleOutputEvent(
    IN OUT PCONNDATA);

BOOL
HandleOtherEvent(
    IN     DWORD     WaitStatus,
    IN OUT PCONNDATA ConnData);

BOOL
FillLocalAddress(
    IN LPVOID          SockAddr);

int
DoRecv(
    IN PCONNDATA ConnData);

int
DoOverlappedCallbackSend(
    IN POUTPUT_REQUEST OutReq,
    IN PCONNDATA       ConnData);

int
DoOverlappedEventSend(
    IN POUTPUT_REQUEST OutReq,
    IN PCONNDATA       ConnData);

int
DoSend(
    IN POUTPUT_REQUEST OutReq,
    IN PCONNDATA       ConnData);

void CALLBACK
SendCompFunc(
    IN DWORD           Error,
    IN DWORD           BytesTransferred,
    IN LPWSAOVERLAPPED OverlappedPtr,
    IN DWORD           Flags);

int CALLBACK
AcceptCondFunc(
    IN LPWSABUF    CallerId,
    IN LPWSABUF    CallerData,
    IN LPQOS       CallerSQos,
    IN LPQOS       CallerGQos,
    IN LPWSABUF    CalleeId,
    OUT LPWSABUF   CalleeData,
    OUT GROUP FAR  *Group,
    IN DWORD   CallbackData);

LPWSAPROTOCOL_INFO
GetProtoFromSocket(
    IN SOCKET Socket);

BOOL
GetMaxMsgSize(
    IN OUT PCONNDATA ConnData);

//
// Function Definitions
//

BOOL
InitWS2(void)
/*++

Routine Description:

    Calls WSAStartup, makes sure we have a good version of WinSock2

Arguments:

    None.

Return Value:

    TRUE - WinSock 2 DLL successfully started up

    FALSE - Error starting up WinSock 2 DLL.

--*/

{
    int           Error;              // catches return value of WSAStartup
    WORD          VersionRequested;   // passed to WSAStartup
    WSADATA       WsaData;            // receives data from WSAStartup
    BOOL          ReturnValue = TRUE; // return value

    // Start WinSock 2.  If it fails, we don't need to call
    // WSACleanup().
    VersionRequested = MAKEWORD(VERSION_MAJOR, VERSION_MINOR);
    Error = WSAStartup(VersionRequested, &WsaData);
    if (Error) {
        MessageBox(GlobalFrameWindow,
                   "Could not find high enough version of WinSock",
                   "Error", MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
        ReturnValue = FALSE;
    } else {

        // Now confirm that the WinSock 2 DLL supports the exact version
        // we want. If not, make sure to call WSACleanup().
        if (LOBYTE(WsaData.wVersion) != VERSION_MAJOR ||
            HIBYTE(WsaData.wVersion) != VERSION_MINOR) {
            MessageBox(GlobalFrameWindow,
                       "Could not find the correct version of WinSock",
                       "Error",  MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
            WSACleanup();
            ReturnValue = FALSE;
        }
    }
    return(ReturnValue);

} // InitWS2()

BOOL
FindProtocols(void)
/*++

Routine Description:

    Finds out about all transport protocols installed on the local
    machine and saves the information into global variables.

Implementation:

    This function uses WSAEnumProtocols to find out about all
    installed protocols on the local machine.  It stores this
    information in two variables which are global to this file;
    InstalledProtocols is a pointer to a buffer of WSAPROTOCOL_INFO
    structs, while NumProtocols is the number of protocols in that
    buffer.  This function is the only function in the file allowed to
    touch these variables.

Arguments:

    None.

Return Value:

    TRUE - Successfully initialized the protocol buffer.

    FALSE - Some kind of problem arose.  The user is informed of the
    error.

--*/
{

    DWORD BufferSize = 0;       // size of InstalledProtocols buffer
    char  MsgText[MSG_LEN];     // holds message strings

    // Call WSAEnumProtocols to figure out how big of a buffer we need.
    NumProtocols = WSAEnumProtocols(NULL,
                                    NULL,
                                    &BufferSize);

    if ((NumProtocols != SOCKET_ERROR) && (WSAGetLastError() != WSAENOBUFS)) {
        // We're in trouble!!
        MessageBox(GlobalFrameWindow, "WSAEnumProtocols is broken.", "Error",
                   MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
        goto Fail;
    }

    //
    // Allocate a buffer, call WSAEnumProtocols to get an array of
    // WSAPROTOCOL_INFO structs.
    //
    InstalledProtocols = (LPWSAPROTOCOL_INFO)malloc(BufferSize);
    if (InstalledProtocols == NULL) {
        MessageBox(GlobalFrameWindow, "malloc failed.", "Error",
                   MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
        goto Fail;
    }

    NumProtocols = WSAEnumProtocols(NULL,
                                    (LPVOID)InstalledProtocols,
                                    &BufferSize);
    if (NumProtocols == SOCKET_ERROR) {
        // uh-oh
        wsprintf(MsgText, "WSAEnumProtocols failed.  Error Code: %d",
                 WSAGetLastError());
        MessageBox(GlobalFrameWindow, MsgText, "Error",
                   MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
        goto Fail;
    }
    return(TRUE);

 Fail:

    WSACleanup();
    return(FALSE);

} // FindProtocols()

BOOL
FillLocalAddress(
    IN LPWSABUF SockAddrBuf)
/*++

Routine Description:

    Takes the buffer passed in, and fills it in with a local address,
    as given by the user, on which a socket may listen.  The buffer
    can be of any length, but the first two bytes must be the address
    family of the protocol, and this value is used to pop up an
    appropriate dialog box.  For address families chat doesn't
    recognize, a default dialog box is shown and the user may fill in
    the address by hand.

Arguments:

    SockAddrBuf -- A buffer that will receive the local address for
    listening as chosen by the user.  The first two bytes should
    indicate the address family.

Return Value:

    TRUE -- FillLocalAddress recognized the address family and
    successfully filled in the struct sockaddr.  This structure is
    ready to be passed to a bind() call.

    FALSE -- FillLocalAddress didn't touch the struct sockaddr,
    probably because the user cancelled the dialog box.

--*/
{

    struct sockaddr_in *SockAddrInet; // used to cast SockAddr to an
                                      // Internet-style address
    BOOL ReturnValue = TRUE;          // holds the return value

    switch (((struct sockaddr *)SockAddrBuf->buf)->sa_family) {

    case AF_INET:

        // Bring up the dialog box that lets the user pick an internet
        // port to listen on.  Cast the pointer so we can now access
        // it's fields as a struct sockaddr_in, the address structure
        // for internet addresses.

        SockAddrInet = (struct sockaddr_in *)SockAddrBuf->buf;
        if (LocalPort == 0) {
            if (!DialogBoxParam(GlobalInstance,
                                "InetListenPortDlg",
                                GlobalFrameWindow,
                                InetListenPortDlgProc,
                                (LPARAM)SockAddrInet)) {
                ReturnValue = FALSE;
            }
        } else {
            SockAddrInet->sin_port = (u_short)LocalPort;
        }
        break;

    default:

        // Bring up the default dialog box to let the user pick an
        // address to listen on, regardless of the address family.
        if (!DialogBoxParam(GlobalInstance,
                            "DefaultListenAddrDlg",
                            GlobalFrameWindow,
                            DefaultListenAddrDlgProc,
                            (LPARAM)SockAddrBuf)) {
            ReturnValue = FALSE;
        }
        break;

    }
    return(ReturnValue);

} // FillLocalAddress()

BOOL
ListenAll(void)

/*++

Routine Description:

    For each installed, connection-oriented protocol, this function
    creates a socket, binds to a local address, and listens on the
    created socket.  The socket is set up for Windows message
    notification for any connection attempts.

Arguments:

    None.

Return Value:

    TRUE - Successfully listened on all installed protocols.

    FALSE - Error listening on all installed protocols.  It is not an
    error if there are more connection-oriented protocols than
    MAX_LISTENING_SOCKETS installed; in this case we just ignore any
    extra protocols.

--*/

{
    LPWSAPROTOCOL_INFO COProtocolInfo; // current protocol info to examine
    int             i;                // counting variable
    char            MsgText[MSG_LEN]; // holds message strings
    int             Error;            // holds return values
    int             Index;            // indexes into text messages
    WSABUF          SockAddrBuf;      // to pass to FillLocalAddress

    // Find all protocols that support connection-oriented data
    // transfer; create a socket, bind to a local address and listen
    // for connections on that socket for each such protocol found.
    // Also fill in an entry in the ListeningSockets array.
    for (i = 0; i < NumProtocols; i++) {

        COProtocolInfo = &InstalledProtocols[i];
        assert(COProtocolInfo != NULL);
        if (UseProtocol(COProtocolInfo)) {

            // We've found a suitable protocol.  Create a socket, fill
            // in the next entry in ListeningSockets
            ListeningSockets[NumFound].Socket = WSASocket(0,
                                                          0,
                                                          0,
                                                          COProtocolInfo,
                                                          0,
                                                          WSA_FLAG_OVERLAPPED);
            if (ListeningSockets[NumFound].Socket == INVALID_SOCKET) {
                MessageBox(GlobalFrameWindow, "Could not open a socket.",
                           "Non-fatal error.",
                           MB_OK | MB_SETFOREGROUND);
                continue;
            }
            ListeningSockets[NumFound].ProtocolInfo = COProtocolInfo;

            // Allocate a block of memory for the socket address and
            // zero it out.
            SockAddrBuf.len = COProtocolInfo->iMaxSockAddr;
            SockAddrBuf.buf = (char *)malloc(SockAddrBuf.len);
            if (!SockAddrBuf.buf) {
                ChatSysError("malloc()",
                             "ListenAll()",
                             TRUE);
            }
            memset(SockAddrBuf.buf, 0, SockAddrBuf.len);

            // Fill in the address family of the socket address. The
            // garbage on the left-hand side of this assignment is to
            // make the generic SockAddrBuf.buf look like a struct
            // sockaddr pointer for the purposes of assigning the
            // first two bytes to be the appropriate address family.
            ((struct sockaddr *)SockAddrBuf.buf)->sa_family =
              (u_short)COProtocolInfo->iAddressFamily;

            // Get a local address to bind the socket to
            FillLocalAddress(&SockAddrBuf);

            // Bind the socket to SockAddr.
            Error = bind(ListeningSockets[NumFound].Socket,
                         (struct sockaddr *)SockAddrBuf.buf,
                         SockAddrBuf.len);
            if (Error == SOCKET_ERROR) {

                // bind() failed
                MessageBox(GlobalFrameWindow, "Could not bind the socket.",
                           "Non-fatal error", MB_OK | MB_SETFOREGROUND);
                free(SockAddrBuf.buf);
                continue;
            }
            free(SockAddrBuf.buf);

            // Set up the socket for windows message event notification.
            // Note that this call automatically puts the socket into
            // non-blocking mode, as if we had called WSAIoctl with
            // the FIONBIO flag.
            Error = WSAAsyncSelect(ListeningSockets[NumFound].Socket,
                                   GlobalFrameWindow,
                                   USMSG_ACCEPT,
                                   FD_ACCEPT );
            if (Error == SOCKET_ERROR) {
                MessageBox(GlobalFrameWindow, "Error: WSAAsyncSelect()",
                           "Non-fatal error", MB_OK | MB_SETFOREGROUND);
                continue;
            }

            // Listen for incoming connection requests on the socket.
            Error = listen(ListeningSockets[NumFound].Socket,
                           SOMAXCONN);
            if (Error == SOCKET_ERROR) {
                MessageBox(GlobalFrameWindow, "Error: listen()",
                           "Non-fatal error",
                           MB_OK | MB_SETFOREGROUND);
                continue;
            }

            // Looks good -- increase the count, check for overflow of
            // the max amount, re-iterate if we're ok.
            if (++NumFound == MAX_LISTENING_SOCKETS) {
                wsprintf(MsgText,
                         "More than %d useable protocols. Ignoring extras.",
                         MAX_LISTENING_SOCKETS);
                MessageBox(GlobalFrameWindow, MsgText, "Alert",
                           MB_OK | MB_SETFOREGROUND);
                break;
            }

        } // if (UseProtocol(COProtocolInfo))
    } // for(; ; ;)

    if (NumFound == 0) {

        Index = wsprintf(MsgText,
                         "Couldn't find a suitable protocol ");
        Index += wsprintf(MsgText + Index,
                          "and/or no listening sockets could be opened.\r\n");
        wsprintf(MsgText + Index, "Shall we continue anyway?");

        if (MessageBox(GlobalFrameWindow, MsgText, "No protocols.",
                       MB_ICONQUESTION | MB_YESNO | MB_SETFOREGROUND)
            == IDNO) {

            WSACleanup();
            return(FALSE);
        }
    }

    return(TRUE);

} // ListenAll()

BOOL
UseProtocol(
    IN LPWSAPROTOCOL_INFO Proto)
/*++

Routine Description:

    Returns true if Proto is suitable for use by Chat.

Arguments:

    Proto -- Points to a protocol information struct.

Return Value:

    TRUE -- Chat likes it.

    FALSE -- Get this chintzy protocol out of here!

--*/
{

    if ((!(Proto->dwServiceFlags1 & XP1_CONNECTIONLESS)) &&
        (Proto->dwServiceFlags1 & XP1_GUARANTEED_DELIVERY) &&
        (Proto->dwServiceFlags1 & XP1_QOS_SUPPORTED) &&
        (Proto->dwServiceFlags1 & XP1_GUARANTEED_ORDER) &&
        (Proto->iAddressFamily == AF_INET)) {

        return(TRUE);

    } else {

        return(FALSE);
    }
} // UseProtocol

void
CleanUpSockets(void)
/*++

Routine Description:

    This function closes all listening sockets.

Arguments:

    None.

Return Value:

    None.

--*/
{

    int i; // counting variable

    for (i = 0; i < NumFound; i++) {
        closesocket(ListeningSockets[i].Socket);
    }
} // CleanUpSockets()

int CALLBACK
AcceptCondFunc(
    IN LPWSABUF   CallerId,
    IN LPWSABUF   CallerData,
    IN LPQOS      CallerSQos,
    IN LPQOS      CallerGQos,
    IN LPWSABUF   CalleeId,
    OUT LPWSABUF  CalleeData,
    OUT GROUP FAR *Group,
    IN DWORD  CallbackData)
/*++

Routine Description:

    Condition function called when an incoming connection request is
    handled.

Implementation:

    This function allows the user to accept or reject an incoming
    connection request after examining the caller's name and the
    subject of the call. If the call is accepted, and
    connection-time data transfer is supported by the particular
    protocol on which the connection is made, a dialog box comes up
    which prompts the user for his/her name.

Arguments:

    CallerId -- Supplies the address of the caller.

    CallerData -- Supplies the caller's user data.  Chat uses this
    parameter to send the caller's name and the subject of the call.

    CallerSQos -- Supplies the forward and backward QOS.

    CallerGQos -- Supplies the forward and backward flow specs for the
    socket group the caller is to create.  Not used by chat.

    CalleeId -- Supplies the local address.

    CalleeData -- Returns user data back to the caller (Chat uses this
    parameter to return the callee's name)

    Group -- Returns the appropriate group action to take on the
    connecting socket.  Not used by chat.  Always returns NULL.

    CallbackData -- Supplies a pointer the CONNDATA structure
    associated with this connection.

Return Value:

    CF_ACCEPT - Accept the connection request from the caller.

    CF_REJECT - Reject the connection request from the caller.

--*/

{
    PCONNDATA  ConnData;    // connection-specific data
    int           ReturnValue = CF_ACCEPT;  // return value
    LPFLOWSPEC    FlowSpec;

    ConnData = (PCONNDATA)CallbackData;

    FlowSpec = &CallerSQos->SendingFlowspec;
    FlowSpec->TokenRate = TOKENRATE;
    FlowSpec->TokenBucketSize = QOS_NOT_SPECIFIED;
    FlowSpec->PeakBandwidth = QOS_NOT_SPECIFIED;
    FlowSpec->Latency = QOS_NOT_SPECIFIED;
    FlowSpec->DelayVariation = QOS_NOT_SPECIFIED;
    FlowSpec->ServiceType = SERVICETYPE_CONTROLLEDLOAD;
    FlowSpec->MaxSduSize = QOS_NOT_SPECIFIED;
    FlowSpec->MinimumPolicedSize = QOS_NOT_SPECIFIED;

    FlowSpec = &CallerSQos->ReceivingFlowspec;
    FlowSpec->TokenRate = QOS_NOT_SPECIFIED;
    FlowSpec->TokenBucketSize = QOS_NOT_SPECIFIED;
    FlowSpec->PeakBandwidth = QOS_NOT_SPECIFIED;
    FlowSpec->Latency = QOS_NOT_SPECIFIED;
    FlowSpec->DelayVariation = QOS_NOT_SPECIFIED;
    FlowSpec->ServiceType = SERVICETYPE_CONTROLLEDLOAD;
    FlowSpec->MaxSduSize = QOS_NOT_SPECIFIED;
    FlowSpec->MinimumPolicedSize = QOS_NOT_SPECIFIED;

#if 0
    // CallerId contains the socket address of the connecting entity.
    // Copy this into the connection-specific data.
    ConnData->RemoteSockAddr.len = CallerId->len;
    ConnData->RemoteSockAddr.buf = malloc(ConnData->RemoteSockAddr.len);
    if (ConnData->RemoteSockAddr.buf == NULL) {
        ChatSysError("malloc()",
                     "AcceptCondFunc()",
                     TRUE);
    }
    memcpy((char *)ConnData->RemoteSockAddr.buf, (char *)CallerId->buf,
           CallerId->len);

    // Translate the RemoteSockAddr into a human readable form, and
    // store it in ConnData->PeerAddress
    GetAddressString(ConnData->PeerAddress,
                     ConnData->RemoteSockAddr.buf,
                     ConnData->RemoteSockAddr.len,
                     ConnData->ProtocolInfo);

    Index = wsprintf(MsgText, "Someone is attempting a chat connection.\r\n");

    if (CallerData != NULL) {

        // The connection request has come with some caller data.  Use
        // it to inform the user of who is trying to connect
        ExtractTwoStrings(CallerData->buf,
                          ConnData->PeerName,
                          NAME_LEN + 1,
                          ConnData->Subject,
                          SUB_LEN + 1);

        // Build the strings for the message box and the title of the
        // connection window
        Index += wsprintf(MsgText + Index,
                 "From: %s\r\nSubject: %s\r\n", ConnData->PeerName,
                          ConnData->Subject);
        wsprintf(TitleText, "Connected to: %s @ %s", ConnData->PeerName,
                 ConnData->PeerAddress);

    } else {

        // There is no caller data...build a string for the title.
        wsprintf(TitleText, "Connected to: %s", ConnData->PeerAddress);
    }

    // Continue building MsgText.
    Index += wsprintf(MsgText + Index, "Address: %s\r\n",
                      ConnData->PeerAddress);
    Index += wsprintf(MsgText + Index, "Would you like to accept it?");

    // Prompt the user to accept or reject the connection.
    //
    // ****NOTE****
    // This is NOT the right way to do this.  The application should
    // not hold up this thread by putting up this message box, or the
    // dialog box below.  As mentioned in the API spec, this function
    // should  return "as soon as possible", and clearly this is not
    // what's being done here.  Please look to future versions of chat
    // for a fix.  Thanks.
    if (MessageBox(ConnData->ConnectionWindow, MsgText,
                   "Connection Request",
                   MB_ICONQUESTION | MB_YESNO | MB_SETFOREGROUND) == IDYES) {

        // The user has accepted the connection request.
        SetWindowText(ConnData->ConnectionWindow, TitleText);
        if (CalleeData != NULL) {

            // We can try to pass user data back. Call up a dialog box
            // to get a name string and put it into CalleeData.
            if (!DialogBoxParam(GlobalInstance,
                                "AcceptConnectionDlg",
                                ConnData->ConnectionWindow,
                                AcceptConnectionDlgProc,
                                (LPARAM)CalleeData)) {

                CalleeData->len = 0;
            }
        }
        ReturnValue = CF_ACCEPT;

    } // if (MessageBox(...))
    else {

        // The user has rejected the connection request.
        ReturnValue = CF_REJECT;
    }
#endif

    return(ReturnValue);

} // AcceptCondFunc()

BOOL
GetAddressString(
    OUT char            *String,
    IN  LPVOID          SockAddr,
    IN  int             SockAddrLen,
    IN  LPWSAPROTOCOL_INFO ProtocolInfo)
/*++

Routine Description:

    This function translates the SockAddr into a human-readable
    string, if possible.  If chat doesn't recognize the protocol, then
    the string "(unknown)" is returned in String.

Arguments:

    String -- Returns the string representing the SockAddr in a
    human-readable form.

    SockAddr -- An address.

    SockAddrLen -- The length, in bytes, of SockAddr.

    ProtocolInfo -- Pointer to the protocol information structure.

Return Value:

    TRUE -- Chat recognized the protocol family and successfully
    translated the address.

    FALSE -- Chat did not recognize the protocol family and returned
    the string "(unknown)".

--*/
{

    char               *TempString;   // string returned by inet_ntoa
    struct sockaddr_in *SockAddrInet; // casts the address to a sockaddr_in
    BOOL               ReturnValue;   // holds the return value

    ReturnValue = TRUE;

    switch (ProtocolInfo->iAddressFamily) {

    case AF_INET:

        // It's an Internet-style address.
        SockAddrInet = (struct sockaddr_in *)SockAddr;
        TempString = inet_ntoa(SockAddrInet->sin_addr);
        if (TempString == NULL) {
            MessageBox(NULL, "inet_ntoa() failed.", "Error.",
                       MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
        } else {
            strcpy(String, TempString);
        }
        ReturnValue = TRUE;
        break;

    default:

        strcpy(String, "(unknown)");
        ReturnValue = FALSE;
        break;
    }
    return(ReturnValue);

} // GetAddressString()

int
HandleEvents(
    IN PCONNDATA          ConnData,
    IN LPWSANETWORKEVENTS NetworkEvents)

/*++

Routine Description:

    Handles network events that may occur on a connected socket.
    The events handled by this function are FD_CLOSE, FD_READ, and
    FD_WRITE.

Arguments:

    ConnData - Supplies a pointer to data for the connection on which
    the event happened.

    NetworkEvents - Supplies a WSANETWORKEVENT structure, which is a
    record of the network events that have occurred as well as any
    accompanying error-codes.

Return Value:

    CHAT_OK -- The network event was successfully handled.

    CHAT_ERROR -- Some kind of error occurred while handling the
    event, and the connection should be closed.

    CHAT_CLOSED -- The connection has been gracefully closed.

--*/

{
    int Result;                // holds the result of DoRecv
    int ReturnValue = CHAT_OK; // return value

    // The following three if statements all execute unless one gets
    // an error or closed socket, in which case we return immediately.
    if (NetworkEvents->lNetworkEvents & FD_READ) {

        // An FD_READ event has occurred on the connected socket.
        if (NetworkEvents->iErrorCode[FD_READ_BIT] == WSAENETDOWN) {

            // There is an error.
            ReturnValue = CHAT_ERROR;
            goto Done;

        } else {

            // Read data off the socket...
            Result = DoRecv(ConnData);
            if ((Result == CHAT_ERROR) || (Result == CHAT_CLOSED)) {
                ReturnValue = Result;
                goto Done;
            }
        }
    }

    if (NetworkEvents->lNetworkEvents & FD_WRITE) {

        // An FD_WRITE event has occurred on the connected socket.
        if (NetworkEvents->iErrorCode[FD_WRITE_BIT] == WSAENETDOWN) {

            // There is an error.
            ReturnValue = CHAT_ERROR;
            goto Done;

        } else {

            // Allow chat to send on this socket, and signal the
            // OuputEventObject in case there is pending output that is
            // not completed due to WSAEWOULDBLOCK.
            ConnData->WriteOk = TRUE;
            SetEvent(ConnData->OutputEventObject);
        }
    }

    if (NetworkEvents->lNetworkEvents & FD_CLOSE) {

        if (NetworkEvents->iErrorCode[FD_CLOSE_BIT] == 0) {

            // A graceful shutdown has occurred...
            ReturnValue = CHAT_CLOSED;
            goto Done;

        } else {

            // This is some other type of abortive close or failure...
            ReturnValue = CHAT_ABORTED;
            goto Done;
        }

    }

    if (NetworkEvents->lNetworkEvents & FD_QOS) {

        DWORD   BytesReturned;
        UCHAR   QosBuffer[ sizeof( QOS ) + sizeof( RSVP_STATUS_INFO )];
        LPQOS   Qos;
        DWORD   status;
        LPRSVP_STATUS_INFO  RsvpStatus;

        //
        // get all of the QOS that may be queued up in the service provider, because we
        // just get one indication that there is QOS there, even though there could be more than
        // one QOS event queued.
        //
        status = NO_ERROR;
        while (status == NO_ERROR) {
            Qos = (LPQOS)QosBuffer;
            Qos->ProviderSpecific.len = sizeof( RSVP_STATUS_INFO );
            Qos->ProviderSpecific.buf = (PUCHAR)(Qos +1);

            status = WSAIoctl( ConnData->Socket,
                               SIO_GET_QOS,
                               NULL,
                               0,
                               QosBuffer,
                               sizeof( QosBuffer ),
                               &BytesReturned,
                               NULL,
                               NULL );

            if (status == NO_ERROR) {
                LPFLOWSPEC  Flow;

                RsvpStatus = (LPRSVP_STATUS_INFO)Qos->ProviderSpecific.buf;
                if (Qos->SendingFlowspec.ServiceType == SERVICETYPE_NOTRAFFIC) {
                    Flow = &Qos->ReceivingFlowspec;
                } else {
                    Flow = &Qos->SendingFlowspec;
                }
#if 0
                wsprintf(MsgText, "QOS code = %d, Rate = %d Bucket = %d \n",
                         RsvpStatus->StatusCode,
                         Flow->TokenRate,
                         Flow->TokenBucketSize);

                //
                // Output the received text into the receive edit control.
                //
OutputString(ConnData->RecvWindow, MsgText);
#endif
            }
        }

    }

 Done:
    return(ReturnValue);

} // HandleEvents()

DWORD
IOThreadFunc(
    IN LPVOID ParamPtr)

/*++

Routine Description:

    This routine is invoked as a separate thread to handle all input
    and output for a connection.

Implementation:

    This thread sits in a loop, waiting for one of several things to
    occur.  These can be:

        1. The user interface thread has some input ready to be sent,
        and has signaled the ouput event object.

        2. WinSock 2 has indicated there is a network event associated
        with the socket.

        3. Callback notification.  This can be via an event or via a
        queued callback function, depending on whether or not we've
        compiled with the CALLBACK_NOTIFICATION flag.

    The return value from the wait indicates what has
    happened, and a switch statement handles all the possible cases.
    When there is an error or the connection is being closed, the loop
    is broken and the thread will exit and the connection window will
    be closed (and sent a WM_DESTROY message).

Arguments:

    ParamPtr - Supplies a pointer to a ConnData structure that holds
    data specific to this connection.

Return Value:

    0 - Always returns 0.  The return value is not needed.

--*/
{
    char             MsgText[MSG_LEN]; // holds message strings
    DWORD            WaitStatus;       // holds return value of the wait
    PCONNDATA        ConnData;         // connection-specific data
    BOOL             KeepGoing = TRUE; // keep processing output requests?
    BOOL             Forever = TRUE;   // constant to avoid warning

    ConnData =  (PCONNDATA)ParamPtr;

    // Initialize the EventArray.  The only two events in it, for now,
    // are the Socket and Ouput events; if this is the event
    // notification version of chat, then there will be an additional
    // event for each overlapped send waiting for a completion
    // notification.
    ConnData->NumEvents = 3;
    ConnData->EventArray[0] = ConnData->SocketEventObject;
    ConnData->EventArray[1] = ConnData->OutputEventObject;
    ConnData->EventArray[2] = ConnData->ThreadTerminationEvent;

    // Initialize the array of output requests, which is indexed in
    // parallel to the above event array.  When an event is signaled,
    // we can figure out which output request and overlapped
    // structures to free by indexing into this array.
    // These first two entries should never be referenced, because
    // their parallel entries in EventArray (see above) are for the
    // permanent Socket and Output event objects.
    ConnData->OutReqArray[0] = ConnData->OutReqArray[1] = NULL;

    while (Forever) {

        // Wait for an event (or a queued callback function) to wake
        // us up.  This is an alertable wait state (fAlertable == TRUE).
        WaitStatus = WSAWaitForMultipleEvents(ConnData->NumEvents,
                                              ConnData->EventArray,
                                              FALSE,        // fWaitAll
                                              WSA_INFINITE, // dwTimeout
                                              TRUE);        // fAlertable

        // Determine why we woke up and act accordingly.  Note that
        // breaking out of the switch causes us to break out of the
        // while loop as well.  When we don't want to break out of the
        // loop, case statements end with the continue statement.
        switch (WaitStatus) {

        case WSA_WAIT_FAILED:

            // A fatal error.  Pop up a message box and break out of
            // the while loop to end the thread.

            wsprintf(MsgText,
                     "WSAWaitForMultipleEvents() failed.  Error code: %d",
                     WSAGetLastError());
            MessageBox(ConnData->ConnectionWindow, MsgText, "Fatal Error",
                       MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
            break;

        case WAIT_IO_COMPLETION:

            // An I/O completion routine has been executed.  Cleanup
            // has already occurred in SendCompFunc, so there is
            // nothing left to do.  Just reiterate through the loop.

            continue;

        case WSA_WAIT_EVENT_0:

            //
            // the socket is closing if the socketevent is null
            //
            if (ConnData->SocketEventObject == NULL) {
                break;
            }
            // The SocketEventObject has been signaled.  Handle it in
            // a separate function.  Break out of the thread if
            // HandleSocketEvent returns FALSE, indicating error.
            if (HandleSocketEvent(ConnData)) {
                continue;
            } else {
                break;
            }

        // Please note: WSA_WAIT_EVENT_[1,2,3] are defined in
        // chatsock.h, not winsock2.h like WSA_WAIT_EVENT_0

        case WSA_WAIT_EVENT_1:

            // The OuputEventObject has been signaled.  Handle it in a
            // separate function, and break out of the thread if it
            // returns FALSE
            if (HandleOutputEvent(ConnData)) {
                continue;
            } else {
                break;
            }

        case WSA_WAIT_EVENT_2:
            CloseHandle( ConnData->ThreadTerminationEvent );
            if (InterlockedDecrement( &ConnData->RefCount ) == 0) {
                free( ConnData );
            }
            return( 0 );
            break;

        default:

            // Some other event has been signaled.  Handle it in a
            // separate function, and break out of the thread if it
            // returns FALSE.
            if (HandleOtherEvent(WaitStatus, ConnData)) {
                continue;
            } else {
                break;
            }

        } // switch (WaitStatus)

        // Break out of the while loop.
        break;

    } // while (1)

    // Thread is ending because the connection was closed or an error
    // occurred
    PostMessage(ConnData->ConnectionWindow,
                WM_CLOSE,
                0,
                0);

    CloseHandle( ConnData->ThreadTerminationEvent );

    ConnData->ThreadTerminationEvent = NULL;

    if (InterlockedDecrement( &ConnData->RefCount ) == 0) {
        free( ConnData );
    }

    return(0);

} // IOThreadFunc()

BOOL
HandleSocketEvent(
    IN OUT PCONNDATA ConnData)
/*++

Routine Description:

    Handles the case in IOThreadFunc where the thread is woken up by a
    signal to the socket event object.

Arguments:

    ConnData - Supplies a pointer to data for the connection on which
    the event happened.

Return Value:

    TRUE -- Chat successfully handled the event and the thread should
    continue on.

    FALSE -- An error occurred and Chat should kill the thread and the
    connection.

--*/
{
    int              Result;           // holds return values
    WSANETWORKEVENTS NetworkEvents;    // tells us what events happened
    char             MsgText[MSG_LEN]; // holds text strings
    BOOL             ReturnValue;      // holds the return value

    // Find out what happened and act accordingly.
    Result = WSAEnumNetworkEvents(ConnData->Socket,
                                  ConnData->SocketEventObject,
                                  &NetworkEvents);
    if (Result == SOCKET_ERROR) {

        // Handle the fatal error.
        wsprintf(MsgText,
                 "WSAEnumNetworkEvents failed.  Error code: %d",
                 WSAGetLastError());
        MessageBox(GlobalFrameWindow, MsgText, "Fatal Error",
                   MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
        ReturnValue = FALSE;

    } else {

        // Handle all of the network events on the given socket
        Result = HandleEvents(ConnData, &NetworkEvents);

        if (Result == CHAT_CLOSED) {

            //
            // don't put up a pop up if we are running with command line args since we
            // want it to be automated in that case
            //
            if (LocalPort == 0) {
                // HandleEvents() has determined that the remote party
                // has terminated the connection.  Inform the user, and
                // return.
                if (ConnData->PeerName[0] != 0) {
                    wsprintf(MsgText,
                             "%s @ %s has terminated the connection.",
                             ConnData->PeerName, ConnData->PeerAddress);
                } else {
                    wsprintf(MsgText,
                             "The party at %s has terminated the connection.",
                             ConnData->PeerAddress);
                }
                MessageBox(ConnData->ConnectionWindow, MsgText, "Sorry!",
                           MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
            }
            ReturnValue = FALSE;

        } else if (Result == CHAT_ABORTED) {

            // HandleEvents has determined that the connection has
            // been aborted due to an undetermined error.
            MessageBox(ConnData->ConnectionWindow,
                       "The connection has been broken.",
                       "Connection aborted.",
                       MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
            ReturnValue = FALSE;

        } else if (Result == CHAT_ERROR) {

            // HandleEvents() has returned an error.  Inform the
            // user and break to exit the thread and kill the window.
            MessageBox(ConnData->ConnectionWindow,
                       "An unidentified network or system error occurred.",
                       "Error.", MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
            ReturnValue = FALSE;

        } else if (Result == CHAT_OK) {

            // Inform the caller that everything went fine.
            ReturnValue = TRUE;

        } else {

            // This case should only occur if there is a
            // programming error.  Break out of the while loop to
            // kill the thread, the window, and therefore the
            // connection.
            MessageBox(ConnData->ConnectionWindow,
                       "HandleEvents() returned an unexpected value.",
                       "Error.", MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
            ReturnValue = FALSE;
        }
    }

    return(ReturnValue);

} // HandleSocketEvent()

BOOL
HandleOutputEvent(
    IN OUT PCONNDATA ConnData)
/*++

Routine Description:

    Handles the case in IOThreadFunc where the thread is woken up by a
    signal to the output event object.

Arguments:

    ConnData - Supplies a pointer to data for the connection on which
    the event happened.

Return Value:

    TRUE -- Chat successfully handled the event and the thread should
    continue on.

    FALSE -- An error occurred and Chat should kill the thread and the
    connection.

--*/
{
    BOOL            ReturnValue; // holds the return value
    BOOL            KeepGoing;   // keep pulling requests off the queue?
    POUTPUT_REQUEST OutReq;      // pointer to the output request
    int             Result;      // holds results of functions

    ReturnValue = TRUE;

    // First we check to see if a previous send attempt failed with
    // WSAEWOULDBLOCK; if so, we don't have to bother trying to send
    // data until we get an FD_WRITE network event, so just return
    // with TRUE to indicate that the thread should wait again.
    if (ConnData->WriteOk) {

        // This loop pulls output requests off the queue and hands
        // them to DoSend.
        KeepGoing = TRUE;
        while (KeepGoing) {

            OutReq = (POUTPUT_REQUEST)QRemove(ConnData->OutputQueue);
            if (OutReq == NULL) {

                // Nothing is left on the queue.
                KeepGoing = FALSE;
                ReturnValue = TRUE;

            } else {

                // Do the output
                Result = DoSend(OutReq, ConnData);
                if (Result == CHAT_WOULD_BLOCK) {

                    // The send would have blocked; we need to
                    // requeue the output request and wait for an
                    // FD_WRITE network event.
                    KeepGoing = FALSE;
                    QInsertAtHead(ConnData->OutputQueue, (LPVOID)OutReq);
                    ReturnValue = TRUE;

                } else if (Result == CHAT_ERROR) {

                    // An error occurred in DoSend.  Set KeepGoing to
                    // FALSE in order to get us out of the loop.
                    KeepGoing = FALSE;
                    ReturnValue = FALSE;
                    MessageBox(ConnData->ConnectionWindow,
                               "Error sending data.",
                               "Error.",
                               MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);

                } else {

                    // DoSend returned CHAT_OK.  Keep looping.
                    continue;
                }
            }
        } // while (KeepGoing)
    } // if (ConnData->WriteOk)

    return(ReturnValue);

} // HandleOutputEvent()

BOOL
HandleOtherEvent(
    IN     DWORD     WaitStatus,
    IN OUT PCONNDATA ConnData)
/*++

Routine Description:

    Handles the case in IOThreadFunc where the thread is woken up by
    an event that is either an overlapped I/O event or was not
    specified in the event array (which is an error!).

Arguments:

    WaitStatus -- Supplies the value returned by
    WSAWaitForMultipleEvents.

    ConnData - Supplies a pointer to data for the connection on which
    the event happened.

Return Value:

    TRUE -- Chat successfully handled the event and the thread should
    continue on.

    FALSE -- An error occurred and Chat should kill the thread and the
    connection.

--*/
{
    POUTPUT_REQUEST OutReq;           // points to an output request
    BOOL            ReturnValue;      // holds the return value
    DWORD           Count;            // counting variable
    char            MsgText[MSG_LEN]; // holds message strings

    // First do a sanity check to make sure the index returned is
    // within the bounds of what WE think is the event array.
    if ((WaitStatus >= WSA_WAIT_EVENT_0) &&
        (WaitStatus <= (WSA_WAIT_EVENT_0  + ConnData->NumEvents - 1))) {

        // Free the data buffer, the overlapped structure, the output
        // request itself, and the event
        OutReq = ConnData->OutReqArray[WaitStatus - WSA_WAIT_EVENT_0];
        free(OutReq->Buffer.buf);
        free(OutReq->Overlapped);
        free(OutReq);
        CloseHandle(ConnData->EventArray[WaitStatus - WSA_WAIT_EVENT_0]);

        // Update all our event and output request arrays to
        // reflect that the overlapped send has completed.
        ConnData->NumEvents--;
        for (Count = (WaitStatus - WSA_WAIT_EVENT_0);
             Count < ConnData->NumEvents;
             Count++) {
            ConnData->EventArray[Count] = ConnData->EventArray[Count + 1];
            ConnData->OutReqArray[Count] = ConnData->OutReqArray[Count + 1];
        }
        ReturnValue = TRUE;

    } else {

        // WSAWaitForMultipleEvents returned an unexpected
        // value...
        wsprintf(MsgText, "WSAWaitForMultipleEvents() returned %d.",
                 WaitStatus);
        MessageBox(GlobalFrameWindow, MsgText, "Error.",
                   MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
        ReturnValue = FALSE;
    }

    return(ReturnValue);

} // HandleOtherEvent()

void
HandleAcceptMessage(
    IN HWND   ConnectionWindow,
    IN SOCKET Socket,
    IN LPARAM LParam)
/*++

Routine Description:

    Handles the reception of a USMSG_ACCEPT message, which indicates a
    connection attempt is incoming.

Implementation:

    This function lets the user decide whether to accept the
    connection; if he or she does, the function  initializes
    connection-specific data, calls WSAEventSelect to register
    interest in certain network events, and starts a network event
    handling thread.

Arguments:

    ConnectionWindow -- Handle to the connection window associated
    with this connection request.

    Socket -- Contains the handle to the listening socket to which the
    connection request has been made.

    LParam -- The LParam that was delivered with the USMSG_ACCEPT
    message; contains the error code.

Return Value:

    None.

--*/
{

    int       Error;            // gets error code if necessary
    char      MsgText[MSG_LEN]; // holds message strings
    DWORD     ThreadId;         // needed for CreateThread
    PCONNDATA ConnData;         // connection-specific data

    ConnData = GetConnData(ConnectionWindow);

    Error = WSAGETSELECTERROR(LParam);

    // Check to see if there was an error on the connection attempt.
    if (Error) {

        // Some kind of error occurred.
        if (Error == WSAENETDOWN) {
            MessageBox(ConnectionWindow,
                       "The network is down!", "Uh-oh",
                       MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
        } else {
            MessageBox(ConnectionWindow,
                       "Unknown error on FD_ACCEPT", "Error",
                       MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
        }
        goto Fail;
    }

    // Get a pointer to the associated protocol information structure
    // for the socket we are listening on (Socket). This will be the
    // same as the protocol info for the new socket.  Note that we
    // could have allocated a new buffer and called getsockopt with
    // the SO_PROTOCOL_INFO option.  But since we've already allocated
    // the memory, this way is more efficient.
    ConnData->ProtocolInfo = GetProtoFromSocket(Socket);
    assert(ConnData->ProtocolInfo != NULL);

    // Accept the connection (this calls AcceptCondFunc, of course,
    // before actually accepting the connection).
    {
        WSABUF SockAddrBuf;

        SockAddrBuf.len = ConnData->ProtocolInfo->iMaxSockAddr;
        SockAddrBuf.buf = (char *)malloc(SockAddrBuf.len);
#if 0
        ConnData->Socket = WSAAccept(Socket,
                                     (struct sockaddr *)SockAddrBuf.buf,
                                     &SockAddrBuf.len,
                                     NULL,
                                     (DWORD)NULL);
#endif
         ConnData->Socket = WSAAccept(Socket,
                                      NULL,
                                      NULL,
                                      AcceptCondFunc,
                                      (DWORD)ConnData);
    }

    if (ConnData->Socket == INVALID_SOCKET) {

        // WSAAccept failed -- inform the user and that's all.
        Error = WSAGetLastError();
        if (Error != WSAECONNREFUSED) {

            // An unexpected error code.
            wsprintf(MsgText, "WSAAccept failed.  Error code: %d",
                     Error);
            MessageBox(ConnectionWindow, MsgText, "Error",
                       MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
            goto Fail;

        } else {

            // AcceptCondFunc returned CF_REJECT...
            MessageBox(GlobalFrameWindow,
                       "The connection attempt has been refused.",
                       "Connection refused.", MB_OK | MB_SETFOREGROUND);
            goto Fail;
        }
    }

    if (!UseAsyncSelect) {
        // Put Connection in Event Object Notification Mode.
        WSAEventSelect(ConnData->Socket,
                       ConnData->SocketEventObject,
                       FD_READ | FD_WRITE | FD_CLOSE | FD_QOS);
    } else {
        // Put Connection in Event Object Notification Mode.
        WSAAsyncSelect(ConnData->Socket,
                       ConnectionWindow,
                       USMSG_QOS,
                       FD_READ | FD_WRITE | FD_CLOSE | FD_QOS);

    }

    // Determine the maximum message size, if any.
    if (!GetMaxMsgSize(ConnData)) {
        goto Fail;
    }

    // Start the I/O thread, and save the thread handle.
    ConnData->IOThreadHandle =
      CreateThread(NULL,
                   0,
                   (LPTHREAD_START_ROUTINE)IOThreadFunc,
                   ConnData,
                   0,
                   &ThreadId);
    if (ConnData->IOThreadHandle == NULL) {
        ChatSysError("CreateThread()",
                     "HandleAcceptMessage()",
                     TRUE);
    }

    return;

 Fail:

    DestroyWindow(ConnectionWindow);
    return;

} // HandleAcceptMessage()

void
HandleConnectMessage(
    IN HWND   ConnectionWindow,
    IN LPARAM LParam)
/*++

Routine Description:

    Handles the reception of a USMSG_CONNECT message, which indicates
    a connection attempt is complete (though not necessarily
    successful).

Implementation:

    As with HandleAcceptMessages, this function initializes
    connection-specific data, calls WSAEventSelect to register
    interest in certain network events, and starts a network event
    handling thread.

Arguments:

    ConnectionWindow -- Handle to the connection window associated
    with this connection request.

    LParam -- Contains the LParam that was a parameter of the
    USMSG_CONNECT message.

Return Value:

    None.

--*/
{

    PCONNDATA ConnData;               // connection-specific data
    char      TitleText[TITLE_LEN];   // text buffer for window title
    DWORD     ThreadId;               // needed for CreateThread()
    int       Error;                  // holds error codes

    Error = WSAGETSELECTERROR(LParam);

    // Check to see if there was an error on the connection attempt.
    if (Error) {

        // Some kind of error occurred.
        if (Error == WSAECONNREFUSED) {

            MessageBox(ConnectionWindow,
                       "Your connection attempt has been refused",
                       "Connection Refused",
                       MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
        } else {

            MessageBox(ConnectionWindow,
                       "Couldn't connect",
                       "Error",
                       MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
        }

        goto Fail;

    }

    // Connection has been accepted.  Change the title of the
    // connection window to reflect who the user has connected to
    ConnData = GetConnData(ConnectionWindow);

    if (ConnData->CalleeBuffer.len != 0) {

        // The callee buffer contains the connection-time data
        // sent by the callee. -- a string containing the name of
        // the callee.
        wsprintf(TitleText, "Connected To: %s @ %s",
                 ConnData->CalleeBuffer.buf, ConnData->PeerAddress);
    } else {

        // ConnData->PeerAddress just contains the address that
        // the user typed in before the connection attempt.
        wsprintf(TitleText, "Connected To: %s", ConnData->PeerAddress);
    }
    SetWindowText(ConnectionWindow, TitleText);

    // Put Connection in Event Object Notification Mode.
    WSAEventSelect(ConnData->Socket,
                   ConnData->SocketEventObject,
                   FD_READ | FD_WRITE | FD_CLOSE | FD_QOS);

    // Determine the maximum message size, if any.
    if (!GetMaxMsgSize(ConnData)) {
        goto Fail;
    }

    // Start the I/O thread, and save the thread handle.
    ConnData->IOThreadHandle =
      CreateThread(NULL,
                   0,
                   (LPTHREAD_START_ROUTINE)IOThreadFunc,
                   ConnData,
                   0,
                   &ThreadId);
    if (ConnData->IOThreadHandle == NULL) {
        ChatSysError("CreateThread()",
                     "HandleConnectMessage()",
                     TRUE);
    }

    return;

 Fail:

    DestroyWindow(ConnectionWindow);
    return;

} // HandleConnectMessage()

BOOL
GetMaxMsgSize(
    IN OUT PCONNDATA ConnData)
/*++

Routine Description:

    Determines the maximum message size (if any) of a connected
    socket.  The connection must already be established, i.e. the
    socket must be bound to a local address, for this function to
    work.  Fills the correct value into a field of the
    connection-specific data.

Arguments:

    ConnData -- Connection data for a connected socket.

Return Value:

    TRUE -- The maximum message size was succesfully determined and
    stored in ConnData->MaxMsgSize.

    FALSE -- There was an error calling getsockopt to get the maximum
    message size.

--*/
{
    BOOL ReturnValue = TRUE;       // return value
    int  DwordLen = sizeof(DWORD); // sizeof a DWORD!
    int  Error;                    // return value of getsockopt

    if ((ConnData->ProtocolInfo->dwMessageSize == 0) ||
        (ConnData->ProtocolInfo->dwMessageSize == 0xffffffff)) {

        // Either the protocol isn't message-oriented, or there is no
        // maximum message size.
        ConnData->MaxMsgSize = NO_MAX_MSG_SIZE;

    } else {

        // There is a maximum message size.  Note it.
        if (ConnData->MaxMsgSize == 0x1) {

            // The actual maximum message size was not stored in the
            // protocol information structure -- rather, we need to
            // get it using getsockopt().
            Error = getsockopt(ConnData->Socket,
                               SOL_SOCKET,
                               SO_MAX_MSG_SIZE,
                               (char *)&ConnData->MaxMsgSize,
                               &DwordLen);
            if (Error) {
                ReturnValue = FALSE;
            }

        } // if (ConnData->MaxMsgSize == 0x1)
        else {

            // The message size is stored in the protocol information
            // structure.  Use it.
            ConnData->MaxMsgSize = ConnData->ProtocolInfo->dwMessageSize;
        }

    } // else

    return(ReturnValue);

} // GetMaxMsgSize()

BOOL
IsSendable(
    char Char)
/*++

Routine Description:

    Determines whether a certain character value is ok to send over
    the socket to the far end.  Eliminates most non-printable
    characters except for newline and backspace.

Arguments:

    Char -- Supplies the character to be tested.

Return Value:

    TRUE -- The character should be sent as is.

    FALSE -- The character should not be sent.

--*/
{

    if (isprint(Char) || (Char == '\b') || (Char == '\r')) {
        return(TRUE);
    } else {
        return(FALSE);
    }

} // IsSendable()

int
DoRecv(
    IN PCONNDATA ConnData)

/*++

Routine Description:

    Receives as much available data as possible up to the size of the
    receive buffer-1 (BUFFER_LENGTH-1).  A single byte is reserved at
    the end of the receive buffer to append a terminating NULL.

Arguments:

    ConnData -- Points to the data for the connection that is ready to
    receive data.

Return Value:

    CHAT_OK -- Received data was successfully sent to the receive edit
           control.

    CHAT_ERROR -- Error receiving data.

    CHAT_CLOSED -- The socket was gracefully closed.

--*/

{
    DWORD  NumBytes;               // stores how many bytes we received
    int    Error;                  // gets error values
    int    Result;                 // gets return value from WSARecv
    char   Buf[BUFFER_LENGTH];     // buffer to receive data
    int    ReturnValue = CHAT_OK;  // returnValue
    WSABUF RecvBuffer;             // WSABuf to pass to WSARecv
    DWORD  Flags = 0;              // flags for WSARecv

    RecvBuffer.buf = Buf;
    RecvBuffer.len = BUFFER_LENGTH - 1;

    // Do the receive
    Result = WSARecv(ConnData->Socket,
                     &RecvBuffer,
                     1,
                     &NumBytes,
                     &Flags,
                     NULL,
                     NULL);

    // Check for errors.
    if (Result == SOCKET_ERROR) {

        Error = WSAGetLastError();

        switch (Error) {

        case WSAENETRESET:  // flow through
        case WSAECONNRESET:

            // The remote party has reset the connection.
            ReturnValue = CHAT_CLOSED;

goto Done;

        case WSAEWOULDBLOCK:

            // No data received; return to wait for another read event.
            ReturnValue = CHAT_OK;

        default:

            // Some other error...hit the panic button.
            ReturnValue = CHAT_ERROR;
            goto Done;
        }

    }

    // Append a NULL to the text buffer.
    RecvBuffer.buf[NumBytes] = '\0';

    // Output the received text into the receive edit control.
    OutputString(ConnData->RecvWindow, RecvBuffer.buf);

 Done:
    return(ReturnValue);

} // DoRecv()

int
DoOverlappedCallbackSend(
    IN POUTPUT_REQUEST OutReq,
    IN PCONNDATA       ConnData)

/*++

Routine Description:

    Sends a buffer of data over a connected socket using overlapped
    I/O with callback function completion notification.

Implementation:

    This function just does an overlapped send, giving WinSock 2 a
    completion function which will be called when the operation has
    finished, and within which cleanup will occur.

Arguments:

    OutReq -- Points to a OUTPUT_REQUEST structure, which specifies an
    output request.

    ConnData -- Supplies a pointer to a CONNDATA structure, which
    identifies a Chat connection.

Return Value:

    CHAT_WOULD_BLOCK -- The send operation failed with WSAEWOULDBLOCK.

    CHAT_ERROR -- A unexpected error occurred.

    CHAT_OK -- The send was successfully initiated. WinSock 2 will
    execute the callback function when the send has completed.

--*/

{
    int             Size = 0;      // how many bytes we send
    int             Error;         // return value of WSASend
    int             Errno;         // result of WSAGetLastError
    LPWSABUF        Buffers;       // points to an array of WSABUFs
    DWORD           BytesSent;     // needed in WSASend
    int             ReturnValue;   // the return value

    ReturnValue = CHAT_OK;

    // Allocate an OVERLAPPED structure.
    OutReq->Overlapped = (LPWSAOVERLAPPED)malloc(sizeof(WSAOVERLAPPED));
    if (OutReq->Overlapped == NULL) {
        ChatSysError("malloc()",
                     "DoOverlappedCallbackSend()",
                     TRUE);
    }
    Buffers = &OutReq->Buffer;

    // The hEvent field of the Overlapped structure is not used
    // for callback notification...thus we can use it any way we
    // want to pass information to the callback routine.  We use
    // it to store a pointer to the OutReq associated with this
    // overlapped send operation, so the callback function can
    // free the memory.
    OutReq->Overlapped->hEvent = (WSAEVENT)OutReq;

    Error = WSASend(ConnData->Socket,
                    Buffers,
                    1,
                    &BytesSent,
                    0,
                    OutReq->Overlapped,
                    SendCompFunc);

    if (Error == SOCKET_ERROR) {

        // There is an error...
        Errno = WSAGetLastError();
        if (Errno == WSAEWOULDBLOCK) {

            // WSAEWOULDBLOCK means we have to wait for an FD_WRITE
            // before we can send.
            ConnData->WriteOk = FALSE;
            ReturnValue = CHAT_WOULD_BLOCK;

        } else if (Errno == WSA_IO_PENDING) {

            // Overlapped send successfully initiated.
            ReturnValue = CHAT_OK;
        }
        else {

            // An unexpected error occurred.
            ReturnValue = CHAT_ERROR;
        }

    }

    // No error -- the I/O request was completed immediately...
    return(ReturnValue);

} // DoOverlappedCallbackSend()

int
DoOverlappedEventSend(
    IN POUTPUT_REQUEST OutReq,
    IN PCONNDATA       ConnData)

/*++

Routine Description:

    Sends a buffer of data over a connected socket using overlapped
    I/O with event notification.

Implementation:

    If the overlapped send is succesfully initiated, this function
    increases the event count and adds an entry to the event array of
    the associated connection.  During IOThreadFunc, the thread will
    thus wait on the new event as well, and will perform the
    appropriate cleanup action when awoken.

Arguments:

    OutReq -- Points to a OUTPUT_REQUEST structure, which specifies an
    output request.

    ConnData -- Supplies a pointer to a CONNDATA structure, which
    identifies a Chat connection.

Return Value:

    CHAT_WOULD_BLOCK -- The send operation failed with WSAEWOULDBLOCK.

    CHAT_ERROR -- A unexpected error occurred.

    CHAT_OK -- The send was successfully initiated and the new event
    placed in the event array; it will be signaled by WinSock 2 when
    the send operation has completed.

--*/

{
    int             Size = 0;      // how many bytes we send
    int             Error;         // return value of WSASend
    int             Errno;         // result of WSAGetLastError
    LPWSABUF        Buffers;       // points to an array of WSABUFs
    DWORD           BytesSent;     // needed in WSASend
    int             ReturnValue;   // the return value

    ReturnValue = CHAT_OK;

    // Allocate an OVERLAPPED structure.

    // Note that the pointer to an overlapped structure is kept in
    // OutReq.  We use an array of OutReq pointers that is parallel to
    // the event array.  With that one pointer, then, we can find the
    // associated Overlapped structure and free it, and then free the
    // OutReq structure itself.
    OutReq->Overlapped = (LPWSAOVERLAPPED)malloc(sizeof(WSAOVERLAPPED));
    if (OutReq->Overlapped == NULL) {
        ChatSysError("malloc()",
                     "DoOverlappedEventSend()",
                     TRUE);
    }
    Buffers = &OutReq->Buffer;

    OutReq->Overlapped->hEvent =
      (WSAEVENT)CreateEvent(NULL,
                            FALSE,
                            FALSE,
                            NULL);
    if (OutReq->Overlapped->hEvent == NULL) {
        ChatSysError("CreateEvent()",
                     "DoOverlappedEventSend()",
                     TRUE);
    }

    // Do the send.
    Error = WSASend(ConnData->Socket,
                    Buffers,
                    1,
                    &BytesSent,
                    0,
                    OutReq->Overlapped,
                    NULL);

    if (Error == SOCKET_ERROR) {

        // There is an error...
        Errno = WSAGetLastError();
        if (Errno == WSAEWOULDBLOCK) {

            // WSAEWOULDBLOCK means we have to wait for an FD_WRITE
            // before we can send.
            ConnData->WriteOk = FALSE;
            ReturnValue = CHAT_WOULD_BLOCK;

        } else if (Errno == WSA_IO_PENDING) {

            // Overlapped send successfully initiated.
            // Increase the event count, and update the event and
            // output request arrays to hold the entries for this
            // overlapped send.
            ConnData->NumEvents++;
            if (ConnData->NumEvents > WSA_MAXIMUM_WAIT_EVENTS) {

                // We're trying to wait on too many events at
                // once.  It's very unlikely this could happen --
                // you'd have to get about 62 overlapped sends, in
                // row, before one of them is completed and
                // signaled.  So just return CHAT_ERROR which will
                // break the connection.
                ReturnValue = CHAT_ERROR;

            } else {

                ConnData->EventArray[ConnData->NumEvents - 1] =
                  OutReq->Overlapped->hEvent;

                ConnData->OutReqArray[ConnData->NumEvents - 1] = OutReq;
                ReturnValue = CHAT_OK;
            }
        } // else if (...)
        else {

            // An unexpected error occurred.
            ReturnValue = CHAT_ERROR;
        }

    } // if (Error == SOCKET_ERROR)

    // No error -- the I/O request was completed immediately...
    ReturnValue = CHAT_OK;

    return(ReturnValue);

} // DoOverlappedEventSend()

int
DoSend(
    IN POUTPUT_REQUEST OutReq,
    IN PCONNDATA       ConnData)

/*++

Routine Description:

    Sends a buffer of data over a connected socket.

Implementation:

    This function uses the information contained in the output request
    structure to determine whether to use overlapped or non-overlapped
    I/O.  If using overlapped I/O, it calls an appropriate function
    depending on whether the symbol CALLBACK_NOTIFICATION has been
    defined. Otherwise, the non-overlapped send in performed
    immediately.

Arguments:

    OutReq -- Points to a OUTPUT_REQUEST structure, which specifies an
    output request.

    ConnData -- Supplies a pointer to a CONNDATA structure, which
    identifies a Chat connection.

Return Value:

    CHAT_WOULD_BLOCK -- The send operation failed with WSAEWOULDBLOCK.

    CHAT_ERROR -- A unexpected error occurred.

    CHAT_OK -- The send was successfully initiated.  If the send was
    overlapped, either a callback function is forthcoming or another
    event was placed in the ConnData->EventArray for IOThreadFunc to
    wait on until it's signaled by WinSock 2.

--*/

{
    int             Size = 0;      // how many bytes we send
    int             Error;         // return value of WSASend
    int             Errno;         // result of WSAGetLastError
    WSABUF          Buffers[1];    // points to the data to be sent
    DWORD           BytesSent;     // needed in WSASend
    int             ReturnValue;   // the return value

    if (OutReq->Type == NON_OVERLAPPED_IO) {

        // Do a non-overlapped send by setting lpOverlapped and
        // lpCompletionRoutine to NULL.
        Buffers[0].len = OutReq->Buffer.len;
        Buffers[0].buf = OutReq->Buffer.buf;
        Error = WSASend(ConnData->Socket,
                        Buffers,
                        1,
                        &BytesSent,
                        0,
                        NULL,
                        NULL);

        if (Error == SOCKET_ERROR) {

            // There's an error...
            Errno = WSAGetLastError();
            if (Errno == WSAEWOULDBLOCK) {

                //  WSAEWOULDBLOCK means we have to wait for an FD_WRITE
                //  before we can send.
                ConnData->WriteOk = FALSE;
                ReturnValue = CHAT_WOULD_BLOCK;

            } else {

                // It's an unexpected error.
                ReturnValue = CHAT_ERROR;
            }
        } else {

            // There's no error.
            ReturnValue = CHAT_OK;
            free(OutReq);
        }

    } else if (OutReq->Type == OVERLAPPED_IO) {

#ifdef CALLBACK_NOTIFICATION

        ReturnValue = DoOverlappedCallbackSend(OutReq, ConnData);

#else

        ReturnValue = DoOverlappedEventSend(OutReq, ConnData);

#endif

    } else {

        // Unknown output type...
        MessageBox(ConnData->ConnectionWindow,
                   "Unknown output type given to IOThread. Aborting.",
                   "Error.", MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
        ReturnValue = CHAT_ERROR;
    }

    return(ReturnValue);

} // DoSend()

void CALLBACK
SendCompFunc(
    IN DWORD           Error,
    IN DWORD           BytesTransferred,
    IN LPWSAOVERLAPPED Overlapped,
    IN DWORD           Flags)
/*++

Routine Description:

    Completion routine called after a successfully initiated overlapped
    send operation completes; cleans up data structures associated
    with the send.

Arguments:

    Error -- Supplies the completion status for the overlapped
    operation.

    BytesTransferred -- Supplies the actual number of bytes sent.

    Overlapped -- Supplies a pointer to a WSAOVERLAPPED structure.
    The hEvent field in the structure contains a pointer to the
    OUTPUT_REQUEST structure associated with this send operation.

    Flags -- Not yet used.

Return Value:

    None

--*/

{
    POUTPUT_REQUEST OutReq; // The output request

    OutReq = (POUTPUT_REQUEST)(Overlapped->hEvent);

    if (Error) {
        MessageBox(NULL,
                   "Error during overlapped send.",
                   "Error",
                   MB_OK | MB_SETFOREGROUND);
    }

    // Free the data buffer, the output request structure,  and the
    // overlapped I/O structure.
    free(OutReq->Buffer.buf);
    free(OutReq);
    free(Overlapped);

} // SendCompFunc()

PCONNDATA
GetConnData(
    IN HWND ConnectionWindow
    )

/*++

Routine Description:

    Retrieves a pointer to the PCONNDATA data structure associated
    with a connection window

Arguments:

    ConnectionWindow - Supplies the handle to a connection window.

Return Value:

    Returns the PCONNDATA associated with the given connection window
    handle.

--*/

{
    return((PCONNDATA)GetWindowLong(ConnectionWindow, GWL_CONNINFO));

} // GetConnData()

BOOL
MakeConnection(
    IN HWND ConnectionWindow)

/*++

Routine Description:

    Initiates a call to another instance of chat.
    1) Creates a socket
    2) Set the socket up for windows message notification of FD_CONNECT
       events.
    3) Allocate a buffer for caller name & subject, and fill it in.
    4) Allocate a buffer to receive callee name.
    5) Set up quality of service for the connection.
    6) Attempt a connection

Arguments:

    ConnectionWindow - Handle to the window that receives
    notification when an FD_CONNECT event occurs.

Return Value:

    TRUE - A connection attempt was successfully initiated.

    FALSE - Error occurred while attempting to initiate a connection.

--*/

{
    int             ConnectStatus;    // the return value of WSAConnect
    int             Error;            // gets error values
    BOOL            ReturnValue;      // holds the return value
    PCONNDATA       ConnData;         // connection-specific data
    LPWSABUF        CallerBuffer;     // user data we will send
    LPWSABUF        CalleeBuffer;     // user data we will receive
    QOS             QualityOfService; // QOS structure, used by WSAConnect
    LPFLOWSPEC      FlowSpec;         // dummy pointer for code readability
    char            MsgText[MSG_LEN]; // holds message strings
    BOOL            QOSSupported;     // does the protocol support QOS?
    BOOL            CTDTSupported;    // support for conn-time data xfer?
    struct sockaddr *SockAddr;        // socket address for WSAConnect
    int             SockAddrLen;      // the length of the above

    ReturnValue = TRUE;
    ConnData = GetConnData(ConnectionWindow);
    QOSSupported = (ConnData->ProtocolInfo->dwServiceFlags1 &
                    XP1_QOS_SUPPORTED);
    CTDTSupported = (ConnData->ProtocolInfo->dwServiceFlags1 &
                      XP1_CONNECT_DATA);
    if (CTDTSupported) {

        // CallerBuffer and CallerBuffer->buf (allocated within the
        // NameAndSubject dialog box procedure) both get freed by the
        // end of this function.  CalleeBuffer.buf gets freed upon
        // connection shutdown -- it needs to be valid after this
        // function has exited.
        CallerBuffer = (LPWSABUF)malloc(sizeof(WSABUF));
        if (CallerBuffer == NULL) {
            ChatSysError("malloc()",
                         "MakeConnection()",
                         TRUE);
        }
        CalleeBuffer = &ConnData->CalleeBuffer;

        // The connection-time data sent back when/if this connection
        // is accepted will just contain the callee's name.
        CalleeBuffer->len = NAME_LEN + 1;
        CalleeBuffer->buf = (char *)malloc(CalleeBuffer->len);
        if (CallerBuffer->buf == NULL) {
            ChatSysError("malloc()",
                         "MakeConnection()",
                         TRUE);
        }

        // Prompt the user for his name and the subject of the call.
        // a pointer to the CallerBuffer is passed to the dialog box
        // procedure and the data is packed into this memory.
        if (!DialogBoxParam(GlobalInstance,
                            "NameAndSubjectDlg",
                            ConnectionWindow,
                            NameAndSubjectDlgProc,
                            (LPARAM)CallerBuffer)) {
            ReturnValue = FALSE;
            goto Done;
        }
    } // if (CTDTSupported)

    // Create a socket for this connection.
    ConnData->Socket = WSASocket(0,
                                 0,
                                 0,
                                 ConnData->ProtocolInfo,
                                 0,
                                 WSA_FLAG_OVERLAPPED);

    if (ConnData->Socket == INVALID_SOCKET) {

        MessageBox(ConnectionWindow, "Could not open a socket.", "Error",
                   MB_OK | MB_SETFOREGROUND);
        ReturnValue = FALSE;
        goto Done;

    } else {

        // Set up socket for windows message event notification.
        WSAAsyncSelect(ConnData->Socket,
                       ConnectionWindow,
                       USMSG_CONNECT,
                       FD_CONNECT );
    }

    if (QOSSupported) {

        // Set  up  quality  of  service  info  we  want.   Do  not include any
        // provider-specific information.
        QualityOfService.ProviderSpecific.len = 0;
        QualityOfService.ProviderSpecific.buf = NULL;

        FlowSpec = &QualityOfService.SendingFlowspec;
        FlowSpec->TokenRate = TOKENRATE;
        FlowSpec->TokenBucketSize = QOS_NOT_SPECIFIED;
        FlowSpec->PeakBandwidth = QOS_NOT_SPECIFIED;
        FlowSpec->Latency = QOS_NOT_SPECIFIED;
        FlowSpec->DelayVariation = QOS_NOT_SPECIFIED;
        FlowSpec->ServiceType = SERVICETYPE_CONTROLLEDLOAD;
        FlowSpec->MaxSduSize = QOS_NOT_SPECIFIED;
        FlowSpec->MinimumPolicedSize = QOS_NOT_SPECIFIED;

        FlowSpec = &QualityOfService.ReceivingFlowspec;
        FlowSpec->TokenRate = TOKENRATE;
        FlowSpec->TokenBucketSize = QOS_NOT_SPECIFIED;
        FlowSpec->PeakBandwidth = QOS_NOT_SPECIFIED;
        FlowSpec->Latency = QOS_NOT_SPECIFIED;
        FlowSpec->DelayVariation = QOS_NOT_SPECIFIED;
        FlowSpec->ServiceType = SERVICETYPE_CONTROLLEDLOAD;
        FlowSpec->MaxSduSize = QOS_NOT_SPECIFIED;
        FlowSpec->MinimumPolicedSize = QOS_NOT_SPECIFIED;
    }

    // Reminder: WinSock 2 requires that we pass a struct sockaddr *
    // to WSAConnect; however, the service provider is free to
    // interpret the pointer as an arbitrary chunk of data of size
    // SockAddrLen.

    SockAddr = (struct sockaddr *)ConnData->RemoteSockAddr.buf;
    SockAddrLen = ConnData->RemoteSockAddr.len;

    // Try to connect; depending on whether QOS or Connection Time
    // Data Transfer are supported, pass the appropriate data.
    if (QOSSupported && CTDTSupported) {

        // Both are supported...
        ConnectStatus = WSAConnect(ConnData->Socket,
                                   SockAddr,
                                   SockAddrLen,
                                   CallerBuffer,
                                   CalleeBuffer,
                                   &QualityOfService,
                                   NULL);
    } else if (!QOSSupported && CTDTSupported) {

        // Only CTDT is supported...
        ConnectStatus = WSAConnect(ConnData->Socket,
                                   SockAddr,
                                   SockAddrLen,
                                   CallerBuffer,
                                   CalleeBuffer,
                                   NULL,
                                   NULL);

    } else if (QOSSupported && !CTDTSupported) {

        // Only QOS is supported...
        ConnectStatus = WSAConnect(ConnData->Socket,
                                   SockAddr,
                                   SockAddrLen,
                                   NULL,
                                   NULL,
                                   &QualityOfService,
                                   NULL);

    } else {

        // Neither is supported...
        ConnectStatus = WSAConnect(ConnData->Socket,
                                   SockAddr,
                                   SockAddrLen,
                                   NULL,
                                   NULL,
                                   NULL,
                                   NULL);
    }

    // Check for errors.
    if (ConnectStatus == SOCKET_ERROR) {

        Error = WSAGetLastError();
        if (Error != WSAEWOULDBLOCK) {
            wsprintf(MsgText, "WSAConnect failed.  Error code: %d",
                     Error);
            MessageBox(ConnectionWindow, MsgText, "Error",
                       MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
            ReturnValue = FALSE;
        }

    } else {

        MessageBox(ConnectionWindow,
                   "WSAConnect should have returned SOCKET_ERROR.",
                   "Error", MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
        ReturnValue = FALSE;
    }

 Done:

    // Free allocated memory, except CalleeBuffer which will be
    // freed when connection window is destroyed.
    if (CTDTSupported) {
        free(CallerBuffer->buf);
        free(CallerBuffer);
    }
    return(ReturnValue);

} // MakeConnection()

BOOL
FillInFamilies(
    IN HWND  DialogWindow,
    IN DWORD FamilyLB)
/*++

Routine Description:

    Fills in the listbox with a string for each protocol on which Chat
    has a listening socket.

Implementation:

    For each socket in the ListeningSockets array, this function:

        1. Gets the associated protocol information structure.

        2. If Chat recognizes the address family, it prints it in a
        string; if not, it prints "unknown".

        3. In the second half of the string, prints out the
        human-readable string contained in the protocol information.

        4. Sends the string to be an entry in the listbox identified
        by FamilyLB.

Arguments:

    DialogWindow -- Window handle for the dialog box.

    FamilyLB -- Integer identifier for a listbox in the dialog box.

Return Value:

    TRUE - All messages were successfully sent to the listbox.

    FALSE - An LB_ADDSTRING message failed.

--*/

{

    int     i;                 // counting variable
    LRESULT Result;            // result of SendMessage calls
    char    LBString[MSG_LEN]; // string to send to the listbox
    int     Offset;             // index into the string

    // Iterate through all members of the ListeningSockets array.
    for (i = 0; i < NumFound; i++){

        // We are prepending a number to the list box string to be displayed
        // since the list box does us the favor of alphabetizing the
        // the sting and we are depending on the list entries appearing in the
        // list box in the order that we added them to the list.

        Offset = wsprintf(LBString, "%03i",i);

        switch (ListeningSockets[i].ProtocolInfo->iAddressFamily) {

        case AF_INET:

            Offset += wsprintf(LBString+ Offset, "AF_INET/");
            wsprintf(LBString + Offset, "%s",
                     ListeningSockets[i].ProtocolInfo->szProtocol);
            Result = SendMessage(GetDlgItem(DialogWindow, FamilyLB),
                                 LB_ADDSTRING, 0, (LPARAM)LBString);
            break;

        default:

            Offset += wsprintf(LBString+ Offset, "(unknown)/");
            wsprintf(LBString + Offset, "%s",
                     ListeningSockets[i].ProtocolInfo->szProtocol);
            Result = SendMessage(GetDlgItem(DialogWindow, FamilyLB),
                                 LB_ADDSTRING, 0, (LPARAM)LBString);
            break;

        }
        if ((Result == LB_ERR) || (Result == LB_ERRSPACE)) {
            return(FALSE);
        }
    }
    return(TRUE);

} // FillInFamilies()

LPWSAPROTOCOL_INFO
GetProtoFromIndex(
    IN int LBIndex)
/*++

Routine Description:

    Takes an index into the ChooseFamily dialog box and returns the
    protocol associated with that index. This works because when the
    dialog box is set up, the strings representing the protocols are
    added to the listbox in the order they fall in ListeningSockets.

Arguments:

    LBIndex -- The index of the user's selection.

Return Value:

    NULL -- The index doesn't correspond to an element in the
    ListeningSockets array.

    LPWSAPROTOCOL_INFO -- A pointer to the protocol information struct
    corresponding to LBIndex.

--*/
{
    if (LBIndex > (NumFound - 1)) {
        return(NULL);
    }
    return(ListeningSockets[LBIndex].ProtocolInfo);

} // GetProtoFromIndex()

LPWSAPROTOCOL_INFO
GetProtoFromSocket(
    IN SOCKET Socket)
/*++

Routine Description:

    Searches the ListeningSockets array for the first entry with a
    Socket field equal to the Socket parameter.  When found, it
    returns a pointer to the associated protocol information
    structure.  If not found, return NULL.

Arguments:

    Socket -- The socket who's associated WSAPROTOCOL_INFO struct we are
    looking for.

Return Value:

    NULL -- The socket was not found in the ListeningSockets array.

    LPWSAPROTOCOL_INFO -- A pointer to the protocol information struct
    for the socket.

--*/
{

    int i; // counting variable

    for (i = 0; i < NumFound; i++) {
        if (ListeningSockets[i].Socket == Socket) {
            return(ListeningSockets[i].ProtocolInfo);
        }
    }
    return(NULL);

} // GetProtoFromSocket()

void
CleanupConnection(
    IN PCONNDATA ConnData)
/*++

Routine Description:

    Frees all memory and objects allocated for this connection.

Arguments:

    ConnData -- Pointer to the connection-specific data structure.

Return Value:

    None.

--*/
{

    // Clean up connection-specific data.  To keep this code
    // readable, we ignore any errors.
    if (ConnData->SocketEventObject != NULL) {
        CloseHandle(ConnData->SocketEventObject);
        ConnData->SocketEventObject = NULL;
    }
    if (ConnData->OutputEventObject != NULL) {
        CloseHandle(ConnData->OutputEventObject);
        ConnData->SocketEventObject = NULL;
    }
    if (ConnData->OutputQueue != NULL) {
        QFree(ConnData->OutputQueue);
        ConnData->OutputQueue = NULL;
    }
    if (ConnData->IOThreadHandle != NULL) {
        CloseHandle(ConnData->IOThreadHandle);
        ConnData->IOThreadHandle = NULL;
        if (ConnData->ThreadTerminationEvent) {
            SetEvent( ConnData->ThreadTerminationEvent );
        }
    }
    if (ConnData->Socket != INVALID_SOCKET) {
        closesocket(ConnData->Socket);
    }
    if (ConnData->RemoteSockAddr.buf != NULL) {
        free(ConnData->RemoteSockAddr.buf);
        ConnData->RemoteSockAddr.buf = NULL;
    }
    if (ConnData->CalleeBuffer.buf != NULL) {
        free(ConnData->CalleeBuffer.buf);
        ConnData->CalleeBuffer.buf = NULL;
    }

    if (InterlockedDecrement( &ConnData->RefCount ) == 0) {
        free( ConnData );
    }

} // CleanupConnection()


CHATSOCK.H

/*++

Copyright (c) 1995 Intel Corp

Module Name:

    chatsock.h

Abstract:

    Header file containing constants, data structure definitions, and
    function prototypes for socket.c.

--*/

#ifndef CHTSOCK_H
#define CHTSOCK_H

#include "nowarn.h"  /* turn off benign warnings */
#ifndef _WINSOCKAPI_
#define _WINSOCKAPI_   /* Prevent inclusion of winsock.h in windows.h */
#endif
#include <windows.h>
#include "nowarn.h"  /* some warnings may have been turned back on */
#include <winsock2.h>

//
// Manifest Constants
//

#define WSA_WAIT_EVENT_1  WSA_WAIT_EVENT_0+1
#define WSA_WAIT_EVENT_2  WSA_WAIT_EVENT_0+2

//
// Typedefs
//

typedef struct _CALLBACK_INFO {
    BOOL  DoneYet;   // has the overlapped I/O completed yet?
    LPSTR Buffer;    // the buffer we gave to WSASend
} CALLBACK_INFO, *PCALLBACK_INFO;

//
// Function Prototypes -- Exported Functions
//

BOOL
InitWS2(void);

BOOL
FindProtocols(void);

BOOL
ListenAll(void);

void
HandleAcceptMessage(
    IN HWND   ConnectionWindow,
    IN SOCKET Socket,
    IN LPARAM LParam);

void
HandleConnectMessage(
    IN HWND   ConnectionWindow,
    IN LPARAM LongParam);

BOOL
IsSendable(
    char Character);

PCONNDATA
GetConnData(
    IN HWND ConnectionWindow);

BOOL
MakeConnection(
    IN HWND WindowHandle);

BOOL
FillInFamilies(
    IN HWND  DialogWindow,
    IN DWORD FamilyLB);

LPWSAPROTOCOL_INFO
GetProtoFromIndex(
    IN int LBIndex);

void
CleanUpSockets(void);

BOOL
GetAddressString(
    OUT char            *String,
    IN  LPVOID          SockAddr,
    IN  int             SockAddrLen,
    IN  LPWSAPROTOCOL_INFO ProtocolInfo);

BOOL
UseProtocol(
    IN LPWSAPROTOCOL_INFO Proto);

void
CleanupConnection(
    IN PCONNDATA ConnData);

#endif // CHTSOCK_H


QOSCHAT.C

/*++

Copyright (c) 1995 Intel Corp

Module Name:

    qoschat.c

Abstract:

    Contains WinMain and other user interface code for the WinSock2
    Chat sample application.

--*/

#include "nowarn.h"  /* turn off benign warnings */
#ifndef _WINSOCKAPI_
#define _WINSOCKAPI_   /* Prevent inclusion of winsock.h in windows.h */
#endif
#include <windows.h>
#include "nowarn.h"  /* some warnings may have been turned back on */
#include <winsock2.h>
#include <stdlib.h>
#include <stdio.h>
#include "qoschat.h"
#include "chatsock.h"
#include "chatdlg.h"
#include "queue.h"

//
// Globals
//
DWORD   ThisAddr=0;
DWORD   OtherAddr=0;
DWORD   NumSockets=1;
DWORD   LocalPort=9009;
BOOL    UseAsyncSelect = FALSE;

//
// Forward References -- internal functions.
//

// Startup/Initialization Functions
int PASCAL
WinMain(
    IN HINSTANCE InstanceHandle,
    IN HINSTANCE PrevInstanceHandle,
    IN LPSTR  CmdLine,
    IN int    CmdShow);

BOOL
InitUI(
    IN HINSTANCE InstanceHandle,
    IN HINSTANCE PrevInstanceHandle);

long CALLBACK
MainWndProc(
    IN HWND   WindowHandle,
    IN UINT   Message,
    IN WPARAM WParam,
    IN LPARAM LParam);

long CALLBACK
ConnWndProc(
    IN HWND   WindowHandle,
    IN UINT   Message,
    IN WPARAM WParam,
    IN LPARAM LParam);

long CALLBACK
SendEditSubClass(
    IN HWND   WindowHandle,
    IN UINT   Message,
    IN WPARAM WParam,
    IN LPARAM LParam);

long CALLBACK
RecvEditSubClass(
    IN HWND   WindowHandle,
    IN UINT   Message,
    IN WPARAM WParam,
    IN LPARAM LParam);

BOOL CALLBACK
CloseEnumProc(
    IN HWND WindowHandle,
    IN LONG LParam);

BOOL
GetConnectionInfo(
    IN HWND ConnectionWindow);

void
QueryAndMaximize(void);

//
// Static Global Variables
//

static HMENU   MainMenu;       // the main MDI menu
static HMENU   MainMenuWindow; // the window associated with the above
static FARPROC OldEditWndProc; // edit control WndProc before subclassing

//
// Externally-Visible Global Variables
//

HANDLE  GlobalInstance;                  // ientifies the instance of chat
char    ConnClassStr[] = "ConnChild";    // string to register window class
char    ChatClassStr[] = "QOSChatFrame"; // string to register window class
HWND    GlobalFrameWindow;               // Chat's main (frame) window

//
// Function Definitions
//

int CALLBACK
WinMain(
    IN HINSTANCE InstanceHandle,
    IN HINSTANCE PrevInstanceHandle,
    IN LPSTR  CmdLine,
    IN int    CmdShow)

/*++

Routine Description:

    WinMain is the main entry point for WinSock 2 Chat.  This function
    calls initialization functions and goes into a message loop.

Arguments:

    InstanceHandle -- Supplies the current instance handle.

    PrevInstanceHandle -- Supplies the previous instance handle.

    CmdLine -- Supplies the address of the command line.

    CmdShow -- Supplies the show state of the window.

Return Value:

    0 -- Initialization failure

    !0 -- Returns the Message.wParam of the WM_QUIT message that ended
    the message loop.

--*/
{
    HANDLE   AccelTable;   // handle to the accelerator table
    HWND     ClientWindow; // MDI Client window
    MSG      Message;      // holds the message
    int      ReturnValue;  // return value

    ReturnValue = 0;
    GlobalInstance = InstanceHandle;

    // Initialize Chat.
    if (!InitUI(InstanceHandle, PrevInstanceHandle)) {
        MessageBox(NULL, "Couldn't Initialize Chat!", "Error",
                   MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
        goto Done;
    }

    ParseCmdLine( CmdLine );

    // Create the frame window.
    GlobalFrameWindow = CreateWindow(ChatClassStr,
                                     "WinSock 2 Chat",
                                     WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
                                     CW_USEDEFAULT,
                                     CW_USEDEFAULT,
                                     CW_USEDEFAULT,
                                     CW_USEDEFAULT,
                                     NULL,
                                     (HMENU)MainMenu,
                                     InstanceHandle,
                                     NULL);

    if (GlobalFrameWindow == NULL) {
        ChatSysError("CreateWindow()",
                     "WinMain()",
                     TRUE);
    }

    // Startup WinSock 2.
    if (!InitWS2()) {
        goto Done;
    }

    // Find out about protocols installed.
    if (!FindProtocols()) {
        goto Done;
    }

    // Listen on a local address for all installed protocols.
    if (!ListenAll()) {
        goto Done;
    }

    // Load some handles, update the application window, and enter the
    // main message loop.
    ClientWindow = GetWindow(GlobalFrameWindow, GW_CHILD);
    AccelTable = LoadAccelerators(InstanceHandle, "MdiAccel");
    if (AccelTable == NULL) {
        ChatSysError("LoadAccelerators()",
                     "WinMain()",
                     FALSE);
    }
    ShowWindow(GlobalFrameWindow, CmdShow);
    UpdateWindow(GlobalFrameWindow);

    // Enter the GetMessage loop.
    while (GetMessage(&Message, NULL, 0, 0)) {
        if (!TranslateMDISysAccel(ClientWindow, &Message) &&
            !TranslateAccelerator(GlobalFrameWindow, AccelTable, &Message)) {
             TranslateMessage(&Message);
             DispatchMessage(&Message);
        }
    }

    // WM_QUIT was received.  Cleanup and return.
    WSACleanup();
    ReturnValue = Message.wParam;

 Done:

    return(ReturnValue);

} // WinMain()

VOID
ParseCmdLine(
    LPSTR   CmdLine
    )
/*++

Routine Description:

    Read the Cmdline and find out what options have been passed into this routine.

Arguments:

    CmdLine - ptr to the command line

Return Value:

    none

--*/
{

    UCHAR   Name[100];

    while (*CmdLine)
    {
        if ((*CmdLine == '-') || (*CmdLine == '/') ){

            char    c, *p = (++CmdLine); // skip the "-"

            if (*p == '\0')
                exit(usage());

            c = *p;

            switch (c) {

            case 'a':

                //
                // Use WSAAsyncSelect rather than WSAEventSelect
                //
                UseAsyncSelect = TRUE;
                break;

            case 'c':

                //
                // Count of sockets and events
                //
                if (*++CmdLine && *++CmdLine)
                {
                    p = CmdLine;

                    NumSockets = atol( p );

                }
                break;

            case 'd':

                //
                // other machine
                //
                if (*++CmdLine && *++CmdLine)
                {
                    p = CmdLine;

                    if (strlen(p) < sizeof(Name)){
                        strcpy(Name,p);
                    }else{
                        exit(usage());
                    }

                    OtherAddr = inet_addr(Name);
                    if (OtherAddr == INADDR_NONE) {
                        struct hostent *host;
                        host = gethostbyname(Name);
                        if (host) {
                            OtherAddr = *((DWORD *)host->h_addr_list[0]);
                        }
                    }

                }
                break;

            case 'l':

                //
                // local address
                //
                if (*++CmdLine && *++CmdLine)
                {
                    p = CmdLine;

                    if (strlen(p) < sizeof(Name)){
                        strcpy(Name,p);
                    }else{
                        exit(usage());
                    }

                    ThisAddr = inet_addr(Name);
                }
                break;

            case 'p':
                //
                // Count of sockets and events
                //
                if (*++CmdLine && *++CmdLine)
                {
                    DWORD   Port;
                    p = CmdLine;

                    Port = atol( p );
                    LocalPort = htons((USHORT)Port);

                }
                break;

            default:

                exit(usage());
                break;

            }
        } else {
            CmdLine++;
        }
    }
}

INT
usage()
/*++

Routine Description:

    Print out the command line args that are possible.

Arguments:

Return Value:

--*/
{
    printf("\nUsage:\n");
    printf("    qoschat -[d <OtherMachine>] -[s] -[r] \n");
    printf("           -c      -specifies the number of sockets to create \n");
    printf("           -d      -specifies the destination/source of the session \n");
    printf("                    (destination if the sender and source if the reciever\n");
    printf("           -l      -specifies the local IP address(optional for multihomed)\n");

    return(0);
}

BOOL
InitUI(
    IN HINSTANCE InstanceHandle,
    IN HINSTANCE PrevInstanceHandle)

/*++

Routine Description:

    Initializes chat's user interface.  Registers the window classes
    used by chat and obtains handles to chat's menus.

Arguments:

    InstanceHandle - Supplies the current instance handle.

    PrevInstanceHandle - Supplies the previous instance handle.

Return Value:

    TRUE - Chat successfully initialized

    FALSE - Failure during chat initialization

--*/
{
    WNDCLASS WndClass;           // window class structure
    BOOL     ReturnValue = TRUE; // holds return value

    // If this is the first instance of chat, register the window
    // classes.
    if (!PrevInstanceHandle) {
        WndClass.style         = CS_HREDRAW | CS_VREDRAW;
        WndClass.lpfnWndProc   = MainWndProc;
        WndClass.cbClsExtra    = 0;
        WndClass.cbWndExtra    = 0;
        WndClass.hInstance     = InstanceHandle;
        WndClass.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
        WndClass.hCursor       = LoadCursor(NULL, IDC_ARROW);
        WndClass.hbrBackground = (HBRUSH)(COLOR_APPWORKSPACE + 1);
        WndClass.lpszMenuName  = NULL;
        WndClass.lpszClassName = ChatClassStr;
        if (!RegisterClass(&WndClass)) {
            ReturnValue = FALSE;
            goto Done;
        }

        WndClass.style         = CS_HREDRAW | CS_VREDRAW;
        WndClass.lpfnWndProc   = ConnWndProc;
        WndClass.cbClsExtra    = 0;
        WndClass.cbWndExtra    = CONN_WND_EXTRA;
        WndClass.hInstance     = InstanceHandle;
        WndClass.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
        WndClass.hCursor       = LoadCursor(NULL, IDC_ARROW);
        WndClass.lpszMenuName  = NULL;
        WndClass.lpszClassName = ConnClassStr;
        if (!RegisterClass(&WndClass)) {
            ReturnValue = FALSE;
            goto Done;
        }
    }

    // Obtain handles to the menus & submenus.
    MainMenu  = LoadMenu(GlobalInstance, "MdiMenuMain");
    if (MainMenu == NULL) {
        ChatSysError("LoadMenu()",
                     "InitUI()",
                     TRUE);
    }
    MainMenuWindow  = GetSubMenu(MainMenu, MAIN_MENU_POS);

 Done:

    return(ReturnValue);

} // InitUI()

long CALLBACK
MainWndProc(
    IN HWND   WindowHandle,
    IN UINT   Message,
    IN WPARAM WParam,
    IN LPARAM LParam)

/*++

Routine Description:

    WinSock 2 chat's main window procedure.

Arguments:

    WindowHandle - Supplies the window handle for chat's frame window.

    Message - Supplies the message identifier.

    WParam - Supplies the first message parameter.

    LParam - Supplies the second message parameter.

Return Value:

    Return value depends on the message sent.

--*/
{
    static HWND        ClientWindow; // the MDI client window
    CLIENTCREATESTRUCT ClientCreate; // MDI Client window creation data
    HWND               ChildWindow;  // gets handle to child conn. windows
    MDICREATESTRUCT    MdiCreate;    // MDI Child window creation data
    INT                i;

    switch (Message) {

    case WM_CREATE:

        // Create the client window.
        ClientCreate.hWindowMenu  = MainMenuWindow;
        ClientCreate.idFirstChild = IDM_FIRSTCHILD;
        ClientWindow = CreateWindow("MDICLIENT",
                                    NULL,
                                    WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE,
                                    0, 0, 0, 0,
                                    WindowHandle,
                                    (HMENU)CLIENT_WINDOW_ID,
                                    GlobalInstance,
                                    (LPSTR)&ClientCreate);
        if (ClientWindow == NULL) {
            ChatSysError("CreateWindow()",
                         "MainWndProc()",
                         TRUE);

        }

        return (0);

    case WM_COMMAND:

        // We know the wm_command is coming from a menu item, so we
        // can safely assume that the high-order word of WParam is
        // zero.
        switch (WParam) {

        case IDM_CONNECT:

            for (i = 0;i < (INT)NumSockets ;i++ ) {
                // Create a Connection child window.
                MdiCreate.szClass = ConnClassStr;
                MdiCreate.szTitle = "Connecting...";
                MdiCreate.hOwner  = GlobalInstance;
                MdiCreate.x       = CW_USEDEFAULT;
                MdiCreate.y       = CW_USEDEFAULT;
                MdiCreate.cx      = CW_USEDEFAULT;
                MdiCreate.cy      = CW_USEDEFAULT;
                MdiCreate.style   = 0;
                MdiCreate.lParam  = 0;
                ChildWindow =
                  (HWND) SendMessage(ClientWindow, WM_MDICREATE, 0,
                                     (LPARAM)(LPMDICREATESTRUCT)&MdiCreate);

                if (ChildWindow == NULL) {
                    ChatSysError("SendMessage(WM_MDICREATE)",
                                 "MainWndProc()",
                                 TRUE);
                }

                // Get the connection information from the caller and
                // attempt to make a connection...
                if (!GetConnectionInfo(ChildWindow) ||
                    !MakeConnection(ChildWindow)) {
                    DestroyWindow(ChildWindow);
                }
            }

            break;

        case IDM_CLOSE:

            // Close the active window.
            ChildWindow = (HWND)SendMessage(ClientWindow, WM_MDIGETACTIVE, 0,
                                            0);
            SendMessage(ClientWindow, WM_MDIDESTROY, (WPARAM)ChildWindow, 0);
            DestroyWindow( ChildWindow);
            break;

        case IDM_EXIT:

            // Exit the program.
            SendMessage(WindowHandle, WM_CLOSE, 0, 0);
            break;

        case IDM_TILE:

            SendMessage(ClientWindow, WM_MDITILE, 0, 0);
            break;

        case IDM_CASCADE:

            SendMessage(ClientWindow, WM_MDICASCADE, 0, 0);
            break;

        case IDM_ARRANGE:

            SendMessage(ClientWindow, WM_MDIICONARRANGE, 0, 0);
            break;

        case IDM_CLOSEALL:

            // Close all child windows.
            EnumChildWindows(ClientWindow, CloseEnumProc, 0);
            break;

        default:

            // Pass to active child.
            ChildWindow = (HWND)SendMessage(ClientWindow, WM_MDIGETACTIVE,
                                            0, 0);

            if (IsWindow(ChildWindow)) {
                SendMessage(ChildWindow, WM_COMMAND, WParam, LParam);
            }

            // This causes us to drop out of the switch and to pass
            // the message on to DefFrameProc().
            break;

        } // switch (WParam)
        break;

    case WM_CLOSE:

        // Close all child windows.
        SendMessage(WindowHandle, WM_COMMAND, IDM_CLOSEALL, 0);
        if (GetWindow(ClientWindow, GW_CHILD) != NULL) {
            return (0);
        }
        break;

    case USMSG_ACCEPT:

        // Create a MDI Child connection window (even though we aren't
        // yet sure if we will accept the connection -- if we don't,
        // it will be immediately destroyed.)
        MdiCreate.szClass = ConnClassStr;
        MdiCreate.szTitle = "Connection Request...";
        MdiCreate.hOwner  = GlobalInstance;
        MdiCreate.x       = CW_USEDEFAULT;
        MdiCreate.y       = CW_USEDEFAULT;
        MdiCreate.cx      = CW_USEDEFAULT;
        MdiCreate.cy      = CW_USEDEFAULT;
        MdiCreate.style   = 0;
        ChildWindow = (HWND)SendMessage(ClientWindow, WM_MDICREATE, 0,
                                        (LPARAM)(LPMDICREATESTRUCT)&MdiCreate);
        if (ChildWindow == NULL) {
            ChatSysError("SendMessage(WM_MDICREATE)",
                         "MainWndProc()",
                         TRUE);
        }

        // Service the incoming connection request.
        HandleAcceptMessage(ChildWindow, (SOCKET)WParam, LParam);
        break;

    case WM_DESTROY:

        // Clean up the listening sockets and kill the application.
        CleanUpSockets();
        PostQuitMessage(0);
        break;

    } // switch(Message)

    // Pass unprocessed messages to DefFrameProc.
    return (DefFrameProc(WindowHandle,
                         ClientWindow,
                         Message,
                         WParam,
                         LParam));

} // MainWndProc()

long CALLBACK
ConnWndProc(
    IN HWND   WindowHandle,
    IN UINT   Message,
    IN WPARAM WParam,
    IN LPARAM LParam)

/*++

Routine Description:

    Windows procedure for each connection window.

Arguments:

    WindowHandle - Supplies the handle to the connection window which
    is to receive the message.

    Message - Supplies the message identifier.

    WParam - Supplies the first message parameter.

    LParam - Supplies the second message parameter.

Return Value:

    Return value depends on the message sent.

--*/
{
    PCONNDATA         ConnData;    // pointer to data for this connection
    RECT              Rect;        // used for painting the window
    HDC               Hdc;         // device context for painting
    DWORD             RsvpStatus;
    WSANETWORKEVENTS  NetworkEvents;
    switch (Message) {

    case WM_CREATE:

        StartConnection( WindowHandle );

        return (0);

    case WM_COMMAND:

        switch (HIWORD(WParam)) {

        case 0:

            // If the high-order word is zero, this message is being
            // sent from a menu-item (hopefully it was passed to us
            // from the frame window, since the mdi child connection
            // windows, of course, have no menus).
            switch (LOWORD(WParam)) {

            case IDM_CLEAR_SENDBUFFER:

                // Clear the send buffer.
                ConnData = GetConnData(WindowHandle);
                PostMessage(ConnData->SendWindow, EM_SETSEL, 0, -1);
                PostMessage(ConnData->SendWindow, WM_CLEAR, 0, 0);
                return (0);

            case IDM_CLEAR_RECVBUFFER:

                // Clear the receive buffer.
                ConnData = GetConnData(WindowHandle);
                PostMessage(ConnData->RecvWindow, EM_SETSEL, 0, -1);
                PostMessage(ConnData->RecvWindow, WM_CLEAR, 0, 0);
                return (0);

            default:

                // An unknown menu item... just return.
                return (0);

            } // switch (LOWORD(WParam))

        case 1:

            // The high-order word is 1, so thus the wm_command
            // message is coming from an accelerator. Ignore it.
            return(0);

        default:

            // Let the default window procedure handle it (below).
            break;

        } // switch (HIWORD(WParam))
        break;

    case WM_PAINT:

        // Window needs to be painted, split the connection window in
        // half.
        GetClientRect(WindowHandle, &Rect);
        Hdc = GetDC(WindowHandle);
        MoveToEx(Hdc, 0, Rect.bottom/2, NULL);
        LineTo(Hdc, Rect.right, Rect.bottom/2);
        ReleaseDC(WindowHandle, Hdc);

        return (DefMDIChildProc(WindowHandle,
                                Message,
                                WParam,
                                LParam));

    case WM_SIZE:

        ConnData = GetConnData(WindowHandle);

        // Resize the edit control windows.
        MoveWindow(ConnData->SendWindow,
                   0,
                   0,
                   LOWORD(LParam),
                   HIWORD(LParam)/2,
                   TRUE);

        MoveWindow(ConnData->RecvWindow,
                   0,
                   HIWORD(LParam)/2 + 1,
                   LOWORD(LParam),
                   HIWORD(LParam) - HIWORD(LParam)/2 - 1,
                   TRUE);

        return (DefMDIChildProc(WindowHandle,
                                Message,
                                WParam,
                                LParam));

    case USMSG_CONNECT:

        // Handle the new connection.
        HandleConnectMessage(WindowHandle, LParam);
        return(0);

    case USMSG_QOS:

        // Connection has been accepted.  Change the title of the
        // connection window to reflect who the user has connected to
        ConnData = GetConnData(WindowHandle);

        RsvpStatus = WSAGETSELECTERROR( LParam );
        NetworkEvents.iErrorCode[FD_QOS_BIT] = RsvpStatus;
        NetworkEvents.lNetworkEvents = WSAGETSELECTEVENT( LParam );

        // An Event has occured in Winsock, so call
        // a separate function.  Break out of the thread if
        // HandleSocketEvent returns FALSE, indicating error.
        //
        if (HandleEvents(ConnData,&NetworkEvents) != CHAT_OK) {

            //
            // close our window if an error occurs
            //
            DestroyWindow(WindowHandle);

        }
        return(0);

    case WM_DESTROY:

        ConnData = GetConnData(WindowHandle);
        CleanupConnection(ConnData);

        return(DefMDIChildProc(WindowHandle,
                               Message,
                               WParam,
                               LParam));

    } // switch (Message)

    // Pass unprocessed message to DefMDIChildProc.
    return(DefMDIChildProc(WindowHandle,
                           Message,
                           WParam,
                           LParam));

} // ConnWndProc()

DWORD
StartConnection(
    IN HWND   WindowHandle
    )

/*++

Routine Description:

    Windows procedure for each connection window.

Arguments:

    WindowHandle - Supplies the handle to the connection window which
    is to receive the message.

    Message - Supplies the message identifier.

    WParam - Supplies the first message parameter.

    LParam - Supplies the second message parameter.

Return Value:

    Return value depends on the message sent.

--*/
{
    PCONNDATA         ConnData;    // pointer to data for this connection
    RECT              Rect;        // used for painting the window
    HDC               Hdc;         // device context for painting

    //
    // Allocate memory for private window data.
    //

    ConnData = (PCONNDATA)malloc(sizeof(CONNDATA));
    if (ConnData == NULL) {
        ChatSysError("malloc()",
                     "ConnWndProc()",
                     TRUE);
    }
    memset((char *)ConnData, 0, sizeof(CONNDATA));

    // Zero is a valid socket ID so set the socket member to INVALID_SOCKET
    ConnData->Socket = INVALID_SOCKET;
    ConnData->RefCount = 2;

    GetClientRect(WindowHandle, &Rect);
    // Create Send/Recv edit controls.
    ConnData->SendWindow =
      CreateWindow("EDIT",
                   NULL,
                   WS_CHILD | WS_VISIBLE   | WS_VSCROLL |
                   ES_LEFT  | ES_MULTILINE | ES_AUTOVSCROLL,
                   0,
                   0,
                   Rect.right,
                   Rect.bottom/2,
                   WindowHandle,
                   (HMENU)EC_SEND_CHILD,
                   GlobalInstance,
                   NULL);

    ConnData->RecvWindow =
      CreateWindow("EDIT",
                   NULL,
                   WS_CHILD | WS_VISIBLE   | WS_VSCROLL |
                   ES_LEFT  | ES_MULTILINE | ES_AUTOVSCROLL,
                   0,
                   Rect.bottom/2 + 1,
                   Rect.right,
                   Rect.bottom - Rect.bottom/2 - 1,
                   WindowHandle,
                   (HMENU)EC_RECV_CHILD,
                   GlobalInstance,
                   NULL);

    if (!ConnData->SendWindow || !ConnData->RecvWindow) {
        ChatSysError("CreateWindow()",
                     "ConnWndProc()",
                     TRUE);
    }

    // Fill in some connection-specific data.
    ConnData->SocketEventObject = CreateEvent(NULL,
                                              FALSE,
                                              FALSE,
                                              NULL);
    ConnData->OutputEventObject = CreateEvent(NULL,
                                              FALSE,
                                              FALSE,
                                              NULL);
    ConnData->ThreadTerminationEvent = CreateEvent(NULL,
                                              FALSE,
                                              FALSE,
                                              NULL);

    ConnData->OutputQueue = QCreate();

    if (!ConnData->SocketEventObject || !ConnData->OutputEventObject ||
        !ConnData->OutputQueue) {
        ChatSysError("CreateEvent() or QCreate()",
                     "ConnWndProc()",
                     TRUE);
    }

    ConnData->ConnectionWindow = WindowHandle;
    ConnData->WriteOk = FALSE;

    // Subclass the edit controls. Both functions necessarily
    // return the same FARPROC...
    OldEditWndProc = (FARPROC)SetWindowLong(ConnData->SendWindow,
                                            GWL_WNDPROC,
                                            (DWORD)SendEditSubClass);
    SetWindowLong(ConnData->RecvWindow,
                  GWL_WNDPROC,
                  (DWORD)RecvEditSubClass);

    // Split the connection window in half.
    Hdc = GetDC(WindowHandle);
    MoveToEx(Hdc, 0, Rect.bottom/2, NULL);
    LineTo(Hdc, Rect.right, Rect.bottom/2);
    ReleaseDC(WindowHandle, Hdc);

    // Set the private window data to be a pointer to ConnData.
    SetWindowLong(WindowHandle, GWL_CONNINFO, (LONG)ConnData);

    return( 0 );
}

long CALLBACK
SendEditSubClass(
    IN HWND   WindowHandle,
    IN UINT   Message,
    IN WPARAM WParam,
    IN LPARAM LParam)

/*++

Routine Description:

    Window procedure used to subclass the edit control for the sending
    window.

Implementation:

    Text is only allowed to be appended to or deleted from the end of
    an edit control's text buffer.  When text is either typed or
    paste, Chat creates an output request data structure and sticks it
    on the output thread for this connection.

Arguments:

    WindowHandle - Supplies the handle to the edit control's window

    Message - Supplies the message identifier.

    WParam - Supplies the first message parameter.

    LParam - Supplies the second message parameter.

Return Value:

    ReturnValue - Depends of the message sent.

--*/
{
    int             Index;        // an index into the edit control's text
    HANDLE          CBDataHandle; // handle to the clipboard data
    LPSTR           CBDataPtr;    // pointer to data to send
    PCONNDATA       ConnData;     // pointer to connection-specific data
    POUTPUT_REQUEST OutReq;       // output request data
    int             TextSize;     // number of bytes of text to send
    DWORD           BytesLeft;    // number of bytes left to send

    ConnData = GetConnData(GetParent(WindowHandle));

    switch (Message) {

    case WM_CHAR:

        // If the character is sendable, enqueue the character on the
        // output queue, and signal the output event.
        if (IsSendable((char)WParam)) {

            OutReq = (POUTPUT_REQUEST) malloc(sizeof(OUTPUT_REQUEST));
            if (OutReq == NULL) {
                ChatSysError("malloc()",
                             "SendEditSubClass()",
                             TRUE);
            }

            // Rather than malloc a one-character buffer, just point
            // Buffer into the already-allocated Char field of the
            // OutReq structure.  This gets rid of a heavy-weight
            // malloc for each character typed.
            OutReq->Buffer.len = sizeof(char);
            OutReq->Buffer.buf = &OutReq->Character;

            // Fill in the OutReq structure.
            *(OutReq->Buffer.buf) = (char)WParam;
            OutReq->Type = NON_OVERLAPPED_IO;
            OutReq->ConnData = ConnData;

            QInsert(ConnData->OutputQueue, (LPVOID)OutReq);
            SetEvent(ConnData->OutputEventObject);
        }

        // Move the cursor to the end of the text buffer.
        Index = GetWindowTextLength(WindowHandle);
        CallWindowProc((WNDPROC)OldEditWndProc,
                       WindowHandle,
                       EM_SETSEL,
                       Index,
                       Index);

        // Make room for the new text in the edit control.
        MakeRoom(WindowHandle, 1);

        // Pass on the message to the original Edit Window Procedure.
        return (CallWindowProc((WNDPROC)OldEditWndProc,
                               WindowHandle,
                               Message,
                               WParam,
                               LParam));

    case WM_PASTE:

        // Get the buffer from the edit control and send it over the
        // socket.
        if (IsClipboardFormatAvailable(CF_TEXT)) {

            if (OpenClipboard(WindowHandle)) {
                CBDataHandle = GetClipboardData(CF_TEXT);

                if (CBDataHandle) {

                    CBDataPtr = GlobalLock(CBDataHandle);
                    TextSize = strlen(CBDataPtr);

                    // Make room in the edit control for the new text; if
                    // this is impossible, just return because MakeRoom
                    // notifies the user internally.
                    if (!MakeRoom(WindowHandle, TextSize)) {
                        GlobalUnlock(CBDataHandle);
                        CloseClipboard();
                        return(0);
                    }

                    // If the text is longer than the maximum message
                    // size, we need to split it into bite-size pieces
                    // for consumption by the transport layer.
                    BytesLeft = TextSize;
                    while (BytesLeft != 0) {

                        // Create a new output request.
                        OutReq =
                          (POUTPUT_REQUEST)malloc(sizeof(OUTPUT_REQUEST));
                        if (OutReq == NULL) {
                            ChatSysError("malloc()",
                                         "SendEditSubClass",
                                         TRUE);
                        }

                        // Determine how much data to send with this
                        // request, and allocate a buffer that size.
                        if (BytesLeft > ConnData->MaxMsgSize) {
                            OutReq->Buffer.len = ConnData->MaxMsgSize;
                        } else {
                            OutReq->Buffer.len = BytesLeft;
                        }
                        OutReq->Buffer.buf =
                          (char *)malloc(OutReq->Buffer.len);
                        if (OutReq->Buffer.buf == NULL) {
                            ChatSysError("malloc()",
                                         "SendEditSubClass",
                                         TRUE);
                        }

                        // Fill in the rest of the OutReq structure
                        // and put it in the output queue.
                        OutReq->Type = OVERLAPPED_IO;
                        OutReq->ConnData = ConnData;
                        memcpy(OutReq->Buffer.buf, CBDataPtr,
                               OutReq->Buffer.len);
                        QInsert(ConnData->OutputQueue, (LPVOID)OutReq);

                        // Update the number of bytes left to send and
                        // the pointer to the rest of the data.
                        BytesLeft -= OutReq->Buffer.len;
                        CBDataPtr += OutReq->Buffer.len;
                    }

                    // Signal the ouput event.
                    SetEvent(ConnData->OutputEventObject);
                    GlobalUnlock(CBDataHandle);

                } else {

                    MessageBox(WindowHandle,
                               "Sorry, couldn't retrieve the clipoard data.",
                               "Error.",
                               MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
                }

                CloseClipboard();

            } // if (OpenClipboard(WindowHandle))
            else {

                MessageBox(WindowHandle,
                           "Couldn't open the clipboard!!",
                           "Try again.",
                           MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
            }

        } // if (IsClipboardFormatAvailable(CF_TEXT))
        else {

            MessageBox(WindowHandle,
                       "Sorry, the CF_TEXT format not available.",
                       "Error.", MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
        }
        // Move the cursor to the end of the text buffer.
        Index = GetWindowTextLength(WindowHandle);
        CallWindowProc((WNDPROC)OldEditWndProc,
                       WindowHandle,
                       EM_SETSEL,
                       Index,
                       Index);

        // Call the real edit control window procedure.
        return (CallWindowProc((WNDPROC)OldEditWndProc,
                               WindowHandle,
                               Message,
                               WParam,
                               LParam));

    case WM_KEYDOWN:

        // Capture the delete key. nVirtKey: 46 = 'Del'.
        if (WParam == 46) {
            return(0);
        } else {
            return (CallWindowProc((WNDPROC)OldEditWndProc, WindowHandle,
                                   Message, WParam, LParam));
        }

    case WM_CUT:  // Flow through
    case WM_UNDO:

        return (0);

    default:

        return (CallWindowProc((WNDPROC)OldEditWndProc,
                               WindowHandle,
                               Message,
                               WParam,
                               LParam));

    } // switch (Message)

} // SendEditSubClass()

long CALLBACK
RecvEditSubClass(
    IN HWND   WindowHandle,
    IN UINT   Message,
    IN WPARAM WParam,
    IN LPARAM LParam)

/*++

Routine Description:

    Window procedure used to subclass the edit control for the
    receiving window.

Implementation:

    The user cannot modify any of the text displayed in the receiving
    edit control. All text received from the socket associated with
    this edit control is displayed.

Arguments:

    WindowHandle - Supplies the handle to the edit control's window

    Message - Supplies the message identifier.

    WParam - Supplies the first message parameter.

    LParam - Supplies the second message parameter.

Return Value:

    ReturnValue - Depends of the message sent.

--*/
{
    switch (Message) {

    case WM_CHAR:       // Flow through
    case WM_KEYDOWN:    // Flow through
    case WM_UNDO:       // Flow through
    case WM_PASTE:      // Flow through
    case WM_CUT:

        return (0);     // Effectively disables the above messages.

    default:

        return (CallWindowProc((WNDPROC)OldEditWndProc,
                               WindowHandle,
                               Message,
                               WParam,
                               LParam));
    }

} // RecvEditSubClass()

BOOL CALLBACK
CloseEnumProc(
    IN HWND WindowHandle,
    IN LONG LParam)

/*++

Routine Description:

    A callback function used to destroy all of chat's connection
    windows.

Arguments:

    WindowHandle - Supplies the handle to the child's window which
                   is to be closed.

    LParam - Not Used.

Return Value:

    TRUE - Returns true in order to continue enumeration.

--*/
{
    if (!GetWindow(WindowHandle, GW_OWNER)) {

        SendMessage(GetParent(WindowHandle), WM_MDIRESTORE,
                    (WPARAM)WindowHandle, 0);

        if (SendMessage(WindowHandle, WM_QUERYENDSESSION, 0, 0)) {
            SendMessage(GetParent(WindowHandle), WM_MDIDESTROY,
                        (WPARAM)WindowHandle, 0);
        }
    }
    return (TRUE);

} // CloseEnumProc()

void
OutputString(
    IN HWND RecvWindow,
    IN char *String)
/*++

Routine Description:

    This function sends the string received over the socket to the
    edit window given by RecvWindow.

Implementation:

    This function treats certain non-printable characters specially so
    that they mean the same thing on this end as they did on the end
    that sent them.

Arguments:

    RecvWindow -- Handle to the edit control window which is to
    receive the text.

    String -- Points to the NULL-terminated string to be output.

Return Value:

    None.

--*/
{

    int  Index, Index1;         // indices into the edit control text
    char *Current = String;     // the character we are examining
    char *First = String;       // the first character we have not output
    char Buffer[BUFFER_LENGTH]; // scratch buffer for SendMessage
    int  NumChars;              // how many characters we put in Buffer

    // Make room for the string in the edit control.
    MakeRoom(RecvWindow, strlen(String));

    // This loop goes through the whole string we have been instructed
    // to print in the edit control; when it encounters certain
    // characters in the string, it sends certain messages to the edit
    // control to simulate the right behavior.
    while (*Current) {

        switch (*Current) {

        case '\b':

            // The current character is a backspace.  Print out the
            // string we have so far, and then manually erase a
            // character.
            *Current = '\0';

            // Output the string at 'First'...
            Index = GetWindowTextLength(RecvWindow);
            SendMessage(RecvWindow, EM_SETSEL, Index, Index);
            SendMessage(RecvWindow, EM_REPLACESEL, 0, (LPARAM)First);

            // Get the last line of the text into 'Buffer'.
            Index = GetWindowTextLength(RecvWindow);
            Index1 = SendMessage(RecvWindow, EM_LINEFROMCHAR, Index, 0);
            *((LPDWORD)Buffer) = BUFFER_LENGTH;
            NumChars = SendMessage(RecvWindow, EM_GETLINE, Index1,
                                   (LPARAM)Buffer);

            // If the line had no characters in it, then it's the
            // beginning of a new line, and we should erase two
            // characters (the preceding \r\n) rather than just one.
            if (NumChars == 0) {
                SendMessage(RecvWindow, EM_SETSEL, Index - 2, Index);
                SendMessage(RecvWindow, EM_REPLACESEL, 0, (LPARAM)"");
            } else {
                SendMessage(RecvWindow, EM_SETSEL, Index - 1, Index);
                SendMessage(RecvWindow, EM_REPLACESEL, 0, (LPARAM)"");
            }

            First = Current + 1;
            break;

        case '\r':

            // A newline.  If the Chat window is minimized, ask the
            // user if they want it maximized.
            QueryAndMaximize();

            // If a remote user typed a newline, we'll receive '\r',
            // but if the remote user pasted it in, we'll get '\r\n'.
            if (*(Current + 1) != '\n') {

                // The newline was typed in...output the string up to
                // the newline, then "\r\n".
                *Current = '\0';

                Index = GetWindowTextLength(RecvWindow);
                SendMessage(RecvWindow, EM_SETSEL, Index, Index);
                SendMessage(RecvWindow, EM_REPLACESEL, 0, (LPARAM)First);

                Index = GetWindowTextLength(RecvWindow);
                SendMessage(RecvWindow, EM_SETSEL, Index, Index);
                SendMessage(RecvWindow, EM_REPLACESEL, 0, (LPARAM)"\r\n");

                First = Current + 1;
            }
            break;

        default:

            break;

        } // switch (*Current)
        Current++;

    } // while (*Current)

    // Ouptut any string leftover in First
    Index = GetWindowTextLength(RecvWindow);
    SendMessage(RecvWindow, EM_SETSEL, Index, Index);
    SendMessage(RecvWindow, EM_REPLACESEL, 0, (LPARAM)First);
}

void
QueryAndMaximize(void)
/*++

Routine Description:

    If Chat's main frame window is minimized, this function asks the
    user if they want to activate and display Chat, and acts
    accordingly.

Arguments:

    None.

Return Value:

    None.

--*/
{
    WINDOWPLACEMENT WinPlace; // holds window placement data

    WinPlace.length = sizeof(WINDOWPLACEMENT);

    // Get chat's main window's state.
    GetWindowPlacement(GlobalFrameWindow, &WinPlace);

    // If it's minimized, ask the user if they want to maximize.
    if (WinPlace.showCmd == SW_SHOWMINIMIZED) {

        if (MessageBox(GlobalFrameWindow,
                       "There is activity on a Chat connection.  Maximize?",
                       "Chat has data.",
                       MB_ICONQUESTION | MB_YESNO | MB_SETFOREGROUND)
            == IDYES) {

            // Maximize chat's main window.
            WinPlace.showCmd = SW_RESTORE;
            SetWindowPlacement(GlobalFrameWindow, &WinPlace);
        }
    }
} // QueryAndMaximize()

BOOL
GetConnectionInfo(
    IN HWND ConnectionWindow)

/*++

Routine Description:

    Queries the user as to what sort of connection (i.e. what address
    family/protocol) she wants to make.

Implementation:

    After popping up a listbox, filling it in with all the available
    protocol choices, and getting the user's choice, this function
    uses it to determine what kind of dialog box to pop up to get the
    actual connection parameters, which are stored in the CONNDATA
    associated with the given ConnectionWindow.

Arguments:

    ConnectionWindow -- Handle to the connection window associated
    with this connection.

Return Value:

    TRUE - The user closed a dialog box by clicking the 'Ok' button.

    FALSE - The user closed a dialog box by clicking the 'Cancel'
            button.

--*/

{
    PCONNDATA ConnData;           // connection-specific data
    BOOL      ReturnValue = TRUE; // holds the return value

    ConnData = GetConnData(ConnectionWindow);

    if (OtherAddr) {
        struct sockaddr_in *SockAddrInet;                 // Inet socket address

        ConnData->ProtocolInfo = GetProtoFromIndex(0);;

        ConnData->RemoteSockAddr.len = sizeof(struct sockaddr_in);
        ConnData->RemoteSockAddr.buf = malloc(ConnData->RemoteSockAddr.len);
        if (ConnData->RemoteSockAddr.buf == NULL) {
            ChatSysError("malloc()",
                         "InetConnDlgProc()",
                         TRUE);
        }

        SockAddrInet = (struct sockaddr_in *)ConnData->RemoteSockAddr.buf;
        SockAddrInet->sin_family = AF_INET;
        SockAddrInet->sin_port = (USHORT)LocalPort;
        SockAddrInet->sin_addr.S_un.S_addr = OtherAddr;

    } else {
        // Pop up a dialog box from which the user chooses the protocol; a
        // pointer to a protocol info struct is stored in ConnData.
        if (!DialogBoxParam(GlobalInstance,
                            "ChooseFamilyDlg",
                            ConnectionWindow,
                            ChooseFamilyDlgProc,
                            (LPARAM)ConnData)) {
            ReturnValue = FALSE;
            goto Done;
        }

        // The user has selected a protocol; now pop up an address family
        // specific dialog box to get the address to which we want to make
        // a chat connection, a pointer to which is stored in
        // ConnData->SockAddr (see InetConnDlg, for example).
        switch (ConnData->ProtocolInfo->iAddressFamily) {

        case AF_INET:

            ReturnValue = DialogBoxParam(GlobalInstance,
                                         "InetConnDlg",
                                         ConnectionWindow,
                                         InetConnDlgProc,
                                         (LPARAM)ConnData);
            goto Done;

        default:

            ReturnValue = DialogBoxParam(GlobalInstance,
                                   "DefaultConnDlg",
                                   ConnectionWindow,
                                   DefaultConnDlgProc,
                                   (LPARAM)ConnData);
            goto Done;
        }
    }

 Done:

    return(ReturnValue);

} // GetConnectionInfo()

BOOL
TranslateHex(
    OUT LPVOID Buffer,
    IN  int    BufferLen,
    IN  char   *HexString,
    IN  HWND   WindowHandle)
/*++

Routine Description:

    This function converts an arbitrarily long string of hexidecimal
    digits into a binary representation.

    Example -- if the string "0fbf" is passed in, then two bytes will
    be written into Buffer -- 15 and 191.

Arguments:

    Buffer -- Points to a buffer where we want the data stored.

    BufferLen -- The length, in bytes, of Buffer.

    HexString -- A null-terminated string of digits with values in the
    ASCII range of 0-9 and a-f.

    WindowHandle -- Window handle needed in case we pop up a dialog
    box.

Return Value:

    TRUE -- HexString was successfully translated.

    FALSE -- One of the characters does not represent a hex digit, or
    the HexString is too long to be translated into BufferLen bytes.

--*/
{

    int  HexStringLen;       // the length of the HexString, in bytes
    int  i;                  // counting variable
    char TwoCharString[3];   // stores two hex characters + a NULL char
    char *NextByte;          // next free byte in the buffer
    BOOL ReturnValue = TRUE; // return value

    HexStringLen  = strlen(HexString);
    NextByte = (char *)Buffer;
    TwoCharString[2] = '\0';

    // Zero out the output buffer.
    memset(NextByte, 0, BufferLen);

    // If the hex string in more than twice as long as the buffer, we
    // don't have a big enough buffer.
    if ((HexStringLen / 2) > BufferLen) {
        ReturnValue = FALSE;
    } else {

        // Go through the hex string two characters at a time...
        for (i = 0; i < HexStringLen; i += 2) {

            // Copy the next two bytes of the hex string into
            // TwoCharString; then check to make sure they are both
            // hexidecimal digits.
            memcpy(TwoCharString, HexString + i, 2);
            if (!isxdigit(TwoCharString[0]) || !isxdigit(TwoCharString[1])) {
                MessageBox(WindowHandle, "Type a hexidecimal number, please",
                           "Error.", MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
                ReturnValue = FALSE;
                break;
            }
            *NextByte++ = (char)strtol(TwoCharString, NULL, 16);

        } // for
    } // else
    return(ReturnValue);

} // TranslateHex()

BOOL
PackTwoStrings(
    OUT char  *Buffer,
    IN  int   BufferLen,
    IN  char  *String1,
    IN  char  *String2)
/*++

Routine Description:

    Packs two strings into one buffer.  Useful for packing caller data
    into a buffer for connection-time data transfer.  It is not an
    error if either string is of zero length, i.e. just the '\0'
    character.

Arguments:

    Buffer -- Pointer to a buffer into which the two strings will be
    packed.

    BufferLen -- The length, in bytes, of Buffer.

    String1 - Address of the first NULL terminated string.

    String2 - Address of the second NULL terminated string.

Return Value:

    TRUE -- The strings were successfully packed into the buffer.

    FALSE -- The passed in buffer was NULL, one or both of the strings
    were NULL, or Buffer is too small.

--*/

{
    int Length1, Length2;    // holds the length of the strings
    BOOL ReturnValue = TRUE; // holds the return value

    if (!Buffer || !String1 || !String2) {
        ReturnValue = FALSE;
        goto Done;
    }

    Length1 = strlen(String1);
    Length2 = strlen(String2);

    if (BufferLen < (Length1 + Length2 + 2)) {
        ReturnValue = FALSE;
        goto Done;
    }

    strcpy(Buffer, String1);
    strcpy(Buffer + Length1 + 1, String2);

 Done:

    return(ReturnValue);

} // PackTwoStrings()

BOOL
ExtractTwoStrings(
    IN  char *Buffer,
    OUT char *String1,
    IN  char Length1,
    OUT char *String2,
    IN  int  Length2)

/*++

Routine Description:

    Extracts two strings from a buffer, expecting them to be packed as
    PackTwoStrings does it.

Arguments:

    Buffer -- The buffer which contains two strings.

    String1 -- A pointer to a buffer of size Length1.

    Length1 -- The length, in bytes, of String1.

    String2 -- A pointer to a buffer of size Length2.

    Length2 -- The length, in bytes, of String2.

Return Value:

    TRUE -- The strings were successfully extracted.

    FALSE -- One of the pointers was NULL, or a packed string was too
    big to be extracted out into a buffer.

--*/

{
    int BufStrLen1, BufStrLen2; // the length of the packed strings
    char *BufStr2;              // points to the second packed string
    BOOL ReturnValue = TRUE;    // holds the return value

    if (!Buffer || !String1 || !String2) {
        ReturnValue = FALSE;
        goto Done;
    }

    BufStrLen1 = strlen(Buffer);
    BufStr2 = Buffer + BufStrLen1 + 1;
    BufStrLen2 = strlen(BufStr2);

    if ((BufStrLen1 > Length1) || (BufStrLen2 > Length2)) {
        ReturnValue = FALSE;
        goto Done;
    }

    strcpy(String1, Buffer);
    strcpy(String2, BufStr2);

 Done:

   return(ReturnValue);

} // ExtractTwoStrings()

void
ChatSysError(
    IN char *FailedFunction,
    IN char *InFunction,
    IN BOOL Fatal)
/*++

Routine Description:

    Pops up a standard message box to display fatal system errors; if
    the error is fatal, the process is ended and this function does
    not return.

Arguments:

    FailedFunction -- Pointer to a string that contains the name of
    the system function that failed.

    InFunction -- Pointer to a string that contains the name of the
    chat function in which the failure occurred.

    Fatal -- Boolean indicating whether the error is fatal to the
    process or not.

Return Value:

    None.

--*/
{

    char MsgText[MSG_LEN]; // holds message strings

    wsprintf(MsgText,
             "System call %s failed in chat function %s. Error code: %d",
             FailedFunction, InFunction, GetLastError());
    MessageBox(GlobalFrameWindow,
               MsgText,
               Fatal ? "Fatal Error." : "Non-Fatal Error.",
               MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);

    if (Fatal) {
        ExitProcess(CHAT_ERROR);
    }

} // ChatSysError()

BOOL
MakeRoom(
    IN HWND EditControl,
    IN int  HowMuch)
/*++

Routine Description:

    This function makes room in an edit control for more text, if
    necessary.

Arguments:

    EditControl -- The edit control in question.

    HowMuch -- How many characters do we want to put into the edit
    control?

Return Value:

    TRUE -- If there was not enough room in the edit control, MakeRoom
    successfully chopped off some text from the beginning, and there
    is now enough room.

    FALSE -- The requested amount can not fit in the edit control,
    period; a message box has informed the user.

--*/
{

    int  TextLength;  // how much text is in the edit control
    int  CharIndex1;
    int  CharIndex2;
    int  LineIndex;   // index variables
    BOOL ReturnValue; // return value

    if (HowMuch > MAX_EC_TEXT) {

        MessageBox(EditControl,
                   "Clipboard too long. Try pasting a smaller amount.",
                   "Error.", MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
        ReturnValue = FALSE;

    } else {

        TextLength = GetWindowTextLength(EditControl);

        if (TextLength + HowMuch > MAX_EC_TEXT) {

            // Inserting the text would exceed our limit.  This code
            // chops off roughly the first half of the edit control's
            // text.
            CharIndex1 = TextLength / 2;
            LineIndex = SendMessage(EditControl, EM_LINEFROMCHAR,
                                    (WPARAM)CharIndex1, 0);
            CharIndex2 = SendMessage(EditControl, EM_LINEINDEX,
                                     (WPARAM)LineIndex, 0);

            SendMessage(EditControl, EM_SETSEL, 0, CharIndex2);
            SendMessage(EditControl, EM_REPLACESEL, 0,
                        (LPARAM)"");
        }

        ReturnValue = TRUE;
    } // else

    return(ReturnValue);
} // MakeRoom()


QOSCHAT.H

/*++

Copyright (c) 1995 Intel Corp

Module Name:

    ws2chat.h

Abstract:

    This module defines the data structures and constants
    for the WinSock 2 sample chat application.

--*/

#ifndef _ws2chat_H
#define _ws2chat_H

#include "nowarn.h"  /* turn off benign warnings */
#ifndef _WINSOCKAPI_
#define _WINSOCKAPI_   /* Prevent inclusion of winsock.h in windows.h */
#endif
#include <windows.h>
#include "nowarn.h"  /* some warnings may have been turned back on */
#include <winsock2.h>
#include "queue.h"

// turn off "unreferenced formal parameter" warning
#pragma warning(disable: 4100)

//
// Manifest Constants
//

// UI/MDI
#define MAIN_MENU_POS         1
#define CLIENT_WINDOW_ID      1

#define EC_SEND_CHILD         0
#define EC_RECV_CHILD         1

#define IDM_CONNECT           10
#define IDM_CLOSE             12
#define IDM_EXIT              13
#define IDM_TILE              30
#define IDM_CASCADE           31
#define IDM_ARRANGE           32
#define IDM_CLOSEALL          33
#define IDM_CLEAR_SENDBUFFER  34
#define IDM_CLEAR_RECVBUFFER  35
#define IDM_FIRSTCHILD        100

// For some reason, 30k is the most characters you can have in an edit
// control at once.  This keeps us from going over.
#define MAX_EC_TEXT 25500

// resource constants
#define IDC_SUBJECT           1000
#define IDC_CALLEENAME        1001
#define IDC_CALLERNAME        1002
#define IDC_FAM_LB            1003
#define IDC_INET_ADDRESS      1004
#define IDC_INET_PORT         1005
#define IDC_ADDRESS           1006
#define IDC_LISTEN_PORT       1007
#define IDC_LISTEN_ADDR       1008
#define IDC_STATIC            -1

// data lengths
#define NAME_LEN              20
#define ADDR_LEN              40
#define SUB_LEN               80
#define MSG_LEN               200
#define TITLE_LEN             40
#define BUFFER_LENGTH         1024

// various stuff
#define MAX_SOCKADDR_LEN      512
#define MAX_LISTENING_SOCKETS 64
#define CONN_WND_EXTRA        8
#define GWL_CONNINFO          0
#define GWL_OLDEDITPROC       4
#define NO_MAX_MSG_SIZE       0xffffffff

// what kind of send?
#define OVERLAPPED_IO         0
#define NON_OVERLAPPED_IO     1

// chat return values
#define CHAT_OK               0
#define CHAT_ERROR            1
#define CHAT_CLOSED           2
#define CHAT_WOULD_BLOCK      3
#define CHAT_ABORTED          4

// for Internet connections
#define INET_ADDR_LEN         64
#define INET_PORT_LEN         5
#define INET_DEFAULT_PORT     9009

// for LSP connections
#define LSP_ADDR_LEN 10
#define LSP_DEFAULT_ADDR 9009

// user message values
#define USMSG_ACCEPT          WM_USER+1
#define USMSG_CONNECT         WM_USER+2
#define USMSG_TEXTOUT         WM_USER+3
#define USMSG_QOS             WM_USER+4

// version of Winsock we need
#define VERSION_MAJOR         2
#define VERSION_MINOR         0

// quality of Service
#define TOKENRATE             100
#define QOS_UNSPECIFIED       (DWORD)-1

//
// Types and Data Structures
//

typedef struct _CONNDATA CONNDATA, *PCONNDATA;
typedef struct _OUTPUT_REQUEST OUTPUT_REQUEST, *POUTPUT_REQUEST;

// structure created by the user interface and passed to the
// input/output thread -- tells the I/O thread what to send and how to
// send it.
struct _OUTPUT_REQUEST {
    int             Type;       // OVERLAPPED_IO or NON_OVERLAPPED_IO
    WSABUF          Buffer;     // holds the buffer (pointer) and size
    char            Character;  // for one-character ouput events
    PCONNDATA       ConnData;   // data for the associated connection
    LPWSAOVERLAPPED Overlapped; // NULL if not an overlapped send
};

// structure for storing data unique to each connection.
struct _CONNDATA {

                    // the socket for the connection
    SOCKET          Socket;

    DWORD           RefCount;       // so we know when to free it

                    // string containing the name of the user (only
                    // valid if user-data is supported by the protocol)
    char            PeerName[NAME_LEN + 1];

                    // string containing the subject of the chat
                    // session, as entered by the calling entity
    char            Subject[SUB_LEN + 1];

                    // string containing the (human-readable) address
                    // of the connected entity
    char            PeerAddress[ADDR_LEN + 1];

                    // handle to thread that takes care of network events
    HANDLE          IOThreadHandle;

                    // handle to event signaled when a network event
                    // occurs
    HANDLE          SocketEventObject;

                    // handle to event signaled when output is ready
                    // to be shipped
    HANDLE          OutputEventObject;

                    // This event is signalled in CleanupConnection to tell the thread to exit
    HANDLE          ThreadTerminationEvent;

                    // points to a queue used to hold output buffers
    PQUEUE          OutputQueue;

                    // handle to window associated with the connection
    HWND            ConnectionWindow;

                    // handle to the sending and receiving edit controls
    HWND            SendWindow;
    HWND            RecvWindow;

                    // the remote socket address for the connection
    WSABUF          RemoteSockAddr;

                    // buffer used by the connecting entity to get
                    // user-data back when the  USMSG_CONNECT is
                    // received
    WSABUF          CalleeBuffer;

                    // points to a protocol information struct which
                    // represents the protocol for this connection
    LPWSAPROTOCOL_INFO ProtocolInfo;

                    // are we waiting for an FD_WRITE?
    BOOL            WriteOk;

                    // How many events the wait in the I/O thread is
                    // waiting on.  Once that thread starts, this will
                    // always be at least 2 -- the SocketEventObject
                    // and the OutputEventObject.
    DWORD           NumEvents;

                    // The array of events which the I/O thread is
                    // currently waiting on.  Contains NumEvents entries.
    WSAEVENT        EventArray[WSA_MAXIMUM_WAIT_EVENTS];

                    // An array of pointers to output request
                    // structures, indexed in parallel to EventArray,
                    // above. This allows us to associate the events
                    // with the output requests, so we can free the
                    // right memory when the event is signaled.
    POUTPUT_REQUEST OutReqArray[WSA_MAXIMUM_WAIT_EVENTS];

                    // The maximum message size we can send on this
                    // socket. This value is either an integer or the
                    // manifest constant NO_MAX_MSG_SIZE.
    DWORD            MaxMsgSize;
};

// structure to associate listening sockets with a protocol
// information struct.
typedef struct _LISTENDATA {
    SOCKET             Socket;       // a listening socket
    LPWSAPROTOCOL_INFO ProtocolInfo; // the associated protocol info. struct
} LISTENDATA, *PLISTENDATA;

//
// Function prototypes for functions to be used outside of ws2chat.c
//

void
OutputString(
    IN HWND RecvWindow,
    IN char *String);

BOOL
ExtractTwoStrings(
    IN  char *Buffer,
    OUT char *String1,
    IN  char Length1,
    OUT char *String2,
    IN  int  Length2);

BOOL
TranslateHex(
    OUT LPVOID Buffer,
    IN  int    BufferLen,
    IN  char   *HexString,
    IN  HWND   WindowHandle);

BOOL
PackTwoStrings(
    OUT char *Buffer,
    IN int   BufferLen,
    IN char  *String1,
    IN char  *String2);

BOOL
MakeRoom(
    IN HWND EditControl,
    IN int  HowMuch);

void
ChatSysError(
    IN char *FailedFunction,
    IN char *InFunction,
    IN BOOL Fatal);

VOID
ParseCmdLine(
    LPSTR   CmdLine
    );

INT
usage();

DWORD
StartConnection(
    IN HWND   WindowHandle
    );

BOOL
HandleSocketEvent(
    IN OUT PCONNDATA ConnData);

int
HandleEvents(
    IN PCONNDATA          ConnData,
    IN LPWSANETWORKEVENTS NetworkEvents);

//
// Externally-Visible Variables
//

extern HANDLE  GlobalInstance;    // Identifies the instance of chat
extern char    ConnClassStr[];    // String to register window class
extern char    ChatClassStr[];    // String to register window class
extern HWND    GlobalFrameWindow; // Chat's main -- or frame -- window

extern DWORD   ThisAddr;
extern DWORD   OtherAddr;
extern DWORD   NumSockets;
extern DWORD   LocalPort;
extern BOOL    UseAsyncSelect;

#endif // _ws2chat_


QUEUE.C

/*++

Copyright (c) 1995 Intel Corp

Module Name:

    queue.h

Abstract:

    Implementation of functions supplied by this queue manager module.

--*/

#include <stdio.h>
#include <malloc.h>
#include "queue.h"

PQUEUE
QCreate(void)
/*++

Routine Description:

    Creates a new QUEUE, initializes it, and returns it. Returns NULL
    if memory cannot be allocated in the heap.

Arguments:

    None.

Return Value:

    NULL if memory allocation fails; else a pointer to a new queue.

--*/
{

    PQUEUE Queue; // points to the new queue

    // Allocate space for a new queue.
    Queue = (PQUEUE)malloc(sizeof(QUEUE));
    if (Queue != NULL){

        // Initialize the new queue.
        Queue->Head = Q_NULL;
        Queue->Tail = Q_NULL;
        InitializeCriticalSection(&Queue->CrSec);
    }
    return(Queue);
}

void
QFree(PQUEUE Queue)
/*++

Routine Description:

    This function removes any items left on the queue, frees them, and
    then frees the queue itself.

Arguments:

    Queue -- Pointer to the queue to free.

Return Value:

    None.

--*/
{

    LPVOID Object; // points to objects we pull off the queue

    EnterCriticalSection(&Queue->CrSec);

    // Free everything on the queue.
    Object = QRemove(Queue);
    while (Object) {
        free(Object);
        Object = QRemove(Queue);
    }

    LeaveCriticalSection(&Queue->CrSec);

    // Free the queue itself.
    free(Queue);
}

LPVOID
QRemove(PQUEUE Queue)
/*++

Routine Description:

    This function dequeues one object from Queue and returns a generic
    pointer to the object.  It is up to the calling function to know
    the size and type of the object dequeued.

Arguments:

    Queue -- The queue from which to dequeue the object.

Return Value:

    NULL if the queue is empty; else, a pointer to the dequeued object.

--*/
{

    LPVOID Data;    // points to the object we pull off the queue

    EnterCriticalSection(&Queue->CrSec);

    if (Queue->Head == Q_NULL){

        // If the queue is empty, we will return NULL.
        Data = NULL;

    } else {

        // Get the pointer, NULL it in the QueueArray.
        Data = Queue->QueueArray[Queue->Head];
        Queue->QueueArray[Queue->Head] = NULL;

        // Check to see if we've just emptied the queue; if so, set
        // the Head and Tail indices to Q_NULL.  If not, set the Head
        // index to the right value.
        if (Queue->Head == Queue->Tail) {
            Queue->Head = Queue->Tail = Q_NULL;
        } else {
            Queue->Head = (Queue->Head + 1) % MAX_QUEUE_SIZE;
        }
    }

    LeaveCriticalSection(&Queue->CrSec);
    return(Data);
}

BOOL
QInsert(
    PQUEUE Queue,
    LPVOID Object)
/*++

Routine Description:

    This function enqueues one item into Queue, if there is room for
    it.

Arguments:

    Queue -- The queue onto which to enqueue Object.

    Object -- A void pointer to the object being enqueued.

Return Value:

    TRUE -- The object was successfully enqueued.

    FALSE -- There were too many items already in the queue; one item
    must be removed in order to insert another.

--*/
{
    BOOL ReturnValue; // holds the return value

    EnterCriticalSection(&Queue->CrSec);

    // If the queue is full, set the return value to FALSE and do
    // nothing; if not, update the indices appropriately and set the
    // return value to TRUE.
    if(((Queue->Tail + 1) % MAX_QUEUE_SIZE == Queue->Head)
       && (Queue->Head != Q_NULL)) {

        ReturnValue = FALSE;

    } else {

        Queue->Tail = (Queue->Tail + 1) % MAX_QUEUE_SIZE;
        Queue->QueueArray[Queue->Tail] = Object;
        if (Queue->Head == Q_NULL){
            Queue->Head = 0;
        }
        ReturnValue = TRUE;
    }

    LeaveCriticalSection(&Queue->CrSec);
    return(ReturnValue);
}

BOOL
QInsertAtHead(
    PQUEUE Queue,
    LPVOID Object)
/*++

Routine Description:

    This function inserts one item at the HEAD of the queue.  Of
    course, this violates queue semantics...but this can be useful at
    times.

Arguments:

    Queue -- The queue into which to insert Object.

    Object -- A void pointer to the object being inserted.

Return Value:

    TRUE -- The object was successfully inserted at the head of the
    queue.

    FALSE -- There were too many items already in the queue; one item
    must be removed in order to insert another.

--*/
{

    BOOL ReturnValue; // holds the return value

    EnterCriticalSection(&Queue->CrSec);

    // If the queue is full, set the return value to FALSE and do
    // nothing; if not, update the indices appropriately and set the
    // return value to TRUE.
    if(((Queue->Tail + 1) % MAX_QUEUE_SIZE == Queue->Head)
       && (Queue->Head != Q_NULL)) {

        ReturnValue = FALSE;

    } else {

        if (Queue->Head == Q_NULL) {

            // The queue was empty, so just use QInsert.
            QInsert(Queue, Object);
        }

        if (Queue->Head == 0) {
            Queue->Head = MAX_QUEUE_SIZE - 1;
        } else {
            Queue->Head = Queue->Head - 1;
        }

        Queue->QueueArray[Queue->Head] = Object;
        ReturnValue = TRUE;
    }

    LeaveCriticalSection(&Queue->CrSec);
    return(ReturnValue);
}

BOOL
QIsEmpty(PQUEUE Queue)
/*++

Routine Description:

    This function tells the caller if the queue in question contains
    any items.

Arguments:

    Queue -- The queue which we want to query.

Return Value:

    TRUE -- The queue is empty.

    FALSE -- The queue contains at least one item.

--*/
{
    return(Queue->Head == Q_NULL ? TRUE : FALSE);
}


QUEUE.H

/*++

Copyright (c) 1995 Intel Corp

Module Name:

    queue.h

Abstract:

    Header file for the queue manager implemented by queue.c.

--*/
#ifndef QUEUE_H
#define QUEUE_H

#include "nowarn.h"  /* turn off benign warnings */
#ifndef _WINSOCKAPI_
#define _WINSOCKAPI_   /* Prevent inclusion of winsock.h in windows.h */
#endif
#include <windows.h>
#include "nowarn.h"  /* some warnings may have been turned back on */

#define ABNORMAL 1
#define NORMAL 0

#define MAX_QUEUE_SIZE 1024
#define Q_NULL -1

typedef struct _QUEUE {

  LPVOID QueueArray[MAX_QUEUE_SIZE];
  int Head;
  int Tail;
  CRITICAL_SECTION CrSec;

} QUEUE, *PQUEUE;

PQUEUE QCreate(void);
void QFree(PQUEUE Queue);
BOOL QInsert(PQUEUE Queue, LPVOID Object);
BOOL QInsertAtHead(PQUEUE Queue, LPVOID Object);
LPVOID QRemove(PQUEUE Queue);
BOOL QIsEmpty(PQUEUE Queue);

#endif


CHATDLG.C

/*++

Copyright (c) 1995 Intel Corp

Module Name:

    chatdlg.c

Abstract:

    Contains dialog-box procedures for the WinSock2 Chat sample
    application.  See qoschat.rc for the actual resource script.

--*/

#include "nowarn.h"  /* turn off benign warnings */
#ifndef _WINSOCKAPI_
#define _WINSOCKAPI_   /* Prevent inclusion of winsock.h in windows.h */
#endif
#include <windows.h>
#include "nowarn.h"  /* some warnings may have been turned back on */
#include <winsock2.h>
#include <stdlib.h>
#include <assert.h>
#include "qoschat.h"
#include "chatsock.h"
#include "chatdlg.h"

BOOL APIENTRY
ChooseFamilyDlgProc(
    IN HWND DialogWindow,
    IN UINT Message,
    IN WPARAM WParam,
    IN LPARAM LParam)
/*++

Routine Description:

    Callback function that processes messages sent to the 'Choose
    Family' dialog box.

Implementation

    This dialog box is used to query the user as to which supported
    protocol he wishes to use for the chat connection he wishes to
    make.  The available protocols are listed in the IDC_FAM_LB
    listbox via the FillInFamilies function in socket.c.

    It is expected that this dialog box gets the WM_INITDIALOG
    message with a LParam that is a pointer to the CONNDATA structure
    for the impending connection.  The result of this function is that
    the ProtocolInfo field of the ConnData struct is filled with a
    pointer to the protocol information structure the user chose in
    the listbox.

Arguments:

    DialogWindow - Supplies handle of the dialog box.

    Message - Supplies the message identifier

    WParam - Supplies the first message parameter

    LParam - Supplies the second message parameter

Return Value:

    TRUE -- This function handled the message.

    FALSE -- This function did not handle the message.

--*/
{
    int              LBIndex;         // index of the item chosen
    static PCONNDATA ConnData = NULL; // connection-specific data

    switch (Message) {

    case WM_INITDIALOG:

        // Get the parameter and fill in the listbox with the
        // available protocols.
        ConnData = (PCONNDATA)LParam;
        assert(ConnData != NULL);
        if (!FillInFamilies(DialogWindow, IDC_FAM_LB)) {
            MessageBox(DialogWindow, "FillInFamilies failed.", "Error.",
                        MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
            EndDialog(DialogWindow, FALSE);
        }

        return(TRUE);

    case WM_COMMAND:

        switch (WParam) {

        case IDOK:

            // Get the index of the listbox item the user has chosen.
            LBIndex = (int)SendMessage(GetDlgItem(DialogWindow, IDC_FAM_LB),
                                  LB_GETCURSEL, 0, 0);
            if (LBIndex == LB_ERR) {

                // Nothing was selected.
                MessageBox(DialogWindow, "Choose an item, or hit cancel.",
                           "No Selection?",
                           MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
                return(TRUE);
            }

            // Get the protocol associated with the listbox item the
            // user selected.
            ConnData->ProtocolInfo = GetProtoFromIndex(LBIndex);
            assert(ConnData->ProtocolInfo != NULL);
            EndDialog(DialogWindow, TRUE);
            return(TRUE);

        case IDCANCEL:

            // Kill the dialog box.
            EndDialog(DialogWindow, FALSE);
            return(TRUE);

        default:

            return(FALSE);
        } // switch (WParam)

    default:

        return(FALSE);
    } // switch (Message)

} // ChooseFamilyDlgProc()

BOOL APIENTRY
InetConnDlgProc(
    IN HWND DialogWindow,
    IN UINT Message,
    IN WPARAM WParam,
    IN LPARAM LParam)

/*++

Routine Description:

    Callback function that processes messages sent to the internet
    connection info dialog box.

Implementation

    This dialog box gets two strings from the user: the internet
    address and port to which she wants to make a chat connection.
    When the user clicks on the OK button, these two strings are
    converted into a sockaddr_in structure and a pointer to this
    structure is saved in the connection window's CONNDATA structure.

    NOTE: No name resolution takes place here!  The address edit
    control expects up to 15 characters, and this string is expected
    to be in.dotted.decimal.notation.  When RNR is available, this is
    where it would be called upon to provide the appropriate
    sockaddr_in structure.  Actually, this dialog box would be
    replaced with a protocol independent name dialog box; RNR would do
    all the rest...

Arguments:

    DialogWindow - Supplies handle of the dialog box.

    Message - Supplies the message identifier

    WParam - Supplies the first message parameter

    LParam - Supplies the second message parameter

Return Value:

    TRUE -- This function handled the message.

    FALSE -- This function did not handle the message; or the message
    was WM_INITDIALOG and we set the focus.

--*/

{
    char               PortText[INET_PORT_LEN + 1];   // holds port string
    char               AddressText[INET_ADDR_LEN + 1];// holds addr string
    static PCONNDATA   ConnData = NULL;               // conn-specific data
    struct sockaddr_in *SockAddrInet;                 // Inet socket address
    u_long             InetAddr;                      // Inet-style address
    u_short            InetPort;                      // Inet-style port
    int                Port;                          // intermediate var.
    BOOL               PortStringTranslated;          // for GetDlgItemInt
    struct hostent *host;

    switch (Message) {

    case WM_INITDIALOG:

        // Get the connection data pointer, initialize the dialog box.
        ConnData = (PCONNDATA)LParam;
        assert(ConnData != NULL);
        SendMessage(GetDlgItem(DialogWindow, IDC_INET_ADDRESS),
                    EM_LIMITTEXT, (WPARAM)INET_ADDR_LEN, 0);
        SendMessage(GetDlgItem(DialogWindow, IDC_INET_PORT),
                    EM_LIMITTEXT, (WPARAM)INET_PORT_LEN, 0);
        wsprintf(PortText,"%d", INET_DEFAULT_PORT);
        SendMessage(GetDlgItem(DialogWindow, IDC_INET_PORT),
                    WM_SETTEXT, 0, (LPARAM)PortText);
        SetFocus(GetDlgItem(DialogWindow, IDC_INET_ADDRESS));
        return(FALSE);

    case WM_COMMAND:

        switch (WParam) {

        case IDOK:

            // The user has pressed the 'OK' button.  Extract the
            // internet address into a buffer and check the value the
            // user typed for the port.
            GetDlgItemText(DialogWindow,
                           IDC_INET_ADDRESS,
                           AddressText,
                           INET_ADDR_LEN);
            strcpy(ConnData->PeerAddress, AddressText);
            Port = (int)GetDlgItemInt(DialogWindow,
                                      IDC_INET_PORT,
                                      &PortStringTranslated,
                                      TRUE);
            if ((Port < 0) || (Port > 65535) || !PortStringTranslated) {
              MessageBox(DialogWindow,
                         "Please choose a port between 0 and 65535.",
                         "Bad Port.",
                         MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
                return(TRUE);
            }

            // Convert the address string to an unsigned long, and
            // convert the port integer to an unsigned short.  If all
            // goes well, fill in the fields and end the dialog box.
            // If not, inform the user and *don't* kill the dialog box.
            InetAddr = inet_addr(AddressText);
            if (InetAddr == INADDR_NONE) {
                host = gethostbyname(AddressText);
                if (host) {
                    InetAddr = *((u_long *)host->h_addr_list[0]);
                }
            }
            InetPort = (u_short)Port;
            if ((InetAddr != INADDR_NONE) && (InetAddr != 0)) {

                // Allocate memory for an internet-style socket address;
                // point ConnData->SockAddr at this memory, and
                // use a local pointer to a struct sockaddr_in to
                // reference the fields.
                ConnData->RemoteSockAddr.len = sizeof(struct sockaddr_in);
                ConnData->RemoteSockAddr.buf =
                  malloc(ConnData->RemoteSockAddr.len);
                if (ConnData->RemoteSockAddr.buf == NULL) {
                    ChatSysError("malloc()",
                                 "InetConnDlgProc()",
                                 TRUE);
                }

                SockAddrInet =
                  (struct sockaddr_in *)ConnData->RemoteSockAddr.buf;
                SockAddrInet->sin_family = AF_INET;
                SockAddrInet->sin_port = InetPort;
                SockAddrInet->sin_addr.S_un.S_addr = InetAddr;
                EndDialog(DialogWindow, TRUE);
                return(TRUE);

            }

            MessageBox(DialogWindow,
                       "Invalid Internet address. Try again or cancel.",
                       "Error", MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
            return(TRUE);

        case IDCANCEL:

            // The user has pressed cancel.  Kill the dialog box.
            EndDialog(DialogWindow, FALSE);
            return(TRUE);

        default:

            break;
        } // switch (WParam)

        break;
    } // switch (Message)

    return(FALSE);
} // InetConnDlgProc()

BOOL APIENTRY
DefaultConnDlgProc(
    IN HWND DialogWindow,
    IN UINT Message,
    IN WPARAM WParam,
    IN LPARAM LParam)
/*++

Routine Description:

    Callback function that processes messages sent to the default
    connection info dialog box.

Implementation

    This dialog box is the default when chat doesn't recognize a
    particular address family.  The user enters actual hex digits and
    the hex value is stored in the SocketAddress associated with the
    connection. Obviously, this requires the user to know how to
    interpret the structure of the socket address for this address
    family.

Arguments:

    DialogWindow - Supplies handle of the dialog box.

    Message - Supplies the message identifier

    WParam - Supplies the first message parameter

    LParam - Supplies the second message parameter

Return Value:

    TRUE -- This function handled the message.

    FALSE -- This function did not handle the message.

--*/
{

    static PCONNDATA ConnData = NULL;                // connection data
    static int       AddrLen;                        // length of addresses
    char             AddrText[MAX_SOCKADDR_LEN * 2]; // address string

    switch (Message) {

    case WM_INITDIALOG:

        // Get the parameter and figure out how long addresses for
        // this protocol may be.
        ConnData = (PCONNDATA)LParam;
        assert(ConnData != NULL);

        AddrLen = ConnData->ProtocolInfo->iMaxSockAddr;

        if (AddrLen > MAX_SOCKADDR_LEN) {
            MessageBox(DialogWindow,
                       "Sorry, socket addresses are too big. Aborting.",
                       "Error.", MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
            EndDialog(DialogWindow, FALSE);
        }

        // Limit the amount of text in the edit control to twice
        // AddrLen, i.e. 2 hex characters per byte.
        SendMessage(GetDlgItem(DialogWindow, IDC_ADDRESS), EM_LIMITTEXT,
                    (WPARAM)(AddrLen * 2), 0);
        return(TRUE);

    case WM_COMMAND:

        switch (WParam) {

        case IDOK:

            // The user clicked 'OK'.  Get the text from the edit
            // control, and convert the hex string into bytes and put
            // the bytes into the socket address for this connection.
            GetDlgItemText(DialogWindow, IDC_ADDRESS, AddrText,
                           MAX_SOCKADDR_LEN * 2);
            ConnData->RemoteSockAddr.len = AddrLen;
            ConnData->RemoteSockAddr.buf =
              malloc(ConnData->RemoteSockAddr.len);
            if (ConnData->RemoteSockAddr.buf == NULL) {
                ChatSysError("malloc()",
                             "DefaultConnDlgProc()",
                             TRUE);
            }

            if (TranslateHex(ConnData->RemoteSockAddr.buf,
                             ConnData->RemoteSockAddr.len,
                             AddrText,
                             DialogWindow)) {
                EndDialog(DialogWindow, TRUE);
            }
            return(TRUE);

        case IDCANCEL:

            // Kill the dialog box.
            EndDialog(DialogWindow, FALSE);
            return(TRUE);

        default:

            return(FALSE);
        } // switch (WParam)

    default:

        return(FALSE);
    } // switch (Message)

} // DefaultConnDlgProc()

BOOL APIENTRY
NameAndSubjectDlgProc(
    IN HWND DialogWindow,
    IN UINT Message,
    IN WPARAM WParam,
    IN LPARAM LParam)
/*++

Routine Description:

    Callback function that processes messages sent to the name and
    subject dialog box.

Implementation

    This dialog box is brought up in MakeConnection (see socket.c)
    only if the protocol for the connection to be made supports
    connection-time data transfer.  The user fills in the two fields
    and this data is packed the WSABUF which is referenced through a
    pointer passed in during WM_INITDIALOG processing.

Arguments:

    DialogWindow - Supplies handle of the dialog box.

    Message - Supplies the message identifier

    WParam - Supplies the first message parameter

    LParam - Supplies the second message parameter

Return Value:

    TRUE -- This function handled the message.

    FALSE -- This function did not handle the message; or the message
    was WM_INITDIALOG and we set the focus.

--*/
{

    static LPWSABUF CallerBuffer;            // caller user data
    char            NameText[NAME_LEN + 1];  // name string
    char            SubjectText[SUB_LEN + 1];// subject string

    switch (Message) {

    case WM_INITDIALOG:

        // Get the parameter and initialize the dialog box.
        CallerBuffer = (LPWSABUF)LParam;
        assert(CallerBuffer != NULL);

        SendMessage(GetDlgItem(DialogWindow, IDC_CALLERNAME),
                    EM_LIMITTEXT, (WPARAM)NAME_LEN, 0);
        SendMessage(GetDlgItem(DialogWindow, IDC_SUBJECT),
                    EM_LIMITTEXT, (WPARAM)SUB_LEN, 0);
        SetFocus(GetDlgItem(DialogWindow, IDC_CALLERNAME));
        return(FALSE);

    case WM_COMMAND:

        switch (WParam) {

        case IDOK:

            // Get the strings the user has typed.
            GetDlgItemText(DialogWindow,
                           IDC_CALLERNAME,
                           NameText,
                           NAME_LEN);
            GetDlgItemText(DialogWindow,
                           IDC_SUBJECT,
                           SubjectText,
                           SUB_LEN);

            CallerBuffer->len = strlen(NameText) + strlen(SubjectText) + 2;
            CallerBuffer->buf = (char *)malloc(CallerBuffer->len);
            if (CallerBuffer->buf == NULL) {
                ChatSysError("malloc()",
                             "NameAndSubjectDlgProc()",
                             TRUE);
            }

            if (!PackTwoStrings(CallerBuffer->buf,
                                CallerBuffer->len,
                                NameText,
                                SubjectText)) {
                MessageBox(DialogWindow, "PackTwoStrings failed. Aborting.",
                           "Error.", MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
                EndDialog(DialogWindow, FALSE);
            }

            EndDialog(DialogWindow, TRUE);
            return(TRUE);

        case IDCANCEL:

            // Kill the dialog box.
            EndDialog(DialogWindow, FALSE);
            return(TRUE);

        default:

            return(FALSE);
        } // switch (WParam)

    default:

        return(FALSE);
    } // switch (Message)

} // NameAndSubjectDlgProc()

BOOL APIENTRY
AcceptConnectionDlgProc(
    IN HWND DialogWindow,
    IN UINT Message,
    IN WPARAM WParam,
    IN LPARAM LParam)

/*++

Routine Description:

    Callback function that processes messages sent to the
    AcceptConnection dialog box.  Get's the callee's name and copies
    it into the CalleeBuffer, which is passed in as a parameter to the
    WM_INITDIALOG message.

Arguments:

    DialogWindow - Supplies handle of the AcceptConnection dialog box

    Message - Supplies the message identifier

    WParam - Supplies the first message parameter

    LParam - Supplies the second message parameter

Return Value:

    TRUE -- This function handled the message.

    FALSE -- This function did not handle the message; or the message
    was WM_INITDIALOG and we set the focus.

--*/

{
    static LPWSABUF CalleeData; // callee user data

    switch (Message) {

    case WM_INITDIALOG:

        CalleeData = (LPWSABUF)LParam;
        assert(CalleeData != NULL);

        // Determine how much room there is in the user data buffer.
        // Limit the text to NAME_LEN characters, or less if we
        // don't have room for that many.
        if (CalleeData->len < (NAME_LEN + 1)) {
            SendMessage(GetDlgItem(DialogWindow, IDC_CALLERNAME),
                        EM_LIMITTEXT, (WPARAM)(CalleeData->len - 1),
                        0);
        } else {
            SendMessage(GetDlgItem(DialogWindow, IDC_CALLERNAME),
                        EM_LIMITTEXT, (WPARAM)NAME_LEN, 0);
        }
        SetFocus(GetDlgItem(DialogWindow, IDC_CALLEENAME));
        return(FALSE);

    case WM_COMMAND:

        switch (WParam) {

        case IDOK:

            // Get the name from the control and but it in CalleeData.
            GetDlgItemText(DialogWindow,
                           IDC_CALLEENAME,
                           CalleeData->buf,
                           NAME_LEN);
            CalleeData->len = strlen(CalleeData->buf + 1);
            EndDialog(DialogWindow, TRUE);
            return(TRUE);

        case IDCANCEL:

            // Kill the dialog box
            EndDialog(DialogWindow, FALSE);
            return(TRUE);

        default:

            break;

        }

        break;
    } // switch (Message)

    return(FALSE);
} // AcceptConnectionDlgProc()

BOOL APIENTRY
InetListenPortDlgProc(
    IN HWND DialogWindow,
    IN UINT Message,
    IN WPARAM WParam,
    IN LPARAM LParam)
/*++

Routine Description:

    Callback function that processes messages sent to the
    listening port dialog box.

Arguments:

    DialogWindow - Supplies handle of the AcceptConnection dialog box

    Message - Supplies the message identifier

    WParam - Supplies the first message parameter

    LParam - Supplies the second message parameter

Return Value:

    TRUE -- This function handled the message.

    FALSE -- This function did not handle the message; or the message
    was WM_INITDIALOG and we set the focus.

--*/
{

    static struct sockaddr_in *SockAddrInet;             // Inet sockaddress
    int                       Port;                      // Inet port
    BOOL                      PortStringTranslated;      // for GetDlgItemInt
    char                      PortText[INET_PORT_LEN+1]; // Inet port string

    switch (Message) {

    case WM_INITDIALOG:

        // Get the parameter and initialize the dialog box.
        SockAddrInet = (struct sockaddr_in *)LParam;
        assert(SockAddrInet != NULL);
        SendMessage(GetDlgItem(DialogWindow, IDC_LISTEN_PORT),
                    EM_LIMITTEXT, (WPARAM)INET_PORT_LEN, 0);
        wsprintf(PortText,"%d", INET_DEFAULT_PORT);
        SendMessage(GetDlgItem(DialogWindow, IDC_LISTEN_PORT),
                    WM_SETTEXT, 0, (LPARAM)PortText);
        SetFocus(GetDlgItem(DialogWindow, IDC_LISTEN_PORT));
        return(FALSE);

    case WM_COMMAND:

        switch (WParam) {

        case IDOK:

            // Get the port.
            Port = (int)GetDlgItemInt(DialogWindow,
                                      IDC_LISTEN_PORT,
                                      &PortStringTranslated,
                                      TRUE);
            if ((Port < 0) || (Port > 65535) || !PortStringTranslated) {
                MessageBox(DialogWindow,
                           "Please choose a port between 0 and 65535.",
                           "Bad Port.",
                           MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
                return(TRUE);
            }

            SockAddrInet->sin_port = (u_short)Port;
            EndDialog(DialogWindow, TRUE);
            return(TRUE);

        case IDCANCEL:

            // Kill the dialog box.
            EndDialog(DialogWindow, FALSE);
            return(TRUE);

        default:
            break;
        }
        break;

    } // switch (Message)

    return(FALSE);
} // InetListenPortDlgProc()

BOOL APIENTRY
DefaultListenAddrDlgProc(
    IN HWND DialogWindow,
    IN UINT Message,
    IN WPARAM WParam,
    IN LPARAM LParam)
/*++

Routine Description:

    Callback function that processes messages sent to the default
    listening address dialog box.

Arguments:

    DialogWindow - Supplies handle of the AcceptConnection dialog box

    Message - Supplies the message identifier

    WParam - Supplies the first message parameter

    LParam - Supplies the second message parameter

Return Value:

    TRUE -- This function handled the message.

    FALSE -- This function did not handle the message; or the message
    was WM_INITDIALOG and we set the focus.

--*/
{

    static LPWSABUF SockAddrBuf;
    static char     *AddrText;
    static int      AddrTextLen;

    switch (Message) {

    case WM_INITDIALOG:

        // Get the parameter and initialize the dialog box.
        SockAddrBuf = (LPWSABUF)LParam;
        assert(SockAddrBuf != NULL);

        AddrTextLen = SockAddrBuf->len * 2 + 1;
        AddrText = (char *)malloc(AddrTextLen);

        SendMessage(GetDlgItem(DialogWindow, IDC_LISTEN_ADDR),
                    EM_LIMITTEXT, AddrTextLen - 1, 0);
        wsprintf(AddrText,"%X",
                 ((struct sockaddr *)SockAddrBuf->buf)->sa_family);
        SendMessage(GetDlgItem(DialogWindow, IDC_LISTEN_ADDR),
                    WM_SETTEXT, 0, (LPARAM)AddrText);
        SetFocus(GetDlgItem(DialogWindow, IDC_LISTEN_ADDR));
        return(FALSE);

    case WM_COMMAND:

        switch (WParam) {

        case IDOK:

            // The user clicked 'OK'.  Get the text from the edit
            // control, and convert the hex string into bytes and put
            // the bytes into the socket address for this connection.
            GetDlgItemText(DialogWindow, IDC_LISTEN_ADDR, AddrText,
                           AddrTextLen);
            if (TranslateHex(SockAddrBuf->buf,
                             SockAddrBuf->len,
                             AddrText,
                             DialogWindow)) {
                EndDialog(DialogWindow, TRUE);
            }

            EndDialog(DialogWindow, TRUE);
            free(AddrText);
            return(TRUE);

        case IDCANCEL:

            // Kill the dialog box.
            EndDialog(DialogWindow, FALSE);
            free(AddrText);
            return(TRUE);

        default:
            break;
        }
        break;

    } // switch (Message)

    return(FALSE);
} // DefaultListenAddrDlgProc()


RNRCLNT.C

/*++

Copyright (c) 1992 Microsoft Corporation

Module Name:

    RnrClnt.c

Abstract:

    Test and demonstration client for the RNR (service Registration and
    Name Resolution) APIs.  This is a simple client application designed
    to show the basic principles involved in using the RNR APIs to _write
    a protocol-independent Windows Sockets client application.

    This client works by examining the protocols loaded on the machine,
    looking for protocols which are reliable and stream-oriented.  Then
    it attempts to locate and connect to the service on these protocols.
    When is has successfully connected to the service, it sends
    exchanges several messages with the service and then terminates the
    connection.

    The OpenConnection() routine implemented herein is intended to be a
    demonstration of RNR functionality commonly used in
    protocol-independent clients.  Application writers are encouraged to
    leverage this code to assist them in writing protocol-independent
    applications on top of the Windows Sockets API.

--*/

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <winsock2.h>
#include <nspapi.h>

#define DEFAULT_TRANSFER_SIZE    512
#define DEFAULT_TRANSFER_COUNT   0x10
#define DEFAULT_CONNECTION_COUNT 1
#define DEFAULT_DELAY            0

#define DEFAULT_RECEIVE_BUFFER_SIZE 4096
#define DEFAULT_SEND_BUFFER_SIZE    4096

#define MAX_PROTOCOLS  10
#define MAX_HOST_NAMES 16

WSADATA WsaData;
DWORD TransferSize = DEFAULT_TRANSFER_SIZE;
DWORD TransferCount = DEFAULT_TRANSFER_COUNT;
PCHAR IoBuffer;
DWORD RepeatCount = 1;
INT ReceiveBufferSize = DEFAULT_RECEIVE_BUFFER_SIZE;
INT SendBufferSize = DEFAULT_SEND_BUFFER_SIZE;

PCHAR RemoteName = "localhost";
PCHAR ServiceTypeName = "EchoExample";

VOID
DoEcho(
    IN SOCKET s );

SOCKET
OpenConnection(
    IN  PTSTR  ServiceName,
    IN  LPGUID ServiceType,
    IN  BOOL   Reliable,
    IN  BOOL   MessageOriented,
    IN  BOOL   StreamOriented,
    IN  BOOL   Connectionless,
    OUT PINT   ProtocolUsed );

INT
Rnr20_GetAddressByName(
    IN     PTSTR         ServiceName,
    IN     LPGUID        ServiceType,
    IN     DWORD         dwNameSpace,
    IN     DWORD         dwNumberOfProtocols,
    IN     LPAFPROTOCOLS lpAfpProtocols,
    IN OUT LPVOID        lpCSAddrInfo,
    IN OUT LPDWORD       lpdwBufferLength );

void _CRTAPI1
main(
    int argc,
    char *argv[] )
{
    INT err;
    DWORD i;
    DWORD protocol[2];
    SOCKET s;
    BYTE buffer[1024];
    BYTE buffer2[1024];
    DWORD bytesRequired;
    PPROTOCOL_INFO protocolInfo;
    GUID serviceType;

    //
    // Initialize the Windows Sockets DLL.
    //

    err = WSAStartup( 0x0202, &WsaData );
    if ( err == SOCKET_ERROR )
    {
        printf( "WSAStartup() failed: %ld\n", GetLastError( ) );
        return;
    }

    //
    // Parse command-line arguments.
    //

    for ( i = 1; i < (ULONG)argc != 0; i++ )
    {
        if ( _strnicmp( argv[i], "/name:", 6 ) == 0 )
        {
            RemoteName = argv[i] + 6;
        }
        else if ( _strnicmp( argv[i], "/type:", 6 ) == 0 )
        {
            ServiceTypeName = argv[i] + 6;
        }
        else if ( _strnicmp( argv[i], "/size:", 6 ) == 0 )
        {
            TransferSize = atoi( argv[i] + 6 );
        }
        else if ( _strnicmp( argv[i], "/count:", 7 ) == 0 )
        {
            TransferCount = atoi( argv[i] + 7 );
        }
        else if ( _strnicmp( argv[i], "/rcvbuf:", 8 ) == 0 )
        {
            ReceiveBufferSize = atoi( argv[i] + 8 );
        }
        else if ( _strnicmp( argv[i], "/sndbuf:", 8 ) == 0 )
        {
            SendBufferSize = atoi( argv[i] + 8 );
        }
        else
        {
            printf( "Usage: rnrclnt [/name:SVCNAME] [/type:TYPENAME] [/size:N]\n" );
            printf( "               [/count:N] [/rcvbuf:N] [/sndbuf:N]\n" );
            exit( 0 );
        }
    }

    //
    // Allocate memory to hold the network I/O buffer.
    //

    IoBuffer = malloc( TransferSize + 1 );
    if ( IoBuffer == NULL )
    {
        printf( "Failed to allocate I/O buffer.\n" );
        exit( 0 );
    }

    //
    // Determine the type (GUID) of the service we are interested in
    // connecting to.
    //

    err = GetTypeByName( ServiceTypeName, &serviceType );
    if ( err == SOCKET_ERROR )
    {
        printf( "GetTypeByName for \"%s\" failed: %ld\n",
                    ServiceTypeName, GetLastError( ) );
        exit( 0 );
    }

    //
    // Open a connected socket to the service.
    //

    s = OpenConnection(
            RemoteName,
            &serviceType,
            TRUE,
            FALSE,
            FALSE,
            FALSE,
            &protocol[0]
            );

    if ( s == INVALID_SOCKET )
    {
        printf( "Failed to open connection to name \"%s\" type \"%s\"\n",
                    RemoteName, ServiceTypeName );
        exit( 0 );
    }

    //
    // The connection succeeded.  Display some information on the
    // protocol which was used.
    //

    bytesRequired = sizeof(buffer);
    protocol[1] = 0;

    err = EnumProtocols( protocol, buffer, &bytesRequired );

    if ( err < 1 )
    {
        printf( "EnumProtocols failed for protocol %ld: %ld\n",
                protocol[0], GetLastError( ) );
        exit( 0 );
    }

    err = GetNameByType( &serviceType, buffer2, sizeof(buffer2) );

    if ( err != NO_ERROR )
    {
        printf( "GetNameByType failed: %ld\n", GetLastError( ) );
        exit ( 0 );
    }

    protocolInfo = (PPROTOCOL_INFO)buffer;
    printf( "Connected to %s/%s with protocol \"%s\" (%ld)\n",
            RemoteName, buffer2,
            protocolInfo->lpProtocol,
            protocolInfo->iProtocol );

    //
    // Send data to and from the service.
    //

    DoEcho( s );

} // main

VOID
DoEcho(
    IN SOCKET s )
{
    INT err;
    INT bytesReceived;
    DWORD i;
    DWORD startTime;
    DWORD endTime;
    DWORD transferStartTime;
    DWORD transferEndTime;
    DWORD totalTime;
    INT thisTransferSize;
    DWORD bytesTransferred = 0;

    startTime = GetTickCount( );

    for ( i = 0; i < TransferCount; i++ )
    {
        thisTransferSize = TransferSize;

        transferStartTime = GetTickCount( );

        err = send( s, IoBuffer, thisTransferSize, 0 );

        if ( err != thisTransferSize )
        {
            printf( "send didn't work, ret = %ld, error = %ld\n",
                    err, GetLastError( ) );
            closesocket( s );
            return;
        }

        bytesReceived = 0;
        do {
            err = recv( s, IoBuffer, thisTransferSize, 0 );

            if ( err == SOCKET_ERROR )
            {
                printf( "recv failed: %ld\n", GetLastError( ) );
                closesocket( s );
                return;
            }
            else if ( err == 0 && thisTransferSize != 0 )
            {
                printf( "socket closed prematurely by remote.\n" );
                return;
            }

            bytesReceived += err;
        } while ( bytesReceived < thisTransferSize );

        transferEndTime = GetTickCount( );
        printf( "%5ld bytes sent and received in %ld ms\n",
                thisTransferSize, transferEndTime - transferStartTime );

        bytesTransferred += thisTransferSize;
    }

    endTime = GetTickCount( );
    totalTime = endTime - startTime;

    printf( "\n%ld bytes transferred in %ld iterations, time = %ld ms\n",
            bytesTransferred, TransferCount, totalTime );
    printf( "Rate = %ld KB/s, %ld T/S, %ld ms/iteration\n",
            (bytesTransferred / totalTime) * 2,
            (TransferCount*1000) / totalTime,
            totalTime / TransferCount );

    err = closesocket( s );

    if ( err == SOCKET_ERROR )
    {
        printf( "closesocket failed: %ld\n", GetLastError( ) );
        return;
    }

    return;

} // DoEcho

SOCKET
OpenConnection(
    IN  PTSTR  ServiceName,
    IN  LPGUID ServiceType,
    IN  BOOL   Reliable,
    IN  BOOL   MessageOriented,
    IN  BOOL   StreamOriented,
    IN  BOOL   Connectionless,
    OUT PINT   ProtocolUsed )

/*++

Routine Description:

    Examines the Windows Sockets transport protocols loaded on a machine
    and determines those which support the characteristics requested by
    the caller.  Attempts to locate and connect to the specified service
    on these protocols.

Arguments:

    ServiceName - a friendly name which identifies the service we want
        to connect to.  On name spaces which support name resolution at
        the service level (e.g.  SAP) this is the name clients will use
        to connect to this service.  On name spaces which support name
        resolution at the host level (e.g.  DNS) this name is ignored
        and applications must use the host name to establish
        communication with this service.

    ServiceType - the GUID value which uniquely identifies the type of
        service we provide.  A GUID is created with the UUIDGEN program.

    Reliable - if TRUE, the caller requests that only transport protocols
        which support reliable data delivery be used.  If FALSE, both
        reliable and unreliable protocols may be used.

    MessageOriented - if TRUE, only message-oriented transport protocols
        should be used.  If FALSE, the caller either does not care
        whether the protocols used are message oriented or desires only
        stream-oriented protocols.

    StreamOriented - if TRUE, only stream-oriented transport protocols
        should be used.  If FALSE, the caller either does not care
        whether the protocols used are stream oriented or desires only
        message-oriented protocols.

    Connectionless - if TRUE, only connectionless protocols should be
        used.  If FALSE, both connection-oriented and connectionless
        protocols may be used.

    ProtocolUsed - if a connection is opened successfully, this
        parameter receives the protocol ID of the protocol used to
        establish the connection.

Return Value:

    A connected socket handle, or INVALID_SOCKET if the connection
    could not be established.

--*/

{
    INT protocols[MAX_PROTOCOLS+1];
    AFPROTOCOLS afProtocols[MAX_PROTOCOLS+1];
    BYTE buffer[2048];
    DWORD bytesRequired;
    INT err;
    PPROTOCOL_INFO protocolInfo;
    PCSADDR_INFO csaddrInfo = NULL;
    INT protocolCount;
    INT addressCount;
    INT i;
    DWORD protocolIndex;
    SOCKET s;

    //
    // First look up the protocols installed on this machine.  The
    // EnumProtocols() API returns about all the Windows Sockets
    // protocols loaded on this machine, and we'll use this information
    // to identify the protocols which provide the necessary semantics.
    //

    bytesRequired = sizeof(buffer);

    err = EnumProtocols( NULL, buffer, &bytesRequired );

    if ( err <= 0 )
    {
        return INVALID_SOCKET;
    }

    //
    // Walk through the available protocols and pick out the ones which
    // support the desired characteristics.
    //

    protocolCount = err;
    protocolInfo = (PPROTOCOL_INFO)buffer;

    for ( i = 0, protocolIndex = 0;
          i < protocolCount && protocolIndex < MAX_PROTOCOLS;
          i++, protocolInfo++ )
    {
        //
        // If "reliable" support is requested, then check if supported
        // by this protocol.  Reliable support means that the protocol
        // guarantees delivery of data in the order in which it is sent.
        // Note that we assume here that if the caller requested reliable
        // service then they do not want a connectionless protocol.
        //

        if ( Reliable )
        {
            //
            // Check to see if the protocol is reliable.  It must
            // guarantee both delivery of all data and the order in
            // which the data arrives.  Also, it must not be a
            // connectionless protocol.
            //

            if ( (protocolInfo->dwServiceFlags &
                      XP_GUARANTEED_DELIVERY) == 0 ||
                 (protocolInfo->dwServiceFlags &
                      XP_GUARANTEED_ORDER) == 0 )
            {
                continue;
            }

            if ( (protocolInfo->dwServiceFlags & XP_CONNECTIONLESS) != 0 )
            {
                continue;
            }

            //
            // Check to see that the protocol matches the stream/message
            // characteristics requested.  A stream oriented protocol
            // either has the XP_MESSAGE_ORIENTED bit turned off, or
            // else supports "pseudo stream" capability.  Pseudo stream
            // means that although the underlying protocol is message
            // oriented, the application may open a socket of type
            // SOCK_STREAM and the protocol will hide message boundaries
            // from the application.
            //

            if ( StreamOriented &&
                 (protocolInfo->dwServiceFlags & XP_MESSAGE_ORIENTED) != 0 &&
                 (protocolInfo->dwServiceFlags & XP_PSEUDO_STREAM) == 0 )
            {
                continue;
            }

            if ( MessageOriented &&
                 (protocolInfo->dwServiceFlags & XP_MESSAGE_ORIENTED) == 0 )
            {
                continue;
            }
        }
        else if ( Connectionless )
        {
            //
            // Make sure that this is a connectionless protocol.  In a
            // connectionless protocol, data is sent as discrete
            // datagrams with no connection establishment required.
            // Connectionless protocols typically have no reliability
            // guarantees.
            //

            if ( (protocolInfo->dwServiceFlags & XP_CONNECTIONLESS) != 0 )
            {
                continue;
            }
        }

        //
        // This protocol fits all the criteria.  Add it to the list of
        // protocols in which we're interested.
        //
        afProtocols[protocolIndex].iProtocol = protocolInfo->iProtocol;
        afProtocols[protocolIndex].iAddressFamily = AF_UNSPEC;

        protocols[protocolIndex++] = protocolInfo->iProtocol;
    }

    //
    // Make sure that we found at least one acceptable protocol.  If
    // there no protocols on this machine which meet the caller's
    // requirements then fail here.
    //

    if ( protocolIndex == 0 )
    {
        return INVALID_SOCKET;
    }

    afProtocols[protocolIndex].iProtocol = 0;
    afProtocols[protocolIndex].iAddressFamily = 0;

    protocols[protocolIndex] = 0;

    //
    // Now attempt to find the address of the service to which we're
    // connecting.  Note that we restrict the scope of the search to
    // those protocols of interest by passing the protocol array we
    // generated above to RnrGetAddressFromName() or GetAddressByName()
    // depending on whether we are running the client on the same machine
    // as the server rnrsrv.exe is running on.  This forces
    // RnrGetAddressFromName() or GetAddressByName() to return socket
    // addresses for only the protocols we specify, ignoring possible
    // addresses for protocols we cannot support because of the caller's
    // constraints.
    //

    bytesRequired = sizeof( buffer );

    if ( !strcmp( ServiceName, "localhost" ) )
    {
        //
        // This is a Winsock 1.0 call . . .
        //
        err = GetAddressByName( NS_DEFAULT,
                                ServiceType,
                                ServiceName,
                                protocols,
                                0,
                                NULL,
                                buffer,
                                &bytesRequired,
                                NULL,
                                NULL );
    }
    else
    {
        //
        // This calls into Winsock 2.0 . . .
        //
        err = Rnr20_GetAddressByName( ServiceName,
                                      ServiceType,
                                      NS_ALL,
                                      protocolIndex,
                                      afProtocols,
                                      buffer,
                                      &bytesRequired );
    }

    if ( err <= 0 )
    {
        return INVALID_SOCKET;
    }

    addressCount = err;
    csaddrInfo = (PCSADDR_INFO) buffer;

    //
    // For each address, open a socket and attempt to connect.  Note that
    // if anything fails for a particular protocol we just skip on to
    // the next protocol.  As soon as we have established a connection,
    // quit trying.
    //

    for ( i = 0; i < addressCount; i++, csaddrInfo++ )
    {
        //
        // Open the socket.  Note that we manually specify stream type
        // if so requested in case the protocol is natively a message
        // protocol but supports stream semantics.
        //

        s = socket( csaddrInfo->LocalAddr.lpSockaddr->sa_family,
                    StreamOriented ? SOCK_STREAM : csaddrInfo->iSocketType,
                    csaddrInfo->iProtocol );

        if ( s == INVALID_SOCKET )
        {
            continue;
        }

        //
        // Bind the socket to the local address specified.
        //

        err = bind( s, csaddrInfo->LocalAddr.lpSockaddr,
                    csaddrInfo->LocalAddr.iSockaddrLength );

        if ( err != NO_ERROR )
        {
            closesocket( s );
            continue;
        }

        //
        // Attempt to connect the socket to the service.  If this fails,
        // keep trying on other protocols.
        //

        err = connect( s, csaddrInfo->RemoteAddr.lpSockaddr,
                       csaddrInfo->RemoteAddr.iSockaddrLength );

        if ( err != NO_ERROR )
        {
            closesocket( s );
            continue;
        }

        //
        // The socket was successfully connected.  Remember the protocol
        // used and return the socket handle to the caller.
        //

        *ProtocolUsed = csaddrInfo->iProtocol;
        return s;
    }

    if ( csaddrInfo )
    {
        (void) LocalFree( (HLOCAL) csaddrInfo );
    }

    //
    // We failed to connect to the service.
    //

    return INVALID_SOCKET;

} // OpenConnection

INT
Rnr20_GetAddressByName(
    IN     PTSTR         szServiceName,
    IN     LPGUID        lpServiceType,
    IN     DWORD         dwNameSpace,
    IN     DWORD         dwNumberOfProtocols,
    IN     LPAFPROTOCOLS lpAfpProtocols,
    IN OUT LPVOID        lpCSAddrInfos,
    IN OUT LPDWORD       lpdwBufferLength )

/*++

Routine Description:

    Calls Winsock 2.0 service lookup routines to find service addresses.

Arguments:

    szServiceName - a friendly name which identifies the service we want
        to find the address of.

    lpServiceType - a GUID that identifies the type of service we want
        to find the address of.

    dwNameSpace - The Winsock2 Name Space to get address from (i.e. NS_ALL)

    dwNumberOfProtocols - Size of the protocol constraint array, may be zero.

    lpAftProtocols -  (Optional) References an array of AFPROTOCOLS structure.
        Only services that utilize these protocols will be returned.

    lpCSAddrInfos - On successful return, this will point to an array of
        CSADDR_INFO structures that contains the host address(es). Memory
        is passed in by callee and the length of the buffer is provided by
        lpdwBufferLength.

    lpdwBufferLength - On input provides the length in bytes of the buffer
        lpCSAddrInfos. On output returns the length of the buffer used or
        what length the buffer needs to be to store the address.

Return Value:

    The number of CSADDR_INFO structures returned in lpCSAddrInfos, or
    (INVALID_SOCKET) with a WIN32 error in GetLastError.

--*/

{
    ULONG            dwLength = 2048;      // Guess at buffer size
    PWSAQUERYSETA    pwsaQuerySet;
    ULONG            err;
    HANDLE           hRnR;
    DWORD            tempSize;
    DWORD            entries = 0;
    DWORD            dwNumberOfCsAddrs;

    RtlZeroMemory( lpCSAddrInfos, *lpdwBufferLength );

    pwsaQuerySet = (PWSAQUERYSETA) LocalAlloc( LMEM_ZEROINIT, dwLength );

    if ( pwsaQuerySet == NULL )
    {
        //
        // Unsuccessful.
        //
        return ERROR_NOT_ENOUGH_MEMORY;
    }

    RtlZeroMemory( pwsaQuerySet, dwLength );

    //
    // Do a lookup using RNRr.
    //
    pwsaQuerySet->dwSize = sizeof( WSAQUERYSETA );
    pwsaQuerySet->lpszServiceInstanceName = szServiceName;
    pwsaQuerySet->lpServiceClassId = lpServiceType;
    pwsaQuerySet->lpVersion = 0;
    pwsaQuerySet->lpszComment = 0;
    pwsaQuerySet->dwNameSpace = dwNameSpace;
    pwsaQuerySet->lpNSProviderId = 0;
    pwsaQuerySet->lpszContext = 0;
    pwsaQuerySet->dwNumberOfProtocols = dwNumberOfProtocols;
    pwsaQuerySet->lpafpProtocols = lpAfpProtocols;

    err = WSALookupServiceBegin( pwsaQuerySet,
                                 LUP_RETURN_NAME |
                                 LUP_RETURN_ADDR,
                                 &hRnR );

    if ( err != NO_ERROR )
    {
        err = WSAGetLastError();

        //
        // Free memory before returning.
        //
        (void) LocalFree( (HLOCAL) pwsaQuerySet );

        //
        // Unsuccessful.
        //
        return (DWORD) err;
    }

    //
    // The query was accepted, so execute it via the Next call.
    //
    tempSize = dwLength;

    err = WSALookupServiceNext( hRnR,
                                0,
                                &tempSize,
                                pwsaQuerySet );

    if ( err != NO_ERROR )
    {
        err = WSAGetLastError();

        if ( err == WSA_E_NO_MORE )
        {
            err = 0;
        }

        if ( err == WSASERVICE_NOT_FOUND )
        {
            err = WSAHOST_NOT_FOUND;
        }

        (void) LocalFree( (HLOCAL) pwsaQuerySet );

        //
        // Unsuccessful.
        //
        return (DWORD) err;

    }

    dwNumberOfCsAddrs = pwsaQuerySet->dwNumberOfCsAddrs;

    if ( dwNumberOfCsAddrs > 0 )
    {
        //
        // Make a copy of the CSAddrInfos returned from WSALookupServiceNext()
        //
        DWORD dwCSAddrInfoLen = dwNumberOfCsAddrs * sizeof( CSADDR_INFO );

        if ( *lpdwBufferLength > dwCSAddrInfoLen )
        {
            RtlCopyMemory( lpCSAddrInfos,
                           pwsaQuerySet->lpcsaBuffer,
                           dwCSAddrInfoLen );
        }
        else
        {
            *lpdwBufferLength = dwCSAddrInfoLen;
            SetLastError( ERROR_NOT_ENOUGH_MEMORY );
            dwNumberOfCsAddrs = INVALID_SOCKET;
        }
    }

    //
    // Close lookup service handle.
    //
    (VOID) WSALookupServiceEnd( hRnR );

    //
    // Free memory used for query set info.
    //
    (void) LocalFree( (HLOCAL) pwsaQuerySet );

    return dwNumberOfCsAddrs;

} // RnrGetHostFromName


RNRSETUP.C

/*++

Copyright (c) 1992 Microsoft Corporation

Module Name:

    RnrClnt.c

Abstract:

    Setup program for installing/removing the "EchoExample" service.

--*/

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <winsock2.h>
#include <nspapi.h>

WSADATA WsaData;

#ifndef NS_NTDS
#define NS_NTDS 9999
#endif
//
// GUID for Echo-Example created with uuidgen:
//     "47da8500-96a1-11cd-901d-204c4f4f5020"
//

GUID ServiceGuid = { 0x47da8500, 0x96a1, 0x11cd, 0x90, 0x1d,
                     0x20, 0x4c, 0x4f, 0x4f, 0x50, 0x20 };

#define ECHO_SERVICE_TYPE_NAME "EchoExample"
#define ECHO_SERVICE_SAPID     999
#define ECHO_SERVICE_TCPPORT   999
#define RNR_SERVICE_NAME       "RnrSvc"
#define RNR_DISPLAY_NAME       "RnrSampleService"

void
DoServiceSetup(
    char * Path
    )
{
    SC_HANDLE ServiceManagerHandle;
    SC_HANDLE ServiceHandle;
    LPSTR KeyName = "System\\CurrentControlSet\\Services\\EventLog\\System\\RnrSvc";
    HKEY RnrKey;
    LONG err;
    DWORD Disposition;

    //
    //  Create the service.
    //

    ServiceManagerHandle = OpenSCManager( NULL,
                                          NULL,
                                          STANDARD_RIGHTS_REQUIRED
                                          | SC_MANAGER_CREATE_SERVICE );

    if( ServiceManagerHandle == NULL ) {
        printf( "OpenSCManager failed: %ld\n", GetLastError() );
        exit(1);
    }

    ServiceHandle = CreateService( ServiceManagerHandle,
                                   RNR_SERVICE_NAME,
                                   RNR_DISPLAY_NAME,
                                   GENERIC_READ | GENERIC_WRITE,
                                   SERVICE_WIN32_OWN_PROCESS,
                                   SERVICE_DEMAND_START,
                                   SERVICE_ERROR_NORMAL,
                                   Path,
                                   NULL,
                                   NULL,
                                   NULL,
                                   NULL,
                                   NULL );

    if( ServiceHandle == NULL ) {
        printf( "CreateService failed: %ld\n", GetLastError() );
        CloseServiceHandle( ServiceManagerHandle );
        exit(1);
    }

    CloseServiceHandle( ServiceHandle );
    CloseServiceHandle( ServiceManagerHandle );

    printf( "%s created with path %s\n",
            RNR_SERVICE_NAME,
            Path );

    //
    //  Add the data to the EventLog's registry key so that the
    //  log insertion strings may be found by the Event Viewer.
    //

    err = RegCreateKeyEx( HKEY_LOCAL_MACHINE,
                          KeyName,
                          0,
                          NULL,
                          REG_OPTION_NON_VOLATILE,
                          KEY_WRITE,
                          NULL,
                          &RnrKey,
                          &Disposition );

    if( err != 0 ) {
        printf( "RegCreateKeyEx failed: %ld\n", err );
        exit(1);
    }

    err = RegSetValueEx( RnrKey,
                         "EventMessageFile",
                         0,
                         REG_EXPAND_SZ,
                         Path,
                         strlen( Path ) + 1 );

    if( err == 0 ) {
        DWORD Value;

        Value = EVENTLOG_ERROR_TYPE
                | EVENTLOG_WARNING_TYPE
                | EVENTLOG_INFORMATION_TYPE;

        err = RegSetValueEx( RnrKey,
                             "TypesSupported",
                             0,
                             REG_DWORD,
                             (CONST BYTE *)&Value,
                             sizeof(Value) );
    }

    RegCloseKey( RnrKey );

    if( err != 0 ) {
        printf( "RegSetValueEx failed: %ld\n", err );
        exit(1);
    }

    exit(0);
}

void _CRTAPI1
main (
    int argc,
    char *argv[]
    )
{
    INT err;

    WSASERVICECLASSINFO ServiceClassInfo;
    WSANSCLASSINFO      lpNSClassInfo[6];

    DWORD Value1 = 1 ;
    DWORD SapValue = ECHO_SERVICE_SAPID ;
    DWORD TcpPortValue = ECHO_SERVICE_TCPPORT ;
    DWORD operation = SERVICE_ADD_TYPE;

    //
    // Initilize the Windows Sockets DLL.
    //

    err = WSAStartup( 0x0202, &WsaData );
    if ( err == SOCKET_ERROR ) {
        printf( "WSAStartup() failed: %ld\n", GetLastError( ) );
        exit(1);
    }

    //
    // Parse command-line arguments.
    //

    if (argc > 2) {
        printf( "usage: rnrsetup [/ADD | /DEL | /SVC:path]\n") ;
        exit(1);
    }

    if (argc == 2)
    {
        if ( _strnicmp( argv[1], "/add", 4 ) == 0 )
        {
            printf( "\nAdding service types to Rnr name spaces.\n" );
        }
        else if ( _strnicmp( argv[1], "/delete", 4 ) == 0 )
        {
            err = WSARemoveServiceClass( &ServiceGuid );

            WSACleanup();

            if ( err != NO_ERROR )
            {
                printf( "\nWSARemoveServiceClass failed: %ld\n",
                        GetLastError( ) );
                exit(1);
            }

            printf( "\nWSARemoveServiceClass succeeded\n" );

            exit(0);
        }
        else if ( _strnicmp( argv[1], "/svc:", 5 ) == 0 )
        {
            printf( "\nAdding service entry to service control manager.\n" );
            DoServiceSetup( strchr( argv[1], ':' ) + 1 );
            printf( "Adding service types to Rnr name spaces.\n" );
        }
        else
        {
            printf( "usage: rnrsetup [/ADD | /DEL | /SVC:path]\n") ;
            exit(1);
        }
    }

    //
    // Set up information to pass to NSPInstallServiceClass() or
    // NSPRemoveServiceClass() to add or delete this
    // service type.
    //

    ServiceClassInfo.lpServiceClassId = &ServiceGuid;
    ServiceClassInfo.lpszServiceClassName = ECHO_SERVICE_TYPE_NAME;
    ServiceClassInfo.dwCount = 6;
    ServiceClassInfo.lpClassInfos = lpNSClassInfo;

    //
    // - - - SAP provider setup - - -
    //
    // The first value tells SAP that this is a connection-oriented
    // service.
    //
    lpNSClassInfo[0].lpszName = SERVICE_TYPE_VALUE_CONN ;
    lpNSClassInfo[0].dwNameSpace = NS_SAP ;
    lpNSClassInfo[0].dwValueType = REG_DWORD ;
    lpNSClassInfo[0].dwValueSize = 4 ;
    lpNSClassInfo[0].lpValue     = &Value1 ;

    //
    // Next, give SAP the object type to use when broadcasting the
    // service name.
    //
    lpNSClassInfo[1].lpszName = SERVICE_TYPE_VALUE_SAPID ;
    lpNSClassInfo[1].dwNameSpace = NS_SAP ;
    lpNSClassInfo[1].dwValueType = REG_DWORD ;
    lpNSClassInfo[1].dwValueSize = sizeof(DWORD) ;
    lpNSClassInfo[1].lpValue     = &SapValue ;

    //
    // - - - TCPIP provider setup - - -
    //
    // Tell the TCPIP name space provider that we will be using TCP
    // port 0x999.
    //
    lpNSClassInfo[2].lpszName = SERVICE_TYPE_VALUE_TCPPORT ;
    lpNSClassInfo[2].dwNameSpace = NS_DNS ;
    lpNSClassInfo[2].dwValueType = REG_DWORD ;
    lpNSClassInfo[2].dwValueSize = sizeof(DWORD) ;
    lpNSClassInfo[2].lpValue     = &TcpPortValue ;

    //
    // - - - NTDS provider setup - - -
    //
    // The first value tells SAP that this is a connection-oriented
    // service.
    //
    lpNSClassInfo[3].lpszName = SERVICE_TYPE_VALUE_CONN ;
    lpNSClassInfo[3].dwNameSpace = NS_NTDS ;
    lpNSClassInfo[3].dwValueType = REG_DWORD ;
    lpNSClassInfo[3].dwValueSize = 4 ;
    lpNSClassInfo[3].lpValue     = &Value1 ;

    //
    // Next, give SAP the object type to use when broadcasting the
    // service name.
    //
    lpNSClassInfo[4].lpszName = SERVICE_TYPE_VALUE_SAPID ;
    lpNSClassInfo[4].dwNameSpace = NS_NTDS ;
    lpNSClassInfo[4].dwValueType = REG_DWORD ;
    lpNSClassInfo[4].dwValueSize = sizeof(DWORD) ;
    lpNSClassInfo[4].lpValue     = &SapValue ;

    //
    // Tell the NTDS name space provider that we will be using TCP
    // port 0x999.
    //
    lpNSClassInfo[5].lpszName = SERVICE_TYPE_VALUE_TCPPORT ;
    lpNSClassInfo[5].dwNameSpace = NS_NTDS ;
    lpNSClassInfo[5].dwValueType = REG_DWORD ;
    lpNSClassInfo[5].dwValueSize = sizeof(DWORD) ;
    lpNSClassInfo[5].lpValue     = &TcpPortValue ;

    //
    // Finally, call WSAInstallServiceClass to actually perform the operation.
    //

    err = WSAInstallServiceClass( &ServiceClassInfo );

    WSACleanup();

    if ( err != NO_ERROR )
    {
        printf( "WSAInstallServiceClass failed: %ld\n", GetLastError( ) );
        exit(1);
    }

    printf( "WSAInstallServiceClass succeeded\n" );

    exit(0);

} // main


RNRSRV.C

/*++

Copyright (c) 1994 Microsoft Corporation

Module Name:

    RnrSrv.c

Abstract:

    Test and demonstration service for the RNR (service Registration and
    Name Resolution) APIs.  This is a simple service designed to show
    the basic principles involved in using the RNR APIs to _write a
    protocol-independent Windows Sockets service.

    This service opens a number of listening sockets, waits for an
    incoming connection from a client, accepts the connection, then
    echos data back to the client until the client terminates the
    virtual circuit.  This service is single-threaded and can handle
    only a single client at a time.

    The OpenListeners() routine implemented herein is intended to be a
    demonstration of RNR functionality commonly used in
    protocol-independent services.  Service writers are encouraged to
    leverage this code to assist them in writing protocol-independent
    services on top of the Windows Sockets API.

--*/

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <winsock2.h>
#include <nspapi.h>

WSADATA WsaData;

PSTR ServiceTypeName = "EchoExample";
PSTR ServiceName = "EchoServer";

#define MAX_SOCKETS    20

INT
OpenListeners (
    IN PTSTR ServiceName,
    IN LPGUID ServiceType,
    IN BOOL Reliable,
    IN BOOL MessageOriented,
    IN BOOL StreamOriented,
    IN BOOL Connectionless,
    OUT SOCKET SocketHandles[],
    OUT INT ProtocolsUsed[]
    );

INT
AdvertiseService(
    IN PTSTR ServiceName,
    IN LPGUID ServiceType,
    IN SOCKET SocketHandles[],
    IN INT SocketCount
    );

void _CRTAPI1
main (
    int argc,
    char *argv[]
    )
{
    INT count, err, i ;
    DWORD tmpProtocol[2];
    BYTE buffer[1024];
    DWORD bytesRequired;
    PPROTOCOL_INFO protocolInfo;
    GUID serviceType;
    FD_SET readfds;
    SOCKET listenSockets[MAX_SOCKETS+1];
    INT protocols[MAX_SOCKETS+1];
    SOCKET s;

    //
    // Initialize the Windows Sockets DLL.
    //

    err = WSAStartup( 0x0202, &WsaData );

    if ( err == SOCKET_ERROR )
    {
        printf( "WSAStartup() failed: %ld\n", GetLastError( ) );
        return;
    }

    //
    // Determine the value of our GUID.  The GUID uniquely identifies
    // the type of service we provide.
    //

    err = GetTypeByName( ServiceTypeName, &serviceType );

    if ( err == SOCKET_ERROR )
    {
        printf( "GetTypeByName for \"%s\" failed: %ld\n",
                    ServiceTypeName, GetLastError( ) );
        exit( 1 );
    }

    //
    // Open listening sockets for this service.
    //

    count = OpenListeners(
                ServiceName,
                &serviceType,
                TRUE,
                FALSE,
                FALSE,
                FALSE,
                listenSockets,
                protocols
                );

    if ( count <= 0 )
    {
        printf( "failed to open listenSockets for name \"%s\" type \"%s\"\n",
                    ServiceName, ServiceTypeName );
        exit( 1 );
    }

    //
    // We successfully opened some listening sockets.  Display some
    // information on each protocol in use.
    //

    tmpProtocol[1] = 0;

    for ( i = 0; i < count; i++ )
    {
        tmpProtocol[0] = protocols[i];

        bytesRequired = sizeof(buffer);
        err = EnumProtocols( tmpProtocol, buffer, &bytesRequired );

        if ( err < 1 )
        {
            printf( "EnumProtocols failed for protocol %ld: %ld\n",
                        tmpProtocol[0], GetLastError( ) );
            exit( 1 );
        }

        protocolInfo = (PPROTOCOL_INFO)buffer;
        printf( "Socket %lx listening on protocol \"%s\" (%ld)\n",
                    listenSockets[i],
                    protocolInfo->lpProtocol,
                    protocolInfo->iProtocol );

    }

    //
    // Advertise the service so thet it can be found.
    //
    printf( "Going to advertise the service.\n" ) ;

    err = AdvertiseService(
                ServiceName,
                &serviceType,
                listenSockets,
                count) ;

    if (err == SOCKET_ERROR)
    {
        printf( "Failed to advertise the service. Error %d\n", GetLastError()) ;
        exit( 1 ) ;
    }

    printf( "Successfully advertised the service.\n" ) ;

    //
    // Loop accepting connections and servicing them.
    //

    FD_ZERO( &readfds );

    while ( TRUE )
    {
        //
        // Add the listening sockets to the FD_SET we'll pass to select.
        //

        for ( i = 0; i < count; i++ )
        {
            FD_SET( listenSockets[i], &readfds );
        }

        //
        // Wait for one of the listenSockets to receive an incoming connection.
        //

        err = select( count, &readfds, NULL, NULL, NULL );

        if ( err < 1 )
        {
            printf( "select() returned %ld, error %ld\n", err, GetLastError( ) );
            exit( 1 );
        }

        //
        // Find the socket that received an incoming connection and accept
        // the connection.
        //

        for ( i = 0; i < count; i++ )
        {
            if ( FD_ISSET( listenSockets[i], &readfds ) )
                break;
        }

        //
        // Accept the connection from the client.  We ignore the client's
        // address here.
        //

        s = accept( listenSockets[i], NULL, NULL );

        if ( s == INVALID_SOCKET )
        {
            printf( "accept() failed, error %ld\n", GetLastError( ) );
            exit( 1 );
        }

        printf( "Accepted incoming connection on socket %lx\n",
                listenSockets[i] );

        //
        // Loop echoing data back to the client.  Note that this
        // single-threaded service can handle only a single client at a
        // time.  A more sophisticated service would service multiple
        // clients simultaneously by using multiple threads or
        // asynchronous I/O.
        //

        while ( TRUE )
        {
            err = recv( s, buffer, sizeof(buffer), 0 );
            if ( err == 0 )
            {
                printf( "Connection terminated gracefully.\n" );
                break;
            }
            else if ( err < 0 )
            {
                err = GetLastError();

                if ( err == WSAEDISCON )
                {
                    printf( "Connection disconnected.\n" );
                }
                else
                {
                    printf( "recv() failed, error %ld.\n", err );
                }

                break;
            }

            err = send( s, buffer, err, 0 );

            if ( err < 0 )
            {
                printf( "send() failed, error %ld\n", GetLastError( ) );
                break;
            }
        }

        //
        // Close the connected socket and continue accepting connections.
        //

        closesocket( s );
    }

} // main

INT
OpenListeners (
    IN PTSTR ServiceName,
    IN LPGUID ServiceType,
    IN BOOL Reliable,
    IN BOOL MessageOriented,
    IN BOOL StreamOriented,
    IN BOOL Connectionless,
    OUT SOCKET SocketHandles[],
    OUT INT ProtocolsUsed[]
    )

/*++

Routine Description:

    Examines the Windows Sockets transport protocols loaded on a machine
    and opens listening sockets on all the protocols which support the
    characteristics requested by the caller.

Arguments:

    ServiceName - a friendly name which identifies this service.  On
        name spaces which support name resolution at the service level
        (e.g.  SAP) this is the name clients will use to connect to this
        service.  On name spaces which support name resolution at the
        host level (e.g.  DNS) this name is ignored and applications
        must use the host name to establish communication with this
        service.

    ServiceType - the GUID value which uniquely identifies the type of
        service we provide.  A GUID is created with the UUIDGEN program.

    Reliable - if TRUE, the caller requests that only transport protocols
        which support reliable data delivery be used.  If FALSE, both
        reliable and unreliable protocols may be used.

    MessageOriented - if TRUE, only message-oriented transport protocols
        should be used.  If FALSE, the caller either does not care
        whether the protocols used are message oriented or desires only
        stream-oriented protocols.

    StreamOriented - if TRUE, only stream-oriented transport protocols
        should be used.  If FALSE, the caller either does not care
        whether the protocols used are stream oriented or desires only
        message-oriented protocols.

    Connectionless - if TRUE, only connectionless protocols should be
        used.  If FALSE, both connection-oriented and connectionless
        protocols may be used.

    SocketHandles - an array of size MAX_SOCKETS which receives listening
        socket handles.

    ProtocolsUsed - an array of size MAX_SOCKETS which receives the
        protocol values for each of the socket handles in the
        SocketHandles array.

Return Value:

    The count of listening sockets successfully opened, or -1 if no
    sockets could be successfully opened that met the desired
    characteristics.

--*/

{
    INT            protocols[MAX_SOCKETS+1];
    BYTE           buffer[2048];
    DWORD          bytesRequired;
    INT            err;
    PPROTOCOL_INFO protocolInfo;
    PCSADDR_INFO   csaddrInfo;
    INT            protocolCount;
    INT            addressCount;
    INT            i;
    DWORD          protocolIndex;
    SOCKET         s;
    DWORD          index = 0;

    //
    // First look up the protocols installed on this machine.  The
    // EnumProtocols() API returns about all the Windows Sockets
    // protocols loaded on this machine, and we'll use this information
    // to identify the protocols which provide the necessary semantics.
    //

    bytesRequired = sizeof(buffer);

    err = EnumProtocols( NULL, buffer, &bytesRequired );

    if ( err <= 0 )
    {
        return 0;
    }

    //
    // Walk through the available protocols and pick out the ones which
    // support the desired characteristics.
    //

    protocolCount = err;
    protocolInfo = (PPROTOCOL_INFO)buffer;

    for ( i = 0, protocolIndex = 0;
          i < protocolCount && protocolIndex < MAX_SOCKETS;
          i++, protocolInfo++ )
    {
        //
        // If "reliable" support is requested, then check if supported
        // by this protocol.  Reliable support means that the protocol
        // guarantees delivery of data in the order in which it is sent.
        // Note that we assume here that if the caller requested reliable
        // service then they do not want a connectionless protocol.
        //

        if ( Reliable )
        {
            //
            // Check to see if the protocol is reliable.  It must
            // guarantee both delivery of all data and the order in
            // which the data arrives.  Also, it must not be a
            // connectionless protocol.
            //

            if ( (protocolInfo->dwServiceFlags &
                      XP_GUARANTEED_DELIVERY) == 0 ||
                 (protocolInfo->dwServiceFlags &
                      XP_GUARANTEED_ORDER) == 0 )
            {
                continue;
            }

            if ( (protocolInfo->dwServiceFlags & XP_CONNECTIONLESS) != 0 )
            {
                continue;
            }

            //
            // Check to see that the protocol matches the stream/message
            // characteristics requested.  A stream oriented protocol
            // either has the XP_MESSAGE_ORIENTED bit turned off, or
            // else supports "pseudo stream" capability.  Pseudo stream
            // means that although the underlying protocol is message
            // oriented, the application may open a socket of type
            // SOCK_STREAM and the protocol will hide message boundaries
            // from the application.
            //

            if ( StreamOriented &&
                 (protocolInfo->dwServiceFlags & XP_MESSAGE_ORIENTED) != 0 &&
                 (protocolInfo->dwServiceFlags & XP_PSEUDO_STREAM) == 0 )
            {
                continue;
            }

            if ( MessageOriented &&
                 (protocolInfo->dwServiceFlags & XP_MESSAGE_ORIENTED) == 0 )
            {
                continue;
            }

        }
        else if ( Connectionless )
        {
            //
            // Make sure that this is a connectionless protocol.  In a
            // connectionless protocol, data is sent as discrete
            // datagrams with no connection establishment required.
            // Connectionless protocols typically have no reliability
            // guarantees.
            //

            if ( (protocolInfo->dwServiceFlags & XP_CONNECTIONLESS) != 0 )
            {
                continue;
            }
        }

        //
        // This protocol fits all the criteria.  Add it to the list of
        // protocols in which we're interested.
        //

        protocols[protocolIndex++] = protocolInfo->iProtocol;
    }

    //
    // Make sure that we found at least one acceptable protocol.  If
    // there no protocols on this machine which meet the caller's
    // requirements then fail here.
    //

    if ( protocolIndex == 0 )
    {
        return 0;
    }

    protocols[protocolIndex] = 0;

    //
    // Now attempt to find the socket addresses to which we need to
    // bind.  Note that we restrict the scope of the search to those
    // protocols of interest by passing the protocol array we generated
    // above to GetAddressByName().  This forces GetAddressByName() to
    // return socket addresses for only the protocols we specify,
    // ignoring possible addresses for protocols we cannot support
    // because of the caller's constraints.
    //

    bytesRequired = sizeof(buffer);

    err = GetAddressByName(
               NS_DEFAULT,
               ServiceType,
               ServiceName,
               protocols,
               RES_SERVICE | RES_FIND_MULTIPLE,
               NULL,                     // lpServiceAsyncInfo
               buffer,
               &bytesRequired,
               NULL,                     // lpAliasBuffer
               NULL                      // lpdwAliasBufferLength
               );

    if ( err <= 0 )
    {
        return 0;
    }

    //
    // For each address, open a socket and attempt to listen. Note
    // that if anything fails for a particular protocol we just skip on
    // to the next protocol. As long as we can successfully listen on
    // one protocol we are satisfied here.
    //

    addressCount = err;
    csaddrInfo = (PCSADDR_INFO)buffer;

    for ( i = 0; i < addressCount; i++, csaddrInfo++ )
    {
        //
        // Open the socket. Note that we manually specify stream type
        // if so requested in case the protocol is natively a message
        // protocol but supports stream semantics.
        //

        s = socket( csaddrInfo->LocalAddr.lpSockaddr->sa_family,
                    StreamOriented ? SOCK_STREAM : csaddrInfo->iSocketType,
                    csaddrInfo->iProtocol );

        if ( s == INVALID_SOCKET )
        {
            continue;
        }

        //
        // Bind the socket to the local address specified.
        //

        err = bind( s, csaddrInfo->LocalAddr.lpSockaddr,
                    csaddrInfo->LocalAddr.iSockaddrLength );

        if ( err != NO_ERROR )
        {
            closesocket( s );
            continue;
        }

        //
        // Start listening for incoming sockets on the socket if this is
        // not a datagram socket.  If this is a datagram socket, then
        // the listen() API doesn't make sense; doing a bind() is
        // sufficient to listen for incoming datagrams on a
        // connectionless protocol.
        //

        if ( csaddrInfo->iSocketType != SOCK_DGRAM )
        {
            err = listen( s, 5 );

            if ( err != NO_ERROR )
            {
                closesocket( s );
                continue;
            }
        }

        //
        // The socket was successfully opened and we're listening on it.
        // Remember the protocol used and the socket handle and continue
        // listening on other protocols.
        //

        ProtocolsUsed[index] = csaddrInfo->iProtocol;
        SocketHandles[index] = s;

        index++;
        if ( index == MAX_SOCKETS )
        {
            return index;
        }
    }

    (void) LocalFree( (HLOCAL) csaddrInfo );

    //
    // Return the count of sockets that we're sucecssfully listening on.
    //

    return index;

} // OpenListeners

INT
AdvertiseService(
    IN PTSTR ServiceName,
    IN LPGUID ServiceType,
    IN SOCKET SocketHandles[],
    IN INT SocketCount
    )
/*++

Routine Description:

    Advertises this service on all the default name spaces.

Arguments:

    ServiceName - the name of the service.

    ServiceType - the GUID value which uniquely the service.

    SocketHandles - array of sockets that we have opened. For each socket,
        we do a getsockname() to discover the actual local address.

    SocketCount - number of sockets in SockHandles[]

Return Value:

    0 if success. SOCK_ERROR otherwise.

--*/

{

    WSAVERSION          Version;
    WSAQUERYSET         QuerySet;
    LPCSADDR_INFO       lpCSAddrInfo;
    PSOCKADDR           sockAddr ;
    BYTE *              addressBuffer;
    DWORD               addressBufferSize ;
    DWORD               successCount = 0 ;
    INT                 i, err ;

    //
    // Allocate some memory for the CSADDR_INFO structures.
    //

    lpCSAddrInfo = (LPCSADDR_INFO) malloc( sizeof(CSADDR_INFO) * SocketCount );

    if (!lpCSAddrInfo)
    {
        SetLastError(ERROR_NOT_ENOUGH_MEMORY) ;
        return SOCKET_ERROR ;
    }

    //
    // Allocate some memory for the SOCKADDR addresses returned
    // by getsockname().
    //

    addressBufferSize = SocketCount * sizeof(SOCKADDR);
    addressBuffer = malloc( addressBufferSize ) ;

    if (!addressBuffer)
    {
        free(lpCSAddrInfo) ;
        SetLastError(ERROR_NOT_ENOUGH_MEMORY) ;
        return SOCKET_ERROR ;
    }

    RtlZeroMemory( &QuerySet, sizeof( WSAQUERYSET ) );

    //
    // For each socket, get its local association.
    //

    sockAddr = (PSOCKADDR) addressBuffer ;

    for (i = 0; i < SocketCount; i++)
    {
        int size = (int) addressBufferSize ;

        //
        // Call getsockname() to get the local association for the socket.
        //

        err = getsockname(
                  SocketHandles[i],
                  sockAddr,
                  &size) ;

        if (err == SOCKET_ERROR)
        {
            continue ;
        }

        //
        // Now setup the Addressing information for this socket.
        // Only the dwAddressType, dwAddressLength and lpAddress
        // is of any interest in this example.
        //

        lpCSAddrInfo[i].LocalAddr.iSockaddrLength = size;
        lpCSAddrInfo[i].LocalAddr.lpSockaddr = sockAddr;
        lpCSAddrInfo[i].RemoteAddr.iSockaddrLength = size;
        lpCSAddrInfo[i].RemoteAddr.lpSockaddr = sockAddr;
        lpCSAddrInfo[i].iSocketType = SOCK_RDM; // Reliable
        lpCSAddrInfo[i].iProtocol = sockAddr->sa_family;

        //
        // Advance pointer and adjust buffer size. Assumes that
        // the structures are aligned.
        //

        addressBufferSize -= size ;
        sockAddr = (PSOCKADDR) ((BYTE*)sockAddr + size)  ;

        successCount++ ;
    }

    //
    // If we got at least one address, go ahead and advertise it.
    //

    if (successCount)
    {
        QuerySet.dwSize = sizeof( WSAQUERYSET );
        QuerySet.lpServiceClassId = ServiceType;
        QuerySet.lpszServiceInstanceName = ServiceName;
        QuerySet.lpszComment = "D/C/M's Example Echo Service";
        QuerySet.lpVersion = &Version;
        QuerySet.lpVersion->dwVersion = 1;
        QuerySet.lpVersion->ecHow = COMP_NOTLESS;
        QuerySet.dwNameSpace = NS_ALL;
        QuerySet.dwNumberOfCsAddrs = successCount;
        QuerySet.lpcsaBuffer = lpCSAddrInfo;

        err = WSASetService( &QuerySet,
                             RNRSERVICE_REGISTER,
                             SERVICE_MULTIPLE );

        if ( err )
            err = SOCKET_ERROR;
    }
    else
        err = SOCKET_ERROR ;

    free (addressBuffer) ;

    return (err) ;
}


README.TXT

Simple IP samples.
-----------------

simples.c:
----------

This is a very simple-minded TCP/UDP server. It listens on a specified port
for client connections. When a client connects, the server receives data,
echoes it back to the client and closes the connection.

Usage:
simples -p <protocol> -e <endpoint> -i <interface>

Where,
 Protocol is one of TCP or UDP,

 Endpoint is the port number to listen on,

 Interface is the IP address to bind to (for multihomed machines this can be
   specified. Machines with just one network interface will not need this
   parameter, typically).

Note:
----

There are differences in the way TCP and UDP "servers" can be written. For
TCP, the paradigm of bind(), listen() and accept() is widely implemented.
For UDP , however, there are two things to consider:

    1. listen() or accept() do not work on a UDP socket. There are APIs
that are oriented towards connection establishment, and are not applicable
to datagram protocols. To implement a UDP server, a process only needs to
do recvfrom() on the socket that is bound to a well-known port. Clients
will send datagrams to this port, and the server can process these.

2. Since there is no connection esablished, the server must treat each
datagram separately.

simplec.c
---------

A simple TCP/UPD client application. It connects to a specified IP address and
port and sends a small message. It can send only one message, or loop for a
specified number of iterations, sending data to the server and receiving a
response.

Usage:
simplec -p <protocol> -n <server> -e <endpoint> -l <iterations>

Where,
 Protocol is TCP or UDP.
 Server is the IP address/ name of the server
 Endpoint is the port number the server is listening on
 iterations specifies how many messages to send.

 '-l' without any arguments will cause the client to send & receive  messages
 until interrupted by Ctrl-C.
 This option *will not* work against simples.exe as the TCP server. This is
 because simples.exe closes the socket after one transaction. You will need
 something like the overlap sample which keeps the socket open indefinitely.

Note:
----
As explained for simples.c, there is no concept of a connection in UDP
communications. However, we can use connect() on a UDP socket. This
establishes the remote (IPaddr, port) to used when sending a datagram.
Thus, we can use send() instead of sendto() on this socket.

This makes the code exactly the same for UDP and TCP sockets. However, it
must be realized that this is still connectionless datagram traffic for
UDP sockets, and must be treated as such.

ioctl.c
-------

This is a TCP-only server that shows how to use select() in a Win32 console
application. The server creates a socket to listen() on, and makes it
non-blocking with ioctlsocket(), and then calls select().
When a client connects to this server, the server multiplexes between the new
connection and the original listening socket, again using select().
As soon as a new connection is established, the server breaks the connection
with the first client.

Usage: (same as simples)

This sample illustrates the point that select() is not very useful in Win32
applications, since it is difficult to keep track of every open socket and
test it for readiness when select() returns.

Applications should use WSAAsyncSelect for GUI-based event
selection, or Overlapped I/O and native Win32 wait functions to achieve the
same effect as select.


IOCTL.C

/******************************************************************************\
* ioctl.c - TCP server
*
*       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.
\******************************************************************************/

#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define DEFAULT_PORT 5001

int ReadAndEcho(SOCKET , char *,int ) ;
int WriteMessage(SOCKET , char *,int ) ;

void Usage(char *progname) {
fprintf(stderr,"Usage\n%s -e [endpoint] -i [interface]\n",
progname);
fprintf(stderr,"Where:\n");
fprintf(stderr,"\tendpoint is the port to listen on\n");
fprintf(stderr,"\tinterface is the ipaddr (in dotted decimal notation)");
fprintf(stderr," to bind to\n");
fprintf(stderr,"Defaults are 5001 and INADDR_ANY\n");
WSACleanup();
exit(1);
}
int main(int argc, char **argv) {

char Buffer[128];
char *interface= NULL;
unsigned short port=DEFAULT_PORT;
int fromlen;
int i, ioctl_opt =1;
struct sockaddr_in local, from;
WSADATA wsaData;
SOCKET listen_socket, msgsock;
fd_set readfds, writefds, exceptfds;

/* Parse arguments */
if (argc >1) {
for(i=1;i <argc;i++) {
if ( (argv[i][0] == '-') || (argv[i][0] == '/') ) {
switch(tolower(argv[i][1])) {
case 'i':
interface = argv[++i];
break;
case 'e':
port = atoi(argv[++i]);
break;
default:
Usage(argv[0]);
break;
}
}
else
Usage(argv[0]);
}
}

if (WSAStartup(0x202,&wsaData) == SOCKET_ERROR) {
fprintf(stderr,"WSAStartup failed with error %d\n",WSAGetLastError());
WSACleanup();
return -1;
}

if (port == 0){
Usage(argv[0]);
}

//
// The fd sets should be zeroed out before using them to prevent errors.
FD_ZERO(&readfds);
FD_ZERO(&writefds);
FD_ZERO(&exceptfds);
memset(Buffer,0,sizeof(Buffer));

local.sin_family = AF_INET;

//
// bind to specific interface if desired.

local.sin_addr.s_addr = (!interface)?INADDR_ANY:inet_addr(interface);

/*
 * Port MUST be in Network Byte Order
 */
local.sin_port = htons(port);

listen_socket = socket(AF_INET, SOCK_STREAM,0); // TCP socket
if (listen_socket == INVALID_SOCKET){
fprintf(stderr,"socket() failed with error %d\n",WSAGetLastError());
WSACleanup();
return -1;
}
//
// bind() associates a local address and port combination with the
// socket just created.

if (bind(listen_socket,(struct sockaddr*)&local,sizeof(local) )
== SOCKET_ERROR) {
fprintf(stderr,"bind() failed with error %d\n",WSAGetLastError());
WSACleanup();
return -1;
}

//
// start listening on the socket for incoming connections
//
if (listen(listen_socket,5) == SOCKET_ERROR) {
fprintf(stderr,"listen() failed with error %d\n",WSAGetLastError());
WSACleanup();
return -1;
}
printf("%s: Listening on port %d\n",argv[0],port);

//
// Set the socket to non-blocking mode.
//
if (ioctlsocket(listen_socket,FIONBIO,&ioctl_opt) == SOCKET_ERROR) {
fprintf(stderr,"ioctlsocket failed %d\n",WSAGetLastError());
WSACleanup();
return -1;
}
//
// The structure of the loop below is very simple. We only accept one
// connection at a time. As soon as another client connects, we
// disconnect the first one, and start talking to the new client.
// All this server does is to echo the data received on the socket
// back to the client.
//
// This is not a very realistic server, but it does serve to show that
// select() does not scale very well on win32. If we were dealing
// with more than one client, we would have to have a list of sockets
// that are in each fdset to be able to check them when select()
// returns.
//
while(1) {

//
// A socket in the listen() state becomes ready to read when a
// client connects to it. An accept() will complete without
// blocking.
// Since select sets the sockets that are ready to be read from or
// written to, we have to include listen_socket in the fdset each time
// through the loop.
//

FD_SET(listen_socket,&readfds);

i = select(0,&readfds,&writefds,&exceptfds,NULL);
if (i == SOCKET_ERROR) {
fprintf(stderr,"select failed %d\n",WSAGetLastError());
}
if (i==0){
fprintf(stderr,"Select returned no fds ready\n");
}

if (FD_ISSET(listen_socket, &readfds)){
//
// close the previous client socket.
// We must also clear it from the fdset to prevent select()
// from failing.
//
closesocket(msgsock);
FD_CLR(msgsock,&readfds);
FD_CLR(msgsock,&writefds);
fromlen = sizeof(from);
msgsock= accept(listen_socket,(struct sockaddr*)&from,&fromlen);
if (msgsock == INVALID_SOCKET) {
fprintf(stderr,"accept failed %d\n",WSAGetLastError());
WSACleanup();
return -1;
}
FD_SET(msgsock,&writefds);
FD_SET(msgsock,&readfds);
continue;
}
if (FD_ISSET(msgsock,&readfds) ) {
//
// socket is ready to read, i.e., there is data on the socket.
//
if (ReadAndEcho(msgsock,Buffer,sizeof(Buffer))<0) {
fprintf(stderr,"terminating connection\n");
FD_CLR(msgsock,&readfds);
FD_CLR(msgsock,&writefds);
closesocket(msgsock);
continue;
}
}
if (FD_ISSET(msgsock,&writefds) ){
if (WriteMessage(msgsock,Buffer,sizeof(Buffer)) <=0) {
fprintf(stderr,"terminating connection\n");
FD_CLR(msgsock,&readfds);
FD_CLR(msgsock,&writefds);
closesocket(msgsock);
continue;
}
}
FD_SET(msgsock,&writefds);
FD_SET(msgsock,&readfds);
}
}
int ReadAndEcho(SOCKET insock, char *Buffer,int size) {
int rc;

rc = recv(insock,Buffer,size,0);

if (rc == SOCKET_ERROR) {
fprintf(stderr,"recv() failed with error %d\n",WSAGetLastError());
return -1;
}
if (rc ==0) {
fprintf(stderr,"Connection closed by client\n");
return 0;
}
printf("Received [%s] from client\n",Buffer);
return rc;
}
int WriteMessage(SOCKET outsock, char *Buffer,int size) {
int rc;
int lasterr;

printf("Sending [%s] to client\n",Buffer);
rc = send(outsock,Buffer,size, 0);

if (rc == SOCKET_ERROR) {
  lasterr = WSAGetLastError();
  if (lasterr == WSAEWOULDBLOCK)
return 0;
  else {
fprintf(stderr,"send() failed with error %d\n",lasterr);
return -1;
  }
}
if (rc ==0) {
fprintf(stderr,"Connection closed by client\n");
}
return rc;
}


SIMPLEC.C

/******************************************************************************\
* simplec.c - Simple TCP/UDP client using Winsock 1.1
*
*       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.
\******************************************************************************/

#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define DEFAULT_PORT 5001
#define DEFAULT_PROTO SOCK_STREAM // TCP

void Usage(char *progname) {
fprintf(stderr,"Usage\n%s -p [protocol] -n [server] -e [endpoint] \
-l [iterations]\n",
progname);
fprintf(stderr,"Where:\n\tprotocol is one of TCP or UDP\n");
fprintf(stderr,"\tserver is the IP address or name of server\n");
fprintf(stderr,"\tendpoint is the port to listen on\n");
fprintf(stderr,"\titerations is the number of loops to execute\n");
fprintf(stderr,"\t(-l by itself makes client run in an infinite loop,");
fprintf(stderr," Hit Ctrl-C to terminate it)\n");
fprintf(stderr,"Defaults are TCP , localhost and 5001\n");
WSACleanup();
exit(1);
}
int main(int argc, char **argv) {

char Buffer[128];
char *server_name= "localhost";
unsigned short port = DEFAULT_PORT;
int retval, loopflag=0;
int i, loopcount,maxloop=-1;
unsigned int addr;
int socket_type = DEFAULT_PROTO;
struct sockaddr_in server;
struct hostent *hp;
WSADATA wsaData;
SOCKET  conn_socket;

if (argc >1) {
for(i=1;i <argc;i++) {
if ( (argv[i][0] == '-') || (argv[i][0] == '/') ) {
switch(tolower(argv[i][1])) {
case 'p':
if (!stricmp(argv[i+1], "TCP") )
socket_type = SOCK_STREAM;
else if (!stricmp(argv[i+1], "UDP") )
socket_type = SOCK_DGRAM;
else
Usage(argv[0]);
i++;
break;

case 'n':
server_name = argv[++i];
break;
case 'e':
port = atoi(argv[++i]);
break;
case 'l':
loopflag =1;
if (argv[i+1]) {
if (argv[i+1][0] != '-')
maxloop = atoi(argv[i+1]);
}
else
maxloop = -1;
i++;
break;
default:
Usage(argv[0]);
break;
}
}
else
Usage(argv[0]);
}
}

if (WSAStartup(0x202,&wsaData) == SOCKET_ERROR) {
fprintf(stderr,"WSAStartup failed with error %d\n",WSAGetLastError());
WSACleanup();
return -1;
}

if (port == 0){
Usage(argv[0]);
}

//
// Attempt to detect if we should call gethostbyname() or
// gethostbyaddr()

if (isalpha(server_name[0])) {   /* server address is a name */
hp = gethostbyname(server_name);
}
else  { /* Convert nnn.nnn address to a usable one */
addr = inet_addr(server_name);
hp = gethostbyaddr((char *)&addr,4,AF_INET);
}
if (hp == NULL ) {
fprintf(stderr,"Client: Cannot resolve address [%s]: Error %d\n",
server_name,WSAGetLastError());
WSACleanup();
exit(1);
}

//
// Copy the resolved information into the sockaddr_in structure
//
memset(&server,0,sizeof(server));
memcpy(&(server.sin_addr),hp->h_addr,hp->h_length);
server.sin_family = hp->h_addrtype;
server.sin_port = htons(port);

conn_socket = socket(AF_INET,socket_type,0); /* Open a socket */
if (conn_socket <0 ) {
fprintf(stderr,"Client: Error Opening socket: Error %d\n",
WSAGetLastError());
WSACleanup();
return -1;
}

//
// Notice that nothing in this code is specific to whether we
// are using UDP or TCP.
// We achieve this by using a simple trick.
//    When connect() is called on a datagram socket, it does not
//    actually establish the connection as a stream (TCP) socket
//    would. Instead, TCP/IP establishes the remote half of the
//    ( LocalIPAddress, LocalPort, RemoteIP, RemotePort) mapping.
//    This enables us to use send() and recv() on datagram sockets,
//    instead of recvfrom() and sendto()

printf("Client connecting to: %s\n",hp->h_name);
if (connect(conn_socket,(struct sockaddr*)&server,sizeof(server))
== SOCKET_ERROR) {
fprintf(stderr,"connect() failed: %d\n",WSAGetLastError());
WSACleanup();
return -1;
}

// cook up a string to send
//
loopcount =0;
while(1) {
wsprintf(Buffer,"This is a small test message [number %d]",loopcount++);
retval = send(conn_socket,Buffer,sizeof(Buffer),0);
if (retval == SOCKET_ERROR) {
fprintf(stderr,"send() failed: error %d\n",WSAGetLastError());
WSACleanup();
return -1;
}
printf("Sent Data [%s]\n",Buffer);
retval = recv(conn_socket,Buffer,sizeof (Buffer),0 );
if (retval == SOCKET_ERROR) {
fprintf(stderr,"recv() failed: error %d\n",WSAGetLastError());
closesocket(conn_socket);
WSACleanup();
return -1;
}
//
// We are not likely to see this with UDP, since there is no
// 'connection' established.
//
if (retval == 0) {
printf("Server closed connection\n");
closesocket(conn_socket);
WSACleanup();
return -1;
}
printf("Received %d bytes, data [%s] from server\n",retval,Buffer);
if (!loopflag){
printf("Terminating connection\n");
break;
}
else {
if ( (loopcount >= maxloop) && (maxloop >0) )
break;
}
}
closesocket(conn_socket);
WSACleanup();
}


SIMPLES.C

/******************************************************************************\
* simples.c - Simple TCP/UDP server using Winsock 1.1
*       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.
\******************************************************************************/

#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define DEFAULT_PORT 5001
#define DEFAULT_PROTO SOCK_STREAM // TCP

void Usage(char *progname) {
fprintf(stderr,"Usage\n%s -p [protocol] -e [endpoint] -i [interface]\n",
progname);
fprintf(stderr,"Where:\n\tprotocol is one of TCP or UDP\n");
fprintf(stderr,"\tendpoint is the port to listen on\n");
fprintf(stderr,"\tinterface is the ipaddr (in dotted decimal notation)");
fprintf(stderr," to bind to\n");
fprintf(stderr,"Defaults are TCP,5001 and INADDR_ANY\n");
WSACleanup();
exit(1);
}
int main(int argc, char **argv) {

char Buffer[128];
char *interface= NULL;
unsigned short port=DEFAULT_PORT;
int retval;
int fromlen;
int i;
int socket_type = DEFAULT_PROTO;
struct sockaddr_in local, from;
WSADATA wsaData;
SOCKET listen_socket, msgsock;

/* Parse arguments */
if (argc >1) {
for(i=1;i <argc;i++) {
if ( (argv[i][0] == '-') || (argv[i][0] == '/') ) {
switch(tolower(argv[i][1])) {
case 'p':
if (!stricmp(argv[i+1], "TCP") )
socket_type = SOCK_STREAM;
else if (!stricmp(argv[i+1], "UDP") )
socket_type = SOCK_DGRAM;
else
Usage(argv[0]);
i++;
break;

case 'i':
interface = argv[++i];
break;
case 'e':
port = atoi(argv[++i]);
break;
default:
Usage(argv[0]);
break;
}
}
else
Usage(argv[0]);
}
}

if (WSAStartup(0x202,&wsaData) == SOCKET_ERROR) {
fprintf(stderr,"WSAStartup failed with error %d\n",WSAGetLastError());
WSACleanup();
return -1;
}

if (port == 0){
Usage(argv[0]);
}

local.sin_family = AF_INET;
local.sin_addr.s_addr = (!interface)?INADDR_ANY:inet_addr(interface);

/*
 * Port MUST be in Network Byte Order
 */
local.sin_port = htons(port);

listen_socket = socket(AF_INET, socket_type,0); // TCP socket

if (listen_socket == INVALID_SOCKET){
fprintf(stderr,"socket() failed with error %d\n",WSAGetLastError());
WSACleanup();
return -1;
}
//
// bind() associates a local address and port combination with the
// socket just created. This is most useful when the application is a
// server that has a well-known port that clients know about in advance.
//

if (bind(listen_socket,(struct sockaddr*)&local,sizeof(local) )
== SOCKET_ERROR) {
fprintf(stderr,"bind() failed with error %d\n",WSAGetLastError());
WSACleanup();
return -1;
}

//
// So far, everything we did was applicable to TCP as well as UDP.
// However, there are certain steps that do not work when the server is
// using UDP.
//

// We cannot listen() on a UDP socket.

if (socket_type != SOCK_DGRAM) {
if (listen(listen_socket,5) == SOCKET_ERROR) {
fprintf(stderr,"listen() failed with error %d\n",WSAGetLastError());
WSACleanup();
return -1;
}
}
printf("%s: 'Listening' on port %d, protocol %s\n",argv[0],port,
(socket_type == SOCK_STREAM)?"TCP":"UDP");
while(1) {
fromlen =sizeof(from);
//
// accept() doesn't make sense on UDP, since we do not listen()
//
if (socket_type != SOCK_DGRAM) {
msgsock = accept(listen_socket,(struct sockaddr*)&from, &fromlen);
if (msgsock == INVALID_SOCKET) {
fprintf(stderr,"accept() error %d\n",WSAGetLastError());
WSACleanup();
return -1;
}
printf("accepted connection from %s, port %d\n",
inet_ntoa(from.sin_addr),
htons(from.sin_port)) ;

}
else
msgsock = listen_socket;

//
// In the case of SOCK_STREAM, the server can do recv() and
// send() on the accepted socket and then close it.

// However, for SOCK_DGRAM (UDP), the server will do
// recvfrom() and sendto()  in a loop.

if (socket_type != SOCK_DGRAM)
retval = recv(msgsock,Buffer,sizeof (Buffer),0 );
else {
retval = recvfrom(msgsock,Buffer,sizeof (Buffer),0,
(struct sockaddr *)&from,&fromlen);
printf("Received datagram from %s\n",inet_ntoa(from.sin_addr));
}

if (retval == SOCKET_ERROR) {
fprintf(stderr,"recv() failed: error %d\n",WSAGetLastError());
closesocket(msgsock);
continue;
}
if (retval == 0) {
printf("Client closed connection\n");
closesocket(msgsock);
continue;
}
printf("Received %d bytes, data [%s] from client\n",retval,Buffer);

printf("Echoing same data back to client\n");
if (socket_type != SOCK_DGRAM)
retval = send(msgsock,Buffer,sizeof(Buffer),0);
else
retval = sendto(msgsock,Buffer,sizeof (Buffer),0,
(struct sockaddr *)&from,fromlen);
if (retval == SOCKET_ERROR) {
fprintf(stderr,"send() failed: error %d\n",WSAGetLastError());
}
if (socket_type != SOCK_DGRAM){
printf("Terminating connection\n");
closesocket(msgsock);
}
else
printf("UDP server looping back for more requests\n");
continue;
}
}


README.TXT

This module illustrates the Win32 Winsock APIs over IPX/SPX protocol
sequence.

    This example implements a client and a server. This example can use IPX,
SPX as well as SPXII protocol sequences. The example has a number command
line options. For example,

    -s To run the example as a server.

    -c To run the example as a client.

    -e <Endpoint> To specify an end point of your choice. This is a mandatory
parameter. Servers use this as the socket number to listen on, clients use
it as the socket number to connect to, on a specified host.

    -n <ServerIpxAddress> To specify the server's IPX address. This is used by
clients. The address must be spcified as <NetworkNumber.NodeNumber>
format, for example AABBCCDD.AABBCCDDEEFF.

    -l <LocalIpxAddress> To specify the local IPX address. This is used by
clients and servers. The address must be spcified as <NetworkNumber.
NodeNumber> format, for example AABBCCDD.AABBCCDDEEFF. If an IPX address
is not specified the created sockets are bound to the default interface,
00000000.000000000000.

    -p <d or s or p> To specify the protocol sequence to be used.
'd' - datagram support, in this case IPX protocol sequence is used.
's' - SOCK_STREAM/SPXII protocol sequence is used(default protocol).
'p' - SOCK_SEQPACKET/SPXII protocol sequence isused.

    -m To Enumerate Local Addresses. This example can be used only to
enumerate the local IPX adapters. This may be important before running the
client to know the server's IPX address.

To run the application as a server, the following command line can be
specified:

sockspx -s -e 8000 -p s

To run the application to act as a client, the following command line can be
specified:

sockspx -c -n AABBCCDD.AABBCCDDEEFF -e 8000 -p s

To enumerate the local IPX adapters, the following command line will have
to be specified:

sockspx -m


SOCKSPX.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.
\******************************************************************************/

/*
Module Name:

    sockspx.c

Abstract:

    This module illustrates the Win32 Winsock APIs over IPX/SPX protocol
    sequence.

    This example implements a client and a server. This example can use IPX,
    SPX as well as SPXII protocol sequences. The example has a number command
    line options. For example,

    -s To run the example as a server.

    -c To run the example as a client.

    -e <Endpoint> To specify an end point of your choice. This is a mandatory
parameter. Servers use this as the socket number to listen on, clients use
it as the socket number to connect to, on a specified host.

    -n <ServerIpxAddress> To specify the server's IPX address. This is used by
clients. The address must be spcified as <NetworkNumber.NodeNumber>
format, for example AABBCCDD.AABBCCDDEEFF.

    -l <LocalIpxAddress> To specify the local IPX address. This is used by
clients and servers. The address must be spcified as <NetworkNumber.
NodeNumber> format, for example AABBCCDD.AABBCCDDEEFF. If an IPX address
is not specified the created sockets are bound to the default interface,
00000000.000000000000.

    -p <d or s or p> To specify the protocol sequence to be used.
'd' - datagram support, in this case IPX protocol sequence is used.
's' - SOCK_STREAM/SPXII protocol sequence is used(default protocol).
'p' - SOCK_SEQPACKET/SPXII protocol sequence isused.

    -m To Enumerate Local Addresses. This example can be used only to
enumerate the local IPX adapters. This may be important before running the
client to know the server's IPX address.

To run the application as a server, the following command line can be
specified:

sockspx -s -e 8000 -p s

To run the application to act as a client, the following command line can be
specified:

sockspx -c -n AABBCCDD.AABBCCDDEEFF -e 8000 -p s

To enumerate the local IPX adapters, the following command line will have
to be specified:

sockspx -m

Author:

    Rajesh Dadhia (rajeshd) 05-Mar-96

Revision History:

*/

#include <stdio.h>
#include <windows.h>
#include <winsock.h>
#include <wsipx.h>
#include <wsnwlink.h>

#define MAX_DATA_LEN 80

BOOL __stdcall
CtrlCHandler (
    DWORD dwEvent
    );

void __stdcall
EnumerateAdapters ( void );

void  __stdcall
DoServer ( void );

void __stdcall
DoClient ( void );

void __stdcall
DoStartup ( void );

void __stdcall
CreateSocket ( void );

void __stdcall
BindSocket (
    SOCKADDR_IPX *psa,
    LPSTR lpsAddress,
    LPSTR lpsEndpoint
    );

void _stdcall
FillIpxAddress (
    SOCKADDR_IPX *psa,
    LPSTR lpsAddress,
    LPSTR lpsEndpoint
    );

INT __stdcall
SendData (
    SOCKET s,
    CHAR *pchBuffer
    );

INT __stdcall
ReceiveData (
    SOCKET s,
    CHAR *pchBuffer
    );

INT __stdcall
SendDatagram (
    SOCKET s,
    CHAR *pchBuffer,
    SOCKADDR_IPX *psa
    );

INT __stdcall
ReceiveDatagarm (
    SOCKET s,
    CHAR *pchBuffer,
    SOCKADDR_IPX *psa,
    INT *pcb );

void __stdcall
DoCleanup ( void );

void __stdcall
CheckParameters (
    INT argc,
    CHAR **argv
    );

BOOL __stdcall
CheckProtocol(
    CHAR chProtocol
);

void __stdcall
Usage (
    CHAR *pszProgramName
    );

void __stdcall
PrintError (
    CHAR *lpszRoutine,
    CHAR *lpszCallName,
    DWORD dwError
    );

void __stdcall
PrintIpxAddress(
    CHAR *lpsNetnum,
    CHAR *lpsNodenum
    );

void __stdcall
AtoH(
    CHAR *szDest,
    CHAR *szSource,
    INT iCount
    );

UCHAR __stdcall
BtoH(
    CHAR ch
);

//
// Global Variables
//

// Role of the Application
BOOL fServer = TRUE,

// Enumerate the Local Adapters
     fEnumerate = FALSE,

// WSAStartup () was sucessfull
     fStarted = FALSE;

// Global socket handles
SOCKET sock = INVALID_SOCKET,
       newsock = INVALID_SOCKET;

// Server's IPX address string
CHAR *pszServerAddress,

// Local IPX address string
     *pszLocalAddress,

// Server's Endpoint(socket) string
     *pszServerEndpoint,

// Protocol type (Datagram, Stream, Sequenced Packet)
     chProtocol = 's';

void __cdecl
main (
    INT argc,
    CHAR **argv
    )
{

    //
    // Install the CTRL+BREAK Handler
    //
    if ( FALSE == SetConsoleCtrlHandler ( (PHANDLER_ROUTINE) CtrlCHandler,
  TRUE
  ) )
    {
PrintError ( "main", "SetConsoleCtrlHandler", GetLastError ( ) );
    }

    //
    // Parse the command line parameters
    //
    CheckParameters ( argc, argv );

    //
    // Check to see if the role of the application is to enumerate local
    // adapters
    //
    if ( TRUE == fEnumerate )
    {
EnumerateAdapters ( );
return;
    }

    //
    // Act as a server
    //
    if( TRUE == fServer )
    {
DoServer ( );
return;
    }

    //
    // Act as client
    //
    else
    {
DoClient ( );
return;
    }
}

//
// CtrlCHandler () intercepts the CTRL+BREAK or CTRL+C events and calls the
// cleanup routines
//
BOOL __stdcall
CtrlCHandler (
    DWORD dwEvent
)
{

    if ( ( CTRL_C_EVENT == dwEvent ) || ( CTRL_BREAK_EVENT == dwEvent ) )
    {
DoCleanup ( );
    }

    return FALSE;
}

//
// EnumerateAdapters () will enumerate the available IPX adapters and print
// the addresses
//
void __stdcall
EnumerateAdapters ( void )
{
  SOCKADDR_IPX sa_ipx;
  IPX_ADDRESS_DATA ipx_data;
  INT iRetVal, cb, nAdapters, i=0;

    //
    // Initialize the Winsock 1.1 DLL
    //
    DoStartup ( );

    //
    // Create a local socket
    //
    chProtocol = 'd';
    CreateSocket ( );

    //
    // Bind to a local address and endpoint
    //
    BindSocket ( &sa_ipx, NULL, NULL );

    //
    // Call getsockopt() see the total number of adapters
    //
    cb = sizeof ( nAdapters );
    iRetVal = getsockopt ( sock,
   NSPROTO_IPX,
   IPX_MAX_ADAPTER_NUM,
   (CHAR *) &nAdapters,
   &cb
   );

    if ( SOCKET_ERROR == iRetVal )
    {
PrintError ( "EnumerateAdapters",
     "getsockopt",
     WSAGetLastError ( )
     );
    }
    fprintf ( stdout, "Total number of adapters -> %d\n", nAdapters );

    //
    // Get the address of each adapter
    //
    while ( nAdapters > 0 )
    {
memset ( &ipx_data, 0, sizeof ( ipx_data ) );
ipx_data.adapternum = (nAdapters -1);
cb = sizeof ( ipx_data );

iRetVal = getsockopt ( sock,
       NSPROTO_IPX,
       IPX_ADDRESS,
       (CHAR *) &ipx_data,
       &cb
       );

if ( SOCKET_ERROR == iRetVal )
{
    PrintError ( "EnumerateAdapters",
 "getsockopt",
 WSAGetLastError ( )
 );
}

//
// Print each address
//
PrintIpxAddress ( ipx_data.netnum, ipx_data.nodenum );
nAdapters--;
    }

    //
    // Call the cleanup routine
    //
    DoCleanup ( );
    return;
}

//
// DoServer () performs the connection-less/connection-oriented server
// related tasks
//
void  __stdcall
DoServer ( void )
{
  // Address structures for the client and the server
  SOCKADDR_IPX sa_ipx, sa_ipx_client;

  // Buffer for the received/sent data
  CHAR chBuffer[MAX_DATA_LEN];

  INT iRetVal, cb;

    DoStartup ( );

    CreateSocket ( );

    //
    // Bind to a local address and endpoint
    //
    BindSocket ( &sa_ipx, pszLocalAddress, pszServerEndpoint);

    //
    // Check the Specified protocol. Call listen(), accept() if a connection
    // oriented protocol is specified
    //
    if ( 'd' != chProtocol   )
    {
iRetVal = listen ( sock, 5 );

if ( SOCKET_ERROR == iRetVal )
{
    PrintError ( "DoServer", "listen", WSAGetLastError ( ) );
}

fprintf ( stdout, "Waiting for a Connection...\n");
fprintf ( stdout, "Press <CTRL+C> or <CTRL+BREAK> to exit\n");
//
// Wait for a connection
//
cb = sizeof ( sa_ipx_client );
newsock = accept ( sock,
   (SOCKADDR *) &sa_ipx_client,
   &cb
   );

if ( INVALID_SOCKET == newsock )
{
    PrintError ( "DoServer", "accept", WSAGetLastError ( ) );
}

//
// Print the address of connected client
//
fprintf ( stdout, "Connected to Client Address - " );
PrintIpxAddress ( sa_ipx_client.sa_netnum, sa_ipx_client.sa_nodenum );

//
// Receive data on newly created socket
//
iRetVal = ReceiveData ( newsock, chBuffer );

//
// Print the contents of received data
//
chBuffer[iRetVal] = '\0';
fprintf ( stdout,"%d bytes of data received->%s\n",iRetVal, chBuffer );
strcpy ( chBuffer, "Hello SPX Client" );

//
// Send data on newly created socket
//
iRetVal = SendData ( newsock, chBuffer );
fprintf ( stdout, "%d bytes of data sent\n", iRetVal );
    }
    //
    // Server will receive and send datagrams
    //
    else
    {
//
// Receive a datagram on the bound socket
//
cb = sizeof ( sa_ipx_client );
iRetVal = ReceiveDatagarm ( sock, chBuffer, &sa_ipx_client, &cb );

//
// Print the contents of received datagram and the senders address
//
fprintf ( stdout, "Message Received from Client Address - " );
PrintIpxAddress( sa_ipx_client.sa_netnum, sa_ipx_client.sa_nodenum );
chBuffer[iRetVal] = '\0';
fprintf( stdout, "Data Received->%s\n", chBuffer );
fprintf( stdout,"%d bytes of data received->%s\n",iRetVal, chBuffer );

//
// Send a datagram on the bound socket to the client
//
strcpy ( chBuffer, "Hello IPX Client" );
iRetVal = SendDatagram (sock, chBuffer, &sa_ipx_client );
fprintf( stdout, "%d bytes of data sent\n", iRetVal );
    }

    //
    // Call the cleanup routine
    //
    DoCleanup ( );
    return;
}

//
// DoClient () performs the connection-less/connection-oriented client
// related tasks
//
void __stdcall
DoClient ( void )
{
  // Address structures for the client and the server
  SOCKADDR_IPX sa_ipx, sa_ipx_server;

  // Buffer for the received/sent data
  CHAR chBuffer[MAX_DATA_LEN];

  INT iRetVal, cb;

    DoStartup ( ) ;

    CreateSocket ( );

    //
    // Bind to a local address and endpoint
    //
    BindSocket ( &sa_ipx, pszLocalAddress, NULL);

    if ( NULL == pszServerEndpoint )
    {
fprintf ( stdout, "Server Endpoint must be specified....Exiting\n");
DoCleanup ( );
exit ( 1 );
    }

    //
    // Fill the sa_ipx_server address address with server address and endpoint
    //
    if ( NULL != pszServerAddress )
    {
FillIpxAddress ( &sa_ipx_server, pszServerAddress, pszServerEndpoint );
    }
    else
    {
fprintf ( stdout, "Server Address must be specified....Exiting\n");
DoCleanup ( );
exit ( 1 );
    }

    //
    // Check the Specified protocol. Call connect() if a connection oriented
    // protocol is specified
    //
    if ( chProtocol != 'd' )
    {
fprintf(stdout, "Connecting to Server -");
PrintIpxAddress ( sa_ipx_server.sa_netnum, sa_ipx_server.sa_nodenum );

//
// Connect to the server
//
iRetVal = connect ( sock,
    (SOCKADDR *) &sa_ipx_server,
    sizeof sa_ipx_server
    );

if ( SOCKET_ERROR == iRetVal )
{
    PrintError ( "DoClient", "connect", WSAGetLastError ( ) );
}

fprintf ( stdout, "Connected to Server Address - " );
PrintIpxAddress( sa_ipx_server.sa_netnum, sa_ipx_server.sa_nodenum );

//
// Send data to the specfied server
//
strcpy ( chBuffer, "Hello SPX Server" );
iRetVal = SendData ( sock, chBuffer );
fprintf( stdout, "%d bytes of data sent\n", iRetVal );

//
// Receive data from the server
//
iRetVal = ReceiveData ( sock, chBuffer );

//
// Print the contents of received data
//
chBuffer[iRetVal] = '\0';
fprintf( stdout, "%d bytes of data received->%s\n", iRetVal,chBuffer);
    }
    else
    {
//
// Send a datagram to the specified server
//
strcpy ( chBuffer, "Hello IPX Server" );
iRetVal = SendDatagram ( sock, chBuffer, &sa_ipx_server );
fprintf ( stdout, "%d bytes of data sent\n", iRetVal );

//
// Receive a datagram from the server
//
cb = sizeof sa_ipx_server;
iRetVal = ReceiveDatagarm ( sock, chBuffer, &sa_ipx_server, &cb );

//
// Print the contents of received data
//
chBuffer[iRetVal] = '\0';
fprintf ( stdout, "%d bytes of data received->%s\n",iRetVal,chBuffer);
    }

    //
    // Call the cleanup routine
    //
    DoCleanup ( );

    return;
}

//
// DoStartup() initializes the Winsock DLL with Winsock version 1.1
//
void __stdcall
DoStartup ( void )
{
  WSADATA wsaData;
  INT iRetVal;

    iRetVal = WSAStartup ( MAKEWORD ( 1,1 ), &wsaData );

    if ( 0 != iRetVal)
    {
    PrintError ( "DoStartup", "WSAStartup", iRetVal );
    }

    /* Set the global flag */
    fStarted = TRUE;

    return;
}

//
// CreateSocket() creates an endpoint (socket) and assigns the returned value
// from socket() to the global socket descriptor 'sock'. The actual protocol
// specified on the command line is checked, in order to make the socket()
// call correctly.
//
void __stdcall
CreateSocket ( void )
{
    sock = socket ( AF_IPX,
    chProtocol == 'd' ? SOCK_DGRAM :
    ( chProtocol == 's'  ? SOCK_STREAM : SOCK_SEQPACKET),
    chProtocol == 'd' ? NSPROTO_IPX : NSPROTO_SPXII
    );

    if ( INVALID_SOCKET == sock )
    {
PrintError ( "CreateSocket", "socket", WSAGetLastError ( ) );
    }

    return;
}

//
// BindSocket() binds the global socket descriptor 'sock' to the specified
// address. If an endpoint is specified it uses that or it binds to a system
// assigned port.
//
void __stdcall
BindSocket (
    SOCKADDR_IPX *psa,
LPSTR lpsAddress,
LPSTR lpsEndpoint
)
{
  INT iRetVal;

    //
    // Fill the givenSOCKADDR_IPX structure
    //
    FillIpxAddress ( psa, lpsAddress, lpsEndpoint );

    iRetVal = bind ( sock,
     (SOCKADDR *) psa,
     sizeof (SOCKADDR_IPX)
     );

    if ( SOCKET_ERROR == iRetVal )
    {
PrintError ( "BindSocket", "bind", WSAGetLastError ( ) );
    }

    //
    // Print the address we are bound to. If a particular interface is not
    // mentioned in the BindSocket() call, this may print the address as
    // 00000000.0000000000000000. This is because of the fact that an
    // interface is picked only when the actual connection establishment
    // occurs, in case of connection oriented socket.
    //
    fprintf ( stdout, "Bound to Local Address - " );
    PrintIpxAddress( psa->sa_netnum, psa->sa_nodenum );

    return;
}

//
// FillIpxAddress() fills a structure of type SOCKADDR_IPX with relevant
// address-family, network number, node number and socket (endpoint)
// parameters.
//
void _stdcall
FillIpxAddress (
    SOCKADDR_IPX *psa,
    LPSTR lpsAddress,
    LPSTR lpsEndpoint
    )
{
  // Location of the separator
  LPSTR pszPoint;

    ZeroMemory ( psa, sizeof ( SOCKADDR_IPX ) );

    psa->sa_family = AF_IPX;

    //
    // Check if an address is specified
    //
    if ( NULL != lpsAddress )
    {
//
// Get the offset for node number/network number separator
//
pszPoint = strchr ( lpsAddress, '.' );

if ( NULL == pszPoint )
{
    fprintf ( stderr, "IPX Address does not have a separator\n");
    DoCleanup ( );
    exit ( 1 );
}

//
// covert the address in the  string format to binary format
//
AtoH ( (CHAR *) psa->sa_netnum, lpsAddress, 4 );
AtoH ( (CHAR *) psa->sa_nodenum, pszPoint + 1, 6 );

    }

    if ( NULL != lpsEndpoint )
    {
    psa->sa_socket = (USHORT) atoi ( lpsEndpoint );
    }

    return;
}

//
// SendData() is generic rotuine to send some data over a
// connection-oriented IPX socket.
//
INT __stdcall
SendData (
    SOCKET s,
    CHAR *pchBuffer
    )
{
  INT iRetVal;

    iRetVal = send ( s,
     pchBuffer,
     strlen ( pchBuffer ),
     0
     );

    if ( SOCKET_ERROR == iRetVal )
    {
PrintError ( "SendData", "send", WSAGetLastError ( ) );
    }

    //
    // return the total number of bytes sent
    //
    return iRetVal;
}

//
// ReceiveData() is generic rotuine to receive some data over a
// connection-oriented IPX socket.
//
INT __stdcall
ReceiveData (
    SOCKET s,
    CHAR *pchBuffer
    )
{
  INT iRetVal;

    iRetVal = recv ( s,
     pchBuffer,
     MAX_DATA_LEN,
     0
     );

    if ( SOCKET_ERROR == iRetVal )
    {
PrintError ( "ReceiveData", "recv", WSAGetLastError ( ) );
    }

    //
    // return the total number of bytes received
    //
    return iRetVal;
}

//
// SendDatagram() is generic rotuine to send a datagram to a
// specifid host.
//
INT __stdcall
SendDatagram (
    SOCKET s,
    CHAR *pchBuffer,
    SOCKADDR_IPX *psa
    )
{
  INT iRetVal;

    iRetVal = sendto ( s,
       pchBuffer,
       strlen ( pchBuffer ),
       0,
       (SOCKADDR *) psa,
       sizeof ( SOCKADDR_IPX )
       );

    if ( SOCKET_ERROR == iRetVal )
    {
    PrintError ( "SendDatagram", "sendto", WSAGetLastError ( ) );
    }

    /* return the total number of bytes sent in the datagram */
    return iRetVal;
 }

//
// ReceiveDatagram() is generic rotuine to receive a datagram from a
// specifid host.
//
INT __stdcall
ReceiveDatagarm (
    SOCKET s,
    CHAR *pchBuffer,
    SOCKADDR_IPX *psa,
    INT *pcb )
{
  INT iRetVal;

    iRetVal = recvfrom ( s,
 pchBuffer,
 MAX_DATA_LEN,
 0,
 (SOCKADDR *) psa,
 pcb
 );

    if ( SOCKET_ERROR == iRetVal )
    {
    PrintError ( "ReceiveDatagram", "recvfrom", WSAGetLastError ( ) );
    }

    //
    // return the total number of bytes received in the datagram
    //
    return iRetVal;
}

//
// DoCleanup () will close the sockets which were opened successfully using
// a call to socket (). Additionally, it will call WSACleanup (), if a call
// to WSAStartup () was made successfully.
//
void __stdcall
DoCleanup ( void )
{
    if ( INVALID_SOCKET != sock )
    {
    closesocket ( sock );
    }

    if ( INVALID_SOCKET != newsock )
    {
    closesocket ( sock );
    }

    if ( TRUE == fStarted )
    {
    WSACleanup ( );
    }

    fprintf ( stdout, "DONE\n");

    return;
}

//
// CheckParameters() parses the command line parametrs.
//
void __stdcall
CheckParameters (
    INT argc,
    CHAR **argv
    )
{
    INT i;

    for ( i = 1; i < argc; i++ )
    {
if ( ( *argv[i] == '-') || ( *argv[i] == '/' ) )
{
    switch ( tolower ( *( argv[i]+1 ) ) )
    {

//
// Role of the application - server
//
case 's':
    fServer = TRUE;
    break;

//
// Role of the application - client
//
case 'c':
    fServer = FALSE;
    break;

//
// Store a pointer to the server endpoint
//
case 'e':
    pszServerEndpoint = argv[++i];
    break;

//
// Store a pointer to the server address
//
case 'n':
    pszServerAddress = argv[++i];
    break;

//
// Store a pointer to the local address
//
case 'l':
    pszLocalAddress = argv[++i];
    break;

//
// Set the flag to indicate enumeration of local adapters
//
case 'm':
    fEnumerate = TRUE;
    break;

//
// Read and validate the protocol specified
//
case 'p':
    chProtocol = tolower ( *argv[++i] );
    if ( FALSE == CheckProtocol ( chProtocol ) )
    {
fprintf ( stderr, "UnKnown protcol specified\n\n");
Usage ( argv[0] );
    }
    break;
    //
    // Help
    //
    case 'h':
    case '?':
    default:
Usage ( argv[0] );
    }
}
//
// Help
//
else
    Usage ( argv[0] );
    }
    return;
}

//
// CheckProtocol() checks if a valid protocol is specified on the command
// line.
//
BOOL __stdcall
CheckProtocol(
    CHAR chProtocol
)
{
    if ( 'd' != chProtocol && 's' != chProtocol && 'p' != chProtocol )
    {
return FALSE;
    }

    return TRUE;
}

//
// Display the usage of command line parameters.
//
void __stdcall
Usage (
    CHAR *pszProgramName
    )
{
    fprintf ( stderr,"Usage:  %s\n", pszProgramName );
    fprintf ( stderr,
    "\t-s or -c (s - server, c - client, default - server)\n" );
    fprintf ( stderr, "\t-e <Endpoint>\n" );
    fprintf ( stderr, "\t-n <ServerIpxAddress>\n" );
    fprintf ( stderr, "\t-l <LocalIpxAddress>\n" );
    fprintf ( stderr,
"\t-p <d or s or p> ( d - datagram, s - stream, p - sequenced packet)\n" );
    fprintf ( stderr, "\t-m To Enumerate Local Addresses\n" );

    exit ( 1 );
}

//
// PrintError () is a generic routine to print the Winsock or Win32
// error codes for the errors occurred during relevant calls.
// If an error occurs, the error code is printed, cleanup routine is
// called and the application exits.
//
void __stdcall
PrintError (
    CHAR *lpszRoutine,
    CHAR *lpszCallName,
    DWORD dwError
    )
{
    fprintf ( stderr,
      "The Call to %s() in the Routine %s() failed with Error %d\n",
      lpszCallName,
      lpszRoutine,
      dwError
      );

    DoCleanup ( );
    exit ( 1 );
}

//
// Print an IPX address.
//
void __stdcall
PrintIpxAddress(
    CHAR *lpsNetnum,
    CHAR *lpsNodenum
    )
{
  INT i;

    for ( i = 0; i < 4; i++ )
    {
fprintf ( stdout, "%02X", (UCHAR) lpsNetnum[i] );
    }

    fprintf ( stdout, "." );

    for ( i = 0; i < 6; i++ )
    {
fprintf ( stdout, "%02X", (UCHAR) lpsNodenum[i] );
    }

    fprintf ( stdout, "\n" );

    return;
}

//
// AtoH () coverts the IPX address specified in the string(ascii) format to
// the binary(hexadecimal) format.
//
void __stdcall
AtoH(
    CHAR *szDest,
    CHAR *szSource,
    INT iCount
    )
{
    while (iCount--)
    {
*szDest++ = ( BtoH ( *szSource++ ) << 4 ) + BtoH ( *szSource++ );
    }
    return;
}

//
// BtoH () returns the equivalent binary value for an individual
// character specified in the ascii format.
//
UCHAR __stdcall
BtoH(
    CHAR ch
    )
{

    if ( ch >= '0' && ch <= '9' )
    {
    return ( ch - '0' );
    }

    if ( ch >= 'A' && ch <= 'F' )
    {
    return ( ch - 'A' + 0xA );
    }

    if (ch >= 'a' && ch <= 'f' )
    {
    return ( ch - 'a' + 0xA );
    }

    //
    // Illegal values in the IPX address will not be excepted
    //
    fprintf( stderr,
    "Illegal value specified in  one of the IPX Addresses-BtoH Failed\n" );

    DoCleanup ( );

    exit ( 1 );
}


README.TXT

This directory includes the following.

- client.c and server.c files : sample WinSock applications.
- makefile : Creates the executables for Client and Server

The actual tp4 binaries can be found in the Windows NT driver library.
I.e.  drvlib\protocol\tp4setup on the CD with the operating system.

The tp4 Users Guide is provided on the Win32 SDK as doc\misc\msosi.doc.
This is provided for people who are building solutions using tp4.


CLIENT.C

/****************************************************************************\
*  dgrecv.c -- sample program demonstrating NWLink.
*
*       Microsoft Developer Support
*       Copyright 1992 - 1997 Microsoft Corporation
*
*  This program is a simple example of using SPX connect.
*
****************************************************************************/
#include <windows.h>
#include <winsock.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include "wshisotp.h"

typedef struct tag_error_struct{
    int     errnum;
    LPSTR   errstr;
} ERROR_STRUCT;

SOCKADDR_TP addr;
SOCKADDR_TP caddr;

UCHAR ch = 0;

char LocalNetworkAddress[22];
char LocalNetworkAddressLength;
short   LocalTsel;
char RemoteNetworkAddress[22];
char RemoteNetworkAddressLength;
short   RemoteTsel;

int     verbose = 1;
int     Socket_Type = SOCK_SEQPACKET;
int     Protocol = ISOPROTO_TP4;
int     Local_Address_Family = AF_ISO;
int     Remote_Address_Family = AF_ISO;
int     Backlog = 1;
int     No_Broadcast = 0;
int     No_Loop = 0;
int     Sleep_Time = 1000;
int     Send_Length = 100;
int     Receive_Length = 200;
int     Local_Packet_Type = 0;

#define WSAERRGAP1 28
#define WSAERRGAP2 19

char msg_no_error[19] =      "No error occurred!";
char msg_unknown_error[14] = "Unknown error";

ERROR_STRUCT errlist[] = {
    {WSAEINTR,           "WSAEINTR - Interrupted"},
    {WSAEBADF,           "WSAEBADF - Bad file number"},
    {WSAEFAULT,          "WSAEFAULT - Bad address"},
    {WSAEINVAL,          "WSAEINVAL - Invalid argument"},
    {WSAEMFILE,          "WSAEMFILE - Too many open files"},

/*
*    Windows Sockets definitions of regular Berkeley error constants
*/

    {WSAEWOULDBLOCK,     "WSAEWOULDBLOCK - Socket marked as non-blocking"},
    {WSAEINPROGRESS,     "WSAEINPROGRESS - Blocking call in progress"},
    {WSAEALREADY,        "WSAEALREADY - Command already completed"},
    {WSAENOTSOCK,        "WSAENOTSOCK - Descriptor is not a socket"},
    {WSAEDESTADDRREQ,    "WSAEDESTADDRREQ - Destination address required"},
    {WSAEMSGSIZE,        "WSAEMSGSIZE - Data size too large"},
    {WSAEPROTOTYPE,      "WSAEPROTOTYPE - Protocol is of wrong type for this socket"},
    {WSAENOPROTOOPT,     "WSAENOPROTOOPT - Protocol option not supported for this socket type"},
    {WSAEPROTONOSUPPORT, "WSAEPROTONOSUPPORT - Protocol is not supported"},
    {WSAESOCKTNOSUPPORT, "WSAESOCKTNOSUPPORT - Socket type not supported by this address family"},
    {WSAEOPNOTSUPP,      "WSAEOPNOTSUPP - Option not supported"},
    {WSAEPFNOSUPPORT,    "WSAEPFNOSUPPORT - "},
    {WSAEAFNOSUPPORT,    "WSAEAFNOSUPPORT - Address family not supported by this protocol"},
    {WSAEADDRINUSE,      "WSAEADDRINUSE - Address is in use"},
    {WSAEADDRNOTAVAIL,   "WSAEADDRNOTAVAIL - Address not available from local machine"},
    {WSAENETDOWN,        "WSAENETDOWN - Network subsystem is down"},
    {WSAENETUNREACH,     "WSAENETUNREACH - Network cannot be reached"},
    {WSAENETRESET,       "WSAENETRESET - Connection has been dropped"},
    {WSAECONNABORTED,    "WSAECONNABORTED - "},
    {WSAECONNRESET,      "WSAECONNRESET - "},
    {WSAENOBUFS,         "WSAENOBUFS - No buffer space available"},
    {WSAEISCONN,         "WSAEISCONN - Socket is already connected"},
    {WSAENOTCONN,        "WSAENOTCONN - Socket is not connected"},
    {WSAESHUTDOWN,       "WSAESHUTDOWN - Socket has been shut down"},
    {WSAETOOMANYREFS,    "WSAETOOMANYREFS - "},
    {WSAETIMEDOUT,       "WSAETIMEDOUT - Command timed out"},
    {WSAECONNREFUSED,    "WSAECONNREFUSED - Connection refused"},
    {WSAELOOP,           "WSAELOOP - "},
    {WSAENAMETOOLONG,    "WSAENAMETOOLONG - "},
    {WSAEHOSTDOWN,       "WSAEHOSTDOWN - "},
    {WSAEHOSTUNREACH,    "WSAEHOSTUNREACH - "},
    {WSAENOTEMPTY,       "WSAENOTEMPTY - "},
    {WSAEPROCLIM,        "WSAEPROCLIM - "},
    {WSAEUSERS,          "WSAEUSERS - "},
    {WSAEDQUOT,          "WSAEDQUOT - "},
    {WSAESTALE,          "WSAESTALE - "},
    {WSAEREMOTE,         "WSAEREMOTE - "},

/*
*    Extended Windows Sockets error constant definitions
*/

    {WSASYSNOTREADY,     "WSASYSNOTREADY - Network subsystem not ready"},
    {WSAVERNOTSUPPORTED, "WSAVERNOTSUPPORTED - Version not supported"},
    {WSANOTINITIALISED,  "WSANOTINITIALISED - WSAStartup() has not been successfully called"},

/*
*    Other error constants.
*/

    {WSAHOST_NOT_FOUND,  "WSAHOST_NOT_FOUND - Host not found"},
    {WSATRY_AGAIN,       "WSATRY_AGAIN - Host not found or SERVERFAIL"},
    {WSANO_RECOVERY,     "WSANO_RECOVERY - Non-recoverable error"},
    {WSANO_DATA,         "WSANO_DATA - (or WSANO_ADDRESS) - No data record of requested type"},
    {-1,                 NULL}
};

/*
*   Function Prototypes
*/

int mem_check(LPSTR, UCHAR, int);
extern int main(int, char **);
extern int net_init(SOCKET *);
extern int make_connection(SOCKET);
extern int do_send_receive(SOCKET);
extern void dos_net_perror(LPSTR );
extern LPCSTR dos_net_strerror(LPSTR );
extern LPCSTR get_error_text(int );

/****************************************************************************
*
*    FUNCTION:  main( int argc, char **argv )
*
*    PURPOSE:   This is the main entry for the program
*
*
*    ARGUMENTS: argc = Number of arguments
*               argv = Array of ptrs to cmd line args
*
*
*        RETURNS:   Exit code for the program
*
*\***************************************************************************/
int main(int argc, char **argv)
{
    SOCKET s;

   /*
   ** Read Local & Remote Transport Address fields from user
   */
   printf("LocalNetworkAddress(max. 20 chars) : ");
   scanf("%s", &LocalNetworkAddress[0]);
   printf("LocalTsel : ");
   scanf("%d", &LocalTsel);
   LocalNetworkAddressLength = strlen(LocalNetworkAddress) ;

   printf("RemoteNetworkAddress(max. 20 chars) : ");
   scanf("%s", &RemoteNetworkAddress[0]);
   printf("RemoteTsel : ");
   scanf("%d", &RemoteTsel);
   RemoteNetworkAddressLength = strlen(RemoteNetworkAddress) ;

printf("local Tsel(%d) Net_length(%d) Net(%s)\n",
        LocalTsel, LocalNetworkAddressLength, LocalNetworkAddress);
printf("Remote Tsel(%d) Net_length(%d) Net(%s)\n",
        RemoteTsel, RemoteNetworkAddressLength, RemoteNetworkAddress);

    /*
    *   Initialize the network and set up the socket
    */

    if (net_init(&s))
        return 1;

    /*
    *   Try to connect to our server
    */

    if (make_connection(s))
        return 1;

    /*
    *   Send/receive data to/from server
    */

    do_send_receive(s);

    /*
    *   All done - close up
    */

    if (verbose)
        printf("calling closesocket(socket = %d)\n", s);

    closesocket(s);
    return 0;
}

/****************************************************************************
*
*    FUNCTION:  net_init( SOCKET *skt )
*
*    PURPOSE:   Initializes the WinSock stuff and sets up our socket.
*
*
*    ARGUMENTS: SOCKET * => struct to fill in with opened socket.
*
*        RETURNS:   0 if ok
*                               1 if error
*
*\***************************************************************************/
int net_init(SOCKET *skt)
{
    SOCKET s;
    int rc, addrlen;
    int reuse = 1;
    WSADATA wsdata;
    WORD    wVersionRequested;

    wVersionRequested = MAKEWORD(1,1);

    /*
    *   Register with the socket library
    */

    rc = WSAStartup(wVersionRequested, &wsdata);

    if (verbose)
        printf("WSAStartup returned 0x%X\n", rc);

    if (rc) {
        printf("WSAStartup failed: error = %d\n", rc);
        return 1;
    }

    /*
    *   Open a STREAMING socket
    */

    s = socket(Local_Address_Family, Socket_Type, Protocol);

    if (verbose)
        printf("socket() returned 0x%X (%d)\n", s, s);

    if (s == INVALID_SOCKET) {
        printf("socket call failed");
        return 1;
    }

    /*
    *   Bind to any address
    */

    ISO_SET_TP_ADDR(&addr, &LocalTsel, sizeof(LocalTsel),
                        LocalNetworkAddress, LocalNetworkAddressLength);

    rc = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char FAR *)&reuse, sizeof(int));
    if (rc == SOCKET_ERROR) {
        dos_net_perror("setsockopt call failed");
        return 1;
    }

    rc = bind(s, (const struct sockaddr *)&addr, sizeof(SOCKADDR_TP));

    if (verbose)
        printf("bind() returned 0x%X\n", rc);

    if (rc == SOCKET_ERROR) {
        dos_net_perror("bind call failed");
        return 1;
    }

    if (verbose)
        printf("calling getsockname(socket = %d)\n", s);

    addrlen = sizeof(SOCKADDR_TP);
    rc = getsockname(s, (struct sockaddr *) &addr, &addrlen);

    if (verbose)
        printf("getsockname() returned 0x%X\n", rc);

    if (rc == SOCKET_ERROR) {
        printf("Error binding to socket");
        closesocket(s);
        return 1;
    }
/*
    if (verbose) {
        printf("addrlen = %d\n", addrlen);
        print_netaddr(addr.sa_netnum, "Bound address = ", "\n");
    }
*/

    /*
    *   Build the address of the node to connect to
    */
ISO_SET_TP_ADDR(&caddr, &RemoteTsel, sizeof(RemoteTsel),
                        RemoteNetworkAddress, RemoteNetworkAddressLength);

/*
    caddr.sa_family = Remote_Address_Family;
    caddr.addr_type = HIERARCHICAL;
    caddr.taddr_len = RemoteNetworkAddressLength + RemoteTselLength;
    caddr.tsel_len = RemoteTselLength;
    memcpy(&caddr.addr, RemoteTsel, RemoteTselLength);
    memcpy(&caddr.addr[RemoteTselLength],
                RemoteNetworkAddress, RemoteNetworkAddressLength);
*/

    /*
    *   Set up socket for return
    */

    *skt = s;

    return 0;
}

/****************************************************************************
*
*    FUNCTION:  make_connection( SOCKET s )
*
*    PURPOSE:   Establishes a connection with our server.
*
*    ARGUMENTS: SOCKET socket to use for connection
*
*        RETURNS:   0 if ok
*                               1 if error
*
*\***************************************************************************/
int make_connection(SOCKET s)
{
    int rc, addrlen;

    /*
    *   Connect
    */

    if (verbose)
        printf("calling connect(socket = %d)\n", s);

    rc = connect(s, (const struct sockaddr FAR *)&caddr, sizeof(SOCKADDR_TP));

    if (verbose)
        printf("connect() returned 0x%X\n", rc);

    if (rc == SOCKET_ERROR) {
        dos_net_perror("connect call failed");
        return 1;
    }

    printf("Connect OK\n");

    /*
    *   Print out address we connected to
    */

    if (verbose)
        printf("calling getpeername(socket = %d)\n", s);

    addrlen = sizeof(SOCKADDR_TP);
    rc = getpeername(s, (struct sockaddr *) &caddr, &addrlen);

    if (verbose)
        printf("getpeername() returned 0x%X\n", rc);
/*
    if (verbose) {
        printf("addrlen = %d\n", addrlen);
        print_netaddr(caddr.sa_netnum, "Remote Address = ", "\n");
    }
*/

    return 0;
}
/****************************************************************************
*
*    FUNCTION:  do_send_receive( SOCKET *s )
*
*    PURPOSE:   Alternately sends/receives data to/from the server.
*
*    ARGUMENTS: SOCKET socket to transmit on
*
*        RETURNS:   0 if ok
*                               1 if error
*
*\***************************************************************************/
int do_send_receive(SOCKET s)
{
    int rc, errflag = 0;
    int sndpkts = 0, rcvpkts = 0;
    LPSTR sendbuf;

    /*
    *   Allocate a send buffer
    */

    if (verbose)
        printf("Allocating %d bytes for send buffer \n");

    sendbuf = malloc(Send_Length);

    if (!sendbuf) {
        printf("Error allocating %d bytes for send buffer\n");
        return 1;
    }

    /*
    *   Send data and recv it back
    */

    while (1) {

        /*
        *   Fill the buffer with our current character
        */

        memset(sendbuf, ch, Send_Length);

        /*
        *   Send data
        */

        if (verbose)
            printf("calling send(socket = %d, length = %d)\n", s, Send_Length);

        rc = send(s, sendbuf, Send_Length, 0);

        if (verbose)
            printf("send() returned 0x%X\n", rc);

        if (rc == SOCKET_ERROR) {
            printf("\nsend() call failed");
            errflag++;
            break;
        }

        if (verbose)
            printf("Sent packet %d: sent %d bytes\n", sndpkts++, rc);
        else
            printf("\rSent packet %d: sent %d bytes... ", sndpkts++, rc);

        /*
        *   Receive the data back
        */
/*
        if (verbose)
            printf("calling recv(socket = %d, length = %d)\n", s, Send_Length);

        rc = recv(s, sendbuf, Send_Length, 0);

        if (verbose)
            printf("recv() returned %d\n", rc);

        if (rc == SOCKET_ERROR) {
            printf("\nrecv() call failed");
            errflag++;
            break;
        }

        if (!rc) {
            printf("connection has been lost\n");
            break;
        }

        printf("Received %d bytes", rc);
*/
        if (verbose)
            printf("\n");

        /*
        *   See if our buffer has the same data in it
        */

        rc = mem_check(sendbuf, ch++, Send_Length);

        if (rc)
            printf("Data compare error: packet = %d, offset = %d\n", (sndpkts-1), rc);
        else if (verbose)
            printf("Data compares okay\n");

        /*
        *   If we are to send just 1, break out
        */

        if (No_Loop)
            break;

        /*
        *   Pause a little while (don't trash the network)
        Sleep(Sleep_Time);
        */

    }

    if (verbose)
        printf("\nFreeing send buffer\n");

    free(sendbuf);

    return errflag;
}

/****************************************************************************
*
*    FUNCTION:  mem_check( LPSTR p, UCHAR ch, int len)
*
*    PURPOSE:   Makes sure that a buffer is filled with only the character
*                   specified.
*
*    ARGUMENTS: LPSTR   => buffer to scan
*               char    character to check for
*               int     length of buffer to check
*
*        RETURNS:   0 if buffer has only the specified character
*                               offset to non-matching character if found
*
************************************************************************/
int mem_check(LPSTR p, UCHAR ch, int len)
{
    int buflen;

    buflen = len;

    while (len--) {
        if ((UCHAR)*p++ != ch) {
            return (buflen - len);
        }
    }

    return 0;
}

void dos_net_perror(LPSTR p)
{
    printf(dos_net_strerror(p));
    printf("\n");

    return;
}

LPCSTR dos_net_strerror(LPSTR p)
{
    int error = 0;
    static char return_string[256];

    /*
    *   Get the error number from the system
    */

    error = h_errno;

    /*
    *   Build the string to return
    */

    sprintf(return_string, "%s :(%d)%s", p, error, get_error_text(error));

    return (LPSTR)return_string;
}

LPCSTR get_error_text(int error)
{
    int search = 0;

    /*
    *   No error
    */

    if (!error)
        return (LPCSTR)msg_no_error;

    /*
    *   Search through our array of error number / string pairs
    *   until we find a matching error number or get to the end
    *   of our list.  If we found a matching error number,
    *   return a LPSTR to the corresponding string.
    */

    while (errlist[search].errstr) {
        if (error == errlist[search].errnum)
            return errlist[search].errstr;
        search++;
    }

    /*
    * If we didn't have the error in our list, return unkown
    */

    return (LPCSTR)msg_unknown_error;
}


SERVER.C

/****************************************************************************\
*  listen.c -- sample program demonstrating NWLink.
*
*       Microsoft Developer Support
*       Copyright 1992 - 1997 Microsoft Corporation
*
*  This program is a simple example of opening a SPX socket,
*  binding to the socket, and listening for a connection.
*
****************************************************************************/
#include <windows.h>
#include <winsock.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include "wshisotp.h"

/*
*   Sockaddr structures
*/

SOCKADDR_TP addr;
SOCKADDR_TP baddr;
SOCKADDR_TP saddr;

/*
*   Function Prototypes
*/

extern int main(int, char **);
extern int net_init(SOCKET *);
extern int do_listen(SOCKET);
extern int wait_for_connection(SOCKET, SOCKET *);
extern int do_recv_send(SOCKET);

char LocalNetworkAddress[22];
char LocalNetworkAddressLength;
short   LocalTsel;

int     verbose = 1;
int     Socket_Type = SOCK_SEQPACKET;
int     Protocol = ISOPROTO_TP4;
int     Local_Address_Family = AF_ISO;
int     Remote_Address_Family = AF_ISO;
int     Backlog = 1;
int     No_Broadcast = 0;
int     No_Loop = 0;
int     Sleep_Time = 1000;
int     Send_Length = 100;
int     Receive_Length = 200;
int     Local_Packet_Type = 0;

/****************************************************************************
*
*    FUNCTION:  main( int argc, char **argv )
*
*    PURPOSE:   This is the main entry for the program
*
*
*    ARGUMENTS: argc = Number of arguments
*               argv = Array of ptrs to cmd line args
*
*
*        RETURNS:   Exit code for the program
*
*\***************************************************************************/
int main(int argc, char **argv)
{
    SOCKET s, s2;

   /*
   ** Read Local Transport Address fields from user
   */
   printf("LocalNetworkAddress(max. 20 chars) : ");
   scanf("%s", &LocalNetworkAddress[0]);
   printf("LocalTsel : ");
   scanf("%d", &LocalTsel);

   LocalNetworkAddressLength = strlen(LocalNetworkAddress) ;

printf("Local Tsel(%d) Net_length(%d) Net(%s)\n",
        LocalTsel, LocalNetworkAddressLength, LocalNetworkAddress);

    /*
    *   Initialize the network and set up our socket
    */

    if (net_init(&s))
        return 1;

    if (verbose)
        printf("calling do_listen\n");

    /*
    *   Go listen for a call
    */

    if (do_listen(s))
        return 1;

    /*
    *   Then wait for a connection
    */

    if (verbose)
        printf("calling wait_for_connection\n");

    if (wait_for_connection(s, &s2))
        return 1;

    /*
    *   Receive data then send it back
    */

    if (verbose)
        printf("calling do_recv_send\n");

    if (do_recv_send(s2))
        return 1;

    /*
    *   All done
    */

    if (verbose)
        printf("closing both sockets\n");

    closesocket(s2);
    closesocket(s);

    return 0;
}

/****************************************************************************
*
*    FUNCTION:  net_init( SOCKET *skt )
*
*    PURPOSE:   Initializes the WinSock stuff and sets up our socket.
*
*
*    ARGUMENTS: SOCKET * => struct to receive our socket info
*
*        RETURNS:   0 if ok
*                               1 if error
*
*\***************************************************************************/
int net_init(SOCKET *skt)
{
    int i, rc, addrlen;
    WSADATA wsdata;
    SOCKET s;
    char *adr;

    /*
    *   Initialize with the WINSOCK library
    */

    if (verbose)
        printf("calling WSAStartup(), ");

    rc = WSAStartup(MAKEWORD(1,1), &wsdata);

    if (verbose)
        printf("return = 0x%X, (%d)\n", rc, rc);

    if (rc) {
        printf("WSAStartup failed: error code = %d\n", rc);
        return 1;
    }

    /*
    *   Open a STREAM socket with SPX
    */

    if (verbose)
        printf("calling socket(addresss family = %d, socket type = %d, protocol = %d)\n", Local_Address_Family, Socket_Type, Protocol);

    s = socket(Local_Address_Family, Socket_Type, Protocol);

    if (verbose)
        printf("socket() returned 0x%X (%d)\n", s, s);

    if (s == INVALID_SOCKET) {
        printf("Socket call failed\n");
        exit(1);
    }

    /*
    *   Bind to a socket.  We want to bind to a well known
    *   socket so that who ever calls us will be able to.
    */
ISO_SET_TP_ADDR(&addr, &LocalTsel, sizeof(LocalTsel),
                        LocalNetworkAddress, LocalNetworkAddressLength);

/*
    addr.sa_family = Local_Address_Family;
    addr.addr_type = HIERARCHICAL;
    addr.taddr_len = LocalNetworkAddressLength + LocalTselLength;
    addr.tsel_len = LocalTselLength;
    memcpy(&addr.addr, LocalTsel, LocalTselLength);
    memcpy(&addr.addr[LocalTselLength],
                LocalNetworkAddress, LocalNetworkAddressLength);
*/

    if (verbose) {
        printf("calling bind(socket = %d): \n  ", s);
    }

    rc = bind(s, (const struct sockaddr *) &addr, sizeof(SOCKADDR_TP));

    if (verbose)
        printf("bind() returned 0x%X (%d)\n", rc, rc);

    if (rc == SOCKET_ERROR) {
        printf("Error binding to socket\n");
        closesocket(s);
        return 1;
    }

    if (verbose)
        printf("calling getsockname(socket = %d), ", s);

    /*
    *   Get the address we bound to and print it out
    */

    addrlen = sizeof(SOCKADDR_TP);
    rc = getsockname(s, (struct sockaddr *) &baddr, &addrlen);

    if (verbose)
        printf("return = 0x%X (%d)\n", rc, rc);

    if (rc == SOCKET_ERROR) {
        printf("Error getting socket name");
        closesocket(s);
        return 1;
    }

    /*
     *   Print out the network address
     */
    if (verbose) {
        adr = (char *)&baddr;
        printf("len(%d) addr : ", addrlen);
        for ( i = 0; i < addrlen; i++)
                printf(" %x ", *(adr + i));
    }

    *skt = s;

    return 0;
}

/****************************************************************************
*
*    FUNCTION:  do_listen( SOCKET s )
*
*    PURPOSE:   Sets the socket up for listening.
*
*    ARGUMENTS: SOCKET socket to listen on
*
*        RETURNS:   0 if ok
*                               1 if error
*
*\***************************************************************************/
int do_listen(SOCKET s)
{
    int rc;

    /*
    *   Enable this socket as a listen socket that can
    *   take <Backlog> connection indication(s) at a time.
    */

    if (verbose)
        printf("calling listen(socket = %d, backlog = %d), ", s, Backlog);

    rc = listen(s, Backlog);

    if (verbose)
        printf("return = 0x%X (%d)\n", rc, rc);

    if (rc == SOCKET_ERROR) {
        printf("listen call failed");
        closesocket(s);
        return 1;
    }

    /*
    *   Wait for a connection and get the connecting socket
    */

    return 0;
}

/****************************************************************************
*
*    FUNCTION:  wait_for_connection( SOCKET s, SOCKET *callsock )
*
*    PURPOSE:   Waits for someone to connect.
*
*    ARGUMENTS: SOCKET socket we are listening on
*                               SOCKET * => area to store client socket info after
*                                           connect
*
*        RETURNS:   0 if ok
*                               1 if error
*
*\***************************************************************************/
int wait_for_connection(SOCKET s, SOCKET *callsock)
{
    SOCKET s2;
    int addrlen = sizeof(SOCKADDR_TP);

    /*
    *   Go wait for somebody to connect
    */

    if (verbose)
        printf("calling accept(socket = %d), ", s);
    else
        printf("Waiting for call...\n");

    s2 = accept(s, (struct sockaddr *) &saddr, &addrlen);

    if (verbose)
        printf("return (socket) = 0x%X (%d)\n", s2, s2);

    if (s2 == INVALID_SOCKET) {
        printf("accept call failed");
        closesocket(s);
        return 1;
    }

    /*
    *   Print out who connected to us

    if (verbose) {
        printf("addrlen = %d\n", addrlen);
        print_netaddr(saddr.sa_netnum, "Callers address = ", "\n");
    }
    */

    *callsock = s2;

    return 0;
}

/****************************************************************************
*
*    FUNCTION:  do_recv_send( SOCKET s2 )
*
*    PURPOSE:   Waits for someone to connect.
*
*    ARGUMENTS: SOCKET socket to transmit on
*
*        RETURNS:   0 if ok
*                               1 if error
*
*\***************************************************************************/
int do_recv_send(SOCKET s2)
{
    int rcount = 0;
    int nbytes, errflag = 0;
    LPSTR recvbuf;

    if (verbose)
        printf("allocating %d bytes for receive buffer\n", Receive_Length);

    recvbuf = malloc(Receive_Length);

    if (!recvbuf) {
        printf("Error allocating %d bytes for receive buffer\n", Receive_Length);
        return 1;
    }

    /*
    *   Recv packets and send them back
    */

    while (1) {

        /*
        *   Receive data
        */

        if (verbose)
            printf("calling recv(socket = %d, receive length = %d)\n", s2, Receive_Length);

        Sleep(Sleep_Time);

        nbytes = recv(s2, recvbuf, Receive_Length, 0);

        if (nbytes == SOCKET_ERROR) {
            printf("recv call failed");
            errflag++;
            break;
        }

        if (verbose)
            printf("Received packet %d: received %d bytes\n", rcount++, nbytes);
        else
            printf("\rReceived packet %d: received %d bytes... ", rcount++, nbytes);

        /*
        *   Send the data back
        */
/*
        if (verbose)
            printf("calling send(socket = %d, send length = %d)\n", s2, nbytes);

        rc = send(s2, recvbuf, nbytes, 0);

        if (rc == SOCKET_ERROR) {
            printf("send call failed\n");
            errflag++;
            break;
        }

        printf("Sent %d bytes", rc);
*/
        if (verbose)
            printf("\n");

        if (No_Loop)
            break;
    }

    if (verbose)
        printf("freeing receive buffer\n");

    free(recvbuf);

    return errflag;
}


README.TXT

Windows Sockets 2 Sample Chat application.

This sample works as a Windows Sockets server or client.  It will listen
for connections, or it will initiate one based on a user menu action.

This sample requires Windows Sockets 2.  Either run it on Windows NT
verison 4.0 or greater, or install the redistributable Windows Sockets
2 for Windows 95.


CHATDLG.H

/*++

Copyright (c) 1995 Intel Corp

Module Name:

    chatdlg.h

Abstract:

    Header file containing function protoypes of functions in dialog.c.

--*/
#include <windows.h>
#include <ws2atm.h>
#include <Windowsx.h>

#define NUM_ATM_ADDR_TYPES      5
#define NUM_BLLI_L2PROTO        15
#define NUM_BLLI_L3PROTO        9
#define NUM_BLLI_L3IPI          3
#define NUM_BHLI_TYPES          6

#define MAX_ADDRESS_TEXT        100
#define ATM_ADDR_LENGTH         20
#define MAX_PARSE_TEXT          80
#define ATM_SNAP_ID_LENGTH      5
#define ATM_HL_INFO_LENGTH      8

static PUCHAR AtmAddrTypeName[] =
{
    "NSAP",
    "E164",
    "Any",
    "Any (Sel)",
    "ANY (Rest)"
};
static DWORD AtmAddrTypeValue[] =
{
    ATM_NSAP,
    ATM_E164,
    SAP_FIELD_ANY,
    SAP_FIELD_ANY_AESA_SEL,
    SAP_FIELD_ANY_AESA_REST
};

static PUCHAR AtmBlliL2ProtoName[] =
{
    "ISO 1745",
    "Q921",
    "X25L",
    "X25M",
    "ELAPB",
    "HDLC NRM",
    "HDLC ABM",
    "HDLC ARM",
    "LLC",
    "X75",
    "Q922",
    "ISO 7776",
    "User Specified",
    "Absent",
    "Any"
};
static DWORD AtmBlliL2ProtoValue[] =
{
    BLLI_L2_ISO_1745,
    BLLI_L2_Q921,
    BLLI_L2_X25L,
    BLLI_L2_X25M,
    BLLI_L2_ELAPB,
    BLLI_L2_HDLC_NRM,
    BLLI_L2_HDLC_ABM,
    BLLI_L2_HDLC_ARM,
    BLLI_L2_LLC,
    BLLI_L2_X75,
    BLLI_L2_Q922,
    BLLI_L2_ISO_7776,
    BLLI_L2_USER_SPECIFIED,
    SAP_FIELD_ABSENT,
    SAP_FIELD_ANY
};

static PUCHAR AtmBlliL3ProtoName[] =
{
    "X25",
    "ISO 8208",
    "X223",
    "ISO 8473",
    "T70",
    "ISO TR9577",
    "User Specified",
    "Absent",
    "Any"
};
static DWORD AtmBlliL3ProtoValue[] =
{
    BLLI_L3_X25,
    BLLI_L3_ISO_8208,
    BLLI_L3_X223,
    BLLI_L3_SIO_8473,
    BLLI_L3_T70,
    BLLI_L3_ISO_TR9577,
    BLLI_L3_USER_SPECIFIED,
    SAP_FIELD_ABSENT,
    SAP_FIELD_ANY
};

static PUCHAR AtmBlliL3IpiName[] =
{
    "SNAP",
    "IP",
    "Absent"
};
static DWORD AtmBlliL3IpiValue[] =
{
    BLLI_L3_IPI_SNAP,
    BLLI_L3_IPI_IP,
    SAP_FIELD_ABSENT
};

static PUCHAR AtmBhliTypeName[] =
{
    "ISO",
    "User Specific",
    "High Layer Profile",
    "Vendor Specific App",
    "Absent",
    "Any"
};
static DWORD AtmBhliTypeValue[] =
{
    BHLI_ISO,
    BHLI_UserSpecific,
    BHLI_HighLayerProfile,
    BHLI_VendorSpecificAppId,
    SAP_FIELD_ABSENT,
    SAP_FIELD_ANY
};

BOOL APIENTRY
InetConnDlgProc(
    IN HWND DialogHandle,
    IN UINT Message,
    IN WPARAM WordParam,
    IN LPARAM LongParam);

BOOL APIENTRY
ATMSockAddrProc(
    IN HWND DialogHandle,
    IN UINT Message,
    IN WPARAM WordParam,
    IN LPARAM LongParam);

BOOL APIENTRY
DefaultConnDlgProc(
    IN HWND DialogWindow,
    IN UINT Message,
    IN WPARAM WordParam,
    IN LPARAM LongParam);

BOOL APIENTRY
ChooseFamilyDlgProc(
    IN HWND DialogHandle,
    IN UINT Message,
    IN WPARAM WordParam,
    IN LPARAM LongParam);

BOOL APIENTRY
NameAndSubjectDlgProc(
    IN HWND DialogWindow,
    IN UINT Message,
    IN WPARAM WordParam,
    IN LPARAM LongParam);

BOOL APIENTRY
AcceptConnectionDlgProc(
    IN HWND DialogHandle,
    IN UINT Message,
    IN WPARAM WordParam,
    IN LPARAM LongParam);

BOOL APIENTRY
InetListenPortDlgProc(
    IN HWND DialogHandle,
    IN UINT Message,
    IN WPARAM WordParam,
    IN LPARAM LongParam);

BOOL ScanAtmAddress(
    HWND        hWndDlg,
    ATM_ADDRESS *pAtmAddr);

BOOL ScanAtmBlli(
    HWND hWndDlg,
    ATM_BLLI *pBLLI);

BOOL ScanAtmBhli(
    HWND hWndDlg,
    ATM_BHLI *pBHLI);


CHATSOCK.C

/*++

Copyright (c) 1995 Intel Corp

Module Name:

    chatsock.c

Abstract:

    Socket-related functions for the WinSock2 Chat sample application.
--*/

#include "nowarn.h"  /* turn off benign warnings */
#ifndef _WINSOCKAPI_
#define _WINSOCKAPI_   /* Prevent inclusion of winsock.h in windows.h */
#endif
#include <windows.h>
#include "nowarn.h"  /* some warnings may have been turned back on */
#include <stdlib.h>
#include <malloc.h>
#include <stdio.h>
#include <assert.h>
#include <ws2atm.h>
#include "ws2chat.h"
#include "chatsock.h"
#include "chatdlg.h"

//
// Static Globals
//

// points to an array of WSAPROTOCOL_INFO structs
static LPWSAPROTOCOL_INFO InstalledProtocols = NULL;

// number of WSAPROTOCOL_INFO structs in the InstalledProtocols buffer
static int NumProtocols = 0;

// static array of sockets, one for each listening socket
static LISTENDATA ListeningSockets[MAX_LISTENING_SOCKETS];

// number of meaningful entries in ListeningSockets
static int NumFound = 0;

//
// Function Prototypes -- Internal Functions
//

DWORD
IOThreadFunc(
    IN LPVOID ParamPtr);

BOOL
HandleSocketEvent(
    IN OUT PCONNDATA ConnData);

BOOL
HandleOutputEvent(
    IN OUT PCONNDATA);

BOOL
HandleOtherEvent(
    IN     DWORD     WaitStatus,
    IN OUT PCONNDATA ConnData);

int
HandleEvents(
    IN PCONNDATA          ConnData,
    IN LPWSANETWORKEVENTS NetworkEvents);

BOOL
FillLocalAddress(
    IN LPVOID          SockAddr);

int
DoRecv(
    IN PCONNDATA ConnData);

int
DoOverlappedCallbackSend(
    IN POUTPUT_REQUEST OutReq,
    IN PCONNDATA       ConnData);

int
DoOverlappedEventSend(
    IN POUTPUT_REQUEST OutReq,
    IN PCONNDATA       ConnData);

int
DoSend(
    IN POUTPUT_REQUEST OutReq,
    IN PCONNDATA       ConnData);

void CALLBACK
SendCompFunc(
    IN DWORD           Error,
    IN DWORD           BytesTransferred,
    IN LPWSAOVERLAPPED OverlappedPtr,
    IN DWORD           Flags);

int CALLBACK
AcceptCondFunc(
    IN LPWSABUF    CallerId,
    IN LPWSABUF    CallerData,
    IN LPQOS       CallerSQos,
    IN LPQOS       CallerGQos,
    IN LPWSABUF    CalleeId,
    OUT LPWSABUF   CalleeData,
    OUT GROUP FAR  *Group,
    IN DWORD   CallbackData);

LPWSAPROTOCOL_INFO
GetProtoFromSocket(
    IN SOCKET Socket);

BOOL
GetMaxMsgSize(
    IN OUT PCONNDATA ConnData);

//
// Function Definitions
//

BOOL
InitWS2(void)
/*++

Routine Description:

    Calls WSAStartup, makes sure we have a good version of WinSock2

Arguments:

    None.

Return Value:

    TRUE - WinSock 2 DLL successfully started up

    FALSE - Error starting up WinSock 2 DLL.

--*/

{
    int           Error;              // catches return value of WSAStartup
    WORD          VersionRequested;   // passed to WSAStartup
    WSADATA       WsaData;            // receives data from WSAStartup
    BOOL          ReturnValue = TRUE; // return value

    // Start WinSock 2.  If it fails, we don't need to call
    // WSACleanup().
    VersionRequested = MAKEWORD(VERSION_MAJOR, VERSION_MINOR);
    Error = WSAStartup(VersionRequested, &WsaData);
    if (Error) {
        MessageBox(GlobalFrameWindow,
                   "Could not find high enough version of WinSock",
                   "Error", MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
        ReturnValue = FALSE;
    } else {

        // Now confirm that the WinSock 2 DLL supports the exact version
        // we want. If not, make sure to call WSACleanup().
        if (LOBYTE(WsaData.wVersion) != VERSION_MAJOR ||
            HIBYTE(WsaData.wVersion) != VERSION_MINOR) {
            MessageBox(GlobalFrameWindow,
                       "Could not find the correct version of WinSock",
                       "Error",  MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
            WSACleanup();
            ReturnValue = FALSE;
        }
    }
    return(ReturnValue);

} // InitWS2()

BOOL
FindProtocols(void)
/*++

Routine Description:

    Finds out about all transport protocols installed on the local
    machine and saves the information into global variables.

Implementation:

    This function uses WSAEnumProtocols to find out about all
    installed protocols on the local machine.  It stores this
    information in two variables which are global to this file;
    InstalledProtocols is a pointer to a buffer of WSAPROTOCOL_INFO
    structs, while NumProtocols is the number of protocols in that
    buffer.  This function is the only function in the file allowed to
    touch these variables.

Arguments:

    None.

Return Value:

    TRUE - Successfully initialized the protocol buffer.

    FALSE - Some kind of problem arose.  The user is informed of the
    error.

--*/
{

    DWORD BufferSize = 0;       // size of InstalledProtocols buffer
    char  MsgText[MSG_LEN];     // holds message strings

    // Call WSAEnumProtocols to figure out how big of a buffer we need.
    NumProtocols = WSAEnumProtocols(NULL,
                                    NULL,
                                    &BufferSize);

    if ((NumProtocols != SOCKET_ERROR) && (WSAGetLastError() != WSAENOBUFS)) {
        // We're in trouble!!
        MessageBox(GlobalFrameWindow, "WSAEnumProtocols is broken.", "Error",
                   MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
        goto Fail;
    }

    // Allocate a buffer, call WSAEnumProtocols to get an array of
    // WSAPROTOCOL_INFO structs.
    InstalledProtocols = (LPWSAPROTOCOL_INFO)malloc(BufferSize);
    if (InstalledProtocols == NULL) {
        MessageBox(GlobalFrameWindow, "malloc failed.", "Error",
                   MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
        goto Fail;
    }
    NumProtocols = WSAEnumProtocols(NULL,
                                    (LPVOID)InstalledProtocols,
                                    &BufferSize);
    if (NumProtocols == SOCKET_ERROR) {
        // uh-oh
        wsprintf(MsgText, "WSAEnumProtocols failed.  Error Code: %d",
                 WSAGetLastError());
        MessageBox(GlobalFrameWindow, MsgText, "Error",
                   MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
        goto Fail;
    }
    return(TRUE);

 Fail:

    WSACleanup();
    return(FALSE);

} // FindProtocols()

BOOL
FillLocalAddress(
    IN struct sockaddr *pSockAddr)
/*++

Routine Description:

    Fills in the struct sockaddr with a local address appropriate for
    the address family, for use in a call to bind.

Arguments:

    SockAddr -- A pointer to a struct sockaddr which will be filled in
    appropriately, as determined by the particular address family.
    The address family field (sa_family) must already be filled into
    the structure before being passed into this function.

Return Value:

    TRUE -- FillLocalAddress recognized the address family and
    successfully filled in the struct sockaddr.  This structure is
    ready to be passed to a bind() call.

    FALSE -- FillLocalAddress didn't recognize the address family and
    didn't touch the struct sockaddr.

--*/
{

    struct sockaddr_in *pInetSockAddr; // used to cast SockAddr to an INET address
    struct sockaddr_atm *pATMSockAddr; // used to cast SockAddr to an ATM address
    BOOL ReturnValue = TRUE;          // holds the return value

    switch (pSockAddr->sa_family) {

        case AF_INET:

            // Cast the pointer so we can now access it's fields as a
            // struct sockaddr_in, the address structure for internet
            // addresses.
            pInetSockAddr = (struct sockaddr_in *)pSockAddr;
            if (!DialogBoxParam(GlobalInstance,
                                "InetListenPortDlg",
                                GlobalFrameWindow,
                                InetListenPortDlgProc,
                                (LPARAM)pInetSockAddr)) {
                ReturnValue = FALSE;
            }
            break;

        case AF_ATM:
            pATMSockAddr = (struct sockaddr_atm *)pSockAddr;

            pATMSockAddr->satm_number.AddressType  = SAP_FIELD_ANY;
            pATMSockAddr->satm_blli.Layer2Protocol = SAP_FIELD_ANY;
            pATMSockAddr->satm_blli.Layer3Protocol = SAP_FIELD_ANY;
            pATMSockAddr->satm_bhli.HighLayerInfoType = BHLI_UserSpecific;

            ReturnValue = DialogBoxParam(GlobalInstance,
                                         "ATMSOCKADDRDLG",
                                         GlobalFrameWindow,
                                         (DLGPROC)ATMSockAddrProc,
                                         (LPARAM)pATMSockAddr);
            break;

        default:

            ReturnValue = FALSE;
            break;
    }
    return(ReturnValue);

} // FillLocalAddress()

BOOL
ListenAll(void)

/*++

Routine Description:

    For each installed, connection-oriented protocol, this function
    creates a socket, binds to a local address, and listens on the
    created socket.  The socket is set up for Windows message
    notification for any connection attempts.

Arguments:

    None.

Return Value:

    TRUE - Successfully listened on all installed protocols.

    FALSE - Error listening on all installed protocols.  It is not an
    error if there are more connection-oriented protocols than
    MAX_LISTENING_SOCKETS installed; in this case we just ignore any
    extra protocols.

--*/

{
    struct sockaddr *SockAddr;        // holds socket addresses
    int             SockAddrLen;      // length, in bytes, of SockAddrLen
    LPWSAPROTOCOL_INFO COProtocolInfo;   // current protocol info to examine
    int             i;                // counting variable
    char            MsgText[MSG_LEN]; // holds message strings
    int             Error;            // holds return values
    int             Index;            // indexes into text messages

    // Find all protocols that support connection-oriented data
    // transfer; create a socket, bind to a local address and listen
    // for connections on that socket for each such protocol found.
    // Also fill in an entry in the ListeningSockets array.
    for (i = 0; i < NumProtocols; i++) {

        COProtocolInfo = &InstalledProtocols[i];
        assert(COProtocolInfo != NULL);
        if (UseProtocol(COProtocolInfo)) {

            // We've found a suitable protocol.  Create a socket, fill
            // in the next entry in ListeningSockets
            ListeningSockets[NumFound].Socket = WSASocket(FROM_PROTOCOL_INFO,
                                                          FROM_PROTOCOL_INFO,
                                                          FROM_PROTOCOL_INFO,
                                                          COProtocolInfo,
                                                          0,
                                                          WSA_FLAG_OVERLAPPED);
            if (ListeningSockets[NumFound].Socket == INVALID_SOCKET) {
                UCHAR textBuf[MAX_ERROR_TEXT];
                sprintf(textBuf, "Could not open a socket. [%x]", WSAGetLastError());
                MessageBox(GlobalFrameWindow, textBuf,
                           "Non-fatal error.",
                           MB_OK | MB_SETFOREGROUND);
                continue;
            }
            ListeningSockets[NumFound].ProtocolInfo = COProtocolInfo;

            // Allocate a block of memory for the socket address and
            // zero it out.
            SockAddrLen = COProtocolInfo->iMaxSockAddr;
            SockAddr = (struct sockaddr *)malloc(SockAddrLen);

            if (!SockAddr) {
                ChatSysError("malloc()",
                             "ListenAll()",
                             TRUE);
            }
            memset((char *)SockAddr, 0, SockAddrLen);

            // Get a local address to bind the socket to
            SockAddr->sa_family = (u_short)COProtocolInfo->iAddressFamily;
            FillLocalAddress(SockAddr);

            // Bind the socket to SockAddr.
            Error = bind(ListeningSockets[NumFound].Socket,
                         SockAddr,
                         SockAddrLen);
            if (Error == SOCKET_ERROR) {
                // bind() failed
                UCHAR textBuf[MAX_ERROR_TEXT];
                sprintf(textBuf, "Could not bind the socket. [%x]", WSAGetLastError());
                MessageBox(GlobalFrameWindow, textBuf,
                           "Non-fatal error", MB_OK | MB_SETFOREGROUND);
                free(SockAddr);
                continue;
            }

            free(SockAddr);

            // Set up the socket for windows message event notification.
            // Note that this call automatically puts the socket into
            // non-blocking mode, as if we had called WSAIoctl with
            // the FIONBIO flag.
            Error = WSAAsyncSelect(ListeningSockets[NumFound].Socket,
                                   GlobalFrameWindow,
                                   USMSG_ACCEPT,
                                   FD_ACCEPT);
            if (Error == SOCKET_ERROR) {
                MessageBox(GlobalFrameWindow, "Error: WSAAsyncSelect()",
                           "Non-fatal error", MB_OK | MB_SETFOREGROUND);
                continue;
            }

            // Listen for incoming connection requests on the socket.
            Error = listen(ListeningSockets[NumFound].Socket,
                           SOMAXCONN);
            if (Error == SOCKET_ERROR) {
                MessageBox(GlobalFrameWindow, "Error: listen()",
                           "Non-fatal error",
                           MB_OK | MB_SETFOREGROUND);
                continue;
            }

            // Looks good -- increase the count, check for overflow of
            // the max amount, re-iterate if we're ok.
            if (++NumFound == MAX_LISTENING_SOCKETS) {
                wsprintf(MsgText,
                         "More than %d useable protocols. Ignoring extras.",
                         MAX_LISTENING_SOCKETS);
                MessageBox(GlobalFrameWindow, MsgText, "Alert",
                           MB_OK | MB_SETFOREGROUND);
                break;
            }

        } // if (UseProtocol(COProtocolInfo))
    } // for(; ; ;)

    if (NumFound == 0) {

        Index = wsprintf(MsgText,
                         "Couldn't find a suitable protocol ");
        Index += wsprintf(MsgText + Index,
                          "and/or no listening sockets could be opened.\r\n");
        wsprintf(MsgText + Index, "Shall we continue anyway?");

        if (MessageBox(GlobalFrameWindow, MsgText, "No protocols.",
                       MB_ICONQUESTION | MB_YESNO | MB_SETFOREGROUND)
            == IDNO) {

            WSACleanup();
            return(FALSE);
        }
    }

    return(TRUE);

} // ListenAll()

BOOL
UseProtocol(
    IN LPWSAPROTOCOL_INFO Proto)
/*++

Routine Description:

    Returns true if Proto is suitable for use by Chat.

Arguments:

    Proto -- Points to a protocol information struct.

Return Value:

    TRUE -- Chat likes it.

    FALSE -- Get this chintzy protocol out of here!

--*/
{

    if (!(Proto->dwServiceFlags1 & XP1_CONNECTIONLESS) &&
        //(Proto->dwServiceFlags1 & XP1_GUARANTEED_DELIVERY) &&
        (Proto->dwServiceFlags1 & XP1_GUARANTEED_ORDER)) {

        return(TRUE);

    } else {

        return(FALSE);
    }
} // UseProtocol

void
CleanUpSockets(void)
/*++

Routine Description:

    This function closes all listening sockets.

Arguments:

    None.

Return Value:

    None.

--*/
{

    int i; // counting variable

    for (i = 0; i < NumFound; i++) {
        closesocket(ListeningSockets[i].Socket);
    }
} // CleanUpSockets()

int CALLBACK
AcceptCondFunc(
    IN LPWSABUF   CallerId,
    IN LPWSABUF   CallerData,
    IN LPQOS      CallerSQos,
    IN LPQOS      CallerGQos,
    IN LPWSABUF   CalleeId,
    OUT LPWSABUF  CalleeData,
    OUT GROUP FAR *Group,
    IN DWORD  CallbackData)
/*++

Routine Description:

    Condition function called when an incoming connection request is
    handled.

Implementation:

    This function allows the user to accept or reject an incoming
    connection request after examining the caller's name and the
    subject of the call. If the call is accepted, and
    connection-time data transfer is supported by the particular
    protocol on which the connection is made, a dialog box comes up
    which prompts the user for his/her name.

Arguments:

    CallerId -- Supplies the address of the caller.

    CallerData -- Supplies the caller's user data.  Chat uses this
    parameter to send the caller's name and the subject of the call.

    CallerSQos -- Supplies the forward and backward QOS.

    CallerGQos -- Supplies the forward and backward flow specs for the
    socket group the caller is to create.  Not used by chat.

    CalleeId -- Supplies the local address.

    CalleeData -- Returns user data back to the caller (Chat uses this
    parameter to return the callee's name)

    Group -- Returns the appropriate group action to take on the
    connecting socket.  Not used by chat.  Always returns NULL.

    CallbackData -- Supplies a pointer the CONNDATA structure
    associated with this connection.

Return Value:

    CF_ACCEPT - Accept the connection request from the caller.

    CF_REJECT - Reject the connection request from the caller.

--*/

{
    char          MsgText[MSG_LEN];         // build message string here
    char          TitleText[TITLE_LEN + 1]; // build title string here
    PCONNDATA     ConnData;                 // connection-specific data
    int           Index;                    // index into MsgText
    int           ReturnValue = CF_ACCEPT;  // return value

    ConnData = (PCONNDATA)CallbackData;

    // CallerId contains the socket address of the connecting entity.
    // Copy this into the connection-specific data.
    ConnData->RemoteSockAddr.len = CallerId->len;
    ConnData->RemoteSockAddr.buf = malloc(ConnData->RemoteSockAddr.len);
    if (ConnData->RemoteSockAddr.buf == NULL) {
        ChatSysError("malloc()",
                     "AcceptCondFunc()",
                     TRUE);
    }
    memcpy((char *)ConnData->RemoteSockAddr.buf, (char *)CallerId->buf,
           CallerId->len);

    // Translate the RemoteSockAddr into a human readable form, and
    // store it in ConnData->PeerAddress
    GetAddressString(ConnData->PeerAddress,
                     ConnData->RemoteSockAddr.buf,
                     ConnData->RemoteSockAddr.len,
                     ConnData->ProtocolInfo);

    Index = wsprintf(MsgText, "Someone is attempting a chat connection.\r\n");

    if (CallerData != NULL) {

        // The connection request has come with some caller data.  Use
        // it to inform the user of who is trying to connect
        ExtractTwoStrings(CallerData->buf,
                          ConnData->PeerName,
                          NAME_LEN + 1,
                          ConnData->Subject,
                          SUB_LEN + 1);

        // Build the strings for the message box and the title of the
        // connection window
        Index += wsprintf(MsgText + Index,
                 "From: %s\r\nSubject: %s\r\n", ConnData->PeerName,
                          ConnData->Subject);
        wsprintf(TitleText, "Connected to: %s @ %s", ConnData->PeerName,
                 ConnData->PeerAddress);

    } else {

        // There is no caller data...build a string for the title.
        wsprintf(TitleText, "Connected to: %s", ConnData->PeerAddress);
    }

    // Continue building MsgText.
    Index += wsprintf(MsgText + Index, "Address: %s\r\n",
                      ConnData->PeerAddress);
    Index += wsprintf(MsgText + Index, "Would you like to accept it?");

    // Prompt the user to accept or reject the connection.
    //
    // ****NOTE****
    // This is NOT the right way to do this.  The application should
    // not hold up this thread by putting up this message box, or the
    // dialog box below.  As mentioned in the API spec, this function
    // should  return "as soon as possible", and clearly this is not
    // what's being done here.  Please look to future versions of chat
    // for a fix.  Thanks.
    if (MessageBox(ConnData->ConnectionWindow, MsgText,
                   "Connection Request",
                   MB_ICONQUESTION | MB_YESNO | MB_SETFOREGROUND) == IDYES) {

        // The user has accepted the connection request.
        SetWindowText(ConnData->ConnectionWindow, TitleText);
        if (CalleeData != NULL) {

            // We can try to pass user data back. Call up a dialog box
            // to get a name string and put it into CalleeData.
            if (!DialogBoxParam(GlobalInstance,
                                "AcceptConnectionDlg",
                                ConnData->ConnectionWindow,
                                AcceptConnectionDlgProc,
                                (LPARAM)CalleeData)) {

                CalleeData->len = 0;
            }
        }
        ReturnValue = CF_ACCEPT;

    } // if (MessageBox(...))
    else {

        // The user has rejected the connection request.
        ReturnValue = CF_REJECT;
    }
    return(ReturnValue);

} // AcceptCondFunc()

BOOL
GetAddressString(
    OUT char            *String,
    IN  LPVOID          SockAddr,
    IN  int             SockAddrLen,
    IN  LPWSAPROTOCOL_INFO ProtocolInfo)
/*++

Routine Description:

    This function translates the SockAddr into a human-readable
    string, if possible.  If chat doesn't recognize the protocol, then
    the string "(unknown)" is returned in String.

Arguments:

    String -- Returns the string representing the SockAddr in a
    human-readable form.

    SockAddr -- An address.

    SockAddrLen -- The length, in bytes, of SockAddr.

    ProtocolInfo -- Pointer to the protocol information structure.

Return Value:

    TRUE -- Chat recognized the protocol family and successfully
    translated the address.

    FALSE -- Chat did not recognize the protocol family and returned
    the string "(unknown)".

--*/
{

    char               *TempString;   // string returned by inet_ntoa
    struct sockaddr_in *SockAddrInet; // casts the address to a sockaddr_in
    BOOL               ReturnValue;   // holds the return value

    ReturnValue = TRUE;

    switch (ProtocolInfo->iAddressFamily) {

        case AF_INET:

            // It's an Internet-style address.
            SockAddrInet = (struct sockaddr_in *)SockAddr;
            TempString = inet_ntoa(SockAddrInet->sin_addr);
            if (TempString == NULL) {
                MessageBox(NULL, "inet_ntoa() failed.", "Error.",
                           MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
            } else {
                strcpy(String, TempString);
            }
            ReturnValue = TRUE;
            break;

        default:

            strcpy(String, "(unknown)");
            ReturnValue = FALSE;
            break;
    }
    return(ReturnValue);

} // GetAddressString()

int
HandleEvents(
    IN PCONNDATA          ConnData,
    IN LPWSANETWORKEVENTS NetworkEvents)

/*++

Routine Description:

    Handles network events that may occur on a connected socket.
    The events handled by this function are FD_CLOSE, FD_READ, and
    FD_WRITE.

Arguments:

    ConnData - Supplies a pointer to data for the connection on which
    the event happened.

    NetworkEvents - Supplies a WSANETWORKEVENT structure, which is a
    record of the network events that have occurred as well as any
    accompanying error-codes.

Return Value:

    CHAT_OK -- The network event was successfully handled.

    CHAT_ERROR -- Some kind of error occurred while handling the
    event, and the connection should be closed.

    CHAT_CLOSED -- The connection has been gracefully closed.

--*/

{
    int Result;                // holds the result of DoRecv
    int ReturnValue = CHAT_OK; // return value

    // The following three if statements all execute unless one gets
    // an error or closed socket, in which case we return immediately.
    if (NetworkEvents->lNetworkEvents & FD_READ) {

        // An FD_READ event has occurred on the connected socket.
        if (NetworkEvents->iErrorCode[FD_READ_BIT] == WSAENETDOWN) {

            // There is an error.
            ReturnValue = CHAT_ERROR;
            goto Done;

        } else {

            // Read data off the socket...
            Result = DoRecv(ConnData);
            if ((Result == CHAT_ERROR) || (Result == CHAT_CLOSED)) {
                ReturnValue = Result;
                goto Done;
            }
        }
    }

    if (NetworkEvents->lNetworkEvents & FD_WRITE) {

        // An FD_WRITE event has occurred on the connected socket.
        if (NetworkEvents->iErrorCode[FD_WRITE_BIT] == WSAENETDOWN) {

            // There is an error.
            ReturnValue = CHAT_ERROR;
            goto Done;

        } else {

            // Allow chat to send on this socket, and signal the
            // OuputEventObject in case there is pending output that is
            // not completed due to WSAEWOULDBLOCK.
            ConnData->WriteOk = TRUE;
            SetEvent(ConnData->OutputEventObject);
        }
    }

    if (NetworkEvents->lNetworkEvents & FD_CLOSE) {

        if (NetworkEvents->iErrorCode[FD_CLOSE_BIT] == 0) {

            // A graceful shutdown has occurred...
            ReturnValue = CHAT_CLOSED;
            goto Done;

        } else {

            // This is some other type of abortive close or failure...
            ReturnValue = CHAT_ABORTED;
            goto Done;
        }

    }

 Done:
    return(ReturnValue);

} // HandleEvents()

DWORD
IOThreadFunc(
    IN LPVOID ParamPtr)

/*++

Routine Description:

    This routine is invoked as a separate thread to handle all input
    and output for a connection.

Implementation:

    This thread sits in a loop, waiting for one of several things to
    occur.  These can be:

        1. The user interface thread has some input ready to be sent,
        and has signaled the ouput event object.

        2. WinSock 2 has indicated there is a network event associated
        with the socket.

        3. Callback notification.  This can be via an event or via a
        queued callback function, depending on whether or not we've
        compiled with the CALLBACK_NOTIFICATION flag.

    The return value from the wait indicates what has
    happened, and a switch statement handles all the possible cases.
    When there is an error or the connection is being closed, the loop
    is broken and the thread will exit and the connection window will
    be closed (and sent a WM_DESTROY message).

Arguments:

    ParamPtr - Supplies a pointer to a ConnData structure that holds
    data specific to this connection.

Return Value:

    0 - Always returns 0.  The return value is not needed.

--*/
{
    char             MsgText[MSG_LEN]; // holds message strings
    DWORD            WaitStatus;       // holds return value of the wait
    PCONNDATA        ConnData;         // connection-specific data
    BOOL             KeepGoing = TRUE; // keep processing output requests?
    BOOL             Forever = TRUE;   // constant to avoid warning

    ConnData =  (PCONNDATA)ParamPtr;

    // Initialize the EventArray.  The only two events in it, for now,
    // are the Socket and Ouput events; if this is the event
    // notification version of chat, then there will be an additional
    // event for each overlapped send waiting for a completion
    // notification.
    ConnData->NumEvents = 2;
    ConnData->EventArray[0] = ConnData->SocketEventObject;
    ConnData->EventArray[1] = ConnData->OutputEventObject;

    // Initialize the array of output requests, which is indexed in
    // parallel to the above event array.  When an event is signaled,
    // we can figure out which output request and overlapped
    // structures to free by indexing into this array.
    // These first two entries should never be referenced, because
    // their parallel entries in EventArray (see above) are for the

// permanent Socket and Output event objects.
    ConnData->OutReqArray[0] = ConnData->OutReqArray[1] = NULL;

    while (Forever) {

        // Wait for an event (or a queued callback function) to wake
        // us up.  This is an alertable wait state (fAlertable == TRUE).
        WaitStatus = WSAWaitForMultipleEvents(ConnData->NumEvents,
                                              ConnData->EventArray,
                                              FALSE,        // fWaitAll
                                              WSA_INFINITE, // dwTimeout
                                              TRUE);        // fAlertable

        // Determine why we woke up and act accordingly.  Note that
        // breaking out of the switch causes us to break out of the
        // while loop as well.  When we don't want to break out of the
        // loop, case statements end with the continue statement.
        switch (WaitStatus) {

        case WSA_WAIT_FAILED:

            // A fatal error.  Pop up a message box and break out of
            // the while loop to end the thread.

            wsprintf(MsgText,
                     "WSAWaitForMultipleEvents() failed.  Error code: %d",
                     WSAGetLastError());
            MessageBox(ConnData->ConnectionWindow, MsgText, "Fatal Error",
                       MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
            break;

        case WAIT_IO_COMPLETION:

            // An I/O completion routine has been executed.  Cleanup
            // has already occurred in SendCompFunc, so there is
            // nothing left to do.  Just reiterate through the loop.

            continue;

        case WSA_WAIT_EVENT_0:

            // The SocketEventObject has been signaled.  Handle it in
            // a separate function.  Break out of the thread if
            // HandleSocketEvent returns FALSE, indicating error.
            if (HandleSocketEvent(ConnData)) {
                continue;
            } else {
                break;
            }

        // Please note: WSA_WAIT_EVENT_[1,2,3] are defined in
        // chatsock.h, not winsock2.h like WSA_WAIT_EVENT_0

        case WSA_WAIT_EVENT_1:

            // The OuputEventObject has been signaled.  Handle it in a
            // separate function, and break out of the thread if it
            // returns FALSE
            if (HandleOutputEvent(ConnData)) {
                continue;
            } else {
                break;
            }

        default:

            // Some other event has been signaled.  Handle it in a
            // separate function, and break out of the thread if it
            // returns FALSE.
            if (HandleOtherEvent(WaitStatus, ConnData)) {
                continue;
            } else {
                break;
            }

        } // switch (WaitStatus)

        // Break out of the while loop.
        break;

    } // while (1)

    // Thread is ending because the connection was closed or an error
    // occurred
    PostMessage(ConnData->ConnectionWindow,
                WM_CLOSE,
                0,
                0);
    return(0);

} // IOThreadFunc()

BOOL
HandleSocketEvent(
    IN OUT PCONNDATA ConnData)
/*++

Routine Description:

    Handles the case in IOThreadFunc where the thread is woken up by a
    signal to the socket event object.

Arguments:

    ConnData - Supplies a pointer to data for the connection on which
    the event happened.

Return Value:

    TRUE -- Chat successfully handled the event and the thread should
    continue on.

    FALSE -- An error occurred and Chat should kill the thread and the
    connection.

--*/
{
    int              Result;           // holds return values
    WSANETWORKEVENTS NetworkEvents;    // tells us what events happened
    char             MsgText[MSG_LEN]; // holds text strings
    BOOL             ReturnValue;      // holds the return value

    // Find out what happened and act accordingly.
    Result = WSAEnumNetworkEvents(ConnData->Socket,
                                  ConnData->SocketEventObject,
                                  &NetworkEvents);
    if (Result == SOCKET_ERROR) {

        // Handle the fatal error.
        wsprintf(MsgText,
                 "WSAEnumNetworkEvents failed.  Error code: %d",
                 Result);
        MessageBox(GlobalFrameWindow, MsgText, "Fatal Error",
                   MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
        ReturnValue = FALSE;

    } else {

        // Handle all of the network events on the given socket
        Result = HandleEvents(ConnData, &NetworkEvents);

        if (Result == CHAT_CLOSED) {

            // HandleEvents() has determined that the remote party
            // has terminated the connection.  Inform the user, and
            // return.
            if (ConnData->PeerName[0] != 0) {
                wsprintf(MsgText,
                         "%s @ %s has terminated the connection.",
                         ConnData->PeerName, ConnData->PeerAddress);
            } else {
                wsprintf(MsgText,
                         "The party at %s has terminated the connection.",
                         ConnData->PeerAddress);
            }
            MessageBox(ConnData->ConnectionWindow, MsgText, "Sorry!",
                       MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
            ReturnValue = FALSE;

        } else if (Result == CHAT_ABORTED) {

            // HandleEvents has determined that the connection has
            // been aborted due to an undetermined error.
            MessageBox(ConnData->ConnectionWindow,
                       "The connection has been broken.",
                       "Connection aborted.",
                       MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
            ReturnValue = FALSE;

        } else if (Result == CHAT_ERROR) {

            // HandleEvents() has returned an error.  Inform the
            // user and break to exit the thread and kill the window.
            MessageBox(ConnData->ConnectionWindow,
                       "An unidentified network or system error occurred.",
                       "Error.", MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
            ReturnValue = FALSE;

        } else if (Result == CHAT_OK) {

            // Inform the caller that everything went fine.
            ReturnValue = TRUE;

        } else {

            // This case should only occur if there is a
            // programming error.  Break out of the while loop to
            // kill the thread, the window, and therefore the
            // connection.
            MessageBox(ConnData->ConnectionWindow,
                       "HandleEvents() returned an unexpected value.",
                       "Error.", MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
            ReturnValue = FALSE;
        }
    }

    return(ReturnValue);

} // HandleSocketEvent()

BOOL
HandleOutputEvent(
    IN OUT PCONNDATA ConnData)
/*++

Routine Description:

    Handles the case in IOThreadFunc where the thread is woken up by a
    signal to the output event object.

Arguments:

    ConnData - Supplies a pointer to data for the connection on which
    the event happened.

Return Value:

    TRUE -- Chat successfully handled the event and the thread should
    continue on.

    FALSE -- An error occurred and Chat should kill the thread and the
    connection.

--*/
{
    BOOL            ReturnValue; // holds the return value
    BOOL            KeepGoing;   // keep pulling requests off the queue?
    POUTPUT_REQUEST OutReq;      // pointer to the output request
    int             Result;      // holds results of functions

    ReturnValue = TRUE;

    // First we check to see if a previous send attempt failed with
    // WSAEWOULDBLOCK; if so, we don't have to bother trying to send
    // data until we get an FD_WRITE network event, so just return
    // with TRUE to indicate that the thread should wait again.
    if (ConnData->WriteOk) {

        // This loop pulls output requests off the queue and hands
        // them to DoSend.
        KeepGoing = TRUE;
        while (KeepGoing) {

            OutReq = (POUTPUT_REQUEST)QRemove(ConnData->OutputQueue);
            if (OutReq == NULL) {

                // Nothing is left on the queue.
                KeepGoing = FALSE;
                ReturnValue = TRUE;

            } else {

                // Do the output
                Result = DoSend(OutReq, ConnData);
                if (Result == CHAT_WOULD_BLOCK) {

                    // The send would have blocked; we need to
                    // requeue the output request and wait for an
                    // FD_WRITE network event.
                    KeepGoing = FALSE;
                    QInsertAtHead(ConnData->OutputQueue, (LPVOID)OutReq);
                    ReturnValue = TRUE;

                } else if (Result == CHAT_ERROR) {

                    // An error occurred in DoSend.  Set KeepGoing to
                    // FALSE in order to get us out of the loop.
                    KeepGoing = FALSE;
                    ReturnValue = FALSE;
                    MessageBox(ConnData->ConnectionWindow,
                               "Error sending data.",
                               "Error.",
                               MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);

                } else {

                    // DoSend returned CHAT_OK.  Keep looping.
                    continue;
                }
            }
        } // while (KeepGoing)
    } // if (ConnData->WriteOk)

    return(ReturnValue);

} // HandleOutputEvent()

BOOL
HandleOtherEvent(
    IN     DWORD     WaitStatus,
    IN OUT PCONNDATA ConnData)
/*++

Routine Description:

    Handles the case in IOThreadFunc where the thread is woken up by
    an event that is either an overlapped I/O event or was not
    specified in the event array (which is an error!).

Arguments:

    WaitStatus -- Supplies the value returned by
    WSAWaitForMultipleEvents.

    ConnData - Supplies a pointer to data for the connection on which
    the event happened.

Return Value:

    TRUE -- Chat successfully handled the event and the thread should
    continue on.

    FALSE -- An error occurred and Chat should kill the thread and the
    connection.

--*/
{
    POUTPUT_REQUEST OutReq;           // points to an output request
    BOOL            ReturnValue;      // holds the return value
    DWORD           Count;            // counting variable
    char            MsgText[MSG_LEN]; // holds message strings

    // First do a sanity check to make sure the index returned is
    // within the bounds of what WE think is the event array.
    if ((WaitStatus >= WSA_WAIT_EVENT_0) &&
        (WaitStatus <= (WSA_WAIT_EVENT_0  + ConnData->NumEvents - 1))) {

        // Free the data buffer, the overlapped structure, the output
        // request itself, and the event
        OutReq = ConnData->OutReqArray[WaitStatus - WSA_WAIT_EVENT_0];
        free(OutReq->Buffer.buf);
        free(OutReq->Overlapped);
        free(OutReq);

        CloseHandle(ConnData->EventArray[WaitStatus - WSA_WAIT_EVENT_0]);

        // Update all our event and output request arrays to
        // reflect that the overlapped send has completed.
        ConnData->NumEvents--;
        for (Count = (WaitStatus - WSA_WAIT_EVENT_0);
             Count < ConnData->NumEvents;
             Count++) {
            ConnData->EventArray[Count] = ConnData->EventArray[Count + 1];
            ConnData->OutReqArray[Count] = ConnData->OutReqArray[Count + 1];
        }
        ReturnValue = TRUE;

    } else {

        // WSAWaitForMultipleEvents returned an unexpected
        // value...
        wsprintf(MsgText, "WSAWaitForMultipleEvents() returned %d.",
                 WaitStatus);
        MessageBox(GlobalFrameWindow, MsgText, "Error.",
                   MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
        ReturnValue = FALSE;
    }

    return(ReturnValue);

} // HandleOtherEvent()

void
HandleAcceptMessage(
    IN HWND   ConnectionWindow,
    IN SOCKET Socket,
    IN LPARAM LParam)
/*++

Routine Description:

    Handles the reception of a USMSG_ACCEPT message, which indicates a
    connection attempt is incoming.

Implementation:

    This function lets the user decide whether to accept the
    connection; if he or she does, the function  initializes
    connection-specific data, calls WSAEventSelect to register
    interest in certain network events, and starts a network event
    handling thread.

Arguments:

    ConnectionWindow -- Handle to the connection window associated
    with this connection request.

    Socket -- Contains the handle to the listening socket to which the
    connection request has been made.

    LParam -- The LParam that was delivered with the USMSG_ACCEPT
    message; contains the error code.

Return Value:

    None.

--*/
{

    int       Error;            // gets error code if necessary
    char      MsgText[MSG_LEN]; // holds message strings
    DWORD     ThreadId;         // needed for CreateThread
    PCONNDATA ConnData;         // connection-specific data

    ConnData = GetConnData(ConnectionWindow);

    Error = WSAGETSELECTERROR(LParam);

    // Check to see if there was an error on the connection attempt.
    if (Error) {

        // Some kind of error occurred.
        if (Error == WSAENETDOWN) {
            MessageBox(ConnectionWindow,
                       "The network is down!", "Uh-oh",
                       MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
        } else {
            MessageBox(ConnectionWindow,
                       "Unknown error on FD_ACCEPT", "Error",
                       MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
        }
        goto Fail;
    }

    // Get a pointer to the associated protocol information structure
    // for the socket we are listening on (Socket). This will be the
    // same as the protocol info for the new socket.  Note that we
    // could have allocated a new buffer and called getsockopt with
    // the SO_PROTOCOL_INFO option.  But since we've already allocated
    // the memory, this way is more efficient.
    ConnData->ProtocolInfo = GetProtoFromSocket(Socket);
    assert(ConnData->ProtocolInfo != NULL);

    // Accept the connection (this calls AcceptCondFunc, of course,
    // before actually accepting the connection).
    {
        struct sockaddr address;
        int address_len;

        address_len = sizeof (address);
        ConnData->Socket = WSAAccept(Socket,
                                     &address,
                                     &address_len,
                                     NULL,
                                     (DWORD)NULL);
//         ConnData->Socket = WSAAccept(Socket,
//                                      NULL,
//                                      NULL,
//                                      AcceptCondFunc,
//                                      (DWORD)ConnData);
    }

    if (ConnData->Socket == INVALID_SOCKET) {

        // WSAAccept failed -- inform the user and that's all.
        Error = WSAGetLastError();
        if (Error != WSAECONNREFUSED) {

            // An unexpected error code.
            wsprintf(MsgText, "WSAAccept failed.  Error code: %d",
                     Error);
            MessageBox(ConnectionWindow, MsgText, "Error",
                       MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
            goto Fail;

        } else {

            // AcceptCondFunc returned CF_REJECT...
            MessageBox(GlobalFrameWindow,
                       "The connection attempt has been refused.",
                       "Connection refused.", MB_OK | MB_SETFOREGROUND);
            goto Fail;
        }
    }

    WSAAsyncSelect(ConnData->Socket,
                   GlobalFrameWindow,
                   0,
                   0);

    // Put Connection in Event Object Notification Mode.
    WSAEventSelect(ConnData->Socket,
                   ConnData->SocketEventObject,
                   FD_READ | FD_WRITE | FD_CLOSE);

    // Determine the maximum message size, if any.
    if (!GetMaxMsgSize(ConnData)) {
        goto Fail;
    }

    // Start the I/O thread, and save the thread handle.
    ConnData->IOThreadHandle =
      CreateThread(NULL,
                   0,
                   (LPTHREAD_START_ROUTINE)IOThreadFunc,
                   ConnData,
                   0,
                   &ThreadId);
    if (ConnData->IOThreadHandle == NULL) {
        ChatSysError("CreateThread()",
                     "HandleAcceptMessage()",
                     TRUE);
    }

    return;

 Fail:

    DestroyWindow(ConnectionWindow);
    return;

} // HandleAcceptMessage()

void
HandleConnectMessage(
    IN HWND   ConnectionWindow,
    IN LPARAM LParam)
/*++

Routine Description:

    Handles the reception of a USMSG_CONNECT message, which indicates
    a connection attempt is complete (though not necessarily
    successful).

Implementation:

    As with HandleAcceptMessages, this function initializes
    connection-specific data, calls WSAEventSelect to register
    interest in certain network events, and starts a network event
    handling thread.

Arguments:

    ConnectionWindow -- Handle to the connection window associated
    with this connection request.

    LParam -- Contains the LParam that was a parameter of the
    USMSG_CONNECT message.

Return Value:

    None.

--*/
{

    PCONNDATA ConnData;               // connection-specific data
    char      TitleText[TITLE_LEN];   // text buffer for window title
    DWORD     ThreadId;               // needed for CreateThread()
    int       Error;                  // holds error codes

    Error = WSAGETSELECTERROR(LParam);

    // Check to see if there was an error on the connection attempt.
    if (Error) {
        char textBuf[80];
        sprintf(textBuf, "Connect error code: %x", Error);
        MessageBox(NULL, textBuf, NULL, MB_OK);

        // Some kind of error occurred.
        if (Error == WSAECONNREFUSED) {

            MessageBox(ConnectionWindow,
                       "Your connection attempt has been refused",
                       "Connection Refused",
                       MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
        } else {

            MessageBox(ConnectionWindow,
                       "Couldn't connect",
                       "Error",
                       MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
        }

        goto Fail;

    }

    // Connection has been accepted.  Change the title of the
    // connection window to reflect who the user has connected to
    ConnData = GetConnData(ConnectionWindow);

    if (ConnData->CalleeBuffer.len != 0) {

        // The callee buffer contains the connection-time data
        // sent by the callee. -- a string containing the name of
        // the callee.
        wsprintf(TitleText, "Connected To: %s @ %s",
                 ConnData->CalleeBuffer.buf, ConnData->PeerAddress);
    } else {

        // ConnData->PeerAddress just contains the address that
        // the user typed in before the connection attempt.
        wsprintf(TitleText, "Connected To: %s", ConnData->PeerAddress);
    }
    SetWindowText(ConnectionWindow, TitleText);

    WSAAsyncSelect(ConnData->Socket,
                   GlobalFrameWindow,
                   0,
                   0);

    // Put Connection in Event Object Notification Mode.
    WSAEventSelect(ConnData->Socket,
                   ConnData->SocketEventObject,
                   FD_READ | FD_WRITE | FD_CLOSE);

    // Determine the maximum message size, if any.
    if (!GetMaxMsgSize(ConnData)) {
        goto Fail;
    }

    // Start the I/O thread, and save the thread handle.
    ConnData->IOThreadHandle =
      CreateThread(NULL,
                   0,
                   (LPTHREAD_START_ROUTINE)IOThreadFunc,
                   ConnData,
                   0,
                   &ThreadId);
    if (ConnData->IOThreadHandle == NULL) {
        ChatSysError("CreateThread()",
                     "HandleConnectMessage()",
                     TRUE);
    }

    return;

 Fail:

    DestroyWindow(ConnectionWindow);
    return;

} // HandleConnectMessage()

BOOL
GetMaxMsgSize(
    IN OUT PCONNDATA ConnData)
/*++

Routine Description:

    Determines the maximum message size (if any) of a connected
    socket.  The connection must already be established, i.e. the
    socket must be bound to a local address, for this function to
    work.  Fills the correct value into a field of the
    connection-specific data.

Arguments:

    ConnData -- Connection data for a connected socket.

Return Value:

    TRUE -- The maximum message size was succesfully determined and
    stored in ConnData->MaxMsgSize.

    FALSE -- There was an error calling getsockopt to get the maximum
    message size.

--*/
{
    BOOL ReturnValue = TRUE;       // return value
    int  DwordLen = sizeof(DWORD); // sizeof a DWORD!
    int  Error;                    // return value of getsockopt

    if ((ConnData->ProtocolInfo->dwMessageSize == 0) ||
        (ConnData->ProtocolInfo->dwMessageSize == 0xffffffff)) {

        // Either the protocol isn't message-oriented, or there is no
        // maximum message size.
        ConnData->MaxMsgSize = NO_MAX_MSG_SIZE;

    } else {

        // There is a maximum message size.  Note it.
        if (ConnData->MaxMsgSize == 0x1) {

            // The actual maximum message size was not stored in the
            // protocol information structure -- rather, we need to
            // get it using getsockopt().
            Error = getsockopt(ConnData->Socket,
                               SOL_SOCKET,
                               SO_MAX_MSG_SIZE,
                               (char *)&ConnData->MaxMsgSize,
                               &DwordLen);
            if (Error) {
                ReturnValue = FALSE;
            }

        } // if (ConnData->MaxMsgSize == 0x1)
        else {

            // The message size is stored in the protocol information
            // structure.  Use it.
            ConnData->MaxMsgSize = ConnData->ProtocolInfo->dwMessageSize;
        }

    } // else

    return(ReturnValue);

} // GetMaxMsgSize()

BOOL
IsSendable(
    char Char)
/*++

Routine Description:

    Determines whether a certain character value is ok to send over
    the socket to the far end.  Eliminates most non-printable
    characters except for newline and backspace.

Arguments:

    Char -- Supplies the character to be tested.

Return Value:

    TRUE -- The character should be sent as is.

    FALSE -- The character should not be sent.

--*/
{

    if (isprint(Char) || (Char == '\b') ||
       (Char == '\r') || (Char == '\t') ||
       (Char == '\n') ){
        return(TRUE);
    } else {
        return(FALSE);
    }

} // IsSendable()

int
DoRecv(
    IN PCONNDATA ConnData)

/*++

Routine Description:

    Receives as much available data as possible up to the size of the
    receive buffer-1 (BUFFER_LENGTH-1).  A single byte is reserved at
    the end of the receive buffer to append a terminating NULL.

Arguments:

    ConnData -- Points to the data for the connection that is ready to
    receive data.

Return Value:

    CHAT_OK -- Received data was successfully sent to the receive edit
           control.

    CHAT_ERROR -- Error receiving data.

    CHAT_CLOSED -- The socket was gracefully closed.

--*/

{
    DWORD  NumBytes;               // stores how many bytes we received
    int    Error;                  // gets error values
    int    Result;                 // gets return value from WSARecv
    char   Buf[BUFFER_LENGTH];     // buffer to receive data
    int    ReturnValue = CHAT_OK;  // returnValue
    WSABUF RecvBuffer;             // WSABuf to pass to WSARecv
    DWORD  Flags;                  // flags for WSARecv

    RecvBuffer.buf = Buf;
    RecvBuffer.len = BUFFER_LENGTH - 1;

    {
        int socket_type;

        socket_type = ConnData->ProtocolInfo->iSocketType;
        if ((socket_type == SOCK_DGRAM) ||
            (socket_type == SOCK_RDM) ||
            (socket_type == SOCK_SEQPACKET)) {
            Flags = MSG_PARTIAL;
            // Allow  message-oriented  delivery to be received within
            // our preferred buffer length without losing the trailing
            // part of longer messages.
        }
        else {
            Flags = 0;
            // The  MSG_PARTIAL  flag  is  not required and may not be
            // allowed for stream-oriented protocols.
        }
    }

    // Do the receive
    Result = WSARecv(ConnData->Socket,
                     &RecvBuffer,
                     1,
                     &NumBytes,
                     &Flags,
                     NULL,
                     NULL);

    // Check for errors.
    if (Result == SOCKET_ERROR) {

        Error = WSAGetLastError();

        switch (Error) {

        case WSAENETRESET:  // flow through
        case WSAECONNRESET:

            // The remote party has reset the connection.
            ReturnValue = CHAT_CLOSED;
            goto Done;

        case WSAEWOULDBLOCK:

            // No data received; return to wait for another read event.
            ReturnValue = CHAT_OK;

        default:

            // Some other error...hit the panic button.
            ReturnValue = CHAT_ERROR;
            goto Done;
        }

    }

    // Append a NULL to the text buffer.
    RecvBuffer.buf[NumBytes] = '\0';

    // Output the received text into the receive edit control.
    OutputString(ConnData->RecvWindow, RecvBuffer.buf);

 Done:
    return(ReturnValue);

} // DoRecv()

int
DoOverlappedCallbackSend(
    IN POUTPUT_REQUEST OutReq,
    IN PCONNDATA       ConnData)

/*++

Routine Description:

    Sends a buffer of data over a connected socket using overlapped
    I/O with callback function completion notification.

Implementation:

    This function just does an overlapped send, giving WinSock 2 a
    completion function which will be called when the operation has
    finished, and within which cleanup will occur.

Arguments:

    OutReq -- Points to a OUTPUT_REQUEST structure, which specifies an
    output request.

    ConnData -- Supplies a pointer to a CONNDATA structure, which
    identifies a Chat connection.

Return Value:

    CHAT_WOULD_BLOCK -- The send operation failed with WSAEWOULDBLOCK.

    CHAT_ERROR -- A unexpected error occurred.

    CHAT_OK -- The send was successfully initiated. WinSock 2 will
    execute the callback function when the send has completed.

--*/

{
    int             Size = 0;      // how many bytes we send
    int             Error;         // return value of WSASend
    int             Errno;         // result of WSAGetLastError
    LPWSABUF        Buffers;       // points to an array of WSABUFs
    DWORD           BytesSent;     // needed in WSASend
    int             ReturnValue;   // the return value

    ReturnValue = CHAT_OK;

    // Allocate an OVERLAPPED structure.
    OutReq->Overlapped = (LPWSAOVERLAPPED)malloc(sizeof(WSAOVERLAPPED));
    if (OutReq->Overlapped == NULL) {
        ChatSysError("malloc()",
                     "DoOverlappedCallbackSend()",
                     TRUE);
    }
    Buffers = &OutReq->Buffer;

    // The hEvent field of the Overlapped structure is not used
    // for callback notification...thus we can use it any way we
    // want to pass information to the callback routine.  We use
    // it to store a pointer to the OutReq associated with this
    // overlapped send operation, so the callback function can
    // free the memory.
    OutReq->Overlapped->hEvent = (WSAEVENT)OutReq;

    Error = WSASend(ConnData->Socket,
                    Buffers,
                    1,
                    &BytesSent,
                    0,
                    OutReq->Overlapped,
                    SendCompFunc);

    if (Error == SOCKET_ERROR) {

        // There is an error...
        Errno = WSAGetLastError();
        if (Errno == WSAEWOULDBLOCK) {

            // WSAEWOULDBLOCK means we have to wait for an FD_WRITE
            // before we can send.
            ConnData->WriteOk = FALSE;
            ReturnValue = CHAT_WOULD_BLOCK;

        } else if (Errno == WSA_IO_PENDING) {

            // Overlapped send successfully initiated.
            ReturnValue = CHAT_OK;
        }
        else {

            // An unexpected error occurred.
            ReturnValue = CHAT_ERROR;
        }

    }

    // No error -- the I/O request was completed immediately...
    return(ReturnValue);

} // DoOverlappedCallbackSend()

int
DoOverlappedEventSend(
    IN POUTPUT_REQUEST OutReq,
    IN PCONNDATA       ConnData)

/*++

Routine Description:

    Sends a buffer of data over a connected socket using overlapped
    I/O with event notification.

Implementation:

    If the overlapped send is succesfully initiated, this function
    increases the event count and adds an entry to the event array of
    the associated connection.  During IOThreadFunc, the thread will
    thus wait on the new event as well, and will perform the
    appropriate cleanup action when awoken.

Arguments:

    OutReq -- Points to a OUTPUT_REQUEST structure, which specifies an
    output request.

    ConnData -- Supplies a pointer to a CONNDATA structure, which
    identifies a Chat connection.

Return Value:

    CHAT_WOULD_BLOCK -- The send operation failed with WSAEWOULDBLOCK.

    CHAT_ERROR -- A unexpected error occurred.

    CHAT_OK -- The send was successfully initiated and the new event
    placed in the event array; it will be signaled by WinSock 2 when
    the send operation has completed.

--*/

{
    int             Size = 0;      // how many bytes we send
    int             Error;         // return value of WSASend
    int             Errno;         // result of WSAGetLastError
    LPWSABUF        Buffers;       // points to an array of WSABUFs
    DWORD           BytesSent;     // needed in WSASend
    int             ReturnValue;   // the return value

    ReturnValue = CHAT_OK;

    // Allocate an OVERLAPPED structure.

    // Note that the pointer to an overlapped structure is kept in
    // OutReq.  We use an array of OutReq pointers that is parallel to
    // the event array.  With that one pointer, then, we can find the
    // associated Overlapped structure and free it, and then free the
    // OutReq structure itself.
    OutReq->Overlapped = (LPWSAOVERLAPPED)malloc(sizeof(WSAOVERLAPPED));
    if (OutReq->Overlapped == NULL) {
        ChatSysError("malloc()",
                     "DoOverlappedEventSend()",
                     TRUE);
    }
    Buffers = &OutReq->Buffer;

    OutReq->Overlapped->hEvent =
      (WSAEVENT)CreateEvent(NULL,
                            FALSE,
                            FALSE,
                            NULL);
    if (OutReq->Overlapped->hEvent == NULL) {
        ChatSysError("CreateEvent()",
                     "DoOverlappedEventSend()",
                     TRUE);
    }

    // Do the send.
    Error = WSASend(ConnData->Socket,
                    Buffers,
                    1,
                    &BytesSent,
                    0,
                    OutReq->Overlapped,
                    NULL);

    if (Error == SOCKET_ERROR) {

        // There is an error...
        Errno = WSAGetLastError();
        if (Errno == WSAEWOULDBLOCK) {

            // WSAEWOULDBLOCK means we have to wait for an FD_WRITE
            // before we can send.
            ConnData->WriteOk = FALSE;
            ReturnValue = CHAT_WOULD_BLOCK;

        } else if (Errno == WSA_IO_PENDING) {

            // Overlapped send successfully initiated.
            // Increase the event count, and update the event and
            // output request arrays to hold the entries for this
            // overlapped send.
            ConnData->NumEvents++;
            if (ConnData->NumEvents > WSA_MAXIMUM_WAIT_EVENTS) {

                // We're trying to wait on too many events at
                // once.  It's very unlikely this could happen --
                // you'd have to get about 62 overlapped sends, in
                // row, before one of them is completed and
                // signaled.  So just return CHAT_ERROR which will
                // break the connection.
                ReturnValue = CHAT_ERROR;

            } else {

                ConnData->EventArray[ConnData->NumEvents - 1] =
                  OutReq->Overlapped->hEvent;

                ConnData->OutReqArray[ConnData->NumEvents - 1] = OutReq;
                ReturnValue = CHAT_OK;
            }
        } // else if (...)
        else {

            // An unexpected error occurred.
            ReturnValue = CHAT_ERROR;
        }

    } // if (Error == SOCKET_ERROR)

    // No error -- the I/O request was completed immediately...
    ReturnValue = CHAT_OK;

    return(ReturnValue);

} // DoOverlappedEventSend()

int
DoSend(
    IN POUTPUT_REQUEST OutReq,
    IN PCONNDATA       ConnData)

/*++

Routine Description:

    Sends a buffer of data over a connected socket.

Implementation:

    This function uses the information contained in the output request
    structure to determine whether to use overlapped or non-overlapped
    I/O.  If using overlapped I/O, it calls an appropriate function
    depending on whether the symbol CALLBACK_NOTIFICATION has been
    defined. Otherwise, the non-overlapped send in performed
    immediately.

Arguments:

    OutReq -- Points to a OUTPUT_REQUEST structure, which specifies an
    output request.

    ConnData -- Supplies a pointer to a CONNDATA structure, which
    identifies a Chat connection.

Return Value:

    CHAT_WOULD_BLOCK -- The send operation failed with WSAEWOULDBLOCK.

    CHAT_ERROR -- A unexpected error occurred.

    CHAT_OK -- The send was successfully initiated.  If the send was
    overlapped, either a callback function is forthcoming or another
    event was placed in the ConnData->EventArray for IOThreadFunc to
    wait on until it's signaled by WinSock 2.

--*/

{
    int             Size = 0;      // how many bytes we send
    int             Error;         // return value of WSASend
    int             Errno;         // result of WSAGetLastError
    WSABUF          Buffers[1];    // points to the data to be sent
    DWORD           BytesSent;     // needed in WSASend
    int             ReturnValue;   // the return value

    if (OutReq->Type == NON_OVERLAPPED_IO) {

        // Do a non-overlapped send by setting lpOverlapped and
        // lpCompletionRoutine to NULL.
        Buffers[0].len = OutReq->Buffer.len;
        Buffers[0].buf = OutReq->Buffer.buf;
        Error = WSASend(ConnData->Socket,
                        Buffers,
                        1,
                        &BytesSent,
                        0,
                        NULL,
                        NULL);

        if (Error == SOCKET_ERROR) {

            // There's an error...
            Errno = WSAGetLastError();
            if (Errno == WSAEWOULDBLOCK) {

                //  WSAEWOULDBLOCK means we have to wait for an FD_WRITE
                //  before we can send.
                ConnData->WriteOk = FALSE;
                ReturnValue = CHAT_WOULD_BLOCK;

            } else {

                // It's an unexpected error.
                ReturnValue = CHAT_ERROR;
            }
        } else {

            // There's no error.
            ReturnValue = CHAT_OK;
            free(OutReq);
        }

    } else if (OutReq->Type == OVERLAPPED_IO) {

#ifdef CALLBACK_NOTIFICATION

        ReturnValue = DoOverlappedCallbackSend(OutReq, ConnData);

#else

        ReturnValue = DoOverlappedEventSend(OutReq, ConnData);

#endif

    } else {

        // Unknown output type...
        MessageBox(ConnData->ConnectionWindow,
                   "Unknown output type given to IOThread. Aborting.",
                   "Error.", MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
        ReturnValue = CHAT_ERROR;
    }

    return(ReturnValue);

} // DoSend()

void CALLBACK
SendCompFunc(
    IN DWORD           Error,
    IN DWORD           BytesTransferred,
    IN LPWSAOVERLAPPED Overlapped,
    IN DWORD           Flags)
/*++

Routine Description:

    Completion routine called after a successfully initiated overlapped
    send operation completes; cleans up data structures associated
    with the send.

Arguments:

    Error -- Supplies the completion status for the overlapped
    operation.

    BytesTransferred -- Supplies the actual number of bytes sent.

    Overlapped -- Supplies a pointer to a WSAOVERLAPPED structure.
    The hEvent field in the structure contains a pointer to the
    OUTPUT_REQUEST structure associated with this send operation.

    Flags -- Not yet used.

Return Value:

    None

--*/

{
    POUTPUT_REQUEST OutReq; // The output request

    OutReq = (POUTPUT_REQUEST)(Overlapped->hEvent);

    if (Error) {
        MessageBox(NULL,
                   "Error during overlapped send.",
                   "Error",
                   MB_OK | MB_SETFOREGROUND);
    }

    // Free the data buffer, the output request structure,  and the
    // overlapped I/O structure.
    free(OutReq->Buffer.buf);
    free(OutReq);
    free(Overlapped);

} // SendCompFunc()

PCONNDATA
GetConnData(
    IN HWND ConnectionWindow
    )

/*++

Routine Description:

    Retrieves a pointer to the PCONNDATA data structure associated
    with a connection window

Arguments:

    ConnectionWindow - Supplies the handle to a connection window.

Return Value:

    Returns the PCONNDATA associated with the given connection window
    handle.

--*/

{
    return((PCONNDATA)GetWindowLong(ConnectionWindow, GWL_CONNINFO));

} // GetConnData()

BOOL
MakeConnection(
    IN HWND ConnectionWindow)

/* ++

Routine Description:

    Initiates a call to another instance of chat.
    1) Creates a socket
    2) Set the socket up for windows message notification of FD_CONNECT
       events.
    3) Allocate a buffer for caller name & subject, and fill it in.
    4) Allocate a buffer to receive callee name.
    5) Set up quality of service for the connection.
    6) Attempt a connection

Arguments:

    ConnectionWindow - Handle to the window that receives
    notification when an FD_CONNECT event occurs.

Return Value:

    TRUE - A connection attempt was successfully initiated.

    FALSE - Error occurred while attempting to initiate a connection.

--*/

{
    int             ConnectStatus;    // the return value of WSAConnect
    int             Error;            // gets error values
    BOOL            ReturnValue;      // holds the return value
    PCONNDATA       ConnData;         // connection-specific data
    LPWSABUF        CallerBuffer;     // user data we will send
    LPWSABUF        CalleeBuffer;     // user data we will receive
    QOS             QualityOfService; // QOS structure, used by WSAConnect
    LPFLOWSPEC      FlowSpec;         // dummy pointer for code readability
    char            MsgText[MSG_LEN]; // holds message strings
    BOOL            QOSSupported;     // does the protocol support QOS?
    BOOL            CTDTSupported;    // support for conn-time data xfer?
    struct sockaddr *SockAddr;        // socket address for WSAConnect
    int             SockAddrLen;      // the length of the above

    ReturnValue = TRUE;
    ConnData = GetConnData(ConnectionWindow);
    QOSSupported = (ConnData->ProtocolInfo->dwServiceFlags1 &
                    XP1_QOS_SUPPORTED);
    CTDTSupported = (ConnData->ProtocolInfo->dwServiceFlags1 &
                      XP1_CONNECT_DATA);
    if (CTDTSupported) {

        // CallerBuffer and CallerBuffer->buf (allocated within the
        // NameAndSubject dialog box procedure) both get freed by the
        // end of this function.  CalleeBuffer.buf gets freed upon
        // connection shutdown -- it needs to be valid after this
        // function has exited.
        CallerBuffer = (LPWSABUF)malloc(sizeof(WSABUF));
        if (CallerBuffer == NULL) {
            ChatSysError("malloc()",
                         "MakeConnection()",
                         TRUE);
        }
        CalleeBuffer = &ConnData->CalleeBuffer;

        // The connection-time data sent back when/if this connection
        // is accepted will just contain the callee's name.
        CalleeBuffer->len = NAME_LEN + 1;
        CalleeBuffer->buf = (char *)malloc(CalleeBuffer->len);
        if (CallerBuffer->buf == NULL) {
            ChatSysError("malloc()",
                         "MakeConnection()",
                         TRUE);
        }

        // Prompt the user for his name and the subject of the call.
        // a pointer to the CallerBuffer is passed to the dialog box
        // procedure and the data is packed into this memory.
        if (!DialogBoxParam(GlobalInstance,
                            "NameAndSubjectDlg",
                            ConnectionWindow,
                            NameAndSubjectDlgProc,
                            (LPARAM)CallerBuffer)) {
            ReturnValue = FALSE;
            goto Done;
        }
    } // if (CTDTSupported)

    // Create a socket for this connection.
    ConnData->Socket = WSASocket(FROM_PROTOCOL_INFO,
                                 FROM_PROTOCOL_INFO,
                                 FROM_PROTOCOL_INFO,
                                 ConnData->ProtocolInfo,
                                 0,
                                 WSA_FLAG_OVERLAPPED);

    if (ConnData->Socket == INVALID_SOCKET) {

        MessageBox(ConnectionWindow, "Could not open a socket.", "Error",
                   MB_OK | MB_SETFOREGROUND);
        ReturnValue = FALSE;
        goto Done;

    } else {

        // Set up socket for windows message event notification.
        WSAAsyncSelect(ConnData->Socket,
                       ConnectionWindow,
                       USMSG_CONNECT,
                       FD_CONNECT);
    }

    if (QOSSupported) {

        // Set  up  quality  of  service  info  we  want.   Do  not include any
        // provider-specific information.
        QualityOfService.ProviderSpecific.len = 0;
        QualityOfService.ProviderSpecific.buf = NULL;

        // FlowSpec is used only to make this code more readable.
        FlowSpec = & QualityOfService.SendingFlowspec;
        FlowSpec->TokenRate = TOKENRATE;
        FlowSpec->TokenBucketSize = QOS_UNSPECIFIED;
        FlowSpec->PeakBandwidth = QOS_UNSPECIFIED;
        FlowSpec->Latency = QOS_UNSPECIFIED;
        FlowSpec->DelayVariation = QOS_UNSPECIFIED;
        FlowSpec->ServiceType = SERVICETYPE_BESTEFFORT;

        // again, the extra FlowSpec variable is for readability
        FlowSpec = & QualityOfService.ReceivingFlowspec;
        FlowSpec->TokenRate = TOKENRATE;
        FlowSpec->TokenBucketSize = QOS_UNSPECIFIED;
        FlowSpec->PeakBandwidth = QOS_UNSPECIFIED;
        FlowSpec->Latency = QOS_UNSPECIFIED;
        FlowSpec->DelayVariation = QOS_UNSPECIFIED;
        FlowSpec->ServiceType = SERVICETYPE_BESTEFFORT;
    }

    // Reminder: WinSock 2 requires that we pass a struct sockaddr *
    // to WSAConnect; however, the service provider is free to
    // interpret the pointer as an arbitrary chunk of data of size
    // SockAddrLen.
    SockAddr = (struct sockaddr *)ConnData->RemoteSockAddr.buf;
    SockAddrLen = ConnData->RemoteSockAddr.len;

    // Try to connect; depending on whether QOS or Connection Time
    // Data Transfer are supported, pass the appropriate data.
    if (QOSSupported && CTDTSupported) {

        // Both are supported...
        ConnectStatus = WSAConnect(ConnData->Socket,
                                   SockAddr,
                                   SockAddrLen,
                                   CallerBuffer,
                                   CalleeBuffer,
                                   &QualityOfService,
                                   NULL);
    } else if (!QOSSupported && CTDTSupported) {

        // Only CTDT is supported...
        ConnectStatus = WSAConnect(ConnData->Socket,
                                   SockAddr,
                                   SockAddrLen,
                                   CallerBuffer,
                                   CalleeBuffer,
                                   NULL,
                                   NULL);

    } else if (QOSSupported && !CTDTSupported) {
        // Only QOS is supported...  case for ATM
        ConnectStatus = WSAConnect(ConnData->Socket,
                                   SockAddr,
                                   SockAddrLen,
                                   NULL,
                                   NULL,
                                   &QualityOfService,
                                   NULL);
    } else {

        // Neither is supported...   case for TCP
        ConnectStatus = WSAConnect(ConnData->Socket,
                                   SockAddr,
                                   SockAddrLen,
                                   NULL,
                                   NULL,
                                   NULL,
                                   NULL);
    }

    // Check for errors.
    if (ConnectStatus == SOCKET_ERROR) {
        Error = WSAGetLastError();
        if (Error != WSAEWOULDBLOCK) {
            wsprintf(MsgText, "WSAConnect failed.  Error code: %d",
                     Error);
            MessageBox(ConnectionWindow, MsgText, "Error",
                       MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
            ReturnValue = FALSE;
        }
    } else {
        MessageBox(ConnectionWindow,
                   "WSAConnect should have returned SOCKET_ERROR.",
                   "Error", MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
        ReturnValue = FALSE;
    }

 Done:
    // Free allocated memory, except CalleeBuffer which will be
    // freed when connection window is destroyed.
    if (CTDTSupported) {
        free(CallerBuffer->buf);
        free(CallerBuffer);
    }
    return(ReturnValue);
} // MakeConnection()

BOOL
FillInFamilies(
    IN HWND  DialogWindow,
    IN DWORD FamilyLB)
/*++

Routine Description:

    Fills in the listbox with a string for each protocol on which Chat
    has a listening socket.

Implementation:

    For each socket in the ListeningSockets array, this function:

        1. Gets the associated protocol information structure.

        2. If Chat recognizes the address family, it prints it in a
        string; if not, it prints "unknown".

        3. In the second half of the string, prints out the
        human-readable string contained in the protocol information.

        4. Sends the string to be an entry in the listbox identified
        by FamilyLB.

Arguments:

    DialogWindow -- Window handle for the dialog box.

    FamilyLB -- Integer identifier for a listbox in the dialog box.

Return Value:

    TRUE - All messages were successfully sent to the listbox.

    FALSE - An LB_ADDSTRING message failed.

--*/

{

    int     i;                 // counting variable
    LRESULT Result;            // result of SendMessage calls
    char    LBString[MSG_LEN]; // string to send to the listbox
    int     Offset;             // index into the string

    // Iterate through all members of the ListeningSockets array.
    for (i = 0; i < NumFound; i++){

        // We are prepending a number to the list box string to be displayed
        // since the list box does us the favor of alphabetizing the
        // the sting and we are depending on the list entries appearing in the
        // list box in the order that we added them to the list.

        Offset = wsprintf(LBString, "%02i ",i);

        switch (ListeningSockets[i].ProtocolInfo->iAddressFamily) {

            case AF_INET:

                Offset += wsprintf(LBString+Offset, "INET/");
                wsprintf(LBString + Offset, "%s",
                         ListeningSockets[i].ProtocolInfo->szProtocol);
                Result = SendMessage(GetDlgItem(DialogWindow, FamilyLB),
                                     LB_ADDSTRING, 0, (LPARAM)LBString);
                break;

            case AF_ATM:
                Offset += wsprintf(LBString+Offset, "ATM/");
                wsprintf(LBString + Offset, "%s",
                         ListeningSockets[i].ProtocolInfo->szProtocol);
                Result = SendMessage(GetDlgItem(DialogWindow, FamilyLB),
                                     LB_ADDSTRING, 0, (LPARAM)LBString);
                break;

            case AF_IPX:
                Offset += wsprintf(LBString+Offset, "IPX/");
                wsprintf(LBString + Offset, "%s",
                         ListeningSockets[i].ProtocolInfo->szProtocol);
                Result = SendMessage(GetDlgItem(DialogWindow, FamilyLB),
                                     LB_ADDSTRING, 0, (LPARAM)LBString);
                break;

            case AF_NETBIOS:
                Offset += wsprintf(LBString+Offset, "NETBIOS/");
                wsprintf(LBString + Offset, "%s",
                         ListeningSockets[i].ProtocolInfo->szProtocol);
                Result = SendMessage(GetDlgItem(DialogWindow, FamilyLB),
                                     LB_ADDSTRING, 0, (LPARAM)LBString);
                break;
            default:

                Offset += wsprintf(LBString+ Offset, "(unknown)/");
                wsprintf(LBString + Offset, "%s",
                         ListeningSockets[i].ProtocolInfo->szProtocol);
                Result = SendMessage(GetDlgItem(DialogWindow, FamilyLB),
                                     LB_ADDSTRING, 0, (LPARAM)LBString);
                break;

        }
        if ((Result == LB_ERR) || (Result == LB_ERRSPACE)) {
            return(FALSE);
        }
    }
    return(TRUE);

} // FillInFamilies()

LPWSAPROTOCOL_INFO
GetProtoFromIndex(
    IN int LBIndex)
/*++

Routine Description:

    Takes an index into the ChooseFamily dialog box and returns the
    protocol associated with that index. This works because when the
    dialog box is set up, the strings representing the protocols are
    added to the listbox in the order they fall in ListeningSockets.

Arguments:

    LBIndex -- The index of the user's selection.

Return Value:

    NULL -- The index doesn't correspond to an element in the
    ListeningSockets array.

    LPWSAPROTOCOL_INFO -- A pointer to the protocol information struct
    corresponding to LBIndex.

--*/
{
    if (LBIndex > (NumFound - 1)) {
        return(NULL);
    }
    return(ListeningSockets[LBIndex].ProtocolInfo);

} // GetProtoFromIndex()

LPWSAPROTOCOL_INFO
GetProtoFromSocket(
    IN SOCKET Socket)
/*++

Routine Description:

    Searches the ListeningSockets array for the first entry with a
    Socket field equal to the Socket parameter.  When found, it
    returns a pointer to the associated protocol information
    structure.  If not found, return NULL.

Arguments:

    Socket -- The socket who's associated WSAPROTOCOL_INFO struct we are
    looking for.

Return Value:

    NULL -- The socket was not found in the ListeningSockets array.

    LPWSAPROTOCOL_INFO -- A pointer to the protocol information struct
    for the socket.

--*/
{

    int i; // counting variable

    for (i = 0; i < NumFound; i++) {
        if (ListeningSockets[i].Socket == Socket) {
            return(ListeningSockets[i].ProtocolInfo);
        }
    }
    return(NULL);

} // GetProtoFromSocket()

void
CleanupConnection(
    IN PCONNDATA ConnData)
/*++

Routine Description:

    Frees all memory and objects allocated for this connection.

Arguments:

    ConnData -- Pointer to the connection-specific data structure.

Return Value:

    None.

--*/
{

    // Clean up connection-specific data.  To keep this code
    // readable, we ignore any errors.
    if (ConnData->SocketEventObject != NULL) {
        CloseHandle(ConnData->SocketEventObject);
        ConnData->SocketEventObject = NULL;
    }
    if (ConnData->OutputEventObject != NULL) {
        CloseHandle(ConnData->OutputEventObject);
        ConnData->OutputEventObject = NULL;
    }
    if (ConnData->OutputQueue != NULL) {
        QFree(ConnData->OutputQueue);
        ConnData->OutputQueue = NULL;
    }
    if (ConnData->IOThreadHandle != NULL) {
        CloseHandle(ConnData->IOThreadHandle);
        ConnData->IOThreadHandle = NULL;
    }
    if (ConnData->Socket != INVALID_SOCKET) {
        closesocket(ConnData->Socket);
    }
    if (ConnData->RemoteSockAddr.buf != NULL) {
        free(ConnData->RemoteSockAddr.buf);
        ConnData->RemoteSockAddr.buf = NULL;
    }
    if (ConnData->CalleeBuffer.buf != NULL) {
        free(ConnData->CalleeBuffer.buf);
        ConnData->CalleeBuffer.buf = NULL;
    }
    free(ConnData);

} // CleanupConnection()


CHATSOCK.H

/*++

Copyright (c) 1995 Intel Corp

Module Name:

    chatsock.h

Abstract:

    Header file containing constants, data structure definitions, and
    function prototypes for socket.c.

--*/

#ifndef CHTSOCK_H
#define CHTSOCK_H

#include "nowarn.h"  /* turn off benign warnings */
#ifndef _WINSOCKAPI_
#define _WINSOCKAPI_   /* Prevent inclusion of winsock.h in windows.h */
#endif
#include <windows.h>
#include "nowarn.h"  /* some warnings may have been turned back on */

//
// Manifest Constants
//

#define WSA_WAIT_EVENT_1  WSA_WAIT_EVENT_0+1
#define WSA_WAIT_EVENT_2  WSA_WAIT_EVENT_0+2

//
// Typedefs
//

typedef struct _CALLBACK_INFO {
    BOOL  DoneYet;   // has the overlapped I/O completed yet?
    LPSTR Buffer;    // the buffer we gave to WSASend
} CALLBACK_INFO, *PCALLBACK_INFO;

//
// Function Prototypes -- Exported Functions
//

BOOL
InitWS2(void);

BOOL
FindProtocols(void);

BOOL
ListenAll(void);

void
HandleAcceptMessage(
    IN HWND   ConnectionWindow,
    IN SOCKET Socket,
    IN LPARAM LParam);

void
HandleConnectMessage(
    IN HWND   ConnectionWindow,
    IN LPARAM LongParam);

BOOL
IsSendable(
    char Character);

PCONNDATA
GetConnData(
    IN HWND ConnectionWindow);

BOOL
MakeConnection(
    IN HWND WindowHandle);

BOOL
FillInFamilies(
    IN HWND  DialogWindow,
    IN DWORD FamilyLB);

LPWSAPROTOCOL_INFO
GetProtoFromIndex(
    IN int LBIndex);

void
CleanUpSockets(void);

BOOL
GetAddressString(
    OUT char            *String,
    IN  LPVOID          SockAddr,
    IN  int             SockAddrLen,
    IN  LPWSAPROTOCOL_INFO ProtocolInfo);

BOOL
UseProtocol(
    IN LPWSAPROTOCOL_INFO Proto);

void
CleanupConnection(
    IN PCONNDATA ConnData);

#endif // CHTSOCK_H


CHATDLG.C

/*++

Copyright (c) 1995 Intel Corp

Module Name:

    dialog.c

Abstract:

    Contains dialog-box procedures for the WinSock2 Chat sample
    application.  See ws2chat.rc for the actual resource script.

--*/

#include "nowarn.h"  /* turn off benign warnings */
#ifndef _WINSOCKAPI_
#define _WINSOCKAPI_   /* Prevent inclusion of winsock.h in windows.h */
#endif

#include <windows.h>
#include "nowarn.h"  /* some warnings may have been turned back on */
#include <ws2atm.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include "ws2chat.h"
#include "chatsock.h"
#include "chatdlg.h"
#include "resource.h"

BOOL APIENTRY
ChooseFamilyDlgProc(
    IN HWND DialogWindow,
    IN UINT Message,
    IN WPARAM WParam,
    IN LPARAM LParam)
/*++

Routine Description:

    Callback function that processes messages sent to the 'Choose
    Family' dialog box.

Implementation

    This dialog box is used to query the user as to which supported
    protocol he wishes to use for the chat connection he wishes to
    make.  The available protocols are listed in the IDC_FAM_LB
    listbox via the FillInFamilies function in socket.c.

    It is expected that this dialog box gets the WM_INITDIALOG
    message with a LParam that is a pointer to the CONNDATA structure
    for the impending connection.  The result of this function is that
    the ProtocolInfo field of the ConnData struct is filled with a
    pointer to the protocol information structure the user chose in
    the listbox.

Arguments:

    DialogWindow - Supplies handle of the dialog box.

    Message - Supplies the message identifier

    WParam - Supplies the first message parameter

    LParam - Supplies the second message parameter

Return Value:

    TRUE -- This function handled the message.

    FALSE -- This function did not handle the message.

--*/
{
    int              LBIndex;         // index of the item chosen
    static PCONNDATA ConnData = NULL; // connection-specific data

    switch (Message) {

    case WM_INITDIALOG:

        // Get the parameter and fill in the listbox with the
        // available protocols.
        ConnData = (PCONNDATA)LParam;
        assert(ConnData != NULL);
        if (!FillInFamilies(DialogWindow, IDC_FAM_LB)) {
            MessageBox(DialogWindow, "FillInFamilies failed.", "Error.",
                        MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
            EndDialog(DialogWindow, FALSE);
        }

        return(TRUE);

    case WM_COMMAND:

        switch (WParam) {

        case IDOK:

            // Get the index of the listbox item the user has chosen.
            LBIndex = (int)SendMessage(GetDlgItem(DialogWindow, IDC_FAM_LB),
                                  LB_GETCURSEL, 0, 0);
            if (LBIndex == LB_ERR) {

                // Nothing was selected.
                MessageBox(DialogWindow, "Choose an item, or hit cancel.",
                           "No Selection?",
                           MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
                return(TRUE);
            }

            // Get the protocol associated with the listbox item the
            // user selected.
            ConnData->ProtocolInfo = GetProtoFromIndex(LBIndex);
            assert(ConnData->ProtocolInfo != NULL);
            EndDialog(DialogWindow, TRUE);
            return(TRUE);

        case IDCANCEL:

            // Kill the dialog box.
            EndDialog(DialogWindow, FALSE);
            return(TRUE);

        default:

            return(FALSE);
        } // switch (WParam)

    default:

        return(FALSE);
    } // switch (Message)

} // ChooseFamilyDlgProc()

BOOL APIENTRY
InetConnDlgProc(
    IN HWND DialogWindow,
    IN UINT Message,
    IN WPARAM WParam,
    IN LPARAM LParam)

/*++

Routine Description:

    Callback function that processes messages sent to the internet
    connection info dialog box.

Implementation

    This dialog box gets two strings from the user: the internet
    address and port to which she wants to make a chat connection.
    When the user clicks on the OK button, these two strings are
    converted into a sockaddr_in structure and a pointer to this
    structure is saved in the connection window's CONNDATA structure.

    NOTE: No name resolution takes place here!  The address edit
    control expects up to 15 characters, and this string is expected
    to be in.dotted.decimal.notation.  When RNR is available, this is
    where it would be called upon to provide the appropriate
    sockaddr_in structure.  Actually, this dialog box would be
    replaced with a protocol independent name dialog box; RNR would do
    all the rest...

Arguments:

    DialogWindow - Supplies handle of the dialog box.

    Message - Supplies the message identifier

    WParam - Supplies the first message parameter

    LParam - Supplies the second message parameter

Return Value:

    TRUE -- This function handled the message.

    FALSE -- This function did not handle the message; or the message
    was WM_INITDIALOG and we set the focus.

--*/

{
    char               PortText[INET_PORT_LEN + 1];   // holds port string
    char               AddressText[INET_ADDR_LEN + 1];// holds addr string
    static PCONNDATA   ConnData = NULL;               // conn-specific data
    struct sockaddr_in *SockAddrInet;                 // Inet socket address
    u_long             InetAddr;                      // Inet-style address
    u_short            InetPort;                      // Inet-style port
    int                Port;                          // intermediate var.
    BOOL               PortStringTranslated;          // for GetDlgItemInt
    struct hostent *host;

    switch (Message) {

    case WM_INITDIALOG:

        // Get the connection data pointer, initialize the dialog box.
        ConnData = (PCONNDATA)LParam;
        assert(ConnData != NULL);
        SendMessage(GetDlgItem(DialogWindow, IDC_INET_ADDRESS),
                    EM_LIMITTEXT, (WPARAM)INET_ADDR_LEN, 0);
        SendMessage(GetDlgItem(DialogWindow, IDC_INET_PORT),
                    EM_LIMITTEXT, (WPARAM)INET_PORT_LEN, 0);
        wsprintf(PortText,"%d", INET_DEFAULT_PORT);
        SendMessage(GetDlgItem(DialogWindow, IDC_INET_PORT),
                    WM_SETTEXT, 0, (LPARAM)PortText);
        SetFocus(GetDlgItem(DialogWindow, IDC_INET_ADDRESS));
        return(FALSE);

    case WM_COMMAND:

        switch (WParam) {

        case IDOK:

            // The user has pressed the 'OK' button.  Extract the
            // internet address into a buffer and check the value the
            // user typed for the port.
            GetDlgItemText(DialogWindow,
                           IDC_INET_ADDRESS,
                           AddressText,
                           INET_ADDR_LEN);
            strcpy(ConnData->PeerAddress, AddressText);
            Port = (int)GetDlgItemInt(DialogWindow,
                                      IDC_INET_PORT,
                                      &PortStringTranslated,
                                      TRUE);
            if ((Port < 0) || (Port > 65535) || !PortStringTranslated) {
              MessageBox(DialogWindow,
                         "Please choose a port between 0 and 65535.",
                         "Bad Port.",
                         MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
                return(TRUE);
            }

            // Convert the address string to an unsigned long, and
            // convert the port integer to an unsigned short.  If all
            // goes well, fill in the fields and end the dialog box.
            // If not, inform the user and *don't* kill the dialog box.
            InetAddr = inet_addr(AddressText);
            if (InetAddr == INADDR_NONE) {
                host = gethostbyname(AddressText);
                if (host) {
                    InetAddr = *((u_long *)host->h_addr_list[0]);
                }
            }
            InetPort = (u_short)Port;
            if ((InetAddr != INADDR_NONE) && (InetAddr != 0)) {

                // Allocate memory for an internet-style socket address;
                // point ConnData->SockAddr at this memory, and
                // use a local pointer to a struct sockaddr_in to
                // reference the fields.
                ConnData->RemoteSockAddr.len = sizeof(struct sockaddr_in);
                ConnData->RemoteSockAddr.buf =
                  malloc(ConnData->RemoteSockAddr.len);
                if (ConnData->RemoteSockAddr.buf == NULL) {
                    ChatSysError("malloc()",
                                 "InetConnDlgProc()",
                                 TRUE);
                }

                SockAddrInet =
                  (struct sockaddr_in *)ConnData->RemoteSockAddr.buf;
                SockAddrInet->sin_family = AF_INET;
                SockAddrInet->sin_port = htons(InetPort);
                SockAddrInet->sin_addr.S_un.S_addr = InetAddr;
                EndDialog(DialogWindow, TRUE);
                return(TRUE);

            }

            MessageBox(DialogWindow,
                       "Invalid Internet address. Try again or cancel.",
                       "Error", MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
            return(TRUE);

        case IDCANCEL:

            // The user has pressed cancel.  Kill the dialog box.
            EndDialog(DialogWindow, FALSE);
            return(TRUE);

        default:

            break;
        } // switch (WParam)

        break;
    } // switch (Message)

    return(FALSE);
} // InetConnDlgProc()

VOID ShowMessage(
    HWND hWnd,
    PSTR pMsg)
    /*
    Routine Description:
            Just pop up an error message box.
    */
{
    MessageBox(NULL, pMsg, "WS2Test", MB_OK);
} // ShowMessage

BOOL ScanAtmAddress(
    HWND        hWndDlg,
    ATM_ADDRESS *pAtmAddr)
    /*
        Routine Description:
            Scanner of ATM Address af a field of ATM socket address.
    */
{
    UCHAR textBuf[MAX_ADDRESS_TEXT];
    UINT value[ATM_ADDR_LENGTH];     // for sscanf
    DWORD length;
    int scanResult;
    UINT i;
    HWND hWndControl;
    UINT selection;

    hWndControl = GetDlgItem(hWndDlg, IDC_ADDR_TYPE);
    selection = ListBox_GetCurSel(hWndControl);
    pAtmAddr->AddressType = AtmAddrTypeValue[selection];

    length = GetDlgItemText(hWndDlg, IDC_ATM_ADDR, textBuf, sizeof(textBuf));
    if (pAtmAddr->AddressType == ATM_E164)
    {
        memcpy(pAtmAddr->Addr, textBuf, length);
        pAtmAddr->NumofDigits = length;
    }
    else if (pAtmAddr->AddressType != SAP_FIELD_ANY)
    {
        scanResult = sscanf(
            textBuf,
            "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x",
            &value[0], &value[1], &value[2], &value[3], &value[4],
            &value[5], &value[6], &value[7], &value[8], &value[9],
            &value[10], &value[11], &value[12], &value[13], &value[14],
            &value[15], &value[16], &value[17], &value[18], &value[19]);
        if (scanResult != ATM_ADDR_LENGTH)
        {
            ShowMessage(hWndDlg, "Invalid value for ATM address.");
            return FALSE;
        } // if
        for (i = 0; i < ATM_ADDR_LENGTH; i++)
        {
            pAtmAddr->Addr[i] = value[i];
        }
        pAtmAddr->NumofDigits = ATM_ADDR_LENGTH;
    }

    return TRUE;

} // ScanAtmAddress

BOOL ScanAtmBlli(
    HWND hWndDlg,
    ATM_BLLI *pBLLI)
    /*
        Routine Description:
            Scanner of BLLI af a field of ATM socket address.
    */
{
    UCHAR textBuf[MAX_PARSE_TEXT];
    UINT value[ATM_SNAP_ID_LENGTH];
    HWND hWndControl;
    UINT selection;
    UINT length;
    int scanResult;
    UINT i;

    hWndControl = GetDlgItem(hWndDlg, IDC_L2PROTO);
    selection = ListBox_GetCurSel(hWndControl);
    pBLLI->Layer2Protocol = AtmBlliL2ProtoValue[selection];

    length = GetDlgItemText(hWndDlg, IDC_L2USER_PROTO, textBuf, sizeof(textBuf));
    if (length != 0)
    {
        if (sscanf(textBuf, "%u", &value[0]) != 1)
        {
            ShowMessage(hWndDlg, "Invalid value for user specified layer 2 protocol info.");
            return FALSE;
        } // if
        else
        {
            pBLLI->Layer2UserSpecifiedProtocol = value[0];
        }
    }
    else
    {
        pBLLI->Layer2UserSpecifiedProtocol = SAP_FIELD_ABSENT;
    }

    hWndControl = GetDlgItem(hWndDlg, IDC_L3PROTO);
    selection = ListBox_GetCurSel(hWndControl);
    pBLLI->Layer3Protocol = AtmBlliL3ProtoValue[selection];

    length = GetDlgItemText(hWndDlg, IDC_L3USER_PROTO, textBuf, sizeof(textBuf));
    if (length != 0)
    {
        if (sscanf(textBuf, "%u", &value[0]) != 1)
        {
            ShowMessage(hWndDlg, "Invalid value for user specified layer 3 protocol info.");
            return FALSE;
        } // if
        else
        {
            pBLLI->Layer3UserSpecifiedProtocol = value[0];
        }
    }
    else
    {
        pBLLI->Layer3UserSpecifiedProtocol = SAP_FIELD_ABSENT;
    }

    hWndControl = GetDlgItem(hWndDlg, IDC_L3IPI);
    selection = ListBox_GetCurSel(hWndControl);
    pBLLI->Layer3IPI = AtmBlliL3IpiValue[selection];

    length = GetDlgItemText(hWndDlg, IDC_SNAP_ID, textBuf, sizeof(textBuf));
    if (length > 0)
    {
        scanResult = sscanf(
            textBuf,
            "%x.%x.%x.%x.%x",
            &value[0], &value[1], &value[2], &value[3], &value[4]);
        if (scanResult != ATM_SNAP_ID_LENGTH)
        {
            ShowMessage(hWndDlg, "Invalid value for SNAP ID.");
            return FALSE;
        } // if
        for (i = 0; i < ATM_SNAP_ID_LENGTH; i++)
        {
            pBLLI->SnapID[i] = value[i];
        }
    }
    else
    {
        memset(pBLLI->SnapID, 0, ATM_SNAP_ID_LENGTH);
    }

    return TRUE;

} // ScanBlli

BOOL ScanAtmBhli(
    HWND hWndDlg,
    ATM_BHLI *pBHLI)
    /*
        Routine Description:
            Scanner of BHLI af a field of ATM socket address.
    */
{
    UCHAR textBuf[MAX_PARSE_TEXT];
    UCHAR value[ATM_HL_INFO_LENGTH];
    HWND hWndControl;
    UINT selection;
    UINT length;
    UINT i;
    int scanResult;

    hWndControl = GetDlgItem(hWndDlg, IDC_BHLI_TYPE);
    selection = ListBox_GetCurSel(hWndControl);
    pBHLI->HighLayerInfoType = AtmBhliTypeValue[selection];
    if (pBHLI->HighLayerInfoType != BHLI_UserSpecific) {
        ShowMessage(hWndDlg, "Please choose user specific for BHLI type.");
        return FALSE;
    }

    length = GetDlgItemText(hWndDlg, IDC_BHLI, textBuf, sizeof(textBuf));
    if (length != 0)
    {
        scanResult = sscanf(
            textBuf,
            "%c%c%c%c%c%c%c%c",
            &value[0], &value[1], &value[2], &value[3],
            &value[4], &value[5], &value[6], &value[7]);

        if ((scanResult != ATM_HL_INFO_LENGTH))
        {
            ShowMessage(hWndDlg, "High Layer Information is a string of 8 chars.");
            return FALSE;
        } // if
        pBHLI->HighLayerInfoLength = (DWORD)scanResult;
        for (i = 0; i < pBHLI->HighLayerInfoLength; i++)
        {
            pBHLI->HighLayerInfo[i] = value[i];
        }
    }
    else
    {
        pBHLI->HighLayerInfoLength = 0;
        memset(pBHLI->HighLayerInfo, 0, ATM_HL_INFO_LENGTH);
    }

    return TRUE;
} // ScanAtmBhli

int InitSelection(
    DWORD   InitValue,
    int     NumOfValues,
    DWORD * ValueList)
{
    int i;
    for (i=0; i<NumOfValues; i++) {
        if (InitValue == ValueList[i]) {
            return i;
        }
    }
    return -1;

}

BOOL APIENTRY
ATMSockAddrProc(
    IN HWND DialogWindow,
    IN UINT Message,
    IN WPARAM WParam,
    IN LPARAM LParam)

/*++

Routine Description:

    Callback function that processes messages sent to the native ATM
    connection info dialog box.

Implementation

    This dialog box gets the ATM socket address for connection party.

Arguments:

    DialogWindow - Supplies handle of the dialog box.

    Message - Supplies the message identifier

    WParam - Supplies the first message parameter

    LParam - Supplies the second message parameter

Return Value:

    TRUE -- This function handled the message.

    FALSE -- This function did not handle the message; or the message
    was WM_INITDIALOG and we set the focus.

--*/

{

    static PCONNDATA   ConnData = NULL;               // conn-specific data
    HWND hListBox;
    int i;
    UCHAR textBuf[MAX_ERROR_TEXT];
    static struct sockaddr_atm * pATMSockAddr;
    UINT selection;

    switch (Message) {

        case WM_INITDIALOG:

            // Get the connection data pointer, initialize the dialog box.
            pATMSockAddr = (struct sockaddr_atm *)LParam;
            assert(pATMSockAddr != NULL);

            //Initialize ATM address type list box
            hListBox = GetDlgItem(DialogWindow, IDC_ADDR_TYPE);
            for (i = 0; i < NUM_ATM_ADDR_TYPES; i++)
            {
                ListBox_AddString(hListBox, AtmAddrTypeName[i]);
            }
            i = InitSelection(pATMSockAddr->satm_number.AddressType,
                                 NUM_ATM_ADDR_TYPES,
                                 AtmAddrTypeValue);
            if (i>=0) {
                ListBox_SetCurSel(hListBox, i);
            }

            if(AtmAddrTypeValue[i] == SAP_FIELD_ANY) {
                EnableWindow(GetDlgItem(DialogWindow, IDC_ATM_ADDR), FALSE);
            }
            else {
                //Initialize ATM address edit box
                sprintf(textBuf,
                    "%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x",
                    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
                SetDlgItemText(DialogWindow, IDC_ATM_ADDR, textBuf);
            }

            //Initialize Layer 2 protocol list box
            hListBox = GetDlgItem(DialogWindow, IDC_L2PROTO);
            for (i = 0; i < NUM_BLLI_L2PROTO; i++)
            {
                ListBox_AddString(hListBox, AtmBlliL2ProtoName[i]);
            }
            i = InitSelection(pATMSockAddr->satm_blli.Layer2Protocol,
                              NUM_BLLI_L2PROTO,
                              AtmBlliL2ProtoValue);
            if (i>=0) {
                ListBox_SetCurSel(hListBox, i);
            }
            if (AtmBlliL2ProtoValue[i] != BLLI_L2_USER_SPECIFIED) {
                EnableWindow(GetDlgItem(DialogWindow, IDC_L2USER_PROTO), FALSE);
            }

            //Initialize Layer 3 protocol list box
            hListBox = GetDlgItem(DialogWindow, IDC_L3PROTO);
            for (i = 0; i < NUM_BLLI_L3PROTO; i++)
            {
                ListBox_AddString(hListBox, AtmBlliL3ProtoName[i]);
            }
            i = InitSelection(pATMSockAddr->satm_blli.Layer3Protocol,
                              NUM_BLLI_L3PROTO,
                              AtmBlliL3ProtoValue);
            if (i>=0) {
                ListBox_SetCurSel(hListBox, i);
            }

            if (AtmBlliL3ProtoValue[i] != BLLI_L3_USER_SPECIFIED) {
                EnableWindow(GetDlgItem(DialogWindow, IDC_L3USER_PROTO), FALSE);
            }

            //Initialize Layer 3 IPI list box
            hListBox = GetDlgItem(DialogWindow, IDC_L3IPI);
            for (i = 0; i < NUM_BLLI_L3IPI; i++)
            {
                ListBox_AddString(hListBox, AtmBlliL3IpiName[i]);
            }
            i = InitSelection(pATMSockAddr->satm_blli.Layer3IPI,
                              NUM_BLLI_L3IPI,
                              AtmBlliL3IpiValue);
            if (i>=0) {
                ListBox_SetCurSel(hListBox, i);
            }

            if (AtmBlliL3IpiValue[i] != BLLI_L3_IPI_SNAP) {
                EnableWindow(GetDlgItem(DialogWindow, IDC_SNAP_ID), FALSE);
            }
            else {
                //Initialize SnapID edit control
                sprintf(textBuf, "%02x.%02x.%02x.%02x.%02x", 0,0,0,0,0);
                SetDlgItemText(DialogWindow, IDC_SNAP_ID, textBuf);
            }

            if (AtmBlliL3ProtoValue[i] != BLLI_L3_ISO_TR9577) {
                EnableWindow(GetDlgItem(DialogWindow, IDC_L3IPI), FALSE);
                EnableWindow(GetDlgItem(DialogWindow, IDC_SNAP_ID), FALSE);
            }

            //Initialize high layer info type list box
            hListBox = GetDlgItem(DialogWindow, IDC_BHLI_TYPE);
            for (i = 0; i < NUM_BHLI_TYPES; i++)
            {
                ListBox_AddString(hListBox, AtmBhliTypeName[i]);
            }
            i = InitSelection(pATMSockAddr->satm_bhli.HighLayerInfoType,
                              NUM_BHLI_TYPES,
                              AtmBhliTypeValue);
            if (i>=0) {
                ListBox_SetCurSel(hListBox, i);
            }

            if (AtmBhliTypeValue[i] == SAP_FIELD_ABSENT ||
                AtmBhliTypeValue[i] == SAP_FIELD_ANY) {
                EnableWindow(GetDlgItem(DialogWindow, IDC_BHLI), FALSE);
            }
            else {
                //Initialize high layer info edit control
                if (pATMSockAddr->satm_bhli.HighLayerInfoType == BHLI_UserSpecific) {
                    SetDlgItemText(DialogWindow, IDC_BHLI, ATM_DEFAULT_BHLI_INFO);
                }
            }

            return(FALSE);

        case WM_COMMAND:

            switch ((USHORT)WParam) {

                case IDC_ADDR_TYPE:
                    hListBox = GetDlgItem(DialogWindow, IDC_ADDR_TYPE);
                    selection = ListBox_GetCurSel(hListBox);
                    if (AtmAddrTypeValue[selection] != SAP_FIELD_ANY) {
                        EnableWindow(GetDlgItem(DialogWindow, IDC_ATM_ADDR), TRUE);
                    }
                    else {
                        EnableWindow(GetDlgItem(DialogWindow, IDC_ATM_ADDR), FALSE);
                    }
                    break;

                case IDC_L2PROTO:
                    hListBox = GetDlgItem(DialogWindow, IDC_L2PROTO);
                    selection = ListBox_GetCurSel(hListBox);
                    if (AtmBlliL2ProtoValue[selection] == BLLI_L2_USER_SPECIFIED) {
                        EnableWindow(GetDlgItem(DialogWindow, IDC_L2USER_PROTO), TRUE);
                    }
                    else {
                        EnableWindow(GetDlgItem(DialogWindow, IDC_L2USER_PROTO), FALSE);
                    }
                    break;

                case IDC_L3PROTO:
                    hListBox = GetDlgItem(DialogWindow, IDC_L3PROTO);
                    selection = ListBox_GetCurSel(hListBox);
                    if (AtmBlliL3ProtoValue[selection] == BLLI_L3_USER_SPECIFIED) {
                        EnableWindow(GetDlgItem(DialogWindow, IDC_L3USER_PROTO), TRUE);
                    }
                    else {
                        EnableWindow(GetDlgItem(DialogWindow, IDC_L3USER_PROTO), FALSE);
                    }
                    if (AtmBlliL3ProtoValue[selection] == BLLI_L3_ISO_TR9577) {
                        EnableWindow(GetDlgItem(DialogWindow, IDC_L3IPI), TRUE);
                        hListBox = GetDlgItem(DialogWindow, IDC_L3IPI);
                        selection = ListBox_GetCurSel(hListBox);
                        if (AtmBlliL3IpiValue[selection] == BLLI_L3_IPI_SNAP) {
                            EnableWindow(GetDlgItem(DialogWindow, IDC_SNAP_ID), TRUE);
                        }
                        else {
                            EnableWindow(GetDlgItem(DialogWindow, IDC_SNAP_ID), FALSE);
                        }
                    }
                    else {
                        EnableWindow(GetDlgItem(DialogWindow, IDC_L3IPI), FALSE);
                        EnableWindow(GetDlgItem(DialogWindow, IDC_SNAP_ID), FALSE);
                    }
                    break;

                case IDC_L3IPI:
                    hListBox = GetDlgItem(DialogWindow, IDC_L3IPI);
                    selection = ListBox_GetCurSel(hListBox);
                    if (AtmBlliL3IpiValue[selection] == BLLI_L3_IPI_SNAP) {
                        EnableWindow(GetDlgItem(DialogWindow, IDC_SNAP_ID), TRUE);
                    }
                    else {
                        EnableWindow(GetDlgItem(DialogWindow, IDC_SNAP_ID), FALSE);
                    }
                    break;

                case IDC_BHLI_TYPE:
                    hListBox = GetDlgItem(DialogWindow, IDC_BHLI_TYPE);
                    selection = ListBox_GetCurSel(hListBox);
                    if (AtmBhliTypeValue[selection] == SAP_FIELD_ABSENT ||
                        AtmBhliTypeValue[selection] == SAP_FIELD_ANY) {
                        EnableWindow(GetDlgItem(DialogWindow, IDC_BHLI), FALSE);
                    }
                    else {
                        EnableWindow(GetDlgItem(DialogWindow, IDC_BHLI), TRUE);
                    }
                    break;

                case IDOK:
                    if ((ScanAtmAddress(DialogWindow, &pATMSockAddr->satm_number)) &&
                        (ScanAtmBlli(DialogWindow, &pATMSockAddr->satm_blli)) &&
                        (ScanAtmBhli(DialogWindow, &pATMSockAddr->satm_bhli)))
                    {
                        pATMSockAddr->satm_family = AF_ATM;
                        EndDialog(DialogWindow, TRUE);
                        return TRUE;
                    }

                    return FALSE;

                case IDCANCEL:

                    // The user has pressed cancel.  Kill the dialog box.
                    EndDialog(DialogWindow, FALSE);
                    return(TRUE);

            default:

                break;
            } // switch (WParam)

            break;
    } // switch (Message)

    return(FALSE);
} // ATMSockAddrProc()

BOOL APIENTRY
DefaultConnDlgProc(
    IN HWND DialogWindow,
    IN UINT Message,
    IN WPARAM WParam,
    IN LPARAM LParam)
/*++

Routine Description:

    Callback function that processes messages sent to the default
    connection info dialog box.

Implementation

    This dialog box is the default when chat doesn't recognize a
    particular address family.  The user enters actual hex digits and
    the hex value is stored in the SocketAddress associated with the
    connection. Obviously, this requires the user to know how to
    interpret the structure of the socket address for this address
    family.

Arguments:

    DialogWindow - Supplies handle of the dialog box.

    Message - Supplies the message identifier

    WParam - Supplies the first message parameter

    LParam - Supplies the second message parameter

Return Value:

    TRUE -- This function handled the message.

    FALSE -- This function did not handle the message.

--*/
{

    static PCONNDATA ConnData = NULL;                // connection data
    static int       AddrLen;                        // length of addresses
    char             AddrText[MAX_SOCKADDR_LEN * 2]; // address string

    switch (Message) {

    case WM_INITDIALOG:

        // Get the parameter and figure out how long addresses for
        // this protocol may be.
        ConnData = (PCONNDATA)LParam;
        assert(ConnData != NULL);

        AddrLen = ConnData->ProtocolInfo->iMaxSockAddr;

        if (AddrLen > MAX_SOCKADDR_LEN) {
            MessageBox(DialogWindow,
                       "Sorry, socket addresses are too big. Aborting.",
                       "Error.", MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
            EndDialog(DialogWindow, FALSE);
        }

        // Limit the amount of text in the edit control to twice
        // AddrLen, i.e. 2 hex characters per byte.
        SendMessage(GetDlgItem(DialogWindow, IDC_ADDRESS), EM_LIMITTEXT,
                    (WPARAM)(AddrLen * 2), 0);
        return(TRUE);

    case WM_COMMAND:

        switch (WParam) {

        case IDOK:

            // The user clicked 'OK'.  Get the text from the edit
            // control, and convert the hex string into bytes and put
            // the bytes into the socket address for this connection.
            GetDlgItemText(DialogWindow, IDC_ADDRESS, AddrText,
                           MAX_SOCKADDR_LEN * 2);
            ConnData->RemoteSockAddr.len = AddrLen;
            ConnData->RemoteSockAddr.buf =
              malloc(ConnData->RemoteSockAddr.len);
            if (ConnData->RemoteSockAddr.buf == NULL) {
                ChatSysError("malloc()",

"DefaultConnDlgProc()",
                             TRUE);
            }

            if (TranslateHex(ConnData->RemoteSockAddr.buf,
                             ConnData->RemoteSockAddr.len,
                             AddrText,
                             DialogWindow)) {
                EndDialog(DialogWindow, TRUE);
            }
            return(TRUE);

        case IDCANCEL:

            // Kill the dialog box.
            EndDialog(DialogWindow, FALSE);
            return(TRUE);

        default:

            return(FALSE);
        } // switch (WParam)

    default:

        return(FALSE);
    } // switch (Message)

} // DefaultConnDlgProc()

BOOL APIENTRY
NameAndSubjectDlgProc(
    IN HWND DialogWindow,
    IN UINT Message,
    IN WPARAM WParam,
    IN LPARAM LParam)
/*++

Routine Description:

    Callback function that processes messages sent to the name and
    subject dialog box.

Implementation

    This dialog box is brought up in MakeConnection (see socket.c)
    only if the protocol for the connection to be made supports
    connection-time data transfer.  The user fills in the two fields
    and this data is packed the WSABUF which is referenced through a
    pointer passed in during WM_INITDIALOG processing.

Arguments:

    DialogWindow - Supplies handle of the dialog box.

    Message - Supplies the message identifier

    WParam - Supplies the first message parameter

    LParam - Supplies the second message parameter

Return Value:

    TRUE -- This function handled the message.

    FALSE -- This function did not handle the message; or the message
    was WM_INITDIALOG and we set the focus.

--*/
{

    static LPWSABUF CallerBuffer;            // caller user data
    char            NameText[NAME_LEN + 1];  // name string
    char            SubjectText[SUB_LEN + 1];// subject string

    switch (Message) {

    case WM_INITDIALOG:

        // Get the parameter and initialize the dialog box.
        CallerBuffer = (LPWSABUF)LParam;
        assert(CallerBuffer != NULL);

        SendMessage(GetDlgItem(DialogWindow, IDC_CALLERNAME),
                    EM_LIMITTEXT, (WPARAM)NAME_LEN, 0);
        SendMessage(GetDlgItem(DialogWindow, IDC_SUBJECT),
                    EM_LIMITTEXT, (WPARAM)SUB_LEN, 0);
        SetFocus(GetDlgItem(DialogWindow, IDC_CALLERNAME));
        return(FALSE);

    case WM_COMMAND:

        switch (WParam) {

        case IDOK:

            // Get the strings the user has typed.
            GetDlgItemText(DialogWindow,
                           IDC_CALLERNAME,
                           NameText,
                           NAME_LEN);
            GetDlgItemText(DialogWindow,
                           IDC_SUBJECT,
                           SubjectText,
                           SUB_LEN);

            CallerBuffer->len = strlen(NameText) + strlen(SubjectText) + 2;
            CallerBuffer->buf = (char *)malloc(CallerBuffer->len);
            if (CallerBuffer->buf == NULL) {
                ChatSysError("malloc()",
                             "NameAndSubjectDlgProc()",
                             TRUE);
            }

            if (!PackTwoStrings(CallerBuffer->buf,
                                CallerBuffer->len,
                                NameText,
                                SubjectText)) {
                MessageBox(DialogWindow, "PackTwoStrings failed. Aborting.",
                           "Error.", MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
                EndDialog(DialogWindow, FALSE);
            }

            EndDialog(DialogWindow, TRUE);
            return(TRUE);

        case IDCANCEL:

            // Kill the dialog box.
            EndDialog(DialogWindow, FALSE);
            return(TRUE);

        default:

            return(FALSE);
        } // switch (WParam)

    default:

        return(FALSE);
    } // switch (Message)

} // NameAndSubjectDlgProc()

BOOL APIENTRY
AcceptConnectionDlgProc(
    IN HWND DialogWindow,
    IN UINT Message,
    IN WPARAM WParam,
    IN LPARAM LParam)

/*++

Routine Description:

    Callback function that processes messages sent to the
    AcceptConnection dialog box.  Get's the callee's name and copies
    it into the CalleeBuffer, which is passed in as a parameter to the
    WM_INITDIALOG message.

Arguments:

    DialogWindow - Supplies handle of the AcceptConnection dialog box

    Message - Supplies the message identifier

    WParam - Supplies the first message parameter

    LParam - Supplies the second message parameter

Return Value:

    TRUE -- This function handled the message.

    FALSE -- This function did not handle the message; or the message
    was WM_INITDIALOG and we set the focus.

--*/

{
    static LPWSABUF CalleeData; // callee user data

    switch (Message) {

    case WM_INITDIALOG:

        CalleeData = (LPWSABUF)LParam;
        assert(CalleeData != NULL);

        // Determine how much room there is in the user data buffer.
        // Limit the text to NAME_LEN characters, or less if we
        // don't have room for that many.
        if (CalleeData->len < (NAME_LEN + 1)) {
            SendMessage(GetDlgItem(DialogWindow, IDC_CALLERNAME),
                        EM_LIMITTEXT, (WPARAM)(CalleeData->len - 1),
                        0);
        } else {
            SendMessage(GetDlgItem(DialogWindow, IDC_CALLERNAME),
                        EM_LIMITTEXT, (WPARAM)NAME_LEN, 0);
        }
        SetFocus(GetDlgItem(DialogWindow, IDC_CALLEENAME));
        return(FALSE);

    case WM_COMMAND:

        switch (WParam) {

        case IDOK:

            // Get the name from the control and but it in CalleeData.
            GetDlgItemText(DialogWindow,
                           IDC_CALLEENAME,
                           CalleeData->buf,
                           NAME_LEN);
            CalleeData->len = strlen(CalleeData->buf + 1);
            EndDialog(DialogWindow, TRUE);
            return(TRUE);

        case IDCANCEL:

            // Kill the dialog box
            EndDialog(DialogWindow, FALSE);
            return(TRUE);

        default:

            break;

        }

        break;
    } // switch (Message)

    return(FALSE);
} // AcceptConnectionDlgProc()

BOOL APIENTRY
InetListenPortDlgProc(
    IN HWND DialogWindow,
    IN UINT Message,
    IN WPARAM WParam,
    IN LPARAM LParam)
/*++

Routine Description:

    Callback function that processes messages sent to the
    listening port dialog box.

Arguments:

    DialogWindow - Supplies handle of the AcceptConnection dialog box

    Message - Supplies the message identifier

    WParam - Supplies the first message parameter

    LParam - Supplies the second message parameter

Return Value:

    TRUE -- This function handled the message.

    FALSE -- This function did not handle the message; or the message
    was WM_INITDIALOG and we set the focus.

--*/
{

    static struct sockaddr_in *SockAddrInet;             // Inet sockaddress
    int                       Port;                      // Inet port
    BOOL                      PortStringTranslated;      // for GetDlgItemInt
    char                      PortText[INET_PORT_LEN+1]; // Inet port string

    switch (Message) {

    case WM_INITDIALOG:

        // Get the parameter and initialize the dialog box.
        SockAddrInet = (struct sockaddr_in *)LParam;
        assert(SockAddrInet != NULL);
        SendMessage(GetDlgItem(DialogWindow, IDC_LISTEN_PORT),
                    EM_LIMITTEXT, (WPARAM)INET_PORT_LEN, 0);
        wsprintf(PortText,"%d", INET_DEFAULT_PORT);
        SendMessage(GetDlgItem(DialogWindow, IDC_LISTEN_PORT),
                    WM_SETTEXT, 0, (LPARAM)PortText);
        SetFocus(GetDlgItem(DialogWindow, IDC_LISTEN_PORT));
        return(FALSE);

    case WM_COMMAND:

        switch (WParam) {

        case IDOK:

            // Get the port.
            Port = (int)GetDlgItemInt(DialogWindow,
                                      IDC_LISTEN_PORT,
                                      &PortStringTranslated,
                                      TRUE);
            if ((Port < 0) || (Port > 65535) || !PortStringTranslated) {
                MessageBox(DialogWindow,
                           "Please choose a port between 0 and 65535.",
                           "Bad Port.",
                           MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
                return(TRUE);
            }

            SockAddrInet->sin_port = htons((u_short)Port);
            EndDialog(DialogWindow, TRUE);
            return(TRUE);

        case IDCANCEL:

            // Kill the dialog box.
            EndDialog(DialogWindow, FALSE);
            return(TRUE);

        default:
            break;
        }
        break;

    } // switch (Message)

    return(FALSE);
} // InetListenPortDlgProc()


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by ws2chat.rc
//
#define IDS_STRING1                     1
#define IDC_EDIT2                       1001
#define IDC_ATM_ADDR                    1001
#define IDC_BHLI_INFO                   1002
#define IDC_ADDR_TYPE                   1004
#define IDC_L2PROTO                     1005
#define IDC_L3PROTO                     1006
#define IDC_L3IPI                       1007
#define IDC_BHLI_TYPE                   1008
#define IDC_L2USER_PROTO                1009
#define IDC_L3USER_PROTO                1010
#define IDC_SNAP_ID                     1011
#define IDC_BHLI                        1012

// 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         1010
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif


WS2CHAT.C

/*++

Copyright (c) 1995 Intel Corp

Module Name:

    ws2chat.c

Abstract:

    Contains WinMain and other user interface code for the WinSock2
    Chat sample application.

--*/

#include "nowarn.h"  /* turn off benign warnings */
#ifndef _WINSOCKAPI_
#define _WINSOCKAPI_   /* Prevent inclusion of winsock.h in windows.h */
#endif
#include <windows.h>

#include "nowarn.h"  /* some warnings may have been turned back on */
#include <stdlib.h>
#include "ws2chat.h"
#include "chatsock.h"
#include "chatdlg.h"
#include "queue.h"

//
// Forward References -- internal functions.
//

// Startup/Initialization Functions
int PASCAL
WinMain(
    IN HINSTANCE InstanceHandle,
    IN HINSTANCE PrevInstanceHandle,
    IN LPSTR  CmdLine,
    IN int    CmdShow);

BOOL
InitUI(
    IN HINSTANCE InstanceHandle,
    IN HINSTANCE PrevInstanceHandle);

long CALLBACK
MainWndProc(
    IN HWND   WindowHandle,
    IN UINT   Message,
    IN WPARAM WParam,
    IN LPARAM LParam);

long CALLBACK
ConnWndProc(
    IN HWND   WindowHandle,
    IN UINT   Message,
    IN WPARAM WParam,
    IN LPARAM LParam);

long CALLBACK
SendEditSubClass(
    IN HWND   WindowHandle,
    IN UINT   Message,
    IN WPARAM WParam,
    IN LPARAM LParam);

long CALLBACK
RecvEditSubClass(
    IN HWND   WindowHandle,
    IN UINT   Message,
    IN WPARAM WParam,
    IN LPARAM LParam);

BOOL CALLBACK
CloseEnumProc(
    IN HWND WindowHandle,
    IN LONG LParam);

BOOL
GetConnectionInfo(
    IN HWND ConnectionWindow);

void
QueryAndMaximize(void);

//
// Static Global Variables
//

static HMENU   MainMenu;       // the main MDI menu
static HMENU   MainMenuWindow; // the window associated with the above
static FARPROC OldEditWndProc; // edit control WndProc before subclassing

//
// Externally-Visible Global Variables
//

HANDLE  GlobalInstance;                  // ientifies the instance of chat
char    ConnClassStr[] = "ConnChild";    // string to register window class
char    ChatClassStr[] = "WS2ChatFrame"; // string to register window class
HWND    GlobalFrameWindow;               // Chat's main (frame) window

//
// Function Definitions
//

int CALLBACK
WinMain(
    IN HINSTANCE InstanceHandle,
    IN HINSTANCE PrevInstanceHandle,
    IN LPSTR  CmdLine,
    IN int    CmdShow)

/*++

Routine Description:

    WinMain is the main entry point for WinSock 2 Chat.  This function
    calls initialization functions and goes into a message loop.

Arguments:

    InstanceHandle -- Supplies the current instance handle.

    PrevInstanceHandle -- Supplies the previous instance handle.

    CmdLine -- Supplies the address of the command line.

    CmdShow -- Supplies the show state of the window.

Return Value:

    0 -- Initialization failure

    !0 -- Returns the Message.wParam of the WM_QUIT message that ended
    the message loop.

--*/
{
    HANDLE   AccelTable;   // handle to the accelerator table
    HWND     ClientWindow; // MDI Client window
    MSG      Message;      // holds the message
    int      ReturnValue;  // return value

    ReturnValue = 0;
    GlobalInstance = InstanceHandle;

    // Initialize Chat.
    if (!InitUI(InstanceHandle, PrevInstanceHandle)) {
        MessageBox(NULL, "Couldn't Initialize Chat!", "Error",
                   MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
        goto Done;
    }

    // Create the frame window.
    GlobalFrameWindow = CreateWindow(ChatClassStr,
                                     "WinSock 2 Chat",
                                     WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
                                     CW_USEDEFAULT,
                                     CW_USEDEFAULT,
                                     CW_USEDEFAULT,
                                     CW_USEDEFAULT,
                                     NULL,
                                     (HMENU)MainMenu,
                                     InstanceHandle,
                                     NULL);

    if (GlobalFrameWindow == NULL) {
        ChatSysError("CreateWindow()",
                     "WinMain()",
                     TRUE);
    }

    // Startup WinSock 2.
    if (!InitWS2()) {
        goto Done;
    }

    // Find out about protocols installed.
    if (!FindProtocols()) {
        goto Done;
    }

    // Listen on a local address for all installed protocols.
    if (!ListenAll()) {
        goto Done;
    }

    // Load some handles, update the application window, and enter the
    // main message loop.
    ClientWindow = GetWindow(GlobalFrameWindow, GW_CHILD);
    AccelTable = LoadAccelerators(InstanceHandle, "MdiAccel");
    if (AccelTable == NULL) {
        ChatSysError("LoadAccelerators()",
                     "WinMain()",
                     FALSE);
    }
    ShowWindow(GlobalFrameWindow, CmdShow);
    UpdateWindow(GlobalFrameWindow);

    // Enter the GetMessage loop.
    while (GetMessage(&Message, NULL, 0, 0)) {
        if (!TranslateMDISysAccel(ClientWindow, &Message) &&
            !TranslateAccelerator(GlobalFrameWindow, AccelTable, &Message)) {
             TranslateMessage(&Message);
             DispatchMessage(&Message);
        }
    }

    // WM_QUIT was received.  Cleanup and return.
    WSACleanup();
    ReturnValue = Message.wParam;

 Done:

    return(ReturnValue);

} // WinMain()

BOOL
InitUI(
    IN HINSTANCE InstanceHandle,
    IN HINSTANCE PrevInstanceHandle)

/*++

Routine Description:

    Initializes chat's user interface.  Registers the window classes
    used by chat and obtains handles to chat's menus.

Arguments:

    InstanceHandle - Supplies the current instance handle.

    PrevInstanceHandle - Supplies the previous instance handle.

Return Value:

    TRUE - Chat successfully initialized

    FALSE - Failure during chat initialization

--*/
{
    WNDCLASS WndClass;           // window class structure
    BOOL     ReturnValue = TRUE; // holds return value

    // If this is the first instance of chat, register the window
    // classes.
    if (!PrevInstanceHandle) {
        WndClass.style         = CS_HREDRAW | CS_VREDRAW;
        WndClass.lpfnWndProc   = MainWndProc;
        WndClass.cbClsExtra    = 0;
        WndClass.cbWndExtra    = 0;
        WndClass.hInstance     = InstanceHandle;
        WndClass.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
        WndClass.hCursor       = LoadCursor(NULL, IDC_ARROW);
        WndClass.hbrBackground = (HBRUSH)(COLOR_APPWORKSPACE + 1);
        WndClass.lpszMenuName  = NULL;
        WndClass.lpszClassName = ChatClassStr;
        if (!RegisterClass(&WndClass)) {
            ReturnValue = FALSE;
            goto Done;
        }

        WndClass.style         = CS_HREDRAW | CS_VREDRAW;
        WndClass.lpfnWndProc   = ConnWndProc;
        WndClass.cbClsExtra    = 0;
        WndClass.cbWndExtra    = CONN_WND_EXTRA;
        WndClass.hInstance     = InstanceHandle;
        WndClass.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
        WndClass.hCursor       = LoadCursor(NULL, IDC_ARROW);
        WndClass.lpszMenuName  = NULL;
        WndClass.lpszClassName = ConnClassStr;
        if (!RegisterClass(&WndClass)) {
            ReturnValue = FALSE;
            goto Done;
        }
    }

    // Obtain handles to the menus & submenus.
    MainMenu  = LoadMenu(GlobalInstance, "MdiMenuMain");
    if (MainMenu == NULL) {
        ChatSysError("LoadMenu()",
                     "InitUI()",
                     TRUE);
    }
    MainMenuWindow  = GetSubMenu(MainMenu, MAIN_MENU_POS);

 Done:

    return(ReturnValue);

} // InitUI()

long CALLBACK
MainWndProc(
    IN HWND   WindowHandle,
    IN UINT   Message,
    IN WPARAM WParam,
    IN LPARAM LParam)

/*++

Routine Description:

    WinSock 2 chat's main window procedure.

Arguments:

    WindowHandle - Supplies the window handle for chat's frame window.

    Message - Supplies the message identifier.

    WParam - Supplies the first message parameter.

    LParam - Supplies the second message parameter.

Return Value:

    Return value depends on the message sent.

--*/
{
    static HWND        ClientWindow; // the MDI client window
    CLIENTCREATESTRUCT ClientCreate; // MDI Client window creation data
    HWND               ChildWindow;  // gets handle to child conn. windows
    MDICREATESTRUCT    MdiCreate;    // MDI Child window creation data

    switch (Message) {

    case WM_CREATE:

        // Create the client window.
        ClientCreate.hWindowMenu  = MainMenuWindow;
        ClientCreate.idFirstChild = IDM_FIRSTCHILD;
        ClientWindow = CreateWindow("MDICLIENT",
                                    NULL,
                                    WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE,
                                    0, 0, 0, 0,
                                    WindowHandle,
                                    (HMENU)CLIENT_WINDOW_ID,
                                    GlobalInstance,
                                    (LPSTR)&ClientCreate);
        if (ClientWindow == NULL) {
            ChatSysError("CreateWindow()",
                         "MainWndProc()",
                         TRUE);

        }

        return (0);

    case WM_COMMAND:

        // We know the wm_command is coming from a menu item, so we
        // can safely assume that the high-order word of WParam is
        // zero.
        switch (WParam) {

        case IDM_CONNECT:

            // Create a Connection child window.
            MdiCreate.szClass = ConnClassStr;
            MdiCreate.szTitle = "Connecting...";
            MdiCreate.hOwner  = GlobalInstance;
            MdiCreate.x       = CW_USEDEFAULT;
            MdiCreate.y       = CW_USEDEFAULT;
            MdiCreate.cx      = CW_USEDEFAULT;
            MdiCreate.cy      = CW_USEDEFAULT;
            MdiCreate.style   = 0;
            MdiCreate.lParam  = 0;
            ChildWindow =
              (HWND) SendMessage(ClientWindow, WM_MDICREATE, 0,
                                 (LPARAM)(LPMDICREATESTRUCT)&MdiCreate);

            if (ChildWindow == NULL) {
                ChatSysError("SendMessage(WM_MDICREATE)",
                             "MainWndProc()",
                             TRUE);
            }

            // Get the connection information from the caller and
            // attempt to make a connection...
            if (!GetConnectionInfo(ChildWindow) ||
                !MakeConnection(ChildWindow)) {
                DestroyWindow(ChildWindow);
            }

            break;

        case IDM_CLOSE:

            // Close the active window.
            ChildWindow = (HWND)SendMessage(ClientWindow, WM_MDIGETACTIVE, 0,
                                            0);
            SendMessage(ClientWindow, WM_MDIDESTROY, (WPARAM)ChildWindow, 0);
            break;

        case IDM_EXIT:

            // Exit the program.
            SendMessage(WindowHandle, WM_CLOSE, 0, 0);
            break;

        case IDM_TILE:

            SendMessage(ClientWindow, WM_MDITILE, 0, 0);
            break;

        case IDM_CASCADE:

            SendMessage(ClientWindow, WM_MDICASCADE, 0, 0);
            break;

        case IDM_ARRANGE:

            SendMessage(ClientWindow, WM_MDIICONARRANGE, 0, 0);
            break;

        case IDM_CLOSEALL:

            // Close all child windows.
            EnumChildWindows(ClientWindow, CloseEnumProc, 0);
            break;

        default:

            // Pass to active child.
            ChildWindow = (HWND)SendMessage(ClientWindow, WM_MDIGETACTIVE,
                                            0, 0);

            if (IsWindow(ChildWindow)) {
                SendMessage(ChildWindow, WM_COMMAND, WParam, LParam);
            }

            // This causes us to drop out of the switch and to pass
            // the message on to DefFrameProc().
            break;

        } // switch (WParam)
        break;

    case WM_CLOSE:

        // Close all child windows.
        SendMessage(WindowHandle, WM_COMMAND, IDM_CLOSEALL, 0);
        if (GetWindow(ClientWindow, GW_CHILD) != NULL) {
            return (0);
        }
        break;

    case USMSG_ACCEPT:

        // Create a MDI Child connection window (even though we aren't
        // yet sure if we will accept the connection -- if we don't,
        // it will be immediately destroyed.)
        MdiCreate.szClass = ConnClassStr;
        MdiCreate.szTitle = "Connection Request...";
        MdiCreate.hOwner  = GlobalInstance;
        MdiCreate.x       = CW_USEDEFAULT;
        MdiCreate.y       = CW_USEDEFAULT;
        MdiCreate.cx      = CW_USEDEFAULT;
        MdiCreate.cy      = CW_USEDEFAULT;
        MdiCreate.style   = 0;
        ChildWindow = (HWND)SendMessage(ClientWindow, WM_MDICREATE, 0,
                                        (LPARAM)(LPMDICREATESTRUCT)&MdiCreate);
        if (ChildWindow == NULL) {
            ChatSysError("SendMessage(WM_MDICREATE)",
                         "MainWndProc()",
                         TRUE);
        }

        // Service the incoming connection request.
        HandleAcceptMessage(ChildWindow, (SOCKET)WParam, LParam);
        break;

    case WM_DESTROY:

        // Clean up the listening sockets and kill the application.
        CleanUpSockets();
        PostQuitMessage(0);
        break;

    } // switch(Message)

    // Pass unprocessed messages to DefFrameProc.
    return (DefFrameProc(WindowHandle,
                         ClientWindow,
                         Message,
                         WParam,
                         LParam));

} // MainWndProc()

long CALLBACK
ConnWndProc(
    IN HWND   WindowHandle,
    IN UINT   Message,
    IN WPARAM WParam,
    IN LPARAM LParam)

/*++

Routine Description:

    Windows procedure for each connection window.

Arguments:

    WindowHandle - Supplies the handle to the connection window which
    is to receive the message.

    Message - Supplies the message identifier.

    WParam - Supplies the first message parameter.

    LParam - Supplies the second message parameter.

Return Value:

    Return value depends on the message sent.

--*/
{
    PCONNDATA         ConnData;    // pointer to data for this connection
    RECT              Rect;        // used for painting the window
    HDC               Hdc;         // device context for painting

    switch (Message) {

    case WM_CREATE:

        // Allocate memory for private window data.
        ConnData = (PCONNDATA)malloc(sizeof(CONNDATA));
        if (ConnData == NULL) {
            ChatSysError("malloc()",
                         "ConnWndProc()",
                         TRUE);
        }
        memset((char *)ConnData, 0, sizeof(CONNDATA));
        // Let this to be filled by a getsockopt call later
        ConnData->MaxMsgSize = 0x01;

        // Zero is a valid socket ID so set the socket member to INVALID_SOCKET
        ConnData->Socket = INVALID_SOCKET;

        GetClientRect(WindowHandle, &Rect);

        // Create Send/Recv edit controls.
        ConnData->SendWindow =
          CreateWindow("EDIT",
                       NULL,
                       WS_CHILD | WS_VISIBLE   | WS_VSCROLL |
                       ES_LEFT  | ES_MULTILINE | ES_AUTOVSCROLL,
                       0,
                       0,
                       Rect.right,
                       Rect.bottom/2,
                       WindowHandle,
                       (HMENU)EC_SEND_CHILD,
                       GlobalInstance,
                       NULL);

        ConnData->RecvWindow =
          CreateWindow("EDIT",
                       NULL,
                       WS_CHILD | WS_VISIBLE   | WS_VSCROLL |
                       ES_LEFT  | ES_MULTILINE | ES_AUTOVSCROLL,
                       0,
                       Rect.bottom/2 + 1,
                       Rect.right,
                       Rect.bottom - Rect.bottom/2 - 1,
                       WindowHandle,
                       (HMENU)EC_RECV_CHILD,
                       GlobalInstance,
                       NULL);

        if (!ConnData->SendWindow || !ConnData->RecvWindow) {
            ChatSysError("CreateWindow()",
                         "ConnWndProc()",
                         TRUE);
        }

        // Fill in some connection-specific data.
        ConnData->SocketEventObject = CreateEvent(NULL,
                                                  FALSE,
                                                  FALSE,
                                                  NULL);
        ConnData->OutputEventObject = CreateEvent(NULL,
                                                  FALSE,
                                                  FALSE,
                                                  NULL);
        ConnData->OutputQueue = QCreate();
        if (!ConnData->SocketEventObject || !ConnData->OutputEventObject ||
            !ConnData->OutputQueue) {
            ChatSysError("CreateEvent() or QCreate()",
                         "ConnWndProc()",
                         TRUE);
        }
        ConnData->ConnectionWindow = WindowHandle;
        ConnData->WriteOk = FALSE;

        // Subclass the edit controls. Both functions necessarily
        // return the same FARPROC...
        OldEditWndProc = (FARPROC)SetWindowLong(ConnData->SendWindow,
                                                GWL_WNDPROC,
                                                (DWORD)SendEditSubClass);
        SetWindowLong(ConnData->RecvWindow,
                      GWL_WNDPROC,
                      (DWORD)RecvEditSubClass);

        // Split the connection window in half.
        Hdc = GetDC(WindowHandle);
        MoveToEx(Hdc, 0, Rect.bottom/2, NULL);
        LineTo(Hdc, Rect.right, Rect.bottom/2);
        ReleaseDC(WindowHandle, Hdc);

        // Set the private window data to be a pointer to ConnData.
        SetWindowLong(WindowHandle, GWL_CONNINFO, (LONG)ConnData);

        return (0);

    case WM_COMMAND:

        switch (HIWORD(WParam)) {

        case 0:

            // If the high-order word is zero, this message is being
            // sent from a menu-item (hopefully it was passed to us
            // from the frame window, since the mdi child connection
            // windows, of course, have no menus).
            switch (LOWORD(WParam)) {

            case IDM_CLEAR_SENDBUFFER:

                // Clear the send buffer.
                ConnData = GetConnData(WindowHandle);
                PostMessage(ConnData->SendWindow, EM_SETSEL, 0, -1);
                PostMessage(ConnData->SendWindow, WM_CLEAR, 0, 0);
                return (0);

            case IDM_CLEAR_RECVBUFFER:

                // Clear the receive buffer.
                ConnData = GetConnData(WindowHandle);
                PostMessage(ConnData->RecvWindow, EM_SETSEL, 0, -1);
                PostMessage(ConnData->RecvWindow, WM_CLEAR, 0, 0);
                return (0);

            default:

                // An unknown menu item... just return.
                return (0);

            } // switch (LOWORD(WParam))

        case 1:

            // The high-order word is 1, so thus the wm_command
            // message is coming from an accelerator. Ignore it.
            return(0);

        default:

            // Let the default window procedure handle it (below).
            break;

        } // switch (HIWORD(WParam))
        break;

    case WM_PAINT:

        // Window needs to be painted, split the connection window in
        // half.
        GetClientRect(WindowHandle, &Rect);
        Hdc = GetDC(WindowHandle);
        MoveToEx(Hdc, 0, Rect.bottom/2, NULL);
        LineTo(Hdc, Rect.right, Rect.bottom/2);
        ReleaseDC(WindowHandle, Hdc);

        return (DefMDIChildProc(WindowHandle,
                                Message,
                                WParam,
                                LParam));

    case WM_SIZE:

        ConnData = GetConnData(WindowHandle);

        // Resize the edit control windows.
        MoveWindow(ConnData->SendWindow,
                   0,
                   0,
                   LOWORD(LParam),
                   HIWORD(LParam)/2,
                   TRUE);

        MoveWindow(ConnData->RecvWindow,
                   0,
                   HIWORD(LParam)/2 + 1,
                   LOWORD(LParam),
                   HIWORD(LParam) - HIWORD(LParam)/2 - 1,
                   TRUE);

        return (DefMDIChildProc(WindowHandle,
                                Message,
                                WParam,
                                LParam));

    case USMSG_CONNECT:

        // Handle the new connection.
        HandleConnectMessage(WindowHandle, LParam);
        return(0);

    case WM_DESTROY:

        ConnData = GetConnData(WindowHandle);
        CleanupConnection(ConnData);

        return(DefMDIChildProc(WindowHandle,
                               Message,
                               WParam,
                               LParam));

    } // switch (Message)

    // Pass unprocessed message to DefMDIChildProc.
    return(DefMDIChildProc(WindowHandle,
                           Message,
                           WParam,
                           LParam));

} // ConnWndProc()

long CALLBACK
SendEditSubClass(
    IN HWND   WindowHandle,
    IN UINT   Message,
    IN WPARAM WParam,
    IN LPARAM LParam)

/*++

Routine Description:

    Window procedure used to subclass the edit control for the sending
    window.

Implementation:

    Text is only allowed to be appended to or deleted from the end of
    an edit control's text buffer.  When text is either typed or
    paste, Chat creates an output request data structure and sticks it
    on the output thread for this connection.

Arguments:

    WindowHandle - Supplies the handle to the edit control's window

    Message - Supplies the message identifier.

    WParam - Supplies the first message parameter.

    LParam - Supplies the second message parameter.

Return Value:

    ReturnValue - Depends of the message sent.

--*/
{
    int             Index;        // an index into the edit control's text
    HANDLE          CBDataHandle; // handle to the clipboard data
    LPSTR           CBDataPtr;    // pointer to data to send
    PCONNDATA       ConnData;     // pointer to connection-specific data
    POUTPUT_REQUEST OutReq;       // output request data
    int             TextSize;     // number of bytes of text to send
    DWORD           BytesLeft;    // number of bytes left to send

    ConnData = GetConnData(GetParent(WindowHandle));

    switch (Message) {

    case WM_CHAR:

        // If the character is sendable, enqueue the character on the
        // output queue, and signal the output event.
        if (IsSendable((char)WParam)) {

            OutReq = (POUTPUT_REQUEST) malloc(sizeof(OUTPUT_REQUEST));
            if (OutReq == NULL) {
                ChatSysError("malloc()",
                             "SendEditSubClass()",
                             TRUE);
            }

            // Rather than malloc a one-character buffer, just point
            // Buffer into the already-allocated Char field of the
            // OutReq structure.  This gets rid of a heavy-weight
            // malloc for each character typed.
            OutReq->Buffer.len = sizeof(char);
            OutReq->Buffer.buf = &OutReq->Character;

            // Fill in the OutReq structure.
            *(OutReq->Buffer.buf) = (char)WParam;
            OutReq->Type = NON_OVERLAPPED_IO;
            OutReq->ConnData = ConnData;

            QInsert(ConnData->OutputQueue, (LPVOID)OutReq);
            SetEvent(ConnData->OutputEventObject);
        }

        // Move the cursor to the end of the text buffer.
        Index = GetWindowTextLength(WindowHandle);
        CallWindowProc((WNDPROC)OldEditWndProc,
                       WindowHandle,
                       EM_SETSEL,
                       Index,
                       Index);

        // Make room for the new text in the edit control.
        MakeRoom(WindowHandle, 1);

        // Pass on the message to the original Edit Window Procedure.
        return (CallWindowProc((WNDPROC)OldEditWndProc,
                               WindowHandle,
                               Message,
                               WParam,
                               LParam));

    case WM_PASTE:

        // Get the buffer from the edit control and send it over the
        // socket.
        if (IsClipboardFormatAvailable(CF_TEXT)) {

            if (OpenClipboard(WindowHandle)) {
                CBDataHandle = GetClipboardData(CF_TEXT);

                if (CBDataHandle) {

                    CBDataPtr = GlobalLock(CBDataHandle);
                    TextSize = strlen(CBDataPtr);

                    // Make room in the edit control for the new text; if
                    // this is impossible, just return because MakeRoom
                    // notifies the user internally.
                    if (!MakeRoom(WindowHandle, TextSize)) {
                        GlobalUnlock(CBDataHandle);
                        CloseClipboard();
                        return(0);
                    }

                    // If the text is longer than the maximum message
                    // size, we need to split it into bite-size pieces
                    // for consumption by the transport layer.
                    BytesLeft = TextSize;
                    while (BytesLeft != 0) {

                        // Create a new output request.
                        OutReq =
                          (POUTPUT_REQUEST)malloc(sizeof(OUTPUT_REQUEST));
                        if (OutReq == NULL) {
                            ChatSysError("malloc()",
                                         "SendEditSubClass",
                                         TRUE);
                        }

                        // Determine how much data to send with this
                        // request, and allocate a buffer that size.
                        if (BytesLeft > ConnData->MaxMsgSize) {
                            OutReq->Buffer.len = ConnData->MaxMsgSize;
                        } else {
                            OutReq->Buffer.len = BytesLeft;
                        }
                        OutReq->Buffer.buf =
                          (char *)malloc(OutReq->Buffer.len);
                        if (OutReq->Buffer.buf == NULL) {
                            ChatSysError("malloc()",
                                         "SendEditSubClass",
                                         TRUE);
                        }

                        // Fill in the rest of the OutReq structure
                        // and put it in the output queue.
                        OutReq->Type = OVERLAPPED_IO;
                        OutReq->ConnData = ConnData;
                        memcpy(OutReq->Buffer.buf, CBDataPtr,
                               OutReq->Buffer.len);
                        QInsert(ConnData->OutputQueue, (LPVOID)OutReq);

                        // Update the number of bytes left to send and
                        // the pointer to the rest of the data.
                        BytesLeft -= OutReq->Buffer.len;
                        CBDataPtr += OutReq->Buffer.len;
                    }

                    // Signal the ouput event.
                    SetEvent(ConnData->OutputEventObject);
                    GlobalUnlock(CBDataHandle);

                } else {

                    MessageBox(WindowHandle,
                               "Sorry, couldn't retrieve the clipoard data.",
                               "Error.",
                               MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
                }

                CloseClipboard();

            } // if (OpenClipboard(WindowHandle))
            else {

                MessageBox(WindowHandle,
                           "Couldn't open the clipboard!!",
                           "Try again.",
                           MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
            }

        } // if (IsClipboardFormatAvailable(CF_TEXT))
        else {

            MessageBox(WindowHandle,
                       "Sorry, the CF_TEXT format not available.",

"Error.", MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
        }
        // Move the cursor to the end of the text buffer.
        Index = GetWindowTextLength(WindowHandle);
        CallWindowProc((WNDPROC)OldEditWndProc,
                       WindowHandle,
                       EM_SETSEL,
                       Index,
                       Index);

        // Call the real edit control window procedure.
        return (CallWindowProc((WNDPROC)OldEditWndProc,
                               WindowHandle,
                               Message,
                               WParam,
                               LParam));

    case WM_KEYDOWN:

        // Capture the delete key. nVirtKey: 46 = 'Del'.
        if (WParam == 46) {
            return(0);
        } else {
            return (CallWindowProc((WNDPROC)OldEditWndProc, WindowHandle,
                                   Message, WParam, LParam));
        }

    case WM_CUT:  // Flow through
    case WM_UNDO:

        return (0);

    default:

        return (CallWindowProc((WNDPROC)OldEditWndProc,
                               WindowHandle,
                               Message,
                               WParam,
                               LParam));

    } // switch (Message)

} // SendEditSubClass()

long CALLBACK
RecvEditSubClass(
    IN HWND   WindowHandle,
    IN UINT   Message,
    IN WPARAM WParam,
    IN LPARAM LParam)

/*++

Routine Description:

    Window procedure used to subclass the edit control for the
    receiving window.

Implementation:

    The user cannot modify any of the text displayed in the receiving
    edit control. All text received from the socket associated with
    this edit control is displayed.

Arguments:

    WindowHandle - Supplies the handle to the edit control's window

    Message - Supplies the message identifier.

    WParam - Supplies the first message parameter.

    LParam - Supplies the second message parameter.

Return Value:

    ReturnValue - Depends of the message sent.

--*/
{
    switch (Message) {

    case WM_CHAR:       // Flow through
    case WM_KEYDOWN:    // Flow through
    case WM_UNDO:       // Flow through
    case WM_PASTE:      // Flow through
    case WM_CUT:

        return (0);     // Effectively disables the above messages.

    default:

        return (CallWindowProc((WNDPROC)OldEditWndProc,
                               WindowHandle,
                               Message,
                               WParam,
                               LParam));
    }

} // RecvEditSubClass()

BOOL CALLBACK
CloseEnumProc(
    IN HWND WindowHandle,
    IN LONG LParam)

/*++

Routine Description:

    A callback function used to destroy all of chat's connection
    windows.

Arguments:

    WindowHandle - Supplies the handle to the child's window which
                   is to be closed.

    LParam - Not Used.

Return Value:

    TRUE - Returns true in order to continue enumeration.

--*/
{
    if (!GetWindow(WindowHandle, GW_OWNER)) {

        SendMessage(GetParent(WindowHandle), WM_MDIRESTORE,
                    (WPARAM)WindowHandle, 0);

        if (SendMessage(WindowHandle, WM_QUERYENDSESSION, 0, 0)) {
            SendMessage(GetParent(WindowHandle), WM_MDIDESTROY,
                        (WPARAM)WindowHandle, 0);
        }
    }
    return (TRUE);

} // CloseEnumProc()

void
OutputString(
    IN HWND RecvWindow,
    IN char *String)
/*++

Routine Description:

    This function sends the string received over the socket to the
    edit window given by RecvWindow.

Implementation:

    This function treats certain non-printable characters specially so
    that they mean the same thing on this end as they did on the end
    that sent them.

Arguments:

    RecvWindow -- Handle to the edit control window which is to
    receive the text.

    String -- Points to the NULL-terminated string to be output.

Return Value:

    None.

--*/
{

    int  Index;         // indices into the edit control text
    char *Current = String;     // the character we are examining
    char *First = String;       // the first character we have not output
    int  NumChars;              // how many characters we put in Buffer

    // Make room for the string in the edit control.
    MakeRoom(RecvWindow, strlen(String));

    // This loop goes through the whole string we have been instructed
    // to print in the edit control; when it encounters certain
    // characters in the string, it sends certain messages to the edit
    // control to simulate the right behavior.
    while (*Current) {

        switch (*Current) {

        case '\b':

            // The current character is a backspace.  Print out the
            // string we have so far, and then manually erase a
            // character.
            *Current = '\0';

            // Output the string at 'First'...
            Index = GetWindowTextLength(RecvWindow);
            SendMessage(RecvWindow, EM_SETSEL, Index, Index);
            SendMessage(RecvWindow, EM_REPLACESEL, 0, (LPARAM)First);

            // Get the number of chars in the last line.
            Index = GetWindowTextLength(RecvWindow);
            NumChars = SendMessage(RecvWindow, EM_LINELENGTH, Index, 0);

            // If the line had no characters in it, then it's the
            // beginning of a new line, and we should erase two
            // characters (the preceding \r\n) rather than just one.
            if (NumChars == 0) {
                SendMessage(RecvWindow, EM_SETSEL, Index - 2, Index);
                SendMessage(RecvWindow, EM_REPLACESEL, 0, (LPARAM)"");
            } else {
                SendMessage(RecvWindow, EM_SETSEL, Index - 1, Index);
                SendMessage(RecvWindow, EM_REPLACESEL, 0, (LPARAM)"");
            }

            First = Current + 1;
            break;

        case '\r':

            // A newline.  If the Chat window is minimized, ask the
            // user if they want it maximized.
            QueryAndMaximize();

            // If a remote user typed a newline, we'll receive '\r',
            // but if the remote user pasted it in, we'll get '\r\n'.
            if (*(Current + 1) != '\n') {

                // The newline was typed in...output the string up to
                // the newline, then "\r\n".
                *Current = '\0';

                Index = GetWindowTextLength(RecvWindow);
                SendMessage(RecvWindow, EM_SETSEL, Index, Index);
                SendMessage(RecvWindow, EM_REPLACESEL, 0, (LPARAM)First);

                Index = GetWindowTextLength(RecvWindow);
                SendMessage(RecvWindow, EM_SETSEL, Index, Index);
                SendMessage(RecvWindow, EM_REPLACESEL, 0, (LPARAM)"\r\n");

                First = Current + 1;
            }

            break;

        default:

            break;

        } // switch (*Current)
        Current++;

    } // while (*Current)

    // Ouptut any string leftover in First
    Index = GetWindowTextLength(RecvWindow);
    SendMessage(RecvWindow, EM_SETSEL, Index, Index);
    SendMessage(RecvWindow, EM_REPLACESEL, 0, (LPARAM)First);
}

void
QueryAndMaximize(void)
/*++

Routine Description:

    If Chat's main frame window is minimized, this function asks the
    user if they want to activate and display Chat, and acts
    accordingly.

Arguments:

    None.

Return Value:

    None.

--*/
{
    WINDOWPLACEMENT WinPlace; // holds window placement data

    WinPlace.length = sizeof(WINDOWPLACEMENT);

    // Get chat's main window's state.
    GetWindowPlacement(GlobalFrameWindow, &WinPlace);

    // If it's minimized, ask the user if they want to maximize.
    if (WinPlace.showCmd == SW_SHOWMINIMIZED) {

        if (MessageBox(GlobalFrameWindow,
                       "There is activity on a Chat connection.  Maximize?",
                       "Chat has data.",
                       MB_ICONQUESTION | MB_YESNO | MB_SETFOREGROUND)
            == IDYES) {

            // Maximize chat's main window.
            WinPlace.showCmd = SW_RESTORE;
            SetWindowPlacement(GlobalFrameWindow, &WinPlace);
        }
    }
} // QueryAndMaximize()

BOOL
GetConnectionInfo(
    IN HWND ConnectionWindow)

/*++

Routine Description:

    Queries the user as to what sort of connection (i.e. what address
    family/protocol) she wants to make.

Implementation:

    After popping up a listbox, filling it in with all the available
    protocol choices, and getting the user's choice, this function
    uses it to determine what kind of dialog box to pop up to get the
    actual connection parameters, which are stored in the CONNDATA
    associated with the given ConnectionWindow.

Arguments:

    ConnectionWindow -- Handle to the connection window associated
    with this connection.

Return Value:

    TRUE - The user closed a dialog box by clicking the 'Ok' button.

    FALSE - The user closed a dialog box by clicking the 'Cancel'
            button.

--*/

{
    PCONNDATA ConnData;           // connection-specific data
    BOOL      ReturnValue = TRUE; // holds the return value
    struct sockaddr_atm * pATMSockAddr;

    ConnData = GetConnData(ConnectionWindow);

    // Pop up a dialog box from which the user chooses the protocol; a
    // pointer to a protocol info struct is stored in ConnData.
    if (!DialogBoxParam(GlobalInstance,
                        "ChooseFamilyDlg",
                        ConnectionWindow,
                        ChooseFamilyDlgProc,
                        (LPARAM)ConnData)) {
        ReturnValue = FALSE;
        goto Done;
    }

    // The user has selected a protocol; now pop up an address family
    // specific dialog box to get the address to which we want to make
    // a chat connection, a pointer to which is stored in
    // ConnData->SockAddr (see InetConnDlg, for example).
    switch (ConnData->ProtocolInfo->iAddressFamily) {

        case AF_INET:

            ReturnValue = DialogBoxParam(GlobalInstance,
                                         "InetConnDlg",
                                         ConnectionWindow,
                                         InetConnDlgProc,
                                         (LPARAM)ConnData);
            goto Done;

        case AF_ATM:
            ConnData->RemoteSockAddr.len = sizeof(struct sockaddr_atm);
            ConnData->RemoteSockAddr.buf =
                        (char FAR *)malloc(ConnData->RemoteSockAddr.len);

            pATMSockAddr = (struct sockaddr_atm *)ConnData->RemoteSockAddr.buf;
            pATMSockAddr->satm_number.AddressType  = ATM_NSAP;
            pATMSockAddr->satm_blli.Layer2Protocol = SAP_FIELD_ABSENT;
            pATMSockAddr->satm_blli.Layer3Protocol = SAP_FIELD_ABSENT;
            pATMSockAddr->satm_bhli.HighLayerInfoType = BHLI_UserSpecific;

            ReturnValue = DialogBoxParam(GlobalInstance,
                                         "ATMSOCKADDRDLG",
                                         ConnectionWindow,
                                         ATMSockAddrProc,
                                         (LPARAM)pATMSockAddr);
            goto Done;

        default:

            ReturnValue = DialogBoxParam(GlobalInstance,
                                   "DefaultConnDlg",
                                   ConnectionWindow,
                                   DefaultConnDlgProc,
                                   (LPARAM)ConnData);
            goto Done;
    }

 Done:

    return(ReturnValue);

} // GetConnectionInfo()

BOOL
TranslateHex(
    OUT LPVOID Buffer,
    IN  int    BufferLen,
    IN  char   *HexString,
    IN  HWND   WindowHandle)
/*++

Routine Description:

    This function converts an arbitrarily long string of hexidecimal
    digits into a binary representation.

    Example -- if the string "0fbf" is passed in, then two bytes will
    be written into Buffer -- 15 and 191.

Arguments:

    Buffer -- Points to a buffer where we want the data stored.

    BufferLen -- The length, in bytes, of Buffer.

    HexString -- A null-terminated string of digits with values in the
    ASCII range of 0-9 and a-f.

    WindowHandle -- Window handle needed in case we pop up a dialog
    box.

Return Value:

    TRUE -- HexString was successfully translated.

    FALSE -- One of the characters does not represent a hex digit, or
    the HexString is too long to be translated into BufferLen bytes.

--*/
{

    int  HexStringLen;       // the length of the HexString, in bytes
    int  i;                  // counting variable
    char TwoCharString[3];   // stores two hex characters + a NULL char
    char *NextByte;          // next free byte in the buffer
    BOOL ReturnValue = TRUE; // return value

    HexStringLen  = strlen(HexString);
    NextByte = (char *)Buffer;
    TwoCharString[2] = '\0';

    // Zero out the output buffer.
    memset(NextByte, 0, BufferLen);

    // If the hex string in more than twice as long as the buffer, we
    // don't have a big enough buffer.
    if ((HexStringLen / 2) > BufferLen) {
        ReturnValue = FALSE;
    } else {

        // Go through the hex string two characters at a time...
        for (i = 0; i < HexStringLen; i += 2) {

            // Copy the next two bytes of the hex string into
            // TwoCharString; then check to make sure they are both
            // hexidecimal digits.
            memcpy(TwoCharString, HexString + i, 2);
            if (!isxdigit(TwoCharString[0]) || !isxdigit(TwoCharString[1])) {
                MessageBox(WindowHandle, "Type a hexidecimal number, please",
                           "Error.", MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
                ReturnValue = FALSE;
                break;
            }
            *NextByte++ = (char)strtol(TwoCharString, NULL, 16);

        } // for
    } // else
    return(ReturnValue);

} // TranslateHex()

BOOL
PackTwoStrings(
    OUT char  *Buffer,
    IN  int   BufferLen,
    IN  char  *String1,
    IN  char  *String2)
/*++

Routine Description:

    Packs two strings into one buffer.  Useful for packing caller data
    into a buffer for connection-time data transfer.  It is not an
    error if either string is of zero length, i.e. just the '\0'
    character.

Arguments:

    Buffer -- Pointer to a buffer into which the two strings will be
    packed.

    BufferLen -- The length, in bytes, of Buffer.

    String1 - Address of the first NULL terminated string.

    String2 - Address of the second NULL terminated string.

Return Value:

    TRUE -- The strings were successfully packed into the buffer.

    FALSE -- The passed in buffer was NULL, one or both of the strings
    were NULL, or Buffer is too small.

--*/

{
    int Length1, Length2;    // holds the length of the strings
    BOOL ReturnValue = TRUE; // holds the return value

    if (!Buffer || !String1 || !String2) {
        ReturnValue = FALSE;
        goto Done;
    }

    Length1 = strlen(String1);
    Length2 = strlen(String2);

    if (BufferLen < (Length1 + Length2 + 2)) {
        ReturnValue = FALSE;
        goto Done;
    }

    strcpy(Buffer, String1);
    strcpy(Buffer + Length1 + 1, String2);

 Done:

    return(ReturnValue);

} // PackTwoStrings()

BOOL
ExtractTwoStrings(
    IN  char *Buffer,
    OUT char *String1,
    IN  char Length1,
    OUT char *String2,
    IN  int  Length2)

/*++

Routine Description:

    Extracts two strings from a buffer, expecting them to be packed as
    PackTwoStrings does it.

Arguments:

    Buffer -- The buffer which contains two strings.

    String1 -- A pointer to a buffer of size Length1.

    Length1 -- The length, in bytes, of String1.

    String2 -- A pointer to a buffer of size Length2.

    Length2 -- The length, in bytes, of String2.

Return Value:

    TRUE -- The strings were successfully extracted.

    FALSE -- One of the pointers was NULL, or a packed string was too
    big to be extracted out into a buffer.

--*/

{
    int BufStrLen1, BufStrLen2; // the length of the packed strings
    char *BufStr2;              // points to the second packed string
    BOOL ReturnValue = TRUE;    // holds the return value

    if (!Buffer || !String1 || !String2) {
        ReturnValue = FALSE;
        goto Done;
    }

    BufStrLen1 = strlen(Buffer);
    BufStr2 = Buffer + BufStrLen1 + 1;
    BufStrLen2 = strlen(BufStr2);

    if ((BufStrLen1 > Length1) || (BufStrLen2 > Length2)) {
        ReturnValue = FALSE;
        goto Done;
    }

    strcpy(String1, Buffer);
    strcpy(String2, BufStr2);

 Done:

   return(ReturnValue);

} // ExtractTwoStrings()

void
ChatSysError(
    IN char *FailedFunction,
    IN char *InFunction,
    IN BOOL Fatal)
/*++

Routine Description:

    Pops up a standard message box to display fatal system errors; if
    the error is fatal, the process is ended and this function does
    not return.

Arguments:

    FailedFunction -- Pointer to a string that contains the name of
    the system function that failed.

    InFunction -- Pointer to a string that contains the name of the
    chat function in which the failure occurred.

    Fatal -- Boolean indicating whether the error is fatal to the
    process or not.

Return Value:

    None.

--*/
{

    char MsgText[MSG_LEN]; // holds message strings

    wsprintf(MsgText,
             "System call %s failed in chat function %s. Error code: %d",
             FailedFunction, InFunction, GetLastError());
    MessageBox(GlobalFrameWindow,
               MsgText,
               Fatal ? "Fatal Error." : "Non-Fatal Error.",
               MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);

    if (Fatal) {
        ExitProcess(CHAT_ERROR);
    }

} // ChatSysError()

BOOL
MakeRoom(
    IN HWND EditControl,
    IN int  HowMuch)
/*++

Routine Description:

    This function makes room in an edit control for more text, if
    necessary.

Arguments:

    EditControl -- The edit control in question.

    HowMuch -- How many characters do we want to put into the edit
    control?

Return Value:

    TRUE -- If there was not enough room in the edit control, MakeRoom
    successfully chopped off some text from the beginning, and there
    is now enough room.

    FALSE -- The requested amount can not fit in the edit control,
    period; a message box has informed the user.

--*/
{

    int  TextLength;  // how much text is in the edit control
    int  CharIndex1;
    int  CharIndex2;
    int  LineIndex;   // index variables
    BOOL ReturnValue; // return value

    if (HowMuch > MAX_EC_TEXT) {

        MessageBox(EditControl,
                   "Clipboard too long. Try pasting a smaller amount.",
                   "Error.", MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
        ReturnValue = FALSE;

    } else {

        TextLength = GetWindowTextLength(EditControl);

        if (TextLength + HowMuch > MAX_EC_TEXT) {

            // Inserting the text would exceed our limit.  This code
            // chops off roughly the first half of the edit control's
            // text.
            CharIndex1 = TextLength / 2;
            LineIndex = SendMessage(EditControl, EM_LINEFROMCHAR,
                                    (WPARAM)CharIndex1, 0);
            CharIndex2 = SendMessage(EditControl, EM_LINEINDEX,
                                     (WPARAM)LineIndex, 0);

            SendMessage(EditControl, EM_SETSEL, 0, CharIndex2);
            SendMessage(EditControl, EM_REPLACESEL, 0,
                        (LPARAM)"");
        }

        ReturnValue = TRUE;
    } // else

    return(ReturnValue);
} // MakeRoom()


WS2CHAT.H

/*++

Copyright (c) 1995 Intel Corp

Module Name:

    ws2chat.h

Abstract:

    This module defines the data structures and constants
    for the WinSock 2 sample chat application.

--*/

#ifndef _ws2chat_H
#define _ws2chat_H

#include "nowarn.h"  /* turn off benign warnings */
#ifndef _WINSOCKAPI_
#define _WINSOCKAPI_   /* Prevent inclusion of winsock.h in windows.h */
#endif

#include "nowarn.h"  /* some warnings may have been turned back on */
#include "queue.h"

// turn off "unreferenced formal parameter" warning
#pragma warning(disable: 4100)

//
// Manifest Constants
//

// UI/MDI
#define MAIN_MENU_POS         1
#define CLIENT_WINDOW_ID      1

#define EC_SEND_CHILD         0
#define EC_RECV_CHILD         1

#define IDM_CONNECT           10
#define IDM_CLOSE             12
#define IDM_EXIT              13
#define IDM_TILE              30
#define IDM_CASCADE           31
#define IDM_ARRANGE           32
#define IDM_CLOSEALL          33
#define IDM_CLEAR_SENDBUFFER  34
#define IDM_CLEAR_RECVBUFFER  35
#define IDM_FIRSTCHILD        100

// For some reason, 30k is the most characters you can have in an edit
// control at once.  This keeps us from going over.
#define MAX_EC_TEXT 25500

// resource constants
#define IDC_SUBJECT           1000
#define IDC_CALLEENAME        1001
#define IDC_CALLERNAME        1002
#define IDC_FAM_LB            1003
#define IDC_INET_ADDRESS      1004
#define IDC_INET_PORT         1005
#define IDC_ADDRESS           1006
#define IDC_LISTEN_PORT       1007
#define IDC_SELECTOR          1008

#define IDC_STATIC            -1

// data lengths
#define NAME_LEN              20
#define ADDR_LEN              40
#define SUB_LEN               80
#define MSG_LEN               200
#define TITLE_LEN             40
#define BUFFER_LENGTH         1024

// various stuff
#define MAX_SOCKADDR_LEN      512
#define MAX_LISTENING_SOCKETS 64
#define CONN_WND_EXTRA        8
#define GWL_CONNINFO          0
#define GWL_OLDEDITPROC       4
#define NO_MAX_MSG_SIZE       0xffffffff
#define MAX_ERROR_TEXT        64

// what kind of send?
#define OVERLAPPED_IO         0
#define NON_OVERLAPPED_IO     1

// chat return values
#define CHAT_OK               0
#define CHAT_ERROR            1
#define CHAT_CLOSED           2
#define CHAT_WOULD_BLOCK      3
#define CHAT_ABORTED          4

// for Internet connections
#define INET_ADDR_LEN         64
#define INET_PORT_LEN         5
#define INET_DEFAULT_PORT     9009

// for ATM connections
#define ATM_BHLI_INFO_LEN     8
#define ATM_DEFAULT_BHLI_INFO "ws2chat1"

// user message values
#define USMSG_ACCEPT          WM_USER+1
#define USMSG_CONNECT         WM_USER+2
#define USMSG_TEXTOUT         WM_USER+3

// version of Winsock we need
#define VERSION_MAJOR         2
#define VERSION_MINOR         0

// quality of Service
#define TOKENRATE             100
#define QOS_UNSPECIFIED       (DWORD)-1

//
// Types and Data Structures
//

typedef struct _CONNDATA CONNDATA, *PCONNDATA;
typedef struct _OUTPUT_REQUEST OUTPUT_REQUEST, *POUTPUT_REQUEST;

// structure created by the user interface and passed to the
// input/output thread -- tells the I/O thread what to send and how to
// send it.
struct _OUTPUT_REQUEST {
    int             Type;       // OVERLAPPED_IO or NON_OVERLAPPED_IO
    WSABUF          Buffer;     // holds the buffer (pointer) and size
    char            Character;  // for one-character ouput events
    PCONNDATA       ConnData;   // data for the associated connection
    LPWSAOVERLAPPED Overlapped; // NULL if not an overlapped send
};

// structure for storing data unique to each connection.
struct _CONNDATA {

                    // the socket for the connection
    SOCKET          Socket;

                    // string containing the name of the user (only
                    // valid if user-data is supported by the protocol)
    char            PeerName[NAME_LEN + 1];

                    // string containing the subject of the chat
                    // session, as entered by the calling entity
    char            Subject[SUB_LEN + 1];

                    // string containing the (human-readable) address
                    // of the connected entity
    char            PeerAddress[ADDR_LEN + 1];

                    // handle to thread that takes care of network events
    HANDLE          IOThreadHandle;

                    // handle to event signaled when a network event
                    // occurs
    HANDLE          SocketEventObject;

                    // handle to event signaled when output is ready
                    // to be shipped
    HANDLE          OutputEventObject;

                    // points to a queue used to hold output buffers
    PQUEUE          OutputQueue;

                    // handle to window associated with the connection
    HWND            ConnectionWindow;

                    // handle to the sending and receiving edit controls
    HWND            SendWindow;
    HWND            RecvWindow;

                    // the remote socket address for the connection
    WSABUF          RemoteSockAddr;

                    // buffer used by the connecting entity to get
                    // user-data back when the  USMSG_CONNECT is
                    // received
    WSABUF          CalleeBuffer;

                    // points to a protocol information struct which
                    // represents the protocol for this connection
    LPWSAPROTOCOL_INFO ProtocolInfo;

                    // are we waiting for an FD_WRITE?
    BOOL            WriteOk;

                    // How many events the wait in the I/O thread is
                    // waiting on.  Once that thread starts, this will
                    // always be at least 2 -- the SocketEventObject
                    // and the OutputEventObject.
    DWORD           NumEvents;

                    // The array of events which the I/O thread is
                    // currently waiting on.  Contains NumEvents entries.
    WSAEVENT        EventArray[WSA_MAXIMUM_WAIT_EVENTS];

                    // An array of pointers to output request
                    // structures, indexed in parallel to EventArray,
                    // above. This allows us to associate the events
                    // with the output requests, so we can free the
                    // right memory when the event is signaled.
    POUTPUT_REQUEST OutReqArray[WSA_MAXIMUM_WAIT_EVENTS];

                    // The maximum message size we can send on this
                    // socket. This value is either an integer or the
                    // manifest constant NO_MAX_MSG_SIZE.
    DWORD            MaxMsgSize;
};

// structure to associate listening sockets with a protocol
// information struct.
typedef struct _LISTENDATA {
    SOCKET             Socket;       // a listening socket
    LPWSAPROTOCOL_INFO ProtocolInfo; // the associated protocol info. struct
} LISTENDATA, *PLISTENDATA;

//
// Function prototypes for functions to be used outside of ws2chat.c
//

void
OutputString(
    IN HWND RecvWindow,
    IN char *String);

BOOL
ExtractTwoStrings(
    IN  char *Buffer,
    OUT char *String1,
    IN  char Length1,
    OUT char *String2,
    IN  int  Length2);

BOOL
TranslateHex(
    OUT LPVOID Buffer,
    IN  int    BufferLen,
    IN  char   *HexString,
    IN  HWND   WindowHandle);

BOOL
PackTwoStrings(
    OUT char *Buffer,
    IN int   BufferLen,
    IN char  *String1,
    IN char  *String2);

BOOL
MakeRoom(
    IN HWND EditControl,
    IN int  HowMuch);

void
ChatSysError(
    IN char *FailedFunction,
    IN char *InFunction,
    IN BOOL Fatal);

//
// Externally-Visible Variables
//

extern HANDLE  GlobalInstance;    // Identifies the instance of chat
extern char    ConnClassStr[];    // String to register window class
extern char    ChatClassStr[];    // String to register window class
extern HWND    GlobalFrameWindow; // Chat's main -- or frame -- window

#endif // _ws2chat_


README.TXT

Demonstration of the Windows Sockets API

SUMMARY
=======

The WSOCK sample demonstrates the basics of sockets programming,
specifically for Windows Sockets. It demonstrates how to accept incoming
connections (using the Windows Sockets Asynchronous Extension APIs, threads,
and traditional BSD-style blocking calls) and how to connect to remote
hosts. Once connected, you can send a text string to the remote host. WSOCK
also allows the user to view information on a user-entered host name.

WSOCK demonstrates how to use AcceptEx which is a Microsoft specific
extension to Windows Sockets and which is only supported on Windows NT.

MORE INFORMATION
================

For the sample to operate correctly, the TCP/IP protocol must be properly
installed. Also, if two machines are used over a network, both machines must
have a HOSTS text file (for Windows NT machines, this file is located in
%SYSTEMROOT%\SYSTEM32\DRIVERS\ETC\HOSTS; for Windows for Workgroups
machines, this file is located in C:/WINDOWS/HOSTS). Within each HOSTS file,
both the remote and local addresses of both machines must be listed.

WSOCK can run on a single machine (execute two copies of WSOCK) or over a
network with two machines. The following example explains how two separate
machines over a network would test WSOCK:

 1. Machine "Bob" executes a copy of WSOCK.
 2. Machine "Fred" executes a copy of WSOCK.
 3. Machine "Bob" chooses one of the Listen menu options (under WinSock)
    [Listen (Blocking), Listen With Threads, or Async Listen].
 4. Machine "Fred" selects the Connect menu option (under WinSock).
 5. Machine "Bob" enters "12" as a TCP port number.
 6. Machine "Bob" waits for a connection.
 7. Machine "Fred" enters "Bob" as the host name to connect to.
 8. Machine "Fred" enters "12" as a TCP port number.

Both machines are now connected and can send strings back and forth by using
the WinSock Send Message menu option.

If Bob exits WSOCK while there is a connection, Fred receives a message box
notification.

FUNCTIONS USED
==============

accept
closesocket
connect
gethostbyname
getservbyname
htons
listen
send
recv
WSAAsyncSelect
WSACleanup
WSAStartup


DIALOGS.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.
//-----------------------------------------------------------------------------

/****************************************************************************\
*  dialogs.c -- sample program demonstrating Windows Sockets APIs.
*
*  Handles the dialog boxes for the WSOCK sample.
*
****************************************************************************/

#include <stdio.h>
#include <stdlib.h> /* atoi */
#include <windows.h>
#include "wsock.h"

extern u_short portno; /* Which tcp port are we going to use? */
extern char szBuff[80];

/****************************************************************************
*
*    FUNCTION: GetTcpPort(HWND, UINT, UINT, LONG)
*
*    PURPOSE: dialog callback procedure.  Allows the user to enter the
*             tcp port number, or a service name.
*
*\***************************************************************************/

LRESULT APIENTRY GetTcpPort(
        HWND hDlg,                /* window handle of the dialog box */
        UINT message,             /* type of message                 */
        UINT wParam,              /* message-specific information    */
        LONG lParam)
{

   switch (message) {
      case WM_INITDIALOG:                     /* message: initialize dialog box */
        SetFocus( GetDlgItem( hDlg, IDD_EDIT));
        return (TRUE);

      case WM_COMMAND: {                    /* message: received a command */
        switch(LOWORD(wParam)) {
            case IDOK:                      /* "OK" box selected?          */
            {
               GetDlgItemText( hDlg, IDD_EDIT, szBuff, 80);
               if ((portno = atoi(szBuff)) == 0)
                  EndDialog( hDlg, 2 );
               else
                  EndDialog( hDlg, 1 );
            }
            break;

         case IDCANCEL: /* System menu close command? */
            EndDialog(hDlg, 0);           /* Exits the dialog box        */
            break;

         default:
           return FALSE;
         }
                return (TRUE);
      }
    }   /* switch message */

    return (FALSE);                           /* Didn't process a message    */
        UNREFERENCED_PARAMETER(lParam);
}

/****************************************************************************
*
*    FUNCTION: GetHostName(HWND, UINT, UINT, LONG)
*
*    PURPOSE: dialog callback procedure.  Allows the user to enter the
*             the host name.  Used for menu item "Check Host Name" and
*             also for menu item "Connect".
*
*\***************************************************************************/

LRESULT APIENTRY GetHostName(
        HWND hDlg,                /* window handle of the dialog box */
        UINT message,             /* type of message                 */
        UINT wParam,              /* message-specific information    */
        LONG lParam)
{

    switch (message) {
       case WM_INITDIALOG:                     /* message: initialize dialog box */
         SetFocus( GetDlgItem( hDlg, IDD_EDIT));
         return (TRUE);
       case WM_COMMAND: {                     /* message: received a command */
          switch(LOWORD(wParam)) {
            case IDOK:          /* "OK" box selected?        */
             {
              GetDlgItemText( hDlg, IDD_EDIT, szBuff, 80);
              EndDialog(hDlg, TRUE);          /* Exits the dialog box        */
             }
             break;
             case IDCANCEL:     /* System menu close command? */
              EndDialog(hDlg, FALSE);         /* Exits the dialog box        */
              break;
             default:
              return FALSE;
           }
        return (TRUE);
      }
    }   /* switch message */

    return (FALSE);                           /* Didn't process a message    */
        UNREFERENCED_PARAMETER(lParam);
}
/****************************************************************************
*
*    FUNCTION: About(HWND, UINT, UINT, LONG)
*
*    PURPOSE: dialog callback procedure for "about" box.
*
*\***************************************************************************/

LRESULT APIENTRY About(
        HWND hDlg,                /* window handle of the dialog box */
        UINT message,             /* type of message                 */
        UINT wParam,              /* message-specific information    */
        LONG lParam)
{
     switch (message) {
        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);        /* WM_COMMAND */
     }
     break;
   } /* End switch message */
   return (FALSE);                            /* Didn't process a message    */
        UNREFERENCED_PARAMETER(lParam);
}

/****************************************************************************
*
*    FUNCTION: DisplayHostEnt(HWND, UINT, UINT, LONG)
*
*    PURPOSE: dialog callback procedure.  Displays the information
*             returned by gethostbyname() (HOSTENT structure).
*
*\***************************************************************************/

LRESULT APIENTRY DisplayHostEnt(
        HWND hDlg,                /* window handle of the dialog box */
        UINT message,             /* type of message                 */
        UINT wParam,              /* message-specific information    */
        LONG lParam)
{

   DWORD ret;
   switch (message) {
        /*
        *   Initialize dialog box
        */
        case WM_INITDIALOG:
                {
                int count = 0;

                SetWindowText( GetDlgItem( hDlg, IDD_HOSTNAME),  (LPCTSTR)phe->h_name);

                while (phe->h_aliases[count] != NULL) {
                        SendDlgItemMessage(hDlg, IDD_LBALIAS, LB_ADDSTRING, 0, (LPARAM)(phe->h_aliases[count]));
                        count++;
                        }

                count = 0;

                /*
                *   Enumerate all the hosts IP addresses.
                */
                while (phe->h_addr_list[count] != NULL) {
                    sprintf( szBuff, "%u.%u.%u.%u",
                    (unsigned char) phe->h_addr_list[count][0],
                    (unsigned char) phe->h_addr_list[count][1],
                    (unsigned char) phe->h_addr_list[count][2],
                    (unsigned char) phe->h_addr_list[count][3]);

                    count++;

                    /*
                    *   Fill the dialog box..
                    */
                    if ((ret = SendDlgItemMessage(hDlg, IDD_LBADDR, LB_ADDSTRING, 0, (LPARAM)(LPCTSTR)szBuff)) == LB_ERR)
                       MessageBox(hDlg, szBuff, "Couldn't add address..", MB_OK);
                    }

                } /* while( more IP addresses ) */
           return (TRUE);       /* WM_INITDIALOG */

        /*
        *   Received a command message
        */
        case WM_COMMAND:
                if (LOWORD(wParam) == IDOK) {
                   EndDialog(hDlg, TRUE);             /* Exits the dialog box        */
                   return TRUE;
                }
                return FALSE;   /* WM_COMMAND */
    }   /* switch message */

    return (FALSE);                                   /* Didn't process a message    */
        UNREFERENCED_PARAMETER(lParam);
} /* DisplayHostEnt */

/****************************************************************************
*
*    FUNCTION: GetSendString(HWND, UINT, UINT, LONG)
*
*    PURPOSE: dialog callback procedure.  Allows the user to enter a
*             string to be sent to the connected remote host.
*
*\***************************************************************************/

LRESULT APIENTRY GetSendString(
        HWND hDlg,                /* window handle of the dialog box */
        UINT message,             /* type of message                 */
        UINT wParam,              /* message-specific information    */
        LONG lParam)
{

    switch (message) {
    case WM_INITDIALOG:                     /* message: initialize dialog box */
        SetFocus( GetDlgItem( hDlg, IDD_EDIT));
        return (TRUE);
    case WM_COMMAND: {                     /* message: received a command */
          switch(LOWORD(wParam)) {
            case IDOK:          /* "OK" box selected?        */
             {

              /*
              *   Store string in szBuff (global buffer)
              */
              GetDlgItemText( hDlg, IDD_EDIT, szBuff, 80);
              EndDialog(hDlg, TRUE);          /* Exits the dialog box        */
             }
             break;
             case IDCANCEL:     /* System menu close command? */
              EndDialog(hDlg, FALSE);         /* Exits the dialog box        */
              break;
             default:
              return FALSE;
           }
        return (TRUE);
      }
    }   /* switch message */

    return (FALSE);                           /* Didn't process a message    */
        UNREFERENCED_PARAMETER(lParam);
} /* GetString */


WSOCK.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.
//-----------------------------------------------------------------------------

/****************************************************************************\
*  wsock.c -- sample program demonstrating Windows Sockets APIs.
*
*  Demonstrates basic sockets programming with the Windows Sockets API.
*  Allows two occurances of the application to connect.  Also, displays
*  information about a host computer.
*
****************************************************************************/

#include <windows.h>       /* required for all Windows applications */
#include <stdio.h>         /* for sprintf                           */
#include <string.h>        /* for strlen                            */
#include <memory.h>
#include <process.h>       /* for _beginthread                      */
#include "wsock.h"         /* specific to this program              */

HANDLE hInst;              /* current instance                      */

SOCKET sock;
u_short portno;            /* Which tcp port are we going to use?   */

char szBuff[ 80 ];         /* Temp buffer - used to pass strings    */
                           /* to and from dialog boxes, etc         */

char gpszHelloWorld[]= "Hello World.";

#define MAX_PENDING_CONNECTS 4  /* The backlog allowed for listen() */
#define NO_FLAGS_SET         0  /* Used with recv()/send()          */
#define MY_MSG_LENGTH       80  /* msg buffer sent back and forth   */

/****************************************************************************
*
*    FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int)
*
*    PURPOSE: calls initialization function, processes message loop
*
*\***************************************************************************/

WINAPI WinMain(
    HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPSTR lpCmdLine,
    int nCmdShow
    )
{

    MSG msg;

    UNREFERENCED_PARAMETER( lpCmdLine );

    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                      */
            NULL,                  /* 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           */
   }
    return (msg.wParam);           /* Returns the value from PostQuitMessage */
}

/****************************************************************************
*
*    FUNCTION: InitApplication(HANDLE)
*
*    PURPOSE: Initializes window data and registers window class
*
*\***************************************************************************/

BOOL InitApplication(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 = (WNDPROC)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.hIcon = LoadIcon (hInstance, "wsockicon"); /* Icon name from .RC        */
    wc.hInstance = hInstance;          /* Application that owns the class.   */
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName =  "WSockMenu";   /* Name of menu resource in .RC file. */
    wc.lpszClassName = "WSockWClass"; /* 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
*
*\***************************************************************************/

BOOL InitInstance(
    HANDLE          hInstance,          /* Current instance identifier.       */
    int             nCmdShow)           /* Param for first ShowWindow() call. */
{
    HWND            hWnd;               /* Main window handle.                */

    /* 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.  */

    hWnd = CreateWindow(
        "WSockWClass",                  /* See RegisterClass() call.          */
        "Windows Sockets Sample Application",   /* Text for window title bar.         */
        WS_OVERLAPPEDWINDOW,            /* Window style.                      */
        CW_USEDEFAULT,                  /* Default horizontal position.       */
        CW_USEDEFAULT,                  /* Default vertical position.         */
        CW_USEDEFAULT,                  /* Default width.                     */
        CW_USEDEFAULT,                  /* Default height.                    */
        NULL,                           /* Overlapped windows have no parent. */
        NULL,                           /* Use the window class menu.         */
        hInstance,                      /* This instance owns this window.    */
        NULL                            /* Pointer not needed.                */
    );

    /* If window could not be created, return "failure" */

    if (!hWnd)
        return (FALSE);

    /* Make the window visible; update its client area; and return "success" */

    ShowWindow(hWnd, nCmdShow);  /* Show the window                        */
    UpdateWindow(hWnd);          /* Sends WM_PAINT message                 */
    return (TRUE);               /* Returns the value from PostQuitMessage */

}

/****************************************************************************\
*
*    FUNCTION: AcceptThreadProc(PTHREADPACK tp)
*
*    PURPOSE:  Use blocking accept() calls and display a message box when
*              a connection is made.
*
*\***************************************************************************/

void AcceptThreadProc( PTHREADPACK ptp )
{
   SOCKADDR_IN acc_sin;    /* Accept socket address - internet style */
   int acc_sin_len;        /* Accept socket address length */
   int status;
   char szMsg[ MY_MSG_LENGTH ];

   acc_sin_len = sizeof(acc_sin);

   wsprintf( szBuff, "thread #%d created.", ptp->nThread);
   MessageBox(ptp->hWnd, szBuff, "FYI", MB_OK);

   sock = accept( sock,(struct sockaddr FAR *) &acc_sin,
            (int FAR *) &acc_sin_len );

   if (sock < 0) {
      sprintf(szBuff, "%d is the error", WSAGetLastError());

      MessageBox(ptp->hWnd, szBuff, "accept(sock) failed", MB_OK);

   }

   wsprintf( szBuff, "Thread #%d accepted something\n\nCheck for incoming messages?", ptp->nThread);

   /*
   *   Now have a connection --
   *   SetConnectMenus() grays/enables proper menu items
   */
   SetConnectMenus( ptp->hWnd );

   while (1) {

      /*
      *   By default sockets are created in blocking mode.
      *   Just keep reading until process destroyed.
      */
      status = recv( sock, szMsg, MY_MSG_LENGTH, NO_FLAGS_SET );

      if (status == SOCKET_ERROR) {
         wsprintf( szMsg, "Error %d", WSAGetLastError() );
         MessageBox( ptp->hWnd, szMsg, "Error with recv()", MB_OK);
         _endthread();
      }
          szMsg[status] = '\0';  /* NULL-terminate the string */

      if (status)
         MessageBox( ptp->hWnd, szMsg, "From thread", MB_OK);
      else  {
         MessageBox( ptp->hWnd, "Connection broken", "Error", MB_OK);
         _endthread();
      }

   }    /* while (forever) */
}

/****************************************************************************\
*
*    FUNCTION:  FillAddr(HWND, PSOCKADDR_IN, BOOL)
*
*    PURPOSE:  Retrieves the IP address and port number.
*
*    COMMENTS:
*        This function is called in two conditions.
*            1.) When a client is preparing to call connect(), or
*            2.) When a server host is going to call bind(), listen() and
*                accept().
*        In both situations, a SOCKADDR_IN structure is filled.
*        However, different fields are filled depending on the condition.
*
*   ASSUMPTION:
*      szBuff is a global variable that contains the remote host name or NULL
*      if local.
*      bClient determines if this is being called by a client ( will be
*         performing a connect ) or a server ( will be listening )
*
*
*\***************************************************************************/

BOOL FillAddr(
        HWND hWnd,
        PSOCKADDR_IN psin,
        BOOL bClient)
{
   DWORD dwSize;
   PHOSTENT phe;
   PSERVENT pse;
   char szTemp[200];
   int status;

   psin->sin_family = AF_INET;

   /*
   *   If we are setting up for a listen() call (bConnect = FALSE),
   *   fill servent with our address.
   */
   if (bClient) {
      phe = gethostbyname(szBuff);
      if (phe == NULL) {
         sprintf(szTemp, "%d is the error. Make sure '%s' is listed in the hosts file.", WSAGetLastError(), szBuff);

         MessageBox(hWnd, szTemp, "gethostbyname() failed.", MB_OK);
         return FALSE;
      }
      memcpy((char FAR *)&(psin->sin_addr), phe->h_addr,
         phe->h_length);

      }
   else { // server

      /*
      *   Retrieve my ip address.  Assuming the hosts file in
      *   in %systemroot%/system/drivers/etc/hosts contains my computer name.
      */

      dwSize = sizeof(szBuff);
      gethostname(szBuff, dwSize);

      psin->sin_addr.s_addr = INADDR_ANY;
      }

   /*
   *   Retrieve the Port number
   */
   status = DialogBox(hInst,             /* current instance         */
      "TCPPORTNUM",                      /* resource to use          */
      hWnd,                              /* parent handle            */
      GetTcpPort);                       /* instance address         */

   switch(status) {
      case 0:               /* User cancelled request from prev. dialog box */
         return FALSE;

      case 1:               /* actual port number entered */
         psin->sin_port = htons(portno);        /* Convert to network ordering */
         break;

      case 2:               /* service name entereted */
         /*
         *   Find the service name, szBuff, which is a type tcp protocol in
         *   the "services" file.
         */
         pse = getservbyname(szBuff, "tcp");
         if (pse == NULL)  {
            sprintf(szBuff, "%d is the error. Make sure this is a valid TCP service.", WSAGetLastError());
            MessageBox(hWnd, szBuff, "getservbyname(sock) failed", MB_OK);
            return FALSE;
         }
         psin->sin_port = pse->s_port;
         break;

      default:
         return FALSE;
   }
   return TRUE;
}

/****************************************************************************
*
*    FUNCTION: SetConnectMenus( HWND )
*
*    PURPOSE: Gray/Enable the proper menu items after a connection has been
*             established.
*
*\***************************************************************************/

void SetConnectMenus( HWND hWnd )
{
   /*
   *   Disable/enable proper menu items.
   */
   EnableMenuItem(GetMenu( hWnd ), IDM_HOSTNAME, MF_ENABLED );
   EnableMenuItem(GetMenu( hWnd ), IDM_LISTEN, MF_GRAYED );
   EnableMenuItem(GetMenu( hWnd ), IDM_CONNECT, MF_GRAYED );
   EnableMenuItem(GetMenu( hWnd ), IDM_ALISTEN, MF_GRAYED );
   EnableMenuItem(GetMenu( hWnd ), IDM_TLISTEN, MF_GRAYED );
   EnableMenuItem(GetMenu( hWnd ), IDM_CANCEL, MF_GRAYED );
   EnableMenuItem(GetMenu( hWnd ), IDM_SENDTCP, MF_ENABLED );
   EnableMenuItem(GetMenu( hWnd ), IDM_ACCEPTEX, MF_GRAYED );
   EnableMenuItem(GetMenu( hWnd ), IDM_CONNECTANDSEND, MF_GRAYED );

   /*
   *   Reflect socket connection in title bar.
   */
   SetWindowText( hWnd, "Connected");
}

/****************************************************************************\
*
*    FUNCTION: MainWndProc(HWND, unsigned, WORD, LONG)
*
*    PURPOSE:  Processes main window messages
*
* MESSAGES:
*  WM_CREATE   - call WSAStartUp() and display description message
*  WSA_ACCEPT  - User-defined message used with WSAAsyncSelect().  Sent
*                by the Windows Sockets DLL when a socket connection is
*                pending.
*
*  WM_COMMAND
*  IDM_CONNECT - Connect to a remote host.
*  IDM_LISTEN  - Use the BSD-Style accept().
*  IDM_ALISTEN - Use the Windows Sockets Asynchronous APIs to detect when
*                a connection is made.
*  IDM_CANCEL  - Cancel the Asynchronous call above.
*  IDM_TLISTEN - Uses two threads to accept network connections (using the
*                BSD-Style accept().
*  IDM_HOSTNAME- Display information about a host.
*  IDM_ABOUT   - About box.
*
*  WM_DESTROY  - destroy window and call the WSACleanUp()
*
*\***************************************************************************/

LONG APIENTRY MainWndProc(
        HWND hWnd,                /* window handle                   */
        UINT message,             /* type of message                 */
        UINT wParam,              /* additional information          */
        LONG lParam)              /* additional information          */
{
   int status;             /* Status Code */
   SOCKADDR_IN local_sin;  /* Local socket - internet style */
   SOCKADDR_IN acc_sin;    /* Accept socket address - internet style */
   int acc_sin_len;        /* Accept socket address length */

   switch (message) {
   case WM_CREATE:
   {
      WSADATA WSAData;
      char szTemp[80];

      if ((status = WSAStartup(MAKEWORD(1,1), &WSAData)) == 0) {
         MessageBox( hWnd, WSAData.szDescription, WSAData.szSystemStatus, MB_OK);
      }
      else {
         sprintf(szTemp, "%d is the err", status);
         MessageBox( hWnd, szTemp, "Error", MB_OK);
      }
   }
   break;   /* WM_CREATE */

   /*
   *    Notification if data is waiting on a socket.  This comes
   *    from Windows Sockets (via WSAAsyncSelect()).
   */
   case WSA_READ:
   {
      char szTemp[ MY_MSG_LENGTH ];

      if (WSAGETSELECTEVENT(lParam) == FD_READ) {
         status = recv((SOCKET)wParam, szTemp, MY_MSG_LENGTH, NO_FLAGS_SET );

         if (status) {
            szTemp[ status ] = '\0';
            MessageBox( hWnd, szTemp, "WSA_READ", MB_OK);
         }
         else
            MessageBox( hWnd, "Connection broken", "Error", MB_OK);
      }
      else {    /* FD_CLOSE -- connection dropped */
         MessageBox( hWnd, "Connection lost", "WSA_READ", MB_OK);
         EnableMenuItem(GetMenu( hWnd ), IDM_HOSTNAME, MF_ENABLED);
         EnableMenuItem(GetMenu( hWnd ), IDM_LISTEN, MF_ENABLED);
         EnableMenuItem(GetMenu( hWnd ), IDM_CONNECT, MF_ENABLED);
         EnableMenuItem(GetMenu( hWnd ), IDM_ALISTEN, MF_ENABLED);
         EnableMenuItem(GetMenu( hWnd ), IDM_TLISTEN, MF_ENABLED);
         EnableMenuItem(GetMenu( hWnd ), IDM_CANCEL, MF_GRAYED);
         EnableMenuItem(GetMenu( hWnd ), IDM_SENDTCP, MF_GRAYED);
      }

   }
   break;       /* WSA_READ*/

   case WSA_ACCEPT: /* Notification if a socket connection is pending. */
      /*
      *   Disable/enable proper menu items.
      */
      EnableMenuItem(GetMenu( hWnd ), IDM_HOSTNAME, MF_ENABLED);
      EnableMenuItem(GetMenu( hWnd ), IDM_LISTEN, MF_ENABLED);
      EnableMenuItem(GetMenu( hWnd ), IDM_CONNECT, MF_ENABLED);
      EnableMenuItem(GetMenu( hWnd ), IDM_ALISTEN, MF_ENABLED);
      EnableMenuItem(GetMenu( hWnd ), IDM_TLISTEN, MF_ENABLED);
      EnableMenuItem(GetMenu( hWnd ), IDM_CANCEL, MF_GRAYED);

      if (WSAGETSELECTERROR( lParam ) == 0) {   /* Success */

         /*
         *   Accept the incoming connection.
         */
         acc_sin_len = sizeof( acc_sin );
         sock = accept( sock,(struct sockaddr FAR *) &acc_sin,
            (int FAR *) &acc_sin_len );

         if (sock < 0) {
            sprintf(szBuff, "%d is the error", WSAGetLastError());

            MessageBox(hWnd, szBuff, "accept(sock) failed", MB_OK);
            break;
         }

         MessageBox(hWnd, "accept()", "Accepted a connection!", MB_OK);

         /*
         *   Now have a connection --
         *   SetConnectMenus() grays/enables proper menu items
         */
         SetConnectMenus( hWnd );

         /*
         *   Send main window a WSA_READ when either data is pending on
         *   the socket (FD_READ) or the connection is closed (FD_CLOSE)
         */
         if ((status = WSAAsyncSelect( sock, hWnd, WSA_READ, FD_READ | FD_CLOSE )) > 0) {
            wsprintf(szBuff, "%d (0x%x)", status, status);
            MessageBox( hWnd, "Error on WSAAsyncSelect()", szBuff, MB_OK);
            closesocket( sock );
         }
      }
      else {
         MessageBox(hWnd, "accept()", "Error occured!", MB_OK);

         /*
         *   Cancel any further notifications.
         */
         WSAAsyncSelect( sock, hWnd, 0, 0);
         SetWindowText( hWnd, "Async Listen call canceled");
      }
      break;   /* WSA_ACCEPT */

   case WM_COMMAND:        /* message: command from application menu */
      switch(LOWORD(wParam)) {
      case IDM_CONNECT: /* Client - connect to remote host */
      {
         /*

         When a network client wants to connect to a server,
         it must have:
            1.) a TCP port number (gotten via getservbyname())
            and
            2.) an IP address of the remote host (gotten via gethostbyname()).

         The following summarizes the steps used to connect.
         Make a dialog box (HostName)
         Get the name of the remote host computer in which
          to connect from the user (store string in "szBuff" global var)
       * Check to see if the hosts file knows the computer (gethostbyname)
       * Get the host information (hostent structure filled)
       * Fill in the address of the remote host into the servent structure (memcpy)
       * Make a dialog box (TCPPORTNUM)
       * Get the NAME of the port to connect to on the remote host from the
         user.
       * Get the port number (getservbyname)
       * Fill in the port number of the servent structure
         Establish a connection (connect)

         The * prefixed steps are done in the FillAddr() procedure.

         */
         SOCKADDR_IN dest_sin;  /* DESTination Socket INternet */

         /* Get the name of the remote host. Store the string in szBuff. */

         status = DialogBox(hInst,
            "HOSTNAME",
            hWnd,
            GetHostName);

         if (!status)   /* User cancelled request from prev. dialog box */
            break;

         sock = socket( AF_INET, SOCK_STREAM, 0);
         if (sock == INVALID_SOCKET) {
            MessageBox(hWnd, "socket() failed", "Error", MB_OK);
            break;
         }

         /*
         *    Retrieve the IP address and TCP Port number
         *    Global variable szBuff contains the remote host name.
         */
         if (!FillAddr( hWnd, &dest_sin, TRUE)) {
            closesocket( sock );
            break;
         }

         if (connect( sock, (PSOCKADDR) &dest_sin, sizeof( dest_sin)) < 0) {
            closesocket( sock );
            MessageBox(hWnd, "connect() failed", "Error", MB_OK);
            break;
         }
         MessageBox(hWnd, "connect() worked!", "Success!", MB_OK);

         /*
         *   Now have a connection --
         *   SetConnectMenus() grays/enables proper menu items
         */
         SetConnectMenus( hWnd );

         /*
         *   Send main window a WSA_READ when either data is pending on
         *   the socket (FD_READ) or the connection is closed (FD_CLOSE)
         */
         if ((status = WSAAsyncSelect( sock, hWnd, WSA_READ, FD_READ | FD_CLOSE )) > 0) {
            wsprintf(szBuff, "%d (0x%x)");
            MessageBox( hWnd, "Error on WSAAsyncSelect()", szBuff, MB_OK);
            closesocket( sock );
         }

      }
      break;   /* IDM_CONNECT */

      case IDM_CONNECTANDSEND:
      {
         //
         //
         //  In order to simulate a more typical real-world client, here
         //   we make the connection, and then immediately send it data.
         //
         //  See IDM_CONNECT above for full comments.

         SOCKADDR_IN dest_sin;

         status = DialogBox(hInst,
            "HOSTNAME",
            hWnd,
            GetHostName);

         if (!status)
            break;

         sock = socket( AF_INET, SOCK_STREAM, 0);
         if (sock == INVALID_SOCKET) {
            MessageBox(hWnd, "socket() failed", "Error", MB_OK);
            break;
         }

         if (!FillAddr( hWnd, &dest_sin, TRUE)) {
            closesocket( sock );
            break;
         }

         if (connect( sock, (PSOCKADDR) &dest_sin, sizeof( dest_sin)) < 0) {
            MessageBox(hWnd, "connect() failed", "Error", MB_OK);
            closesocket( sock );
            break;
         }

         if (send (sock, gpszHelloWorld, lstrlen (gpszHelloWorld), 0 ) == SOCKET_ERROR) {
            wsprintf(szBuff, "WSAGetLastError: %d", WSAGetLastError());
            MessageBox( hWnd, szBuff, "Error on send()", MB_OK);
            closesocket( sock );
            break;
         }

         MessageBox(hWnd, "connect() & send() worked!", "Success!", MB_OK);

         SetConnectMenus( hWnd );

         if ((status = WSAAsyncSelect( sock, hWnd, WSA_READ, FD_READ | FD_CLOSE )) > 0) {
            wsprintf(szBuff, "WSAGetLastError: %d", WSAGetLastError());
            MessageBox( hWnd, "Error on WSAAsyncSelect()", szBuff, MB_OK);
            closesocket( sock );
         }

      }
      break;   // IDM_CONNECTANDSEND

      case IDM_ACCEPTEX:
      {
        //
        // Demonstrate the use of AcceptEx().
        //

#define MAX_BYTES        2000
#define PADDED_ADDR_SIZE sizeof(SOCKADDR_IN)+16
        SOCKET sListenSocket;
        SOCKET sAcceptSocket;
        char   pData [MAX_BYTES];
        OVERLAPPED olResult;
        DWORD dwBytes;

        //
        // For this API, we need to create two sockets ahead of time.
        //  The listen socket goes through the standard bind/listen steps.
        //

        sListenSocket = socket( AF_INET, SOCK_STREAM, 0);
        sAcceptSocket = socket( AF_INET, SOCK_STREAM, 0);
        if ((sListenSocket == INVALID_SOCKET) || (sAcceptSocket == INVALID_SOCKET)) {
           MessageBox(hWnd, "sock == INVALID_SOCKET", "socket() failed", MB_OK);
           closesocket(sListenSocket);
           closesocket(sAcceptSocket);
           break;
        }

        //
        //   Retrieve the IP address and TCP Port number
        //

        if (!FillAddr(hWnd, &local_sin, FALSE ))
           break;

        //
        //   Associate an address with the socket
        //

        if (bind( sListenSocket, (struct sockaddr FAR *) &local_sin, sizeof(local_sin)) == SOCKET_ERROR) {
           wsprintf(szBuff, "WSAGetLastError: %d", WSAGetLastError());
           MessageBox(hWnd, szBuff, "bind(sock) failed", MB_OK);
           break;
        }

        //
        //   And go into the listening state.
        //

        if (listen( sListenSocket, MAX_PENDING_CONNECTS ) < 0) {
           wsprintf(szBuff, "WSAGetLastError: %d", WSAGetLastError());
           MessageBox(hWnd, szBuff, "listen(sock) failed", MB_OK);
           break;
        }

        //
        //   Disable/enable proper menu items.
        //

        EnableMenuItem(GetMenu( hWnd ), IDM_HOSTNAME, MF_GRAYED);
        EnableMenuItem(GetMenu( hWnd ), IDM_LISTEN, MF_GRAYED);
        EnableMenuItem(GetMenu( hWnd ), IDM_ALISTEN, MF_GRAYED);
        EnableMenuItem(GetMenu( hWnd ), IDM_TLISTEN, MF_GRAYED);
        EnableMenuItem(GetMenu( hWnd ), IDM_CONNECT, MF_GRAYED);
        EnableMenuItem(GetMenu( hWnd ), IDM_ACCEPTEX, MF_GRAYED);
        EnableMenuItem(GetMenu( hWnd ), IDM_CONNECTANDSEND, MF_GRAYED);

        SetWindowText( hWnd, "AcceptEx..");

        //
        //  Create an event for our overlapped structure (required).
        //

        memset (&olResult,  0, sizeof(olResult));
        olResult.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
        dwBytes = 0;

        //
        //  AcceptEx makes the most sense when used with I/O
        //   CompletionPorts and TransmitFile.
        //   We show a very basic usage here...
        //

        if (AcceptEx( sListenSocket,
                      sAcceptSocket,
                      pData,
                      MAX_BYTES - 2*PADDED_ADDR_SIZE,
                      PADDED_ADDR_SIZE,
                      PADDED_ADDR_SIZE,
                      &dwBytes,
                      &olResult) == FALSE) {

            if (WSAGetLastError() != ERROR_IO_PENDING) {
               wsprintf(szBuff, "WSAGetLastError: %d", WSAGetLastError());
               MessageBox( hWnd, szBuff, "Error on AcceptEx()", MB_OK);
               break;
            } else {

               //
               // In our contrived scenario, we expect GetLastError to be
               //  ERROR_IO_PENDING.  A real app would do other work.
               //  Since this is a simple sample, we will just block waiting
               //  for some input to come through the socket.
               //

               GetOverlappedResult ( (HANDLE) sAcceptSocket,
                                     &olResult,
                                     &dwBytes,
                                     TRUE);
               pData[dwBytes] = 0;
               MessageBox(hWnd, pData, "AcceptEx received data", MB_OK);

            }
        }

        //
        //  As per the documentation, poke the new socket so that it will have
        //   the correct properties and can be used by other functions.
        //

        if (setsockopt( sAcceptSocket,
                        SOL_SOCKET,
                        SO_UPDATE_ACCEPT_CONTEXT,
                        (char *)&sListenSocket,
                        sizeof(sListenSocket) )) {
                wsprintf(szBuff, "WSAGetLastError: %d", WSAGetLastError());
                MessageBox( hWnd, szBuff, "Error in setsockopt()", MB_OK);
                break;
        } else {

        //
        //  sAcceptSocket is now OK for use by other functions.
        //  set it into our global socket (sock)
        //
            sock = sAcceptSocket;
        }

        //
        //  In any case, we are done with our listen socket
        //

        closesocket( sListenSocket );

        //
        //  Now have a connection --
        //   SetConnectMenus() grays/enables proper menu items

        SetConnectMenus( hWnd );

        //
        //  Send main window a WSA_READ when either data is pending on
        //  the socket (FD_READ) or the connection is closed (FD_CLOSE)

        if ((status = WSAAsyncSelect( sock, hWnd, WSA_READ, FD_READ | FD_CLOSE )) > 0) {
           wsprintf(szBuff, "%d (0x%x)");
           MessageBox( hWnd, "Error on WSAAsyncSelect()", szBuff, MB_OK);
           closesocket( sock );
        }
      }
      break;   /* IDM_ACCEPTEX */

      case IDM_LISTEN:
      {
         sock = socket( AF_INET, SOCK_STREAM, 0);
         if (sock == INVALID_SOCKET) {
            MessageBox(hWnd, "socket() failed", "Error", MB_OK);
            closesocket(sock);
            break;
         }

         /*
         *   Retrieve the IP address and TCP Port number
         */

         if (!FillAddr(hWnd, &local_sin, FALSE ))
            break;

         /*
         *   Disable/enable proper menu items.
         */
         EnableMenuItem(GetMenu( hWnd ), IDM_HOSTNAME, MF_GRAYED);
         EnableMenuItem(GetMenu( hWnd ), IDM_LISTEN, MF_GRAYED);
         EnableMenuItem(GetMenu( hWnd ), IDM_ALISTEN, MF_GRAYED);
         EnableMenuItem(GetMenu( hWnd ), IDM_TLISTEN, MF_GRAYED);
         EnableMenuItem(GetMenu( hWnd ), IDM_CONNECT, MF_GRAYED);
         EnableMenuItem(GetMenu( hWnd ), IDM_ACCEPTEX, MF_GRAYED);
         EnableMenuItem(GetMenu( hWnd ), IDM_CONNECTANDSEND, MF_GRAYED);

         SetWindowText( hWnd, "Waiting for connection..");

         /*
         *   Associate an address with a socket. (bind)
         */
         if (bind( sock, (struct sockaddr FAR *) &local_sin, sizeof(local_sin)) == SOCKET_ERROR) {
            sprintf(szBuff, "%d is the error", WSAGetLastError());

            MessageBox(hWnd, szBuff, "bind(sock) failed", MB_OK);
            break;
         }

         if (listen( sock, MAX_PENDING_CONNECTS ) < 0) {
            sprintf(szBuff, "%d is the error", WSAGetLastError());

            MessageBox(hWnd, szBuff, "listen(sock) failed", MB_OK);
            break;
         }

         acc_sin_len = sizeof(acc_sin);

         sock = accept( sock,(struct sockaddr FAR *) &acc_sin,
            (int FAR *) &acc_sin_len );
         if (sock < 0) {
            sprintf(szBuff, "%d is the error", WSAGetLastError());

            MessageBox(hWnd, szBuff, "accept(sock) failed", MB_OK);
            break;
         }

         MessageBox(hWnd, "accept()", "Accepted a connection!", MB_OK);

         /*
         *   Now have a connection --
         *   SetConnectMenus() grays/enables proper menu items
         */
         SetConnectMenus( hWnd );

         /*
         *   Send main window a WSA_READ when either data is pending on
         *   the socket (FD_READ) or the connection is closed (FD_CLOSE)
         */
         if ((status = WSAAsyncSelect( sock, hWnd, WSA_READ, FD_READ | FD_CLOSE )) > 0) {
            wsprintf(szBuff, "%d (0x%x)");
            MessageBox( hWnd, "Error on WSAAsyncSelect()", szBuff, MB_OK);
            closesocket( sock );
         }

      }
      break;   /* IDM_LISTEN */

      /*
      *   Asynchronous Listen - Using WSA extensions.
      */
      case IDM_ALISTEN:
      {

         sock = socket( AF_INET, SOCK_STREAM, 0);
         if (sock == INVALID_SOCKET) {
            MessageBox(hWnd, "socket() failed", "Error", MB_OK);
            break;
         }
         /*
         *   Retrieve the IP address and TCP Port number
         */

         if (!FillAddr( hWnd, &local_sin, FALSE)) {
            closesocket( sock );
            break;
         }

         /*
         *   Disable/enable proper menu items.
         */
         EnableMenuItem(GetMenu( hWnd ), IDM_HOSTNAME, MF_GRAYED);
         EnableMenuItem(GetMenu( hWnd ), IDM_LISTEN, MF_GRAYED);
         EnableMenuItem(GetMenu( hWnd ), IDM_CONNECT, MF_GRAYED);
         EnableMenuItem(GetMenu( hWnd ), IDM_ALISTEN, MF_GRAYED);
         EnableMenuItem(GetMenu( hWnd ), IDM_TLISTEN, MF_GRAYED);
         EnableMenuItem(GetMenu( hWnd ), IDM_CANCEL, MF_ENABLED);
         EnableMenuItem(GetMenu( hWnd ), IDM_ACCEPTEX, MF_GRAYED);
         EnableMenuItem(GetMenu( hWnd ), IDM_CONNECTANDSEND, MF_GRAYED);

         SetWindowText( hWnd, "Waiting for connection.. (Async)");

         /*
         *   Associate an address with a socket. (bind)
         */
         if (bind( sock, (struct sockaddr FAR *) &local_sin, sizeof(local_sin)) == SOCKET_ERROR) {
            sprintf(szBuff, "%d is the error", WSAGetLastError());

            MessageBox(hWnd, szBuff, "bind(sock) failed", MB_OK);
            closesocket( sock );
            break;
         }

         if (listen( sock, MAX_PENDING_CONNECTS ) < 0) {
            sprintf(szBuff, "%d is the error", WSAGetLastError());

            MessageBox(hWnd, szBuff, "listen(sock) failed", MB_OK);
            break;
         }

         /*
         *   Send window a WSA_ACCEPT when something is trying to connect.
         */
         if ((status = WSAAsyncSelect( sock, hWnd, WSA_ACCEPT, FD_ACCEPT)) > 0) {
            wsprintf( szBuff, "%d (0x%x)");
            MessageBox( hWnd, "Error on WSAAsyncSelect()", szBuff, MB_OK);
            SetWindowText( hWnd, "Async listen cancelled");
            closesocket( sock );
         }

      }
      break;   /* IDM_ALISTEN */

      /*
      *   Cancel an asynchronous call.
      */
      case IDM_CANCEL:
         WSAAsyncSelect( sock, hWnd, 0, 0);
         SetWindowText( hWnd, "Async Listen cancelled..");

         /*
         *   Disable/enable proper menu items.
         */
         EnableMenuItem(GetMenu( hWnd ), IDM_HOSTNAME, MF_ENABLED);
         EnableMenuItem(GetMenu( hWnd ), IDM_LISTEN, MF_ENABLED);
         EnableMenuItem(GetMenu( hWnd ), IDM_CONNECT, MF_ENABLED);
         EnableMenuItem(GetMenu( hWnd ), IDM_ALISTEN, MF_ENABLED);
         EnableMenuItem(GetMenu( hWnd ), IDM_TLISTEN, MF_ENABLED);
         EnableMenuItem(GetMenu( hWnd ), IDM_CANCEL, MF_GRAYED);
         EnableMenuItem(GetMenu( hWnd ), IDM_SENDTCP, MF_GRAYED);
         EnableMenuItem(GetMenu( hWnd ), IDM_ACCEPTEX, MF_ENABLED);
         EnableMenuItem(GetMenu( hWnd ), IDM_CONNECTANDSEND, MF_ENABLED);

         break;   /* IDM_CANCEL */

      /*
      * Listen in the main thread -- spawn and accept two network
      *  connections inside two threads.
      */
      case IDM_TLISTEN:
      {
         static THREADPACK tp;

         sock = socket( AF_INET, SOCK_STREAM, 0);
         if (sock == INVALID_SOCKET) {
            MessageBox(hWnd, "socket() failed", "Error", MB_OK);
            closesocket(sock);
            break;
         }

         /*
         *   Retrieve the IP address and TCP Port number
         */

         if (!FillAddr(hWnd, &local_sin, FALSE ))
            break;

         /*
         *   Disable/enable proper menu items.
         */
         EnableMenuItem(GetMenu( hWnd ), IDM_HOSTNAME, MF_GRAYED);
         EnableMenuItem(GetMenu( hWnd ), IDM_LISTEN, MF_GRAYED);
         EnableMenuItem(GetMenu( hWnd ), IDM_ALISTEN, MF_GRAYED);
         EnableMenuItem(GetMenu( hWnd ), IDM_TLISTEN, MF_GRAYED);
         EnableMenuItem(GetMenu( hWnd ), IDM_CONNECT, MF_GRAYED);
         EnableMenuItem(GetMenu( hWnd ), IDM_SENDTCP, MF_GRAYED);
         EnableMenuItem(GetMenu( hWnd ), IDM_ACCEPTEX, MF_GRAYED);
         EnableMenuItem(GetMenu( hWnd ), IDM_CONNECTANDSEND, MF_GRAYED);

         SetWindowText( hWnd, "Waiting for connection..");

         /*
         *   Associate an address with a socket. (bind)
         */
         if (bind( sock, (struct sockaddr FAR *) &local_sin, sizeof(local_sin)) == SOCKET_ERROR) {
            sprintf(szBuff, "%d is the error", WSAGetLastError());

            MessageBox(hWnd, szBuff, "bind(sock) failed", MB_OK);
            break;
         }

         if (listen( sock, MAX_PENDING_CONNECTS ) < 0) {
            sprintf(szBuff, "%d is the error", WSAGetLastError());

            MessageBox(hWnd, szBuff, "listen(sock) failed", MB_OK);
            break;
         }

         tp.nThread = 0;
         tp.hWnd = hWnd;

         _beginthread(AcceptThreadProc, 0, &tp);

         }
         break;   /* IDM_TLISTEN */

      /*
      *   Display host information.
      */
      case IDM_HOSTNAME:

         /*
         *  Prompt the user and retrieve the text name of the host.
         */
         status = DialogBox(hInst,
            "HOSTNAME",
            hWnd,
            GetHostName);

         if (status == TRUE) {   /* If user hit "OK" .. */

         /*
         *   Get the host information
         */

         if ((phe = gethostbyname( szBuff )) == NULL) {
            MessageBox(hWnd, "gethostbyname() failed", "Error", MB_OK);
            break;
         }
         else {

         /*
         *   Display the host information ..
         */
            DialogBox(hInst,
               "DISPLAYHOST",
               hWnd,
               DisplayHostEnt);
         }
      }
      break;   /* IDM_HOSTNAME */
      /*
      *   Send a message to (via TCP connection) to remote host.
      */
      case IDM_SENDTCP:
         DialogBox(hInst,                /* current instance         */
            "GetString",                 /* resource to use          */
            hWnd,                        /* parent handle            */
            GetSendString);              /* instance address         */

        /*
        *   Assumption -- The GetString dialog box proc fills the global
        *   string buffer, szBuff, with the desired string to send.
        */
        send(sock, szBuff, strlen(szBuff), NO_FLAGS_SET );

        break;   /* IDM_SENDTCP */

      case IDM_ABOUT:
         DialogBox(hInst,                /* current instance         */
            "AboutBox",                  /* resource to use          */
            hWnd,                        /* parent handle            */
            About);                      /* About() instance address */

         break;   /* IDM_ABOUT */

      default:
         /* Lets Windows process it          */
         return (DefWindowProc(hWnd, message, wParam, lParam));
         break;
      }
   break;

   /*
   *   Clean up.  Takes care of any open socket descriptors.
   */
   case WM_DESTROY:
      WSACleanup();
      PostQuitMessage(0);
      break;

   default:                       /* Passes it on if unproccessed    */
      return (DefWindowProc(hWnd, message, wParam, lParam));

   }
   return (0);

}


WSOCK.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 WSA_ACCEPT   (WM_USER + 0)
#define WSA_READ     (WM_USER + 1)

#define IDM_ABOUT           100
#define IDM_HOSTNAME        102
#define IDM_LISTEN          103
#define IDM_CONNECT         104
#define IDM_ALISTEN         105
#define IDM_CANCEL          106
#define IDM_TLISTEN         107
#define IDM_SENDTCP         108
#define IDM_CONNECTANDSEND  109
#define IDM_ACCEPTEX        110

/*
*   Structure passed into the accept thread
*/
typedef struct {
   int    nThread;  // Thread number.
   HWND  hWnd;
   } THREADPACK, *PTHREADPACK;

BOOL InitApplication(HANDLE);
BOOL InitInstance(HANDLE, int);
void SetConnectMenus( HWND );
LONG APIENTRY MainWndProc(HWND, UINT, UINT, LONG);
LRESULT APIENTRY About(HWND, UINT, UINT, LONG);
LRESULT APIENTRY GetHostName(HWND, UINT, UINT, LONG);
LRESULT APIENTRY DisplayHostEnt(HWND, UINT, UINT, LONG);
LRESULT APIENTRY GetTcpPort(HWND, UINT, UINT, LONG);
LRESULT APIENTRY GetSendString(HWND, UINT, UINT, LONG);

/* GLOBAL VARIABLES ======================================*/
PHOSTENT phe;

#define IDD_EDIT                    201
#define IDD_TEXT                    202
#define IDD_ALIAS                   203
#define IDD_HOSTNAME                206
#define IDD_LBALIAS                                             207
#define IDD_LBADDR                                              208