// This code is a part of the Telephony Framework C++ Library.
// Copyright (C) 1997 Chris Sells. All rights reserved.
// TfxUtil.cpp: TFX utility function implementations

#include "stdafx.h"
#include "TfxUtil.h"

#ifdef _DEBUG

void TfxResultTrace(
    TRESULT     tr,
    LPCSTR      szName,
    TREQUEST    nRequestID) // = 0
{
    struct EE { TRESULT tr; LPCSTR psz; };
    static EE rgErrors[] =
    {
#define EE(e) {e, #e}
        EE(TAPIERR_CONNECTED),
        EE(TAPIERR_DROPPED),
        EE(TAPIERR_NOREQUESTRECIPIENT),
        EE(TAPIERR_REQUESTQUEUEFULL),
        EE(TAPIERR_INVALDESTADDRESS),
        EE(TAPIERR_INVALWINDOWHANDLE),
        EE(TAPIERR_INVALDEVICECLASS),
        EE(TAPIERR_INVALDEVICEID),
        EE(TAPIERR_DEVICECLASSUNAVAIL),
        EE(TAPIERR_DEVICEIDUNAVAIL),
        EE(TAPIERR_DEVICEINUSE),
        EE(TAPIERR_DESTBUSY),
        EE(TAPIERR_DESTNOANSWER),
        EE(TAPIERR_DESTUNAVAIL),
        EE(TAPIERR_UNKNOWNWINHANDLE),
        EE(TAPIERR_UNKNOWNREQUESTID),
        EE(TAPIERR_REQUESTFAILED),
        EE(TAPIERR_REQUESTCANCELLED),
        EE(TAPIERR_INVALPOINTER),
        EE(LINEERR_ALLOCATED),
        EE(LINEERR_BADDEVICEID),
        EE(LINEERR_BEARERMODEUNAVAIL),
        EE(LINEERR_CALLUNAVAIL),
        EE(LINEERR_COMPLETIONOVERRUN),
        EE(LINEERR_CONFERENCEFULL),
        EE(LINEERR_DIALBILLING),
        EE(LINEERR_DIALDIALTONE),
        EE(LINEERR_DIALPROMPT),
        EE(LINEERR_DIALQUIET),
        EE(LINEERR_INCOMPATIBLEAPIVERSION),
        EE(LINEERR_INCOMPATIBLEEXTVERSION),
        EE(LINEERR_INIFILECORRUPT),
        EE(LINEERR_INUSE),
        EE(LINEERR_INVALADDRESS),
        EE(LINEERR_INVALADDRESSID),
        EE(LINEERR_INVALADDRESSMODE),
        EE(LINEERR_INVALADDRESSSTATE),
        EE(LINEERR_INVALAPPHANDLE),
        EE(LINEERR_INVALAPPNAME),
        EE(LINEERR_INVALBEARERMODE),
        EE(LINEERR_INVALCALLCOMPLMODE),
        EE(LINEERR_INVALCALLHANDLE),
        EE(LINEERR_INVALCALLPARAMS),
        EE(LINEERR_INVALCALLPRIVILEGE),
        EE(LINEERR_INVALCALLSELECT),
        EE(LINEERR_INVALCALLSTATE),
        EE(LINEERR_INVALCALLSTATELIST),
        EE(LINEERR_INVALCARD),
        EE(LINEERR_INVALCOMPLETIONID),
        EE(LINEERR_INVALCONFCALLHANDLE),
        EE(LINEERR_INVALCONSULTCALLHANDLE),
        EE(LINEERR_INVALCOUNTRYCODE),
        EE(LINEERR_INVALDEVICECLASS),
        EE(LINEERR_INVALDEVICEHANDLE),
        EE(LINEERR_INVALDIALPARAMS),
        EE(LINEERR_INVALDIGITLIST),
        EE(LINEERR_INVALDIGITMODE),
        EE(LINEERR_INVALDIGITS),
        EE(LINEERR_INVALEXTVERSION),
        EE(LINEERR_INVALGROUPID),
        EE(LINEERR_INVALLINEHANDLE),
        EE(LINEERR_INVALLINESTATE),
        EE(LINEERR_INVALLOCATION),
        EE(LINEERR_INVALMEDIALIST),
        EE(LINEERR_INVALMEDIAMODE),
        EE(LINEERR_INVALMESSAGEID),
        EE(LINEERR_INVALPARAM),
        EE(LINEERR_INVALPARKID),
        EE(LINEERR_INVALPARKMODE),
        EE(LINEERR_INVALPOINTER),
        EE(LINEERR_INVALPRIVSELECT),
        EE(LINEERR_INVALRATE),
        EE(LINEERR_INVALREQUESTMODE),
        EE(LINEERR_INVALTERMINALID),
        EE(LINEERR_INVALTERMINALMODE),
        EE(LINEERR_INVALTIMEOUT),
        EE(LINEERR_INVALTONE),
        EE(LINEERR_INVALTONELIST),
        EE(LINEERR_INVALTONEMODE),
        EE(LINEERR_INVALTRANSFERMODE),
        EE(LINEERR_LINEMAPPERFAILED),
        EE(LINEERR_NOCONFERENCE),
        EE(LINEERR_NODEVICE),
        EE(LINEERR_NODRIVER),
        EE(LINEERR_NOMEM),
        EE(LINEERR_NOREQUEST),
        EE(LINEERR_NOTOWNER),
        EE(LINEERR_NOTREGISTERED),
        EE(LINEERR_OPERATIONFAILED),
        EE(LINEERR_OPERATIONUNAVAIL),
        EE(LINEERR_RATEUNAVAIL),
        EE(LINEERR_RESOURCEUNAVAIL),
        EE(LINEERR_REQUESTOVERRUN),
        EE(LINEERR_STRUCTURETOOSMALL),
        EE(LINEERR_TARGETNOTFOUND),
        EE(LINEERR_TARGETSELF),
        EE(LINEERR_UNINITIALIZED),
        EE(LINEERR_USERUSERINFOTOOBIG),
        EE(LINEERR_REINIT),
        EE(LINEERR_ADDRESSBLOCKED),
        EE(LINEERR_BILLINGREJECTED),
        EE(LINEERR_INVALFEATURE),
        EE(LINEERR_NOMULTIPLEINSTANCE),
        EE(PHONEERR_ALLOCATED),
        EE(PHONEERR_BADDEVICEID),
        EE(PHONEERR_INCOMPATIBLEAPIVERSION),
        EE(PHONEERR_INCOMPATIBLEEXTVERSION),
        EE(PHONEERR_INIFILECORRUPT),
        EE(PHONEERR_INUSE),
        EE(PHONEERR_INVALAPPHANDLE),
        EE(PHONEERR_INVALAPPNAME),
        EE(PHONEERR_INVALBUTTONLAMPID),
        EE(PHONEERR_INVALBUTTONMODE),
        EE(PHONEERR_INVALBUTTONSTATE),
        EE(PHONEERR_INVALDATAID),
        EE(PHONEERR_INVALDEVICECLASS),
        EE(PHONEERR_INVALEXTVERSION),
        EE(PHONEERR_INVALHOOKSWITCHDEV),
        EE(PHONEERR_INVALHOOKSWITCHMODE),
        EE(PHONEERR_INVALLAMPMODE),
        EE(PHONEERR_INVALPARAM),
        EE(PHONEERR_INVALPHONEHANDLE),
        EE(PHONEERR_INVALPHONESTATE),
        EE(PHONEERR_INVALPOINTER),
        EE(PHONEERR_INVALPRIVILEGE),
        EE(PHONEERR_INVALRINGMODE),
        EE(PHONEERR_NODEVICE),
        EE(PHONEERR_NODRIVER),
        EE(PHONEERR_NOMEM),
        EE(PHONEERR_NOTOWNER),
        EE(PHONEERR_OPERATIONFAILED),
        EE(PHONEERR_OPERATIONUNAVAIL),
        EE(PHONEERR_RESOURCEUNAVAIL),
        EE(PHONEERR_REQUESTOVERRUN),
        EE(PHONEERR_STRUCTURETOOSMALL),
        EE(PHONEERR_UNINITIALIZED),
        EE(PHONEERR_REINIT),
#undef  EE
    };

    // Failed function call or reply
    if( TFAILED(tr) )
    {
        // Get error from tapi32.dll
        char    szTapiError[256] = "<unknown>";
        FormatMessageA(FORMAT_MESSAGE_FROM_HMODULE,
                       GetModuleHandle("tapi32.dll"),
                       TAPIERROR_FORMATMESSAGE(tr),
                       0,
                       szTapiError,
                       sizeof(szTapiError),
                       0);

        for( int i = 0; i < DIM(rgErrors); i++ )
        {
            if( rgErrors[i].tr == tr )
            {
                if( nRequestID )
                {
                    TRACE("TAPI %s (0x%x) error= %s: %s\n", szName, nRequestID, rgErrors[i].psz, szTapiError);
                }
                else
                {
                    TRACE3("TAPI %s error= %s: %s\n", szName, rgErrors[i].psz, szTapiError);
                }

                return;
            }
        }
        TRACE3("TAPI %s error= 0x%x: %s\n", szName, tr, szTapiError);
    }
    // Pending function call
    else if( TPENDING(tr) )
    {
        TRACE2("TAPI %s request= 0x%x\n", szName, tr);
    }
    // Succeeded request
    else if( nRequestID )
    {
        TRACE1("TAPI 0x%x success\n", nRequestID);
    }
}

void TfxEventTrace(DWORD nDevice, DWORD nMsg, DWORD nParam1, DWORD nParam2, DWORD nParam3)
{
    struct EventEntry { DWORD nMsg; DWORD nSubMsg; LPCSTR pszMsg; LPCSTR pszSubMsg; };
    static EventEntry rgEvents[] =
    {
#define EEM(nMsg) {nMsg, 0, #nMsg, 0}
#define EES(nMsg, nSubMsg) {nMsg, nSubMsg, #nMsg, #nSubMsg}

    EES(LINE_ADDRESSSTATE, LINEADDRESSSTATE_OTHER),
    EES(LINE_ADDRESSSTATE, LINEADDRESSSTATE_DEVSPECIFIC),
    EES(LINE_ADDRESSSTATE, LINEADDRESSSTATE_INUSEZERO),
    EES(LINE_ADDRESSSTATE, LINEADDRESSSTATE_INUSEONE),
    EES(LINE_ADDRESSSTATE, LINEADDRESSSTATE_INUSEMANY),
    EES(LINE_ADDRESSSTATE, LINEADDRESSSTATE_NUMCALLS),
    EES(LINE_ADDRESSSTATE, LINEADDRESSSTATE_FORWARD),
    EES(LINE_ADDRESSSTATE, LINEADDRESSSTATE_TERMINALS),
    EES(LINE_ADDRESSSTATE, LINEADDRESSSTATE_CAPSCHANGE),

    EES(LINE_CALLINFO, LINECALLINFOSTATE_OTHER),
    EES(LINE_CALLINFO, LINECALLINFOSTATE_DEVSPECIFIC),
    EES(LINE_CALLINFO, LINECALLINFOSTATE_BEARERMODE),
    EES(LINE_CALLINFO, LINECALLINFOSTATE_RATE),
    EES(LINE_CALLINFO, LINECALLINFOSTATE_MEDIAMODE),
    EES(LINE_CALLINFO, LINECALLINFOSTATE_APPSPECIFIC),
    EES(LINE_CALLINFO, LINECALLINFOSTATE_CALLID),
    EES(LINE_CALLINFO, LINECALLINFOSTATE_RELATEDCALLID),
    EES(LINE_CALLINFO, LINECALLINFOSTATE_ORIGIN),
    EES(LINE_CALLINFO, LINECALLINFOSTATE_REASON),
    EES(LINE_CALLINFO, LINECALLINFOSTATE_COMPLETIONID),
    EES(LINE_CALLINFO, LINECALLINFOSTATE_NUMOWNERINCR),
    EES(LINE_CALLINFO, LINECALLINFOSTATE_NUMOWNERDECR),
    EES(LINE_CALLINFO, LINECALLINFOSTATE_NUMMONITORS),
    EES(LINE_CALLINFO, LINECALLINFOSTATE_TRUNK),
    EES(LINE_CALLINFO, LINECALLINFOSTATE_CALLERID),
    EES(LINE_CALLINFO, LINECALLINFOSTATE_CALLEDID),
    EES(LINE_CALLINFO, LINECALLINFOSTATE_CONNECTEDID),
    EES(LINE_CALLINFO, LINECALLINFOSTATE_REDIRECTIONID),
    EES(LINE_CALLINFO, LINECALLINFOSTATE_REDIRECTINGID),
    EES(LINE_CALLINFO, LINECALLINFOSTATE_DISPLAY),
    EES(LINE_CALLINFO, LINECALLINFOSTATE_USERUSERINFO),
    EES(LINE_CALLINFO, LINECALLINFOSTATE_HIGHLEVELCOMP),
    EES(LINE_CALLINFO, LINECALLINFOSTATE_LOWLEVELCOMP),
    EES(LINE_CALLINFO, LINECALLINFOSTATE_CHARGINGINFO),
    EES(LINE_CALLINFO, LINECALLINFOSTATE_TERMINAL),
    EES(LINE_CALLINFO, LINECALLINFOSTATE_DIALPARAMS),
    EES(LINE_CALLINFO, LINECALLINFOSTATE_MONITORMODES),
#if (TAPI_CURRENT_VERSION >= 0x00020000)
    EES(LINE_CALLINFO, LINECALLINFOSTATE_TREATMENT),
    EES(LINE_CALLINFO, LINECALLINFOSTATE_QOS),
    EES(LINE_CALLINFO, LINECALLINFOSTATE_CALLDATA),
#endif

    EES(LINE_CALLSTATE, LINECALLSTATE_IDLE),
    EES(LINE_CALLSTATE, LINECALLSTATE_OFFERING),
    EES(LINE_CALLSTATE, LINECALLSTATE_ACCEPTED),
    EES(LINE_CALLSTATE, LINECALLSTATE_DIALTONE),
    EES(LINE_CALLSTATE, LINECALLSTATE_DIALING),
    EES(LINE_CALLSTATE, LINECALLSTATE_RINGBACK),
    EES(LINE_CALLSTATE, LINECALLSTATE_BUSY),
    EES(LINE_CALLSTATE, LINECALLSTATE_SPECIALINFO),
    EES(LINE_CALLSTATE, LINECALLSTATE_CONNECTED),
    EES(LINE_CALLSTATE, LINECALLSTATE_PROCEEDING),
    EES(LINE_CALLSTATE, LINECALLSTATE_ONHOLD),
    EES(LINE_CALLSTATE, LINECALLSTATE_CONFERENCED),
    EES(LINE_CALLSTATE, LINECALLSTATE_ONHOLDPENDCONF),
    EES(LINE_CALLSTATE, LINECALLSTATE_ONHOLDPENDTRANSFER),
    EES(LINE_CALLSTATE, LINECALLSTATE_DISCONNECTED),
    EES(LINE_CALLSTATE, LINECALLSTATE_UNKNOWN),

    EEM(LINE_CLOSE),
    EEM(LINE_DEVSPECIFIC),
    EEM(LINE_DEVSPECIFICFEATURE),

    EES(LINE_GATHERDIGITS, LINEGATHERTERM_BUFFERFULL),
    EES(LINE_GATHERDIGITS, LINEGATHERTERM_TERMDIGIT),
    EES(LINE_GATHERDIGITS, LINEGATHERTERM_FIRSTTIMEOUT),
    EES(LINE_GATHERDIGITS, LINEGATHERTERM_INTERTIMEOUT),
    EES(LINE_GATHERDIGITS, LINEGATHERTERM_CANCEL),

    EES(LINE_GENERATE, LINEGENERATETERM_DONE),
    EES(LINE_GENERATE, LINEGENERATETERM_CANCEL),

    EES(LINE_LINEDEVSTATE, LINEDEVSTATE_OTHER),
    EES(LINE_LINEDEVSTATE, LINEDEVSTATE_RINGING),
    EES(LINE_LINEDEVSTATE, LINEDEVSTATE_CONNECTED),
    EES(LINE_LINEDEVSTATE, LINEDEVSTATE_DISCONNECTED),
    EES(LINE_LINEDEVSTATE, LINEDEVSTATE_MSGWAITON),
    EES(LINE_LINEDEVSTATE, LINEDEVSTATE_MSGWAITOFF),
    EES(LINE_LINEDEVSTATE, LINEDEVSTATE_INSERVICE),
    EES(LINE_LINEDEVSTATE, LINEDEVSTATE_OUTOFSERVICE),
    EES(LINE_LINEDEVSTATE, LINEDEVSTATE_MAINTENANCE),
    EES(LINE_LINEDEVSTATE, LINEDEVSTATE_OPEN),
    EES(LINE_LINEDEVSTATE, LINEDEVSTATE_CLOSE),
    EES(LINE_LINEDEVSTATE, LINEDEVSTATE_NUMCALLS),
    EES(LINE_LINEDEVSTATE, LINEDEVSTATE_NUMCOMPLETIONS),
    EES(LINE_LINEDEVSTATE, LINEDEVSTATE_TERMINALS),
    EES(LINE_LINEDEVSTATE, LINEDEVSTATE_ROAMMODE),
    EES(LINE_LINEDEVSTATE, LINEDEVSTATE_BATTERY),
    EES(LINE_LINEDEVSTATE, LINEDEVSTATE_SIGNAL),
    EES(LINE_LINEDEVSTATE, LINEDEVSTATE_DEVSPECIFIC),
    EES(LINE_LINEDEVSTATE, LINEDEVSTATE_REINIT),
    EES(LINE_LINEDEVSTATE, LINEDEVSTATE_LOCK),
    EES(LINE_LINEDEVSTATE, LINEDEVSTATE_CAPSCHANGE),
    EES(LINE_LINEDEVSTATE, LINEDEVSTATE_CONFIGCHANGE),
    EES(LINE_LINEDEVSTATE, LINEDEVSTATE_TRANSLATECHANGE),
    EES(LINE_LINEDEVSTATE, LINEDEVSTATE_COMPLCANCEL),
    EES(LINE_LINEDEVSTATE, LINEDEVSTATE_REMOVED),

    EEM(LINE_MONITORDIGITS),

    EES(LINE_MONITORMEDIA, LINEMEDIAMODE_UNKNOWN),
    EES(LINE_MONITORMEDIA, LINEMEDIAMODE_INTERACTIVEVOICE),
    EES(LINE_MONITORMEDIA, LINEMEDIAMODE_AUTOMATEDVOICE),
    EES(LINE_MONITORMEDIA, LINEMEDIAMODE_DATAMODEM),
    EES(LINE_MONITORMEDIA, LINEMEDIAMODE_G3FAX),
    EES(LINE_MONITORMEDIA, LINEMEDIAMODE_TDD),
    EES(LINE_MONITORMEDIA, LINEMEDIAMODE_G4FAX),
    EES(LINE_MONITORMEDIA, LINEMEDIAMODE_DIGITALDATA),
    EES(LINE_MONITORMEDIA, LINEMEDIAMODE_TELETEX),
    EES(LINE_MONITORMEDIA, LINEMEDIAMODE_VIDEOTEX),
    EES(LINE_MONITORMEDIA, LINEMEDIAMODE_TELEX),
    EES(LINE_MONITORMEDIA, LINEMEDIAMODE_MIXED),
    EES(LINE_MONITORMEDIA, LINEMEDIAMODE_ADSI),
    EES(LINE_MONITORMEDIA, LINEMEDIAMODE_VOICEVIEW),

    EEM(LINE_MONITORTONE),
    EEM(LINE_REPLY),

    EES(LINE_REQUEST, LINEREQUESTMODE_MAKECALL),
    EES(LINE_REQUEST, LINEREQUESTMODE_MEDIACALL),
    EES(LINE_REQUEST, LINEREQUESTMODE_DROP),

    EEM(PHONE_BUTTON),
    EEM(PHONE_CLOSE),
    EEM(PHONE_DEVSPECIFIC),
    EEM(PHONE_REPLY),

    EES(PHONE_STATE, PHONESTATE_OTHER),
    EES(PHONE_STATE, PHONESTATE_CONNECTED),
    EES(PHONE_STATE, PHONESTATE_DISCONNECTED),
    EES(PHONE_STATE, PHONESTATE_OWNER),
    EES(PHONE_STATE, PHONESTATE_MONITORS),
    EES(PHONE_STATE, PHONESTATE_DISPLAY),
    EES(PHONE_STATE, PHONESTATE_LAMP),
    EES(PHONE_STATE, PHONESTATE_RINGMODE),
    EES(PHONE_STATE, PHONESTATE_RINGVOLUME),
    EES(PHONE_STATE, PHONESTATE_HANDSETHOOKSWITCH),
    EES(PHONE_STATE, PHONESTATE_HANDSETVOLUME),
    EES(PHONE_STATE, PHONESTATE_HANDSETGAIN),
    EES(PHONE_STATE, PHONESTATE_SPEAKERHOOKSWITCH),
    EES(PHONE_STATE, PHONESTATE_SPEAKERVOLUME),
    EES(PHONE_STATE, PHONESTATE_SPEAKERGAIN),
    EES(PHONE_STATE, PHONESTATE_HEADSETHOOKSWITCH),
    EES(PHONE_STATE, PHONESTATE_HEADSETVOLUME),
    EES(PHONE_STATE, PHONESTATE_HEADSETGAIN),
    EES(PHONE_STATE, PHONESTATE_SUSPEND),
    EES(PHONE_STATE, PHONESTATE_RESUME),
    EES(PHONE_STATE, PHONESTATE_DEVSPECIFIC),
    EES(PHONE_STATE, PHONESTATE_REINIT),
    EES(PHONE_STATE, PHONESTATE_CAPSCHANGE),
    EES(PHONE_STATE, PHONESTATE_REMOVED),

    EEM(LINE_CREATE),
    EEM(PHONE_CREATE),

#if (TAPI_CURRENT_VERSION >= 0x00020000)
    EEM(LINE_AGENTSPECIFIC),
    EEM(LINE_AGENTSTATUS),
    EEM(LINE_APPNEWCALL),
    EEM(LINE_PROXYREQUEST),
    EEM(LINE_REMOVE),
    EEM(PHONE_REMOVE),
#endif

#undef  EEM
#undef  EES
    };

    for( int i = 0; i < DIM(rgEvents); i++ )
    {
        if( nMsg == rgEvents[i].nMsg && 
            (rgEvents[i].nSubMsg == 0 || nParam1 == rgEvents[i].nSubMsg) )
        {
            TRACE("TAPI device= %x  event= %s%s%s\n",
                  nDevice, rgEvents[i].pszMsg,
                  (rgEvents[i].nSubMsg ? " -- " : ""),
                  (rgEvents[i].nSubMsg ? rgEvents[i].pszSubMsg : ""));
            return;
        }
    }

    TRACE("TAPI device= %x  event= %x\n", nDevice, nMsg);
}

#endif  // _DEBUG

///////////////////////////////////////////////////////////////////
// Special Win95 TAPI Recovery

#define MAX_DEC_INT_SIZE 10 // 10 digits
static LPCSTR   g_pszRecoverKey = "Software\\Tfx\\Win95TapiRecover";

static
bool IsProcessActive(DWORD pid)
{
    bool    b = false;
    HANDLE  h = 0;
    DWORD   n = 0;

    if( (h = OpenProcess(STANDARD_RIGHTS_REQUIRED, FALSE, pid)) &&
        GetExitCodeProcess(h, &n) &&
        (n == STILL_ACTIVE) )
    {
        b = true;
    }

    if( h ) CloseHandle(h);

    return b;
}


void TapiRecover::PreInitialize()
{
    // If we're keeping HLINEAPPs/HLINEPHONEs and HWNDs in 10 digits,
    // they better be DWORDs
    ASSERT(sizeof(HLINEAPP) == sizeof(DWORD));
    ASSERT(sizeof(HPHONEAPP) == sizeof(DWORD));
    ASSERT(sizeof(HWND) == sizeof(DWORD));

    // Compose key
    CString sKey = g_pszRecoverKey;
    sKey += '\\';
    sKey += SubKeyName();

    // Enumerate stored keys looking for dead ones
    HKEY    hkey;
    if( RegOpenKey(HKEY_CURRENT_USER, sKey, &hkey) == ERROR_SUCCESS )
    {
        // Subkey name is PID
        // Note: Using PIDs as unique IDs would be a problem under NT
        // as PIDs are often reused, but shouldn't be a problem under
        // Win95 (the only place TAPI 1.4 runs) because Win95 doesn't
        // reuse PIDs often.
        DWORD   nSubKeys = 0;
        RegQueryInfoKey (hkey, 0, 0, 0, &nSubKeys, 0, 0, 0, 0, 0, 0, 0);

        // Work backwards thru the keys so that deleting them along the way is OK
        char    szPid[MAX_DEC_INT_SIZE+1];
        for( DWORD nIndex = nSubKeys - 1;
             (nIndex >= 0) && (RegEnumKey(hkey, nIndex, szPid, DIM(szPid)) == ERROR_SUCCESS);
             nIndex-- )
        {
            char    szHapp[MAX_DEC_INT_SIZE+1];
            LONG    cb = sizeof(szHapp);
            if( RegQueryValue(hkey, szPid, szHapp, &cb) == ERROR_SUCCESS &&
                cb == sizeof(szHapp) )
            {
                // Extract PID and HxxxAPP
                DWORD   pid = (DWORD)atol(szPid);
                void*   happ = (void*)atol(szHapp);

                // If the PID is no longer running,
                // shutdown TAPI for that process and remove the key
                if( !IsProcessActive(pid) )
                {
                    ShutdownApp(happ);
                    TRACE0("The Win95 TAPI Recovery just saved you a restart.\n");
                    RegDeleteKey(hkey, szPid);
                }
            }
        }

        RegCloseKey(hkey);
    }
}

void TapiRecover::Initialize(void* happ)
{
    // Compose key
    CString sKey;
    sKey.Format("%s\\%s\\%0*lu",
                g_pszRecoverKey,
                SubKeyName(),
                MAX_DEC_INT_SIZE,
                GetCurrentProcessId());

    // Compose value
    CString sValue;
    sValue.Format("%0*lu", MAX_DEC_INT_SIZE, happ);

    // Add key
    RegSetValue(HKEY_CURRENT_USER, sKey, REG_SZ, sValue, sValue.GetLength() + 1);
}

void TapiRecover::Shutdown()
{
    // Compose key
    CString sKey;
    sKey.Format("%s\\%s\\%0*lu",
                g_pszRecoverKey,
                SubKeyName(),
                MAX_DEC_INT_SIZE,
                GetCurrentProcessId());
    
    // Delete key
    RegDeleteKey(HKEY_CURRENT_USER, sKey);
}
